diff --git a/components/eamxx/cime_config/namelist_defaults_scream.xml b/components/eamxx/cime_config/namelist_defaults_scream.xml
index 9a7975f01ba..a1435b03de1 100644
--- a/components/eamxx/cime_config/namelist_defaults_scream.xml
+++ b/components/eamxx/cime_config/namelist_defaults_scream.xml
@@ -268,6 +268,64 @@ be lost if SCREAM_HACK_XML is not enabled.
+
+
+
+
+ true
+ true
+ true
+ true
+
+ 172800.0
+ 3.0E-008
+ 4
+ 193.0
+ 20100101
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/linoz/ne30pg2/linoz1850-2015_2010JPL_CMIP6_10deg_58km_ne30pg2_c20240724.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/linoz/ne4pg2/linoz1850-2015_2010JPL_CMIP6_10deg_58km_ne4pg2_c20240724.nc
+
+ 20150101
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/invariants/ne30pg2/oxid_1.9x2.5_L26_1850-2015_ne30pg2_c20241009.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/invariants/ne4pg2/oxid_1.9x2.5_L26_1850-2015_ne4pg2_c20241009.nc
+ 20100101
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/linoz/Linoz_Chlorine_Loading_CMIP6_0003-2017_c20171114.nc
+
+ ${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
+
+ ${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/maps/map_ne30pg2_to_ne120pg2_20231201.nc
+ ${DIN_LOC_ROOT}/atm/scream/maps/map_ne30pg2_to_ne256pg2_20231201.nc
+ ${DIN_LOC_ROOT}/atm/scream/maps/map_ne30pg2_to_ne512pg2_20231201.nc
+ ${DIN_LOC_ROOT}/atm/scream/maps/map_ne30pg2_to_ne1024pg2_20231201.nc
+
+
+
${DIN_LOC_ROOT}/atm/scream/mam4xx/physprops/mam4_mode1_rrtmg_aeronetdust_c20240206.nc
@@ -290,7 +348,7 @@ be lost if SCREAM_HACK_XML is not enabled.
-
+
${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/surface/DMSflux.2010.ne30pg2_conserv.POPmonthlyClimFromACES4BGC_c20240816.nc
${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/surface/cmip6_mam4_so2_surf_ne30pg2_2010_clim_c20240816.nc
${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/surface/cmip6_mam4_bc_a4_surf_ne30pg2_2010_clim_c20240816.nc
@@ -300,8 +358,8 @@ be lost if SCREAM_HACK_XML is not enabled.
${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/surface/cmip6_mam4_pom_a4_surf_ne30pg2_2010_clim_c20240816.nc
${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/surface/cmip6_mam4_so4_a1_surf_ne30pg2_2010_clim_c20240816.nc
${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/surface/cmip6_mam4_so4_a2_surf_ne30pg2_2010_clim_c20240816.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
@@ -311,7 +369,7 @@ be lost if SCREAM_HACK_XML is not enabled.
${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/ne4pg2/surface/DMSflux.2010.ne4pg2_conserv.POPmonthlyClimFromACES4BGC_c20240814.nc
@@ -576,9 +634,9 @@ be lost if SCREAM_HACK_XML is not enabled.
0.0
0.0,0.0
- 2.6e-08
- 0.41417721820867320E-007
- 0.15100083211582764E+004
+ 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
@@ -646,7 +704,7 @@ be lost if SCREAM_HACK_XML is not enabled.
+ -->
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
new file mode 100644
index 00000000000..1d6757a5bd9
--- /dev/null
+++ b/components/eamxx/cime_config/testdefs/testmods_dirs/scream/mam4xx/aero_microphysics/shell_commands
@@ -0,0 +1,17 @@
+
+#!/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
+
+#------------------------------------------------------
+#Update IC file and add drydep 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/src/physics/mam/CMakeLists.txt b/components/eamxx/src/physics/mam/CMakeLists.txt
index 9874f79f9eb..fb6a48733f8 100644
--- a/components/eamxx/src/physics/mam/CMakeLists.txt
+++ b/components/eamxx/src/physics/mam/CMakeLists.txt
@@ -42,6 +42,7 @@ add_subdirectory(${EXTERNALS_SOURCE_DIR}/mam4xx ${CMAKE_BINARY_DIR}/externals/ma
# EAMxx mam4xx-based atmospheric processes
add_library(mam
eamxx_mam_microphysics_process_interface.cpp
+ ${SCREAM_BASE_DIR}/src/physics/rrtmgp/shr_orb_mod_c2f.F90
eamxx_mam_optics_process_interface.cpp
eamxx_mam_dry_deposition_process_interface.cpp
eamxx_mam_aci_process_interface.cpp
@@ -54,7 +55,7 @@ target_include_directories(mam PUBLIC
${EXTERNALS_SOURCE_DIR}/haero
${EXTERNALS_SOURCE_DIR}/mam4xx/src
)
-target_link_libraries(mam PUBLIC physics_share scream_share mam4xx haero)
+target_link_libraries(mam PUBLIC physics_share csm_share scream_share mam4xx haero)
#if (NOT SCREAM_LIB_ONLY)
# add_subdirectory(tests)
diff --git a/components/eamxx/src/physics/mam/eamxx_mam_aci_process_interface.cpp b/components/eamxx/src/physics/mam/eamxx_mam_aci_process_interface.cpp
index f2d58c54e44..449ba4a55d8 100644
--- a/components/eamxx/src/physics/mam/eamxx_mam_aci_process_interface.cpp
+++ b/components/eamxx/src/physics/mam/eamxx_mam_aci_process_interface.cpp
@@ -63,26 +63,22 @@ void MAMAci::set_grids(
// Define the different field layouts that will be used for this process
using namespace ShortFieldTagsNames;
- // Layout for 3D (2d horiz X 1d vertical) variables
- // mid points
- FieldLayout scalar3d_layout_mid{{COL, LEV}, {ncol_, nlev_}};
+ // Layout for 2D (2d horiz) variable
+ const FieldLayout scalar2d = grid_->get_2d_scalar_layout();
+
+ // Layout for 3D (2d horiz X 1d vertical) variable defined at mid-level and
// interfaces
- FieldLayout scalar3d_layout_int{{COL, ILEV}, {ncol_, nlev_ + 1}};
+ const FieldLayout scalar3d_mid = grid_->get_3d_scalar_layout(true);
+ const FieldLayout scalar3d_int = grid_->get_3d_scalar_layout(false);
// layout for 2D (1d horiz X 1d vertical) variable
FieldLayout scalar2d_layout_col{{COL}, {ncol_}};
- auto make_layout = [](const std::vector &extents,
- const std::vector &names) {
- std::vector tags(extents.size(), CMP);
- return FieldLayout(tags, extents, names);
- };
-
using namespace ekat::units;
- auto q_unit = kg / kg; // units of mass mixing ratios of tracers
- auto n_unit = 1 / kg; // units of number mixing ratios of tracers
+ 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
- auto nondim = ekat::units::Units::nondimensional();
+ constexpr auto nondim = ekat::units::Units::nondimensional();
// atmospheric quantities
// specific humidity [kg/kg]
@@ -101,28 +97,28 @@ void MAMAci::set_grids(
add_tracer("ni", grid_, n_unit);
// Temperature[K] at midpoints
- add_field("T_mid", scalar3d_layout_mid, K, grid_name);
+ add_field("T_mid", scalar3d_mid, K, grid_name);
// Vertical pressure velocity [Pa/s] at midpoints
- add_field("omega", scalar3d_layout_mid, Pa / s, grid_name);
+ add_field("omega", scalar3d_mid, Pa / s, grid_name);
// Total pressure [Pa] at midpoints
- add_field("p_mid", scalar3d_layout_mid, Pa, grid_name);
+ add_field("p_mid", scalar3d_mid, Pa, grid_name);
// Total pressure [Pa] at interfaces
- add_field("p_int", scalar3d_layout_int, Pa, grid_name);
+ add_field("p_int", scalar3d_int, Pa, grid_name);
// Layer thickness(pdel) [Pa] at midpoints
- add_field("pseudo_density", scalar3d_layout_mid, Pa, grid_name);
+ add_field("pseudo_density", scalar3d_mid, Pa, grid_name);
// planetary boundary layer height
add_field("pbl_height", scalar2d_layout_col, m, grid_name);
// cloud fraction [nondimensional] computed by eamxx_cld_fraction_process
- add_field("cldfrac_tot", scalar3d_layout_mid, nondim, grid_name);
+ add_field("cldfrac_tot", scalar3d_mid, nondim, grid_name);
- auto m2 = pow(m, 2);
- auto s2 = pow(s, 2);
+ constexpr auto m2 = pow(m, 2);
+ constexpr auto s2 = pow(s, 2);
// NOTE: w_variance im microp_aero.F90 in EAM is at "itim_old" dynamics time
// step. Since, we are using SE dycore, itim_old is 1 which is equivalent to
@@ -130,29 +126,30 @@ void MAMAci::set_grids(
// and we might need to revisit this.
// Vertical velocity variance at midpoints
- add_field("w_variance", scalar3d_layout_mid, m2 / s2, grid_name);
+ add_field("w_variance", scalar3d_mid, m2 / s2, grid_name);
// NOTE: "cldfrac_liq" is updated in SHOC. "cldfrac_liq" in C++ code is
// equivalent to "alst" in the shoc_intr.F90. In the C++ code, it is used as
// "shoc_cldfrac" and in the F90 code it is called "cloud_frac"
// Liquid stratiform cloud fraction at midpoints
- add_field("cldfrac_liq", scalar3d_layout_mid, nondim, grid_name);
+ add_field("cldfrac_liq", scalar3d_mid, nondim, grid_name);
// Previous value of liquid stratiform cloud fraction at midpoints
- add_field("cldfrac_liq_prev", scalar3d_layout_mid, nondim,
- grid_name);
+ add_field("cldfrac_liq_prev", scalar3d_mid, nondim, grid_name);
// Eddy diffusivity for heat
- add_field("eddy_diff_heat", scalar3d_layout_mid, m2 / s, grid_name);
+ add_field("eddy_diff_heat", scalar3d_mid, m2 / s, grid_name);
- // Layout for 4D (2d horiz X 1d vertical x number of modes) variables
- const int num_aero_modes = mam_coupling::num_aero_modes();
- FieldLayout scalar4d_layout_mid =
- make_layout({ncol_, num_aero_modes, nlev_}, {"COL", "NMODES", "LEV"});
+ // Number of modes
+ constexpr int nmodes = mam4::AeroConfig::num_modes();
+
+ // layout for 3D (ncol, nmodes, nlevs)
+ FieldLayout scalar3d_mid_nmodes =
+ grid_->get_3d_vector_layout(true, nmodes, "nmodes");
// dry diameter of aerosols [m]
- add_field("dgnum", scalar4d_layout_mid, m, grid_name);
+ add_field("dgnum", scalar3d_mid_nmodes, m, grid_name);
// ========================================================================
// Output from this whole process
@@ -171,8 +168,7 @@ void MAMAci::set_grids(
// NOT advected
const char *cld_nmr_field_name =
mam_coupling::cld_aero_nmr_field_name(mode);
- add_field(cld_nmr_field_name, scalar3d_layout_mid, n_unit,
- grid_name);
+ add_field(cld_nmr_field_name, scalar3d_mid, n_unit, grid_name);
for(int a = 0; a < mam_coupling::num_aero_species(); ++a) {
// (interstitial) aerosol tracers of interest: mass (q) mixing ratios
@@ -187,8 +183,7 @@ void MAMAci::set_grids(
const char *cld_mmr_field_name =
mam_coupling::cld_aero_mmr_field_name(mode, a);
if(strlen(cld_mmr_field_name) > 0) {
- add_field(cld_mmr_field_name, scalar3d_layout_mid, q_unit,
- grid_name);
+ add_field(cld_mmr_field_name, scalar3d_mid, q_unit, grid_name);
}
} // end for loop num species
} // end for loop for num modes
@@ -203,54 +198,52 @@ void MAMAci::set_grids(
// ------------------------------------------------------------------------
// number of activated aerosol for ice nucleation[#/kg]
- add_field("ni_activated", scalar3d_layout_mid, n_unit, grid_name);
+ add_field("ni_activated", scalar3d_mid, n_unit, grid_name);
// ------------------------------------------------------------------------
// Output from droplet activation process (dropmixnuc)
// ------------------------------------------------------------------------
// tendency in droplet number mixing ratio [#/kg/s]
- add_field("nc_nuceat_tend", scalar3d_layout_mid, n_unit / s,
- grid_name);
+ add_field("nc_nuceat_tend", scalar3d_mid, n_unit / s, grid_name);
// FIXME: [TEMPORARY]droplet number mixing ratio source tendency [#/kg/s]
- add_field("nsource", scalar3d_layout_mid, n_unit / s, grid_name);
+ add_field("nsource", scalar3d_mid, n_unit / s, grid_name);
// FIXME: [TEMPORARY]droplet number mixing ratio tendency due to mixing
// [#/kg/s]
- add_field("ndropmix", scalar3d_layout_mid, n_unit / s, grid_name);
+ add_field("ndropmix", scalar3d_mid, n_unit / s, grid_name);
// FIXME: [TEMPORARY]droplet number as seen by ACI [#/kg]
- add_field("nc_inp_to_aci", scalar3d_layout_mid, n_unit / s,
- grid_name);
- const auto cm_tmp = m / 100; // FIXME: [TEMPORARY] remove this
- const auto cm3 = cm_tmp * cm_tmp * cm_tmp; // FIXME: [TEMPORARY] remove this
+ add_field("nc_inp_to_aci", scalar3d_mid, n_unit / s, grid_name);
+ constexpr auto cm_tmp = m / 100; // FIXME: [TEMPORARY] remove this
+ constexpr auto cm3 = pow(cm_tmp, 3); // FIXME: [TEMPORARY] remove this
// FIXME: [TEMPORARY] remove the following ccn outputs
- add_field("ccn_0p02", scalar3d_layout_mid, cm3, grid_name);
- add_field("ccn_0p05", scalar3d_layout_mid, cm3, grid_name);
- add_field("ccn_0p1", scalar3d_layout_mid, cm3, grid_name);
- add_field("ccn_0p2", scalar3d_layout_mid, cm3, grid_name);
- add_field("ccn_0p5", scalar3d_layout_mid, cm3, grid_name);
- add_field("ccn_1p0", scalar3d_layout_mid, cm3, grid_name);
+ add_field("ccn_0p02", scalar3d_mid, cm3, grid_name);
+ add_field("ccn_0p05", scalar3d_mid, cm3, grid_name);
+ add_field("ccn_0p1", scalar3d_mid, cm3, grid_name);
+ add_field("ccn_0p2", scalar3d_mid, cm3, grid_name);
+ add_field("ccn_0p5", scalar3d_mid, cm3, grid_name);
+ add_field("ccn_1p0", scalar3d_mid, cm3, grid_name);
// ------------------------------------------------------------------------
// Output from hetrozenous freezing
// ------------------------------------------------------------------------
- const auto cm = m / 100;
+ constexpr auto cm = m / 100;
// units of number mixing ratios of tracers
- auto frz_unit = 1 / (cm * cm * cm * s);
+ constexpr auto frz_unit = 1 / (cm * cm * cm * s);
// heterogeneous freezing by immersion nucleation [cm^-3 s^-1]
- add_field("hetfrz_immersion_nucleation_tend", scalar3d_layout_mid,
+ add_field("hetfrz_immersion_nucleation_tend", scalar3d_mid,
frz_unit, grid_name);
// heterogeneous freezing by contact nucleation [cm^-3 s^-1]
- add_field("hetfrz_contact_nucleation_tend", scalar3d_layout_mid,
- frz_unit, grid_name);
+ add_field("hetfrz_contact_nucleation_tend", scalar3d_mid, frz_unit,
+ grid_name);
// heterogeneous freezing by deposition nucleation [cm^-3 s^-1]
- add_field("hetfrz_deposition_nucleation_tend", scalar3d_layout_mid,
+ add_field("hetfrz_deposition_nucleation_tend", scalar3d_mid,
frz_unit, grid_name);
} // function set_grids ends
@@ -299,11 +292,11 @@ void MAMAci::initialize_impl(const RunType run_type) {
// store rest fo the atm fields in dry_atm_in
dry_atm_.z_surf = 0;
- 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_int = get_field_in("p_int").get_view();
- dry_atm_.p_del = get_field_in("pseudo_density").get_view();
- dry_atm_.omega = get_field_in("omega").get_view();
+ 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_int = get_field_in("p_int").get_view();
+ dry_atm_.p_del = get_field_in("pseudo_density").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_.qv = buffer_.qv_dry;
@@ -561,7 +554,8 @@ void MAMAci::run_impl(const double dt) {
// output
w0_, rho_);
- compute_tke_at_interfaces(team_policy, w_sec_mid_, dry_atm_.dz, nlev_, w_sec_int_,
+ compute_tke_at_interfaces(team_policy, w_sec_mid_, dry_atm_.dz, nlev_,
+ w_sec_int_,
// output
tke_);
@@ -594,25 +588,26 @@ void MAMAci::run_impl(const double dt) {
// output
cloud_frac_, cloud_frac_prev_);
- mam_coupling::compute_recipical_pseudo_density(team_policy, dry_atm_.p_del, nlev_,
- // output
- rpdel_);
+ mam_coupling::compute_recipical_pseudo_density(team_policy, dry_atm_.p_del,
+ nlev_,
+ // output
+ rpdel_);
Kokkos::fence(); // wait for rpdel_ to be computed.
// Compute activated CCN number tendency (tendnd_) and updated
// cloud borne aerosols (stored in a work array) and interstitial
// aerosols tendencies
- call_function_dropmixnuc(team_policy, dt, dry_atm_, rpdel_, kvh_mid_, kvh_int_, wsub_,
- cloud_frac_, cloud_frac_prev_, dry_aero_, nlev_,
- // output
- coltend_, coltend_cw_, qcld_, ndropcol_, ndropmix_,
- nsource_, wtke_, ccn_,
- // ## output to be used by the other processes ##
- qqcw_fld_work_, ptend_q_, factnum_, tendnd_,
- // work arrays
- raercol_cw_, raercol_, state_q_work_, nact_, mact_,
- dropmixnuc_scratch_mem_);
+ call_function_dropmixnuc(
+ team_policy, dt, dry_atm_, rpdel_, kvh_mid_, kvh_int_, wsub_, cloud_frac_,
+ cloud_frac_prev_, dry_aero_, nlev_,
+ // output
+ coltend_, coltend_cw_, qcld_, ndropcol_, ndropmix_, nsource_, wtke_, ccn_,
+ // ## output to be used by the other processes ##
+ qqcw_fld_work_, ptend_q_, factnum_, tendnd_,
+ // work arrays
+ raercol_cw_, raercol_, state_q_work_, nact_, mact_,
+ dropmixnuc_scratch_mem_);
Kokkos::fence(); // wait for ptend_q_ to be computed.
Kokkos::deep_copy(ccn_0p02_,
diff --git a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp
index 090fdfcb731..b0ab232c255 100644
--- a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp
+++ b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp
@@ -1,215 +1,403 @@
#include
-#include
-#include
-#include
-#include "scream_config.h" // for SCREAM_CIME_BUILD
+// impl namespace for some driver level functions for microphysics
-#include // for serial NetCDF file reads on MPI root
-#include
+#include "readfiles/photo_table_utils.cpp"
+#include "physics/rrtmgp/shr_orb_mod_c2f.hpp"
-// NOTE: see the impl/ directory for the contents of the impl namespace
-#include "impl/compute_o3_column_density.cpp"
-#include "impl/compute_water_content.cpp"
-#include "impl/gas_phase_chemistry.cpp"
+namespace scream {
-namespace scream
-{
+MAMMicrophysics::MAMMicrophysics(const ekat::Comm &comm,
+ const ekat::ParameterList ¶ms)
+ : AtmosphereProcess(comm, params), aero_config_() {
+ config_.amicphys.do_cond = m_params.get("mam4_do_cond");
+ config_.amicphys.do_rename = m_params.get("mam4_do_rename");
+ config_.amicphys.do_newnuc = m_params.get("mam4_do_newnuc");
+ config_.amicphys.do_coag = m_params.get("mam4_do_coag");
-MAMMicrophysics::MAMMicrophysics(
- const ekat::Comm& comm,
- const ekat::ParameterList& params)
- : AtmosphereProcess(comm, params),
- aero_config_() {
- configure(params);
+ // these parameters guide the coupling between parameterizations
+ // NOTE: mam4xx was ported with these parameters fixed, so it's probably not
+ // NOTE: safe to change these without code modifications.
+
+ // controls treatment of h2so4 condensation in mam_gasaerexch_1subarea
+ // 1 = sequential calc. of gas-chem prod then condensation loss
+ // 2 = simultaneous calc. of gas-chem prod and condensation loss
+ config_.amicphys.gaexch_h2so4_uptake_optaa = 2;
+
+ // controls treatment of h2so4 concentrationin mam_newnuc_1subarea
+ // 1 = use average value calculated in standard cam5.2.10 and earlier
+ // 2 = use average value calculated in mam_gasaerexch_1subarea
+ // 11 = use average of initial and final values from mam_gasaerexch_1subarea
+ // 12 = use final value from mam_gasaerexch_1subarea
+ config_.amicphys.newnuc_h2so4_conc_optaa = 2;
+
+ // LINOZ namelist parameters
+ config_.linoz.o3_lbl = m_params.get("mam4_o3_lbl");
+ config_.linoz.o3_tau = m_params.get("mam4_o3_tau");
+ config_.linoz.o3_sfc = m_params.get("mam4_o3_sfc");
+ config_.linoz.psc_T = m_params.get("mam4_psc_T");
}
AtmosphereProcessType MAMMicrophysics::type() const {
return AtmosphereProcessType::Physics;
}
-std::string MAMMicrophysics::name() const {
- return "mam4_micro";
-}
+// ================================================================
+// SET_GRIDS
+// ================================================================
+void MAMMicrophysics::set_grids(
+ const std::shared_ptr grids_manager) {
+ // set grid for all the inputs and outputs
+ // use physics grid
+ grid_ = grids_manager->get_grid("Physics");
+ const auto &grid_name = grid_->name();
-namespace {
+ ncol_ = grid_->get_num_local_dofs(); // number of columns on this rank
+ nlev_ = grid_->get_num_vertical_levels(); // number of levels per column
-void set_data_file(const char *name, const char *path, char location[MAX_FILENAME_LEN]) {
- EKAT_REQUIRE_MSG(strlen(SCREAM_DATA_DIR) + strlen(path) < MAX_FILENAME_LEN,
- "Error! " << name << " path is too long (must be < " << MAX_FILENAME_LEN << " characters)");
- sprintf(location, "%s/%s", SCREAM_DATA_DIR, path);
-}
+ // get column geometry and locations
+ col_latitudes_ = grid_->get_geometry_data("lat").get_view();
-}
+ // define the different field layouts that will be used for this process
+ using namespace ShortFieldTagsNames;
-#define set_file_location(data_file, path) set_data_file(#data_file, path, data_file)
+ // Layout for 2D (2d horiz) variable
+ const FieldLayout scalar2d = grid_->get_2d_scalar_layout();
-void MAMMicrophysics::set_defaults_() {
- config_.amicphys.do_cond = true;
- config_.amicphys.do_rename = true;
- config_.amicphys.do_newnuc = true;
- config_.amicphys.do_coag = true;
+ // Layout for 3D (2d horiz X 1d vertical) variable defined at mid-level and
+ // interfaces
+ const FieldLayout scalar3d_mid = grid_->get_3d_scalar_layout(true);
+ const FieldLayout scalar3d_int = grid_->get_3d_scalar_layout(false);
- config_.amicphys.nucleation = {};
- config_.amicphys.nucleation.dens_so4a_host = 1770.0;
- config_.amicphys.nucleation.mw_so4a_host = 115.0;
- config_.amicphys.nucleation.newnuc_method_user_choice = 2;
- config_.amicphys.nucleation.pbl_nuc_wang2008_user_choice = 1;
- config_.amicphys.nucleation.adjust_factor_pbl_ratenucl = 1.0;
- config_.amicphys.nucleation.accom_coef_h2so4 = 1.0;
- config_.amicphys.nucleation.newnuc_adjust_factor_dnaitdt = 1.0;
+ using namespace ekat::units;
+ 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
- // these parameters guide the coupling between parameterizations
- // NOTE: mam4xx was ported with these parameters fixed, so it's probably not
- // NOTE: safe to change these without code modifications.
- config_.amicphys.gaexch_h2so4_uptake_optaa = 2;
- config_.amicphys.newnuc_h2so4_conc_optaa = 2;
+ // --------------------------------------------------------------------------
+ // These variables are "Required" or pure inputs for the process
+ // --------------------------------------------------------------------------
- //===========================================================
- // default data file locations (relative to SCREAM_DATA_DIR)
- //===========================================================
+ // ----------- Atmospheric quantities -------------
- // many of these paths were extracted from
- // e3smv2/bld/namelist_files/namelist_defaults_eam.xml
+ // Specific humidity [kg/kg](Require only for building DS)
+ add_tracer("qv", grid_, kg/kg); // specific humidity
- // photolysis
- set_file_location(config_.photolysis.rsf_file, "../waccm/phot/RSF_GT200nm_v3.0_c080811.nc");
- set_file_location(config_.photolysis.xs_long_file, "../waccm/phot/temp_prs_GT200nm_JPL10_c130206.nc");
+ // Cloud liquid mass mixing ratio [kg/kg](Require only for building DS)
+ add_tracer("qc", grid_, kg/kg); // cloud liquid wet mixing ratio
- // stratospheric chemistry
- set_file_location(config_.linoz.chlorine_loading_file, "../cam/chem/trop_mozart/ub/Linoz_Chlorine_Loading_CMIP6_0003-2017_c20171114.nc");
+ // Cloud ice mass mixing ratio [kg/kg](Require only for building DS)
+ add_tracer("qi", grid_, kg/kg); // ice wet mixing ratio
- // dry deposition
- set_file_location(config_.drydep.srf_file, "../cam/chem/trop_mam/atmsrf_ne4pg2_200527.nc");
-}
+ // Cloud liquid number mixing ratio [1/kg](Require only for building DS)
+ add_tracer("nc", grid_, n_unit); // cloud liquid wet number mixing ratio
-void MAMMicrophysics::configure(const ekat::ParameterList& params) {
- set_defaults_();
- // FIXME: implement "namelist" parsing
-}
+ // Cloud ice number mixing ratio [1/kg](Require only for building DS)
+ add_tracer("ni", grid_, n_unit); // ice number mixing ratio
-void MAMMicrophysics::set_grids(const std::shared_ptr grids_manager) {
- using namespace ekat::units;
+ // Temperature[K] at midpoints
+ add_field("T_mid", scalar3d_mid, K, grid_name);
- Units nondim = Units::nondimensional();
- Units n_unit (1/kg,"#/kg"); // number mixing ratios [# / kg air]
- const auto m2 = pow(m,2);
- const auto s2 = pow(s,2);
+ // Vertical pressure velocity [Pa/s] at midpoints (Require only for building
+ // DS)
+ add_field("omega", scalar3d_mid, Pa / s, grid_name);
- grid_ = grids_manager->get_grid("Physics");
- const auto& grid_name = grid_->name();
+ // Total pressure [Pa] at midpoints
+ add_field("p_mid", scalar3d_mid, Pa, grid_name);
- ncol_ = grid_->get_num_local_dofs(); // number of columns on this rank
- nlev_ = grid_->get_num_vertical_levels(); // number of levels per column
+ // Total pressure [Pa] at interfaces
+ add_field("p_int", scalar3d_int, Pa, grid_name);
- // get column geometry and locations
- col_areas_ = grid_->get_geometry_data("area").get_view();
- col_latitudes_ = grid_->get_geometry_data("lat").get_view();
- col_longitudes_ = grid_->get_geometry_data("lon").get_view();
+ // Layer thickness(pdel) [Pa] at midpoints
+ add_field("pseudo_density", scalar3d_mid, Pa, grid_name);
- // define the different field layouts that will be used for this process
- using namespace ShortFieldTagsNames;
+ // Planetary boundary layer height [m]
+ add_field("pbl_height", scalar2d, m, grid_name);
- // layout for 2D (1d horiz X 1d vertical) variable
- FieldLayout scalar2d_layout_col{ {COL}, {ncol_} };
+ constexpr auto m2 = pow(m, 2);
+ constexpr auto s2 = pow(s, 2);
- // layout for 3D (2d horiz X 1d vertical) variables
- FieldLayout scalar3d_layout_mid{ {COL, LEV}, {ncol_, nlev_} };
+ // Surface geopotential [m2/s2]
+ add_field("phis", scalar2d, m2 / s2, grid_name);
- // define fields needed in mam4xx
+ //----------- Variables from microphysics scheme -------------
+ constexpr auto nondim = ekat::units::Units::nondimensional();
+ // Total cloud fraction [fraction]
+ add_field("cldfrac_liq", scalar3d_mid, nondim, grid_name);
- // atmospheric quantities
- add_field("omega", scalar3d_layout_mid, Pa/s, grid_name); // vertical pressure velocity
- add_field("T_mid", scalar3d_layout_mid, K, grid_name); // Temperature
- add_field("p_mid", scalar3d_layout_mid, Pa, grid_name); // total pressure
- add_field("pbl_height", scalar2d_layout_col, m, grid_name); // planetary boundary layer height
- add_field("pseudo_density", scalar3d_layout_mid, Pa, grid_name); // p_del, hydrostatic pressure
- add_field("phis", scalar2d_layout_col, m2/s2, grid_name);
- add_field("cldfrac_tot", scalar3d_layout_mid, nondim, grid_name); // cloud fraction
- add_tracer("qv", grid_, kg/kg); // specific humidity
- add_tracer("qi", grid_, kg/kg); // ice wet mixing ratio
- add_tracer("ni", grid_, n_unit); // ice number mixing ratio
+ //----------- Variables from other mam4xx processes ------------
+ // Number of modes
+ constexpr int nmodes = mam4::AeroConfig::num_modes();
- // droplet activation can alter cloud liquid and number mixing ratios
- add_tracer("qc", grid_, kg/kg); // cloud liquid wet mixing ratio
- add_tracer("nc", grid_, n_unit); // cloud liquid wet number mixing ratio
+ // layout for 3D (ncol, nmodes, nlevs)
+ FieldLayout scalar3d_mid_nmodes =
+ grid_->get_3d_vector_layout(true, nmodes, "nmodes");
+
+ // Geometric mean dry diameter for number distribution [m]
+ add_field("dgnum", scalar3d_mid_nmodes, m, grid_name);
+ // Geometric mean wet diameter for number distribution [m]
+ add_field("dgnumwet", scalar3d_mid_nmodes, m, grid_name);
+
+ constexpr auto m3 = pow(m, 3);
+ // Wet density of interstitial aerosol [kg/m3]
+ add_field("wetdens", scalar3d_mid_nmodes, kg / m3, grid_name);
+
+ //----------- Variables from coupler (land component)---------
+ // surface albedo shortwave, direct
+ add_field("sfc_alb_dir_vis", scalar2d, nondim, grid_name);
+
+ // ---------------------------------------------------------------------
+ // These variables are "updated" or inputs/outputs for the process
+ // ---------------------------------------------------------------------
+
+ // (interstitial) aerosol tracers of interest: mass (q) and number (n) mixing
+ // ratios
+ for(int m = 0; m < nmodes; ++m) {
+ const char *int_nmr_field_name = mam_coupling::int_aero_nmr_field_name(m);
- // (interstitial) aerosol tracers of interest: mass (q) and number (n) mixing ratios
- for (int m = 0; m < mam_coupling::num_aero_modes(); ++m) {
- const char* int_nmr_field_name = mam_coupling::int_aero_nmr_field_name(m);
add_tracer(int_nmr_field_name, grid_, n_unit);
- for (int a = 0; a < mam_coupling::num_aero_species(); ++a) {
- const char* int_mmr_field_name = mam_coupling::int_aero_mmr_field_name(m, a);
- if (strlen(int_mmr_field_name) > 0) {
+ for(int a = 0; a < mam_coupling::num_aero_species(); ++a) {
+ const char *int_mmr_field_name =
+ mam_coupling::int_aero_mmr_field_name(m, a);
+
+ if(strlen(int_mmr_field_name) > 0) {
add_tracer(int_mmr_field_name, grid_, kg/kg);
}
- }
- }
+ } // for loop species
+ } // for loop nmodes interstitial
+ // (cloud) aerosol tracers of interest: mass (q) and number (n) mixing ratios
+ for(int m = 0; m < nmodes; ++m) {
+ const char *cld_nmr_field_name = mam_coupling::cld_aero_nmr_field_name(m);
+
+ add_field(cld_nmr_field_name, scalar3d_mid, n_unit, grid_name);
+ for(int a = 0; a < mam_coupling::num_aero_species(); ++a) {
+ const char *cld_mmr_field_name =
+ mam_coupling::cld_aero_mmr_field_name(m, a);
+
+ if(strlen(cld_mmr_field_name) > 0) {
+ add_field(cld_mmr_field_name, scalar3d_mid, q_unit, grid_name);
+ }
+ } // for loop species
+ } // for loop nmodes cld borne
// aerosol-related gases: mass mixing ratios
- for (int g = 0; g < mam_coupling::num_aero_gases(); ++g) {
- const char* gas_mmr_field_name = mam_coupling::gas_mmr_field_name(g);
+ for(int g = 0; g < mam_coupling::num_aero_gases(); ++g) {
+ const char *gas_mmr_field_name = mam_coupling::gas_mmr_field_name(g);
add_tracer(gas_mmr_field_name, grid_, kg/kg);
}
- // Tracers group -- do we need this in addition to the tracers above? In any
- // case, this call should be idempotent, so it can't hurt.
- add_group("tracers", grid_name, 1, Bundling::Required);
-}
-
-// this checks whether we have the tracers we expect
-void MAMMicrophysics::
-set_computed_group_impl(const FieldGroup& group) {
- const auto& name = group.m_info->m_group_name;
- EKAT_REQUIRE_MSG(name=="tracers",
- "Error! MAM4 expects a 'tracers' field group (got '" << name << "')\n");
-
- EKAT_REQUIRE_MSG(group.m_info->m_bundled,
- "Error! MAM4 expects bundled fields for tracers.\n");
-
- // how many aerosol/gas tracers do we expect?
- int num_tracers = 2 * (mam_coupling::num_aero_modes() +
- mam_coupling::num_aero_tracers()) +
- mam_coupling::num_aero_gases();
- EKAT_REQUIRE_MSG(group.m_info->size() >= num_tracers,
- "Error! MAM4 requires at least " << num_tracers << " aerosol tracers.");
-}
-
-size_t MAMMicrophysics::requested_buffer_size_in_bytes() const
-{
+ // Creating a Linoz reader and setting Linoz parameters involves reading data
+ // from a file and configuring the necessary parameters for the Linoz model.
+ {
+ linoz_file_name_ = m_params.get("mam4_linoz_file_name");
+ const std::string linoz_map_file =
+ m_params.get("aero_microphys_remap_file", "");
+ const std::vector var_names{
+ "o3_clim", "o3col_clim", "t_clim", "PmL_clim",
+ "dPmL_dO3", "dPmL_dT", "dPmL_dO3col", "cariolle_pscs"};
+
+ // in format YYYYMMDD
+ const int linoz_cyclical_ymd = m_params.get("mam4_linoz_ymd");
+ scream::mam_coupling::setup_tracer_data(linoz_data_, linoz_file_name_,
+ linoz_cyclical_ymd);
+ LinozHorizInterp_ = scream::mam_coupling::create_horiz_remapper(
+ grid_, linoz_file_name_, linoz_map_file, var_names, linoz_data_);
+ LinozDataReader_ = scream::mam_coupling::create_tracer_data_reader(
+ LinozHorizInterp_, linoz_file_name_);
+
+ // linoz reader
+ const auto io_grid_linoz = LinozHorizInterp_->get_src_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 =
+ io_grid_linoz
+ ->get_num_vertical_levels(); // Number of levels per column
+ const int nvars = int(var_names.size());
+ linoz_data_.init(num_cols_io_linoz, num_levs_io_linoz, nvars);
+ linoz_data_.allocate_temporal_views();
+ } // LINOZ reader
+
+ {
+ oxid_file_name_ = m_params.get("mam4_oxid_file_name");
+ const std::string oxid_map_file =
+ m_params.get("aero_microphys_remap_file", "");
+ // NOTE: order matches mam4xx:
+ const std::vector var_names{"O3", "OH", "NO3", "HO2"};
+
+ // in format YYYYMMDD
+ const int oxid_ymd = m_params.get("mam4_oxid_ymd");
+ scream::mam_coupling::setup_tracer_data(tracer_data_, oxid_file_name_,
+ oxid_ymd);
+ TracerHorizInterp_ = scream::mam_coupling::create_horiz_remapper(
+ grid_, oxid_file_name_, oxid_map_file, var_names, tracer_data_);
+ TracerDataReader_ = scream::mam_coupling::create_tracer_data_reader(
+ TracerHorizInterp_, oxid_file_name_);
+
+ const int nvars = int(var_names.size());
+ const auto io_grid = TracerHorizInterp_->get_src_grid();
+ const int num_cols_io =
+ io_grid->get_num_local_dofs(); // Number of columns on this rank
+ const int num_levs_io =
+ io_grid->get_num_vertical_levels(); // Number of levels per column
+ tracer_data_.init(num_cols_io, num_levs_io, nvars);
+ tracer_data_.allocate_temporal_views();
+
+ for(int ivar = 0; ivar < nvars; ++ivar) {
+ cnst_offline_[ivar] = view_2d("cnst_offline_", ncol_, nlev_);
+ }
+ } // oxid file reader
+
+ {
+ const std::string extfrc_map_file =
+ m_params.get("aero_microphys_remap_file", "");
+ // NOTE: order of forcing species is important.
+ // extfrc_lst(: 9) = {'SO2 ','so4_a1 ','so4_a2
+ // ','pom_a4 ','bc_a4 ', 'num_a1 ','num_a2
+ // ','num_a4 ','SOAG ' }
+ // This order corresponds to files in namelist e3smv2
+ extfrc_lst_ = {"so2", "so4_a1", "so4_a2", "pom_a4", "bc_a4",
+ "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";
+ const auto file_name = m_params.get(item_name);
+ vert_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"] = {
+ "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"};
+ // 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",
+ "num_a1_POM_ELEV_BB"};
+ vert_emis_var_names_["soag"] = {"SOAbb_src", "SOAbg_src", "SOAff_src"};
+
+ int verti_emiss_cyclical_ymd = m_params.get("verti_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];
+
+ scream::mam_coupling::TracerData data_tracer;
+ scream::mam_coupling::setup_tracer_data(data_tracer, file_name,
+ verti_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
+ 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 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 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_;
+ 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_);
+ }
+ offset_emis_ver += nvars;
+ ++i;
+ } // end i
+ EKAT_REQUIRE_MSG(
+ offset_emis_ver <= int(mam_coupling::MAX_NUM_VERT_EMISSION_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");
+
+ } // Tracer external forcing data
+} // set_grids
+
+// ================================================================
+// REQUEST_BUFFER_SIZE_IN_BYTES
+// ================================================================
+// ON HOST, returns the number of bytes of device memory needed by
+// the above. Buffer type given the number of columns and vertical
+// levels
+size_t MAMMicrophysics::requested_buffer_size_in_bytes() const {
return mam_coupling::buffer_size(ncol_, nlev_);
}
-void MAMMicrophysics::init_buffers(const ATMBufferManager &buffer_manager) {
- EKAT_REQUIRE_MSG(buffer_manager.allocated_bytes() >= requested_buffer_size_in_bytes(),
- "Error! Insufficient buffer size.\n");
+// ================================================================
+// INIT_BUFFERS
+// ================================================================
+// ON HOST, initializeѕ the Buffer type with sufficient memory to
+// store intermediate (dry) quantities on the given number of
+// columns with the given number of vertical levels. Returns the
+// number of bytes allocated.
- size_t used_mem = mam_coupling::init_buffer(buffer_manager, ncol_, nlev_, buffer_);
- EKAT_REQUIRE_MSG(used_mem==requested_buffer_size_in_bytes(),
- "Error! Used memory != requested memory for MAMMicrophysics.");
+void MAMMicrophysics::init_buffers(const ATMBufferManager &buffer_manager) {
+ size_t used_mem =
+ mam_coupling::init_buffer(buffer_manager, ncol_, nlev_, buffer_);
+ EKAT_REQUIRE_MSG(used_mem == requested_buffer_size_in_bytes(),
+ "Error! Used memory != requested memory for MAMMicrophysics."
+ " Used memory: "
+ << std::to_string(used_mem)
+ << "."
+ " Requested memory: "
+ << std::to_string(requested_buffer_size_in_bytes())
+ << ". \n");
}
-
+// ================================================================
+// INITIALIZE_IMPL
+// ================================================================
void MAMMicrophysics::initialize_impl(const RunType run_type) {
-
- step_ = 0;
-
- // populate the wet and dry atmosphere states with views from fields and
- // the buffer
- wet_atm_.qv = get_field_in("qv").get_view();
- wet_atm_.qc = get_field_out("qc").get_view();
- wet_atm_.nc = get_field_out("nc").get_view();
- wet_atm_.qi = get_field_in("qi").get_view();
- wet_atm_.ni = get_field_in("ni").get_view();
-
-
- 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_.cldfrac = get_field_in("cldfrac_tot").get_view(); // FIXME: tot or liq?
- dry_atm_.pblh = get_field_in("pbl_height").get_view();
- dry_atm_.phis = get_field_in("phis").get_view();
- dry_atm_.omega = get_field_in("omega").get_view();
+ // Determine orbital year. If orbital_year is negative, use current year
+ // from timestamp for orbital year; if positive, use provided orbital year
+ // for duration of simulation.
+ m_orbital_year = m_params.get("orbital_year", -9999);
+
+ // Get orbital parameters from yaml file
+ m_orbital_eccen = m_params.get("orbital_eccentricity", -9999);
+ m_orbital_obliq = m_params.get("orbital_obliquity", -9999);
+ m_orbital_mvelp = m_params.get("orbital_mvelp", -9999);
+
+ // ---------------------------------------------------------------
+ // Input fields read in from IC file, namelist or other processes
+ // ---------------------------------------------------------------
+
+ // Populate the wet atmosphere state with views from fields
+ // FIMXE: specifically look which among these are actually used by the process
+
+ wet_atm_.qv = get_field_in("qv").get_view();
+ 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();
+
+ 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_int = get_field_in("p_int").get_view();
+ dry_atm_.p_del = get_field_in("pseudo_density").get_view();
+ dry_atm_.cldfrac = get_field_in("cldfrac_liq").get_view();
+ dry_atm_.pblh = get_field_in("pbl_height").get_view();
+ dry_atm_.phis = get_field_in("phis").get_view();
+ dry_atm_.omega = get_field_in("omega").get_view();
dry_atm_.z_mid = buffer_.z_mid;
dry_atm_.dz = buffer_.dz;
dry_atm_.z_iface = buffer_.z_iface;
@@ -219,43 +407,60 @@ void MAMMicrophysics::initialize_impl(const RunType run_type) {
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
-
- // perform any initialization work
- if (run_type==RunType::Initial) {
- }
-
- // set wet/dry aerosol state data (interstitial aerosols only)
- for (int m = 0; m < mam_coupling::num_aero_modes(); ++m) {
- const char* int_nmr_field_name = mam_coupling::int_aero_nmr_field_name(m);
- wet_aero_.int_aero_nmr[m] = get_field_out(int_nmr_field_name).get_view();
+ dry_atm_.z_surf = 0.0; // It is always zero.
+
+ // get surface albedo: shortwave, direct
+ d_sfc_alb_dir_vis_ = get_field_in("sfc_alb_dir_vis").get_view();
+
+ // interstitial and cloudborne aerosol tracers of interest: mass (q) and
+ // number (n) mixing ratios
+ for(int m = 0; m < mam_coupling::num_aero_modes(); ++m) {
+ // interstitial aerosol tracers of interest: number (n) mixing ratios
+ const char *int_nmr_field_name = mam_coupling::int_aero_nmr_field_name(m);
+ wet_aero_.int_aero_nmr[m] =
+ get_field_out(int_nmr_field_name).get_view();
dry_aero_.int_aero_nmr[m] = buffer_.dry_int_aero_nmr[m];
- for (int a = 0; a < mam_coupling::num_aero_species(); ++a) {
- const char* int_mmr_field_name = mam_coupling::int_aero_mmr_field_name(m, a);
- if (strlen(int_mmr_field_name) > 0) {
- wet_aero_.int_aero_mmr[m][a] = get_field_out(int_mmr_field_name).get_view();
+
+ // cloudborne aerosol tracers of interest: number (n) mixing ratios
+ const char *cld_nmr_field_name = mam_coupling::cld_aero_nmr_field_name(m);
+ wet_aero_.cld_aero_nmr[m] =
+ get_field_out(cld_nmr_field_name).get_view();
+ dry_aero_.cld_aero_nmr[m] = buffer_.dry_cld_aero_nmr[m];
+
+ for(int a = 0; a < mam_coupling::num_aero_species(); ++a) {
+ // (interstitial) aerosol tracers of interest: mass (q) mixing ratios
+ const char *int_mmr_field_name =
+ mam_coupling::int_aero_mmr_field_name(m, a);
+ if(strlen(int_mmr_field_name) > 0) {
+ wet_aero_.int_aero_mmr[m][a] =
+ get_field_out(int_mmr_field_name).get_view();
dry_aero_.int_aero_mmr[m][a] = buffer_.dry_int_aero_mmr[m][a];
}
- }
- }
+
+ // (cloudborne) aerosol tracers of interest: mass (q) mixing ratios
+ const char *cld_mmr_field_name =
+ mam_coupling::cld_aero_mmr_field_name(m, a);
+ if(strlen(cld_mmr_field_name) > 0) {
+ wet_aero_.cld_aero_mmr[m][a] =
+ get_field_out(cld_mmr_field_name).get_view();
+ dry_aero_.cld_aero_mmr[m][a] = buffer_.dry_cld_aero_mmr[m][a];
+ }
+ } // for loop species
+ } // for loop num_aero_modes()
// set wet/dry aerosol-related gas state data
- for (int g = 0; g < mam_coupling::num_aero_gases(); ++g) {
- const char* mmr_field_name = mam_coupling::gas_mmr_field_name(g);
- wet_aero_.gas_mmr[g] = get_field_out(mmr_field_name).get_view();
+ for(int g = 0; g < mam_coupling::num_aero_gases(); ++g) {
+ const char *mmr_field_name = mam_coupling::gas_mmr_field_name(g);
+ wet_aero_.gas_mmr[g] = get_field_out(mmr_field_name).get_view();
dry_aero_.gas_mmr[g] = buffer_.dry_gas_mmr[g];
}
// create our photolysis rate calculation table
- photo_table_ = impl::read_photo_table(get_comm(),
- config_.photolysis.rsf_file,
- config_.photolysis.xs_long_file);
-
- // FIXME: read relevant land use data from drydep surface file
+ const std::string rsf_file = m_params.get("mam4_rsf_file");
+ const std::string xs_long_file =
+ m_params.get("mam4_xs_long_file");
- // set up our preprocess/postprocess functors
- preprocess_.initialize(ncol_, nlev_, wet_atm_, wet_aero_, dry_atm_, dry_aero_);
- postprocess_.initialize(ncol_, nlev_, wet_atm_, wet_aero_, dry_atm_, dry_aero_);
+ photo_table_ = impl::read_photo_table(rsf_file, xs_long_file);
// set field property checks for the fields in this process
/* e.g.
@@ -267,249 +472,342 @@ void MAMMicrophysics::initialize_impl(const RunType run_type) {
add_postcondition_check(get_field_out("tke"),m_grid,0);
*/
- // set up WSM for internal local variables
- // (we'll probably need this later, but we'll just use ATMBufferManager for now)
- //const auto default_policy = ekat::ExeSpaceUtils::get_default_team_policy(ncol_, nlev_);
- //workspace_mgr_.setup(buffer_.wsm_data, nlev_+1, 13+(n_wind_slots+n_trac_slots), default_policy);
-}
+ {
+ // climatology data for linear stratospheric chemistry
+ auto linoz_o3_clim = buffer_.scratch[0]; // ozone (climatology) [vmr]
+ auto linoz_o3col_clim =
+ buffer_.scratch[1]; // column o3 above box (climatology) [Dobson Units
+ // (DU)]
+ auto linoz_t_clim = buffer_.scratch[2]; // temperature (climatology) [K]
+ auto linoz_PmL_clim =
+ buffer_.scratch[3]; // P minus L (climatology) [vmr/s]
+ auto linoz_dPmL_dO3 =
+ buffer_.scratch[4]; // sensitivity of P minus L to O3 [1/s]
+ auto linoz_dPmL_dT =
+ buffer_.scratch[5]; // sensitivity of P minus L to T3 [K]
+ auto linoz_dPmL_dO3col = buffer_.scratch[6]; // sensitivity of P minus L to
+ // overhead O3 column [vmr/DU]
+ auto linoz_cariolle_pscs =
+ buffer_.scratch[7]; // Cariolle parameter for PSC loss of ozone [1/s]
+
+ auto ts = timestamp();
+ std::string linoz_chlorine_file =
+ m_params.get("mam4_linoz_chlorine_file");
+ int chlorine_loading_ymd = m_params.get("mam4_chlorine_loading_ymd");
+ scream::mam_coupling::create_linoz_chlorine_reader(
+ linoz_chlorine_file, ts, chlorine_loading_ymd, chlorine_values_,
+ chlorine_time_secs_);
+ } // LINOZ
+
+ const int photo_table_len = get_photo_table_work_len(photo_table_);
+ work_photo_table_ = view_2d("work_photo_table", ncol_, photo_table_len);
-void MAMMicrophysics::run_impl(const double dt) {
+ // here's where we store per-column photolysis rates
+ photo_rates_ = view_3d("photo_rates", ncol_, nlev_, mam4::mo_photo::phtcnt);
+
+ // Load the first month into extfrc_lst_end.
+ // Note: At the first time step, the data will be moved into extfrc_lst_beg,
+ // and extfrc_lst_end will be reloaded from file with the new month.
+ const int curr_month = timestamp().get_month() - 1; // 0-based
+
+ scream::mam_coupling::update_tracer_data_from_file(
+ LinozDataReader_, curr_month, *LinozHorizInterp_, linoz_data_);
+
+ scream::mam_coupling::update_tracer_data_from_file(
+ TracerDataReader_, curr_month, *TracerHorizInterp_, tracer_data_);
+
+ 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]);
+ }
+
+ invariants_ = view_3d("invarians", ncol_, nlev_, mam4::gas_chemistry::nfs);
+
+ constexpr int extcnt = mam4::gas_chemistry::extcnt;
+ extfrc_ = view_3d("extfrc_", ncol_, nlev_, extcnt);
+
+ //
+ acos_cosine_zenith_host_ = view_1d_host("host_acos(cosine_zenith)", ncol_);
+ acos_cosine_zenith_ = view_1d("device_acos(cosine_zenith)", ncol_);
+
+ //-----------------------------------------------------------------
+ // Setup preprocessing and post processing
+ //-----------------------------------------------------------------
+ preprocess_.initialize(ncol_, nlev_, wet_atm_, wet_aero_, dry_atm_,
+ dry_aero_);
+ postprocess_.initialize(ncol_, nlev_, wet_atm_, wet_aero_, dry_atm_,
+ dry_aero_);
+
+} // initialize_impl
- const auto scan_policy = ekat::ExeSpaceUtils::get_thread_range_parallel_scan_team_policy(ncol_, nlev_);
- const auto policy = ekat::ExeSpaceUtils::get_default_team_policy(ncol_, nlev_);
+// ================================================================
+// RUN_IMPL
+// ================================================================
+void MAMMicrophysics::run_impl(const double dt) {
+ const auto scan_policy = ekat::ExeSpaceUtils<
+ KT::ExeSpace>::get_thread_range_parallel_scan_team_policy(ncol_, nlev_);
+ const auto policy =
+ ekat::ExeSpaceUtils::get_default_team_policy(ncol_, nlev_);
// preprocess input -- needs a scan for the calculation of atm height
Kokkos::parallel_for("preprocess", scan_policy, preprocess_);
Kokkos::fence();
- // reset internal WSM variables
- //workspace_mgr_.reset_internals();
-
- // NOTE: nothing depends on simulation time (yet), so we can just use zero for now
- double t = 0.0;
-
- // here's where we store per-column photolysis rates
- using View2D = haero::DeviceType::view_2d;
- View2D photo_rates("photo_rates", nlev_, mam4::mo_photo::phtcnt);
+ const auto wet_geometric_mean_diameter_i =
+ get_field_in("dgnumwet").get_view();
+ const auto dry_geometric_mean_diameter_i =
+ get_field_in("dgnum").get_view();
+ const auto wetdens = get_field_in("wetdens").get_view();
// climatology data for linear stratospheric chemistry
- auto linoz_o3_clim = buffer_.scratch[0]; // ozone (climatology) [vmr]
- auto linoz_o3col_clim = buffer_.scratch[1]; // column o3 above box (climatology) [Dobson Units (DU)]
- auto linoz_t_clim = buffer_.scratch[2]; // temperature (climatology) [K]
- auto linoz_PmL_clim = buffer_.scratch[3]; // P minus L (climatology) [vmr/s]
- auto linoz_dPmL_dO3 = buffer_.scratch[4]; // sensitivity of P minus L to O3 [1/s]
- auto linoz_dPmL_dT = buffer_.scratch[5]; // sensitivity of P minus L to T3 [K]
- auto linoz_dPmL_dO3col = buffer_.scratch[6]; // sensitivity of P minus L to overhead O3 column [vmr/DU]
- auto linoz_cariolle_psc = buffer_.scratch[7]; // Cariolle parameter for PSC loss of ozone [1/s]
-
+ // ozone (climatology) [vmr]
+ auto linoz_o3_clim = buffer_.scratch[0];
+ // column o3 above box (climatology) [Dobson Units (DU)]
+ auto linoz_o3col_clim = buffer_.scratch[1];
+ auto linoz_t_clim = buffer_.scratch[2]; // temperature (climatology) [K]
+ auto linoz_PmL_clim = buffer_.scratch[3]; // P minus L (climatology) [vmr/s]
+ // sensitivity of P minus L to O3 [1/s]
+ auto linoz_dPmL_dO3 = buffer_.scratch[4];
+ // sensitivity of P minus L to T3 [K]
+ auto linoz_dPmL_dT = buffer_.scratch[5];
+ // sensitivity of P minus L to overhead O3 column [vmr/DU]
+ auto linoz_dPmL_dO3col = buffer_.scratch[6];
+ // Cariolle parameter for PSC loss of ozone [1/s]
+ auto linoz_cariolle_pscs = buffer_.scratch[7];
+
+ view_2d linoz_output[8];
+ linoz_output[0] = linoz_o3_clim;
+ linoz_output[1] = linoz_o3col_clim;
+ linoz_output[2] = linoz_t_clim;
+ linoz_output[3] = linoz_PmL_clim;
+ linoz_output[4] = linoz_dPmL_dO3;
+ linoz_output[5] = linoz_dPmL_dT;
+ linoz_output[6] = linoz_dPmL_dO3col;
+ linoz_output[7] = linoz_cariolle_pscs;
// it's a bit wasteful to store this for all columns, but simpler from an
// allocation perspective
auto o3_col_dens = buffer_.scratch[8];
- const_view_1d &col_latitudes = col_latitudes_;
- mam_coupling::DryAtmosphere &dry_atm = dry_atm_;
- mam_coupling::AerosolState &dry_aero = dry_aero_;
- mam4::mo_photo::PhotoTableData &photo_table = photo_table_;
- const int nlev = nlev_;
- const Config &config = config_;
- // FIXME: read relevant linoz climatology data from file(s) based on time
-
- // FIXME: read relevant chlorine loading data from file based on time
-
- // loop over atmosphere columns and compute aerosol microphyscs
- auto some_step = step_;
-
- Kokkos::parallel_for(policy, KOKKOS_LAMBDA(const ThreadTeam& team) {
- const int icol = team.league_rank(); // column index
-
- Real col_lat = col_latitudes(icol); // column latitude (degrees?)
-
- // fetch column-specific atmosphere state data
- auto atm = mam_coupling::atmosphere_for_column(dry_atm, icol);
- auto z_iface = ekat::subview(dry_atm.z_iface, icol);
- Real phis = dry_atm.phis(icol);
-
- // set surface state data
- haero::Surface sfc{};
-
- // fetch column-specific subviews into aerosol prognostics
- mam4::Prognostics progs = mam_coupling::interstitial_aerosols_for_column(dry_aero, icol);
-
- // set up diagnostics
- mam4::Diagnostics diags(nlev);
-
- // calculate o3 column densities (first component of col_dens in Fortran code)
- auto o3_col_dens_i = ekat::subview(o3_col_dens, icol);
- impl::compute_o3_column_density(team, atm, progs, o3_col_dens_i);
-
- // set up photolysis work arrays for this column.
- mam4::mo_photo::PhotoTableWorkArrays photo_work_arrays;
- // FIXME: set views here
-
- // ... look up photolysis rates from our table
- // NOTE: the table interpolation operates on an entire column of data, so we
- // NOTE: must do it before dispatching to individual vertical levels
- Real zenith_angle = 0.0; // FIXME: need to get this from EAMxx [radians]
- Real surf_albedo = 0.0; // FIXME: surface albedo
- Real esfact = 0.0; // FIXME: earth-sun distance factor
- mam4::ColumnView lwc; // FIXME: liquid water cloud content: where do we get this?
- mam4::mo_photo::table_photo(photo_rates, atm.pressure, atm.hydrostatic_dp,
- atm.temperature, o3_col_dens_i, zenith_angle, surf_albedo, lwc,
- atm.cloud_fraction, esfact, photo_table, photo_work_arrays);
-
- // compute external forcings at time t(n+1) [molecules/cm^3/s]
- constexpr int extcnt = mam4::gas_chemistry::extcnt;
- view_2d extfrc; // FIXME: where to allocate? (nlev, extcnt)
- mam4::mo_setext::Forcing forcings[extcnt]; // FIXME: forcings seem to require file data
- mam4::mo_setext::extfrc_set(forcings, extfrc);
-
- // compute aerosol microphysics on each vertical level within this column
- Kokkos::parallel_for(Kokkos::TeamThreadRange(team, nlev), [&](const int k) {
-
- constexpr int num_modes = mam4::AeroConfig::num_modes();
- constexpr int gas_pcnst = mam_coupling::gas_pcnst();
- constexpr int nqtendbb = mam_coupling::nqtendbb();
-
- // extract atm state variables (input)
- Real temp = atm.temperature(k);
- Real pmid = atm.pressure(k);
- Real pdel = atm.hydrostatic_dp(k);
- Real zm = atm.height(k);
- Real zi = z_iface(k);
- Real pblh = atm.planetary_boundary_layer_height;
- Real qv = atm.vapor_mixing_ratio(k);
- Real cldfrac = atm.cloud_fraction(k);
-
- // extract aerosol state variables into "working arrays" (mass mixing ratios)
- // (in EAM, this is done in the gas_phase_chemdr subroutine defined within
- // mozart/mo_gas_phase_chemdr.F90)
- Real q[gas_pcnst] = {};
- Real qqcw[gas_pcnst] = {};
- mam_coupling::transfer_prognostics_to_work_arrays(progs, k, q, qqcw);
-
- // convert mass mixing ratios to volume mixing ratios (VMR), equivalent
- // to tracer mixing ratios (TMR))
- Real vmr[gas_pcnst], vmrcw[gas_pcnst];
- mam_coupling::convert_work_arrays_to_vmr(q, qqcw, vmr, vmrcw);
-
- // aerosol/gas species tendencies (output)
- Real vmr_tendbb[gas_pcnst][nqtendbb] = {};
- Real vmrcw_tendbb[gas_pcnst][nqtendbb] = {};
-
- // create work array copies to retain "pre-chemistry" values
- Real vmr_pregaschem[gas_pcnst] = {};
- Real vmr_precldchem[gas_pcnst] = {};
- Real vmrcw_precldchem[gas_pcnst] = {};
- for (int i = 0; i < gas_pcnst; ++i) {
- vmr_pregaschem[i] = vmr[i];
- vmr_precldchem[i] = vmr[i];
- vmrcw_precldchem[i] = vmrcw[i];
- }
+ /* Gather time and state information for interpolation */
+ const auto ts = timestamp() + dt;
+
+ const Real chlorine_loading = scream::mam_coupling::chlorine_loading_advance(
+ ts, chlorine_values_, chlorine_time_secs_);
+
+ // /* Update the TracerTimeState to reflect the current time, note the
+ // addition of dt */
+ trace_time_state_.t_now = ts.frac_of_year_in_days();
+ scream::mam_coupling::advance_tracer_data(
+ TracerDataReader_, // in
+ *TracerHorizInterp_, // out
+ ts, // in
+ trace_time_state_, tracer_data_, // out
+ dry_atm_.p_mid, dry_atm_.z_iface, // in
+ cnst_offline_); // out
+ Kokkos::fence();
- //---------------------
- // Gas Phase Chemistry
- //---------------------
- Real photo_rates_k[mam4::mo_photo::phtcnt];
- for (int i = 0; i < mam4::mo_photo::phtcnt; ++i) {
- photo_rates_k[i] = photo_rates(k, i);
- }
- Real extfrc_k[extcnt];
- for (int i = 0; i < extcnt; ++i) {
- extfrc_k[i] = extfrc(k, i);
- }
- constexpr int nfs = mam4::gas_chemistry::nfs; // number of "fixed species"
- // NOTE: we compute invariants here and pass them out to use later with
- // NOTE: setsox
- Real invariants[nfs];
- impl::gas_phase_chemistry(zm, zi, phis, temp, pmid, pdel, dt,
- photo_rates_k, extfrc_k, vmr, invariants);
-
- //----------------------
- // Aerosol microphysics
- //----------------------
- // the logic below is taken from the aero_model_gasaerexch subroutine in
- // eam/src/chemistry/modal_aero/aero_model.F90
-
- // aqueous chemistry ...
- const int loffset = 8; // offset of first tracer in work arrays
- // (taken from mam4xx setsox validation test)
- const Real mbar = haero::Constants::molec_weight_dry_air;
- constexpr int indexm = 0; // FIXME: index of xhnm in invariants array (??)
- Real cldnum = 0.0; // FIXME: droplet number concentration: where do we get this?
- setsox_single_level(loffset, dt, pmid, pdel, temp, mbar, lwc(k),
- cldfrac, cldnum, invariants[indexm], config.setsox, vmrcw, vmr);
-
- // calculate aerosol water content using water uptake treatment
- // * dry and wet diameters [m]
- // * wet densities [kg/m3]
- // * aerosol water mass mixing ratio [kg/kg]
- Real dgncur_a[num_modes] = {};
- Real dgncur_awet[num_modes] = {};
- Real wetdens[num_modes] = {};
- Real qaerwat[num_modes] = {};
- impl::compute_water_content(progs, k, qv, temp, pmid, dgncur_a, dgncur_awet, wetdens, qaerwat);
-
- // do aerosol microphysics (gas-aerosol exchange, nucleation, coagulation)
- impl::modal_aero_amicphys_intr(config.amicphys, step_, dt, t, pmid, pdel,
- zm, pblh, qv, cldfrac, vmr, vmrcw, vmr_pregaschem,
- vmr_precldchem, vmrcw_precldchem, vmr_tendbb,
- vmrcw_tendbb, dgncur_a, dgncur_awet,
- wetdens, qaerwat);
-
- //-----------------
- // LINOZ chemistry
- //-----------------
-
- // the following things are diagnostics, which we're not
- // including in the first rev
- Real do3_linoz, do3_linoz_psc, ss_o3, o3col_du_diag, o3clim_linoz_diag,
- zenith_angle_degrees;
-
- // FIXME: Need to get chlorine loading data from file
- Real chlorine_loading = 0.0;
-
- Real rlats = col_lat * M_PI / 180.0; // convert column latitude to radians
- int o3_ndx = 0; // index of "O3" in solsym array (in EAM)
- mam4::lin_strat_chem::lin_strat_chem_solve_kk(o3_col_dens_i(k), temp,
- zenith_angle, pmid, dt, rlats,
- linoz_o3_clim(icol, k), linoz_t_clim(icol, k), linoz_o3col_clim(icol, k),
- linoz_PmL_clim(icol, k), linoz_dPmL_dO3(icol, k), linoz_dPmL_dT(icol, k),
- linoz_dPmL_dO3col(icol, k), linoz_cariolle_psc(icol, k),
- chlorine_loading, config.linoz.psc_T, vmr[o3_ndx],
- do3_linoz, do3_linoz_psc, ss_o3,
- o3col_du_diag, o3clim_linoz_diag, zenith_angle_degrees);
-
- // update source terms above the ozone decay threshold
- if (k > nlev - config.linoz.o3_lbl - 1) {
- Real do3_mass; // diagnostic, not needed
- mam4::lin_strat_chem::lin_strat_sfcsink_kk(dt, pdel, vmr[o3_ndx], config.linoz.o3_sfc,
- config.linoz.o3_tau, do3_mass);
- }
+ scream::mam_coupling::advance_tracer_data(
+ LinozDataReader_, // in
+ *LinozHorizInterp_, // out
+ ts, // in
+ linoz_time_state_, linoz_data_, // out
+ dry_atm_.p_mid, dry_atm_.z_iface, // in
+ linoz_output); // out
+ Kokkos::fence();
- // ... check for negative values and reset to zero
- for (int i = 0; i < gas_pcnst; ++i) {
- if (vmr[i] < 0.0) vmr[i] = 0.0;
- }
+ vert_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 int nsectors = int(var_names.size());
+ view_2d vert_emis_output[nsectors];
+ for(int isp = 0; isp < nsectors; ++isp) {
+ vert_emis_output[isp] = vert_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);
+ i++;
+ Kokkos::fence();
+ }
- //----------------------
- // Dry deposition (gas)
- //----------------------
+ const_view_1d &col_latitudes = col_latitudes_;
+ const_view_1d &d_sfc_alb_dir_vis = d_sfc_alb_dir_vis_;
- // FIXME: C++ port in progress!
- //mam4::drydep::drydep_xactive(...);
+ mam_coupling::DryAtmosphere &dry_atm = dry_atm_;
+ mam_coupling::AerosolState &dry_aero = dry_aero_;
- // transfer updated prognostics from work arrays
- mam_coupling::convert_work_arrays_to_mmr(vmr, vmrcw, q, qqcw);
- mam_coupling::transfer_work_arrays_to_prognostics(q, qqcw, progs, k);
- });
- });
+ mam4::mo_photo::PhotoTableData &photo_table = photo_table_;
+ const Config &config = config_;
+ const auto &work_photo_table = work_photo_table_;
+ const auto &photo_rates = photo_rates_;
+
+ const auto &invariants = invariants_;
+ const auto &cnst_offline = cnst_offline_;
+
+ // Compute orbital parameters; these are used both for computing
+ // the solar zenith angle.
+ // Note: We are following the RRTMGP EAMxx interface to compute the zenith
+ // angle. This operation is performed on the host because the routine
+ // shr_orb_cosz_c2f has not been ported to C++.
+ auto ts2 = timestamp();
+ auto orbital_year = m_orbital_year;
+ // Note: We need double precision because
+ // shr_orb_params_c2f and shr_orb_decl_c2f only support double precision.
+ double obliqr, lambm0, mvelpp;
+ double eccen = m_orbital_eccen;
+ double obliq = m_orbital_obliq;
+ double mvelp = m_orbital_mvelp;
+ // Use the orbital parameters to calculate the solar declination and
+ // eccentricity factor
+ double delta, eccf;
+ if(eccen >= 0 && obliq >= 0 && mvelp >= 0) {
+ // use fixed orbital parameters; to force this, we need to set
+ // orbital_year to SHR_ORB_UNDEF_INT, which is exposed through
+ // our c2f bridge as shr_orb_undef_int_c2f
+ orbital_year = shr_orb_undef_int_c2f;
+ } else if(orbital_year < 0) {
+ // compute orbital parameters based on current year
+ orbital_year = ts2.get_year();
+ }
+ shr_orb_params_c2f(&orbital_year, // in
+ &eccen, &obliq, &mvelp, &obliqr, &lambm0, &mvelpp); // out
+
+ // Want day + fraction; calday 1 == Jan 1 0Z
+ auto calday = ts2.frac_of_year_in_days() + 1;
+ shr_orb_decl_c2f(calday, eccen, mvelpp, lambm0, obliqr, // in
+ &delta, &eccf); // out
+ {
+ const auto col_latitudes_host =
+ grid_->get_geometry_data("lat").get_view();
+ const auto col_longitudes_host =
+ grid_->get_geometry_data("lon").get_view();
+ // get a host copy of lat/lon
+ // Determine the cosine zenith angle
+ // NOTE: Since we are bridging to F90 arrays this must be done on HOST and
+ // then deep copied to a device view.
+
+ // Now use solar declination to calculate zenith angle for all points
+ for(int i = 0; i < ncol_; i++) {
+ Real lat =
+ col_latitudes_host(i) * M_PI / 180.0; // Convert lat/lon to radians
+ Real lon = col_longitudes_host(i) * M_PI / 180.0;
+ // what's the aerosol microphys frequency?
+ Real temp = shr_orb_cosz_c2f(calday, lat, lon, delta, dt);
+ acos_cosine_zenith_host_(i) = acos(temp);
+ }
+ Kokkos::deep_copy(acos_cosine_zenith_, acos_cosine_zenith_host_);
+ }
+ 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& extfrc = extfrc_;
+ const auto& forcings = forcings_;
+ constexpr int extcnt = mam4::gas_chemistry::extcnt;
+
+ const int offset_aerosol = mam4::utils::gasses_start_ind();
+ Real adv_mass_kg_per_moles[gas_pcnst];
+ // NOTE: Making copies of clsmap_4 and permute_4 to fix undefined arrays on
+ // the device.
+ int clsmap_4[gas_pcnst], permute_4[gas_pcnst];
+ for(int i = 0; i < gas_pcnst; ++i) {
+ // NOTE: state_q is kg/kg-dry-air; adv_mass is in g/mole.
+ // Convert adv_mass to kg/mole as vmr_from_mmr function uses
+ // molec_weight_dry_air with kg/mole units
+ adv_mass_kg_per_moles[i] = mam4::gas_chemistry::adv_mass[i] / 1e3;
+ clsmap_4[i] = mam4::gas_chemistry::clsmap_4[i];
+ permute_4[i] = mam4::gas_chemistry::permute_4[i];
+ }
+ // loop over atmosphere columns and compute aerosol microphyscs
+ Kokkos::parallel_for(
+ policy, KOKKOS_LAMBDA(const ThreadTeam &team) {
+ const int icol = team.league_rank(); // column index
+ const Real col_lat = col_latitudes(icol); // column latitude (degrees?)
+
+ // convert column latitude to radians
+ const Real rlats = col_lat * M_PI / 180.0;
+
+ // fetch column-specific atmosphere state data
+ const auto atm = mam_coupling::atmosphere_for_column(dry_atm, icol);
+ const auto wet_diameter_icol =
+ ekat::subview(wet_geometric_mean_diameter_i, icol);
+ const auto dry_diameter_icol =
+ ekat::subview(dry_geometric_mean_diameter_i, icol);
+ const auto wetdens_icol = ekat::subview(wetdens, icol);
+
+ // fetch column-specific subviews into aerosol prognostics
+ mam4::Prognostics progs =
+ mam_coupling::aerosols_for_column(dry_aero, icol);
+
+ const auto invariants_icol = ekat::subview(invariants, icol);
+ mam4::mo_setext::Forcing forcings_in[extcnt];
+
+ for(int i = 0; i < extcnt; ++i) {
+ const int nsectors = forcings[i].nsectors;
+ const int frc_ndx = forcings[i].frc_ndx;
+ const auto file_alt_data = forcings[i].file_alt_data;
+
+ forcings_in[i].nsectors = nsectors;
+ forcings_in[i].frc_ndx = frc_ndx;
+ // 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];
+ forcings_in[i].fields_data[isec] = ekat::subview(field, icol);
+ }
+ } // extcnt for loop
+
+ const auto extfrc_icol = ekat::subview(extfrc, icol);
+
+ view_1d cnst_offline_icol[mam4::mo_setinv::num_tracer_cnst];
+ for(int i = 0; i < mam4::mo_setinv::num_tracer_cnst; ++i) {
+ cnst_offline_icol[i] = ekat::subview(cnst_offline[i], icol);
+ }
+
+ // calculate o3 column densities (first component of col_dens in Fortran
+ // code)
+ auto o3_col_dens_i = ekat::subview(o3_col_dens, icol);
+ const auto &work_photo_table_icol =
+ ekat::subview(work_photo_table, icol);
+
+ const auto &photo_rates_icol = ekat::subview(photo_rates, icol);
+
+ const auto linoz_o3_clim_icol = ekat::subview(linoz_o3_clim, icol);
+ const auto linoz_t_clim_icol = ekat::subview(linoz_t_clim, icol);
+ const auto linoz_o3col_clim_icol =
+ ekat::subview(linoz_o3col_clim, icol);
+ const auto linoz_PmL_clim_icol = ekat::subview(linoz_PmL_clim, icol);
+ const auto linoz_dPmL_dO3_icol = ekat::subview(linoz_dPmL_dO3, icol);
+ const auto linoz_dPmL_dT_icol = ekat::subview(linoz_dPmL_dT, icol);
+ const auto linoz_dPmL_dO3col_icol =
+ ekat::subview(linoz_dPmL_dO3col, icol);
+ const auto linoz_cariolle_pscs_icol =
+ ekat::subview(linoz_cariolle_pscs, icol);
+ // Note: All variables are inputs, except for progs, which is an
+ // input/output variable.
+ mam4::microphysics::perform_atmospheric_chemistry_and_microphysics(
+ team, dt, rlats, cnst_offline_icol, forcings_in, atm, progs,
+ photo_table, chlorine_loading, config.setsox, config.amicphys,
+ config.linoz.psc_T, zenith_angle(icol), d_sfc_alb_dir_vis(icol),
+ o3_col_dens_i, photo_rates_icol, extfrc_icol, invariants_icol,
+ work_photo_table_icol, linoz_o3_clim_icol, linoz_t_clim_icol,
+ linoz_o3col_clim_icol, linoz_PmL_clim_icol, linoz_dPmL_dO3_icol,
+ linoz_dPmL_dT_icol, linoz_dPmL_dO3col_icol,
+ 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);
+ }); // parallel_for for the column loop
+ Kokkos::fence();
// postprocess output
Kokkos::parallel_for("postprocess", policy, postprocess_);
Kokkos::fence();
-}
-void MAMMicrophysics::finalize_impl() {
-}
+} // MAMMicrophysics::run_impl
-} // namespace scream
+} // namespace scream
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 5f5a44b846b..6b1dd33dfaa 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
@@ -4,25 +4,12 @@
#include
#include
#include
-
-#include "impl/mam4_amicphys.cpp" // mam4xx top-level microphysics function(s)
-
-#include
-#include
+#include "readfiles/tracer_reader_utils.hpp"
+// For calling MAM4 processes
#include
-
#include
-#ifndef KOKKOS_ENABLE_CUDA
-#define protected_except_cuda public
-#define private_except_cuda public
-#else
-#define protected_except_cuda protected
-#define private_except_cuda private
-#endif
-
-namespace scream
-{
+namespace scream {
// The process responsible for handling MAM4 aerosol microphysics. The AD
// stores exactly ONE instance of this class in its list of subcomponents.
@@ -31,28 +18,19 @@ class MAMMicrophysics final : public scream::AtmosphereProcess {
using KT = ekat::KokkosTypes;
// views for single- and multi-column data
- using view_1d_int = typename KT::template view_1d;
using view_1d = typename KT::template view_1d;
using view_2d = typename KT::template view_2d;
+ using view_3d = typename KT::template view_3d;
using const_view_1d = typename KT::template view_1d;
- using const_view_2d = typename KT::template view_2d;
-
- // unmanaged views (for buffer and workspace manager)
- using uview_1d = Unmanaged>;
- using uview_2d = Unmanaged>;
- // a quantity stored in a single vertical column with a single index
- using ColumnView = mam4::ColumnView;
+ using view_1d_host = typename KT::view_1d::HostMirror;
// a thread team dispatched to a single vertical column
using ThreadTeam = mam4::ThreadTeam;
-public:
-
+ public:
// Constructor
- MAMMicrophysics(const ekat::Comm& comm, const ekat::ParameterList& params);
-
-protected_except_cuda:
+ MAMMicrophysics(const ekat::Comm &comm, const ekat::ParameterList ¶ms);
// --------------------------------------------------------------------------
// AtmosphereProcess overrides (see share/atm_process/atmosphere_process.hpp)
@@ -60,59 +38,59 @@ class MAMMicrophysics final : public scream::AtmosphereProcess {
// process metadata
AtmosphereProcessType type() const override;
- std::string name() const override;
- // set aerosol microphysics configuration parameters (called by constructor)
- void configure(const ekat::ParameterList& params);
+ // The name of the subcomponent
+ std::string name() const { return "mam_aero_microphysics"; }
// grid
- void set_grids(const std::shared_ptr grids_manager) override;
+ void set_grids(
+ const std::shared_ptr grids_manager) override;
// management of common atm process memory
size_t requested_buffer_size_in_bytes() const override;
void init_buffers(const ATMBufferManager &buffer_manager) override;
- // process behavior
+ // Initialize variables
void initialize_impl(const RunType run_type) override;
- void run_impl(const double dt) override;
- void finalize_impl() override;
- // performs some checks on the tracers group
- void set_computed_group_impl(const FieldGroup& group) override;
+ // Run the process by one time step
+ void run_impl(const double dt) override;
-private_except_cuda:
+ // Finalize
+ void finalize_impl(){/*Do nothing*/};
+ private:
// number of horizontal columns and vertical levels
int ncol_, nlev_;
- // configuration data (for the moment, we plan to be able to move this to
- // the device, so we can't use C++ strings)
+ // The orbital year, used for zenith angle calculations:
+ // If > 0, use constant orbital year for duration of simulation
+ // If < 0, use year from timestamp for orbital parameters
+ int m_orbital_year;
+
+ // Orbital parameters, used for zenith angle calculations.
+ // If >= 0, bypass computation based on orbital year and use fixed parameters
+ // If < 0, compute based on orbital year, specified above
+ // These variables are required to be double.
+ double m_orbital_eccen; // Eccentricity
+ double m_orbital_obliq; // Obliquity
+ double m_orbital_mvelp; // Vernal Equinox Mean Longitude of Perihelion
+
struct Config {
- // photolysis parameters
- struct {
- char rsf_file[MAX_FILENAME_LEN];
- char xs_long_file[MAX_FILENAME_LEN];
- } photolysis;
// stratospheric chemistry parameters
struct {
- int o3_lbl; // number of layers with ozone decay from the surface
- int o3_sfc; // set from namelist input linoz_sfc
- int o3_tau; // set from namelist input linoz_tau
- Real psc_T; // set from namelist input linoz_psc_T
- char chlorine_loading_file[MAX_FILENAME_LEN];
+ int o3_lbl; // number of layers with ozone decay from the surface
+ Real o3_sfc; // set from namelist input linoz_sfc
+ Real o3_tau; // set from namelist input linoz_tau
+ Real psc_T; // set from namelist input linoz_psc_T
} linoz;
// aqueous chemistry parameters
mam4::mo_setsox::Config setsox;
// aero microphysics configuration (see impl/mam4_amicphys.cpp)
- impl::AmicPhysConfig amicphys;
-
- // dry deposition parameters
- struct {
- char srf_file[MAX_FILENAME_LEN];
- } drydep;
+ mam4::microphysics::AmicPhysConfig amicphys;
};
Config config_;
@@ -124,39 +102,41 @@ class MAMMicrophysics final : public scream::AtmosphereProcess {
// on host: initializes preprocess functor with necessary state data
void initialize(const int ncol, const int nlev,
- const mam_coupling::WetAtmosphere& wet_atm,
- const mam_coupling::AerosolState& wet_aero,
- const mam_coupling::DryAtmosphere& dry_atm,
- const mam_coupling::AerosolState& dry_aero) {
- ncol_ = ncol;
- nlev_ = nlev;
- wet_atm_ = wet_atm;
- wet_aero_ = wet_aero;
- dry_atm_ = dry_atm;
- dry_aero_ = dry_aero;
+ const mam_coupling::WetAtmosphere &wet_atm,
+ const mam_coupling::AerosolState &wet_aero,
+ const mam_coupling::DryAtmosphere &dry_atm,
+ const mam_coupling::AerosolState &dry_aero) {
+ ncol_pre_ = ncol;
+ nlev_pre_ = nlev;
+ wet_atm_pre_ = wet_atm;
+ wet_aero_pre_ = wet_aero;
+ dry_atm_pre_ = dry_atm;
+ dry_aero_pre_ = dry_aero;
}
KOKKOS_INLINE_FUNCTION
- void operator()(const Kokkos::TeamPolicy::member_type& team) const {
- const int i = team.league_rank(); // column index
-
- compute_vertical_layer_heights(team, dry_atm_, i);
- team.team_barrier(); // allows kernels below to use layer heights
- compute_updraft_velocities(team, wet_atm_, dry_atm_, i);
- compute_dry_mixing_ratios(team, wet_atm_, dry_atm_, i);
- compute_dry_mixing_ratios(team, wet_atm_, wet_aero_, dry_aero_, i);
+ void operator()(
+ const Kokkos::TeamPolicy::member_type &team) const {
+ const int i = team.league_rank(); // column index
+
+ compute_dry_mixing_ratios(team, wet_atm_pre_, dry_atm_pre_, i);
+ compute_dry_mixing_ratios(team, wet_atm_pre_, wet_aero_pre_,
+ dry_aero_pre_, i);
team.team_barrier();
- } // operator()
+
+ compute_vertical_layer_heights(team, dry_atm_pre_, i);
+ compute_updraft_velocities(team, wet_atm_pre_, dry_atm_pre_, i);
+ } // operator()
// number of horizontal columns and vertical levels
- int ncol_, nlev_;
+ int ncol_pre_, nlev_pre_;
// local atmospheric and aerosol state data
- mam_coupling::WetAtmosphere wet_atm_;
- mam_coupling::DryAtmosphere dry_atm_;
- mam_coupling::AerosolState wet_aero_, dry_aero_;
+ mam_coupling::WetAtmosphere wet_atm_pre_;
+ mam_coupling::DryAtmosphere dry_atm_pre_;
+ mam_coupling::AerosolState wet_aero_pre_, dry_aero_pre_;
- }; // MAMMicrophysics::Preprocess
+ }; // MAMMicrophysics::Preprocess
// Postprocessing functor
struct Postprocess {
@@ -164,33 +144,35 @@ class MAMMicrophysics final : public scream::AtmosphereProcess {
// on host: initializes postprocess functor with necessary state data
void initialize(const int ncol, const int nlev,
- const mam_coupling::WetAtmosphere& wet_atm,
- const mam_coupling::AerosolState& wet_aero,
- const mam_coupling::DryAtmosphere& dry_atm,
- const mam_coupling::AerosolState& dry_aero) {
- ncol_ = ncol;
- nlev_ = nlev;
- wet_atm_ = wet_atm;
- wet_aero_ = wet_aero;
- dry_atm_ = dry_atm;
- dry_aero_ = dry_aero;
+ const mam_coupling::WetAtmosphere &wet_atm,
+ const mam_coupling::AerosolState &wet_aero,
+ const mam_coupling::DryAtmosphere &dry_atm,
+ const mam_coupling::AerosolState &dry_aero) {
+ ncol_post_ = ncol;
+ nlev_post_ = nlev;
+ wet_atm_post_ = wet_atm;
+ wet_aero_post_ = wet_aero;
+ dry_atm_post_ = dry_atm;
+ dry_aero_post_ = dry_aero;
}
KOKKOS_INLINE_FUNCTION
- void operator()(const Kokkos::TeamPolicy::member_type& team) const {
- const int i = team.league_rank(); // column index
- compute_wet_mixing_ratios(team, dry_atm_, dry_aero_, wet_aero_, i);
+ void operator()(
+ const Kokkos::TeamPolicy::member_type &team) const {
+ const int i = team.league_rank(); // column index
+ compute_wet_mixing_ratios(team, dry_atm_post_, dry_aero_post_,
+ wet_aero_post_, i);
team.team_barrier();
- } // operator()
+ } // operator()
// number of horizontal columns and vertical levels
- int ncol_, nlev_;
+ int ncol_post_, nlev_post_;
// local atmospheric and aerosol state data
- mam_coupling::WetAtmosphere wet_atm_;
- mam_coupling::DryAtmosphere dry_atm_;
- mam_coupling::AerosolState wet_aero_, dry_aero_;
- }; // MAMMicrophysics::Postprocess
+ mam_coupling::WetAtmosphere wet_atm_post_;
+ mam_coupling::DryAtmosphere dry_atm_post_;
+ mam_coupling::AerosolState wet_aero_post_, dry_aero_post_;
+ }; // MAMMicrophysics::Postprocess
// MAM4 aerosol particle size description
mam4::AeroConfig aero_config_;
@@ -202,29 +184,63 @@ class MAMMicrophysics final : public scream::AtmosphereProcess {
// atmospheric and aerosol state variables
mam_coupling::WetAtmosphere wet_atm_;
mam_coupling::DryAtmosphere dry_atm_;
- mam_coupling::AerosolState wet_aero_, dry_aero_;
+ mam_coupling::AerosolState wet_aero_, dry_aero_;
// photolysis rate table (column-independent)
mam4::mo_photo::PhotoTableData photo_table_;
// column areas, latitudes, longitudes
- const_view_1d col_areas_, col_latitudes_, col_longitudes_;
+ const_view_1d col_latitudes_;
- // time step number
- int step_;
+ // surface albedo: shortwave, direct
+ const_view_1d d_sfc_alb_dir_vis_;
// workspace manager for internal local variables
- //ekat::WorkspaceManager workspace_mgr_;
+ // ekat::WorkspaceManager workspace_mgr_;
mam_coupling::Buffer buffer_;
// physics grid for column information
std::shared_ptr grid_;
- // sets defaults for "namelist parameters"
- void set_defaults_();
-
-}; // MAMMicrophysics
-
-} // namespace scream
-
-#endif // EAMXX_MAM_MICROPHYSICS_HPP
+ mam_coupling::TracerTimeState linoz_time_state_;
+ view_2d work_photo_table_;
+ std::vector chlorine_values_;
+ std::vector chlorine_time_secs_;
+ view_3d photo_rates_;
+
+ // invariants members
+ mam_coupling::TracerTimeState trace_time_state_;
+ std::shared_ptr TracerDataReader_;
+ std::shared_ptr TracerHorizInterp_;
+ mam_coupling::TracerData tracer_data_;
+ view_3d invariants_;
+ std::string oxid_file_name_;
+ view_2d cnst_offline_[4];
+
+ // linoz reader
+ std::shared_ptr LinozDataReader_;
+ std::shared_ptr LinozHorizInterp_;
+ mam_coupling::TracerData linoz_data_;
+ std::string linoz_file_name_;
+
+ // 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_;
+ 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];
+ view_3d extfrc_;
+ mam_coupling::ForcingHelper forcings_[mam4::gas_chemistry::extcnt];
+
+ view_1d_host acos_cosine_zenith_host_;
+ view_1d acos_cosine_zenith_;
+
+}; // MAMMicrophysics
+
+} // namespace scream
+
+#endif // EAMXX_MAM_MICROPHYSICS_HPP
diff --git a/components/eamxx/src/physics/mam/eamxx_mam_wetscav_process_interface.cpp b/components/eamxx/src/physics/mam/eamxx_mam_wetscav_process_interface.cpp
index 32148d580fe..863da8021dd 100644
--- a/components/eamxx/src/physics/mam/eamxx_mam_wetscav_process_interface.cpp
+++ b/components/eamxx/src/physics/mam/eamxx_mam_wetscav_process_interface.cpp
@@ -28,8 +28,8 @@ void MAMWetscav::set_grids(
// The units of mixing ratio Q are technically non-dimensional.
// Nevertheless, for output reasons, we like to see 'kg/kg'.
- auto q_unit = kg / kg;
- auto n_unit = 1 / kg; // units of number mixing ratios of tracers
+ auto q_unit = kg / kg;
+ auto n_unit = 1 / kg; // units of number mixing ratios of tracers
m_grid = grids_manager->get_grid("Physics");
const auto &grid_name = m_grid->name();
@@ -205,7 +205,7 @@ void MAMWetscav::set_grids(
static constexpr auto m3 = m * m * m;
// Aerosol dry particle diameter [m]
- add_field("dgncur_a", scalar3d_mid_nmodes, m, grid_name);
+ add_field("dgnum", scalar3d_mid_nmodes, m, grid_name);
// Wet aerosol density [kg/m3]
add_field("wetdens", scalar3d_mid_nmodes, kg / m3, grid_name);
@@ -475,7 +475,7 @@ void MAMWetscav::run_impl(const double dt) {
const auto wet_geometric_mean_diameter_i =
get_field_out("dgnumwet").get_view();
const auto dry_geometric_mean_diameter_i =
- get_field_out("dgncur_a").get_view();
+ get_field_out("dgnum").get_view();
const auto qaerwat = get_field_out("qaerwat").get_view();
const auto wetdens = get_field_out("wetdens").get_view();
diff --git a/components/eamxx/src/physics/mam/impl/README.md b/components/eamxx/src/physics/mam/impl/README.md
deleted file mode 100644
index f05a484a82a..00000000000
--- a/components/eamxx/src/physics/mam/impl/README.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# MAM4 Integration Code
-
-This folder contains C++ implementations of the higher-level MAM4 interface
-routines for aerosol microphysics, cloud-aerosol interactions, and optical
-properties. We've retained the overall structure of the original Fortran code
-to make it easier to understand for folks who are more familiar with the
-original implementation of MAM4.
-
-## Contents
-
-* `mam4_amicphys.cpp` - high-level MAM4 microphysics interface code
diff --git a/components/eamxx/src/physics/mam/impl/compute_o3_column_density.cpp b/components/eamxx/src/physics/mam/impl/compute_o3_column_density.cpp
deleted file mode 100644
index ceb992bc886..00000000000
--- a/components/eamxx/src/physics/mam/impl/compute_o3_column_density.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-namespace scream::impl {
-
-KOKKOS_INLINE_FUNCTION
-void compute_o3_column_density(const ThreadTeam& team, const haero::Atmosphere& atm,
- const mam4::Prognostics &progs, ColumnView o3_col_dens) {
- constexpr int gas_pcnst = mam4::gas_chemistry::gas_pcnst; // number of gas phase species
- constexpr int nfs = mam4::gas_chemistry::nfs; // number of "fixed species"
- // constexpr Real mwdry = 1.0/haero::Constants::molec_weight_dry_air;
-
- Real o3_col_deltas[mam4::nlev+1] = {}; // o3 column density above model [1/cm^2]
- // NOTE: if we need o2 column densities, set_ub_col and setcol must be changed
- Kokkos::parallel_for(Kokkos::TeamThreadRange(team, atm.num_levels()), [&](const int k) {
-
- // Real temp = atm.temperature(k);
- // Real pmid = atm.pressure(k);
- Real pdel = atm.hydrostatic_dp(k);
- // Real qv = atm.vapor_mixing_ratio(k);
-
- // ... map incoming mass mixing ratios to working array
- Real q[gas_pcnst], qqcw[gas_pcnst];
- mam_coupling::transfer_prognostics_to_work_arrays(progs, k, q, qqcw);
-
- // ... set atmosphere mean mass to the molecular weight of dry air
- // and compute water vapor vmr
- // Real mbar = mwdry;
- // Real h2ovmr = mam4::conversions::vmr_from_mmr(qv, mbar);
-
- // ... Xform from mmr to vmr
- Real vmr[gas_pcnst], vmrcw[gas_pcnst];
- mam_coupling::convert_work_arrays_to_vmr(q, qqcw, vmr, vmrcw);
-
- // ... compute invariants for this level
- Real invariants[nfs];
- // setinv(invariants, temp, h2ovmr, vmr, pmid); FIXME: not yet ported
-
- // compute the change in o3 density for this column above its neighbor
- mam4::mo_photo::set_ub_col(o3_col_deltas[k+1], vmr, invariants, pdel);
- });
- // sum the o3 column deltas to densities
- mam4::mo_photo::setcol(o3_col_deltas, o3_col_dens);
-}
-
-} // namespace scream::impl
diff --git a/components/eamxx/src/physics/mam/impl/compute_water_content.cpp b/components/eamxx/src/physics/mam/impl/compute_water_content.cpp
deleted file mode 100644
index ea7190afff9..00000000000
--- a/components/eamxx/src/physics/mam/impl/compute_water_content.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-#include
-
-namespace scream::impl {
-
-KOKKOS_INLINE_FUNCTION
-void compute_water_content(const mam4::Prognostics &progs, int k,
- Real qv, Real temp, Real pmid,
- Real dgncur_a[mam4::AeroConfig::num_modes()],
- Real dgncur_awet[mam4::AeroConfig::num_modes()],
- Real wetdens[mam4::AeroConfig::num_modes()],
- Real qaerwat[mam4::AeroConfig::num_modes()]) {
- constexpr int num_modes = mam4::AeroConfig::num_modes();
- constexpr int num_aero_ids = mam4::AeroConfig::num_aerosol_ids();
-
- // get some information about aerosol species
- // FIXME: this isn't great!
- constexpr int maxd_aspectype = mam4::water_uptake::maxd_aspectype;
- int nspec_amode[num_modes], lspectype_amode[maxd_aspectype][num_modes];
- Real specdens_amode[maxd_aspectype], spechygro[maxd_aspectype];
- mam4::water_uptake::get_e3sm_parameters(nspec_amode, lspectype_amode,
- specdens_amode, spechygro);
-
- // extract aerosol tracers for this level into state_q, which is needed
- // for computing dry aerosol properties below
- // FIXME: we should eliminate this index translation stuff
- constexpr int nvars = aero_model::pcnst;
- Real state_q[nvars]; // aerosol tracers for level k
- for (int imode = 0; imode < num_modes; ++imode) {
- int la, lc; // interstitial and cloudborne indices within state_q
-
- // number mixing ratios
- mam4::convproc::assign_la_lc(imode, -1, la, lc);
- state_q[la] = progs.n_mode_i[imode](k);
- state_q[lc] = progs.n_mode_c[imode](k);
- // aerosol mass mixing ratios
- for (int iaero = 0; iaero < num_aero_ids; ++iaero) {
- mam4::convproc::assign_la_lc(imode, iaero, la, lc);
- auto mode = static_cast(imode);
- auto aero = static_cast(iaero);
- int ispec = mam4::aerosol_index_for_mode(mode, aero);
- if (ispec != -1) {
- state_q[la] = progs.q_aero_i[imode][ispec](k);
- state_q[lc] = progs.q_aero_c[imode][ispec](k);
- }
- }
- }
-
- // compute the dry volume for each mode, and from it the current dry
- // geometric nominal particle diameter.
- // FIXME: We have to do some gymnastics here to set up the calls to
- // FIXME: calcsize. This could be improved.
- Real inv_densities[num_modes][num_aero_ids] = {};
- for (int imode = 0; imode < num_modes; ++imode) {
- const int n_spec = mam4::num_species_mode(imode);
- for (int ispec = 0; ispec < n_spec; ++ispec) {
- const int iaer = static_cast(mam4::mode_aero_species(imode, ispec));
- const Real density = mam4::aero_species(iaer).density;
- inv_densities[imode][ispec] = 1.0 / density;
- }
- }
- for (int imode = 0; imode < num_modes; ++imode) {
- Real dryvol_i, dryvol_c; // interstitial and cloudborne dry volumes
- mam4::calcsize::compute_dry_volume_k(k, imode, inv_densities, progs,
- dryvol_i, dryvol_c);
-
- // NOTE: there's some disagreement over whether vol2num should be called
- // NOTE: num2vol here, so I'm just adopting the nomenclature used by
- // NOTE: the following call to calcsize)
- const mam4::Mode& mode = mam4::modes(imode);
- Real vol2num_min = 1.0/mam4::conversions::mean_particle_volume_from_diameter(
- mode.max_diameter, mode.mean_std_dev);
- Real vol2num_max = 1.0/mam4::conversions::mean_particle_volume_from_diameter(
- mode.min_diameter, mode.mean_std_dev);
- Real vol2num;
- mam4::calcsize::update_diameter_and_vol2num(dryvol_i,
- progs.n_mode_i[imode](k), vol2num_min, vol2num_max,
- mode.min_diameter, mode.max_diameter, mode.mean_std_dev,
- dgncur_a[imode], vol2num);
- }
-
- // calculate dry aerosol properties
- Real hygro[num_modes], naer[num_modes], dryrad[num_modes],
- dryvol[num_modes], drymass[num_modes],
- rhcrystal[num_modes], rhdeliques[num_modes], specdens_1[num_modes];
- mam4::water_uptake::modal_aero_water_uptake_dryaer(nspec_amode, specdens_amode,
- spechygro, lspectype_amode, state_q, dgncur_a, hygro,
- naer, dryrad, dryvol, drymass, rhcrystal, rhdeliques, specdens_1);
-
- // calculate wet aerosol properties
- Real rh = mam4::conversions::relative_humidity_from_vapor_mixing_ratio(qv, temp, pmid);
- Real wetrad[num_modes], wetvol[num_modes], wtrvol[num_modes];
- mam4::water_uptake::modal_aero_water_uptake_wetaer(rhcrystal, rhdeliques, dgncur_a,
- dryrad, hygro, rh, naer, dryvol, wetrad, wetvol, wtrvol, dgncur_awet,
- qaerwat);
- mam4::water_uptake::modal_aero_water_uptake_wetdens(wetvol, wtrvol,
- drymass, specdens_1, wetdens);
-}
-
-} // namespace scream::impl
diff --git a/components/eamxx/src/physics/mam/impl/gas_phase_chemistry.cpp b/components/eamxx/src/physics/mam/impl/gas_phase_chemistry.cpp
deleted file mode 100644
index 5401fa5e3da..00000000000
--- a/components/eamxx/src/physics/mam/impl/gas_phase_chemistry.cpp
+++ /dev/null
@@ -1,381 +0,0 @@
-#include
-
-namespace scream::impl {
-
-using mam4::utils::min_max_bound;
-
-using HostView1D = haero::DeviceType::view_1d::HostMirror;
-using HostViewInt1D = haero::DeviceType::view_1d::HostMirror;
-
-//-------------------------------------------------------------------------
-// Reading the photolysis table
-//-------------------------------------------------------------------------
-// This logic is currently implemented using serial NetCDF calls for
-// clarity of purpose. We should probably read the data for the photolysis
-// table using SCREAM's SCORPIO interface instead, but I wanted to make
-// clear what we're trying to do in terms of "elementary" operations first.
-
-// ON HOST (MPI root rank only), reads the dimension of a NetCDF variable from
-// the file with the given ID
-int nc_dimension(const char *file, int nc_id, const char *dim_name) {
- int dim_id;
- int result = nc_inq_dimid(nc_id, dim_name, &dim_id);
- EKAT_REQUIRE_MSG(result == 0, "Error! Couldn't fetch " << dim_name <<
- " dimension ID from NetCDF file '" << file << "'\n");
- size_t dim;
- result = nc_inq_dimlen(nc_id, dim_id, &dim);
- EKAT_REQUIRE_MSG(result == 0, "Error! Couldn't fetch " << dim_name <<
- " dimension from NetCDF file '" << file << "'\n");
- return static_cast(dim);
-}
-
-// ON HOST (MPI root rank only), reads data from the given NetCDF variable from
-// the file with the given ID into the given Kokkos host View
-template
-void read_nc_var(const char *file, int nc_id, const char *var_name, V host_view) {
- int var_id;
- int result = nc_inq_varid(nc_id, var_name, &var_id);
- EKAT_REQUIRE_MSG(result == 0, "Error! Couldn't fetch ID for variable '" << var_name <<
- "' from NetCDF file '" << file << "'\n");
- result = nc_get_var(nc_id, var_id, host_view.data());
- EKAT_REQUIRE_MSG(result == 0, "Error! Couldn't read data for variable '" << var_name <<
- "' from NetCDF file '" << file << "'\n");
-}
-
-// ON HOST (MPI root rank only), reads data from the NetCDF variable with the
-// given ID, from the file with the given ID, into the given Kokkos host View
-template
-void read_nc_var(const char *file, int nc_id, int var_id, V host_view) {
- int result = nc_get_var(nc_id, var_id, host_view.data());
- EKAT_REQUIRE_MSG(result == 0, "Error! Couldn't read data for variable with ID " <<
- var_id << " from NetCDF file '" << file << "'\n");
-}
-
-// ON HOST (MPI root only), sets the lng_indexer and pht_alias_mult_1 host views
-// according to parameters in our (hardwired) chemical mechanism
-void set_lng_indexer_and_pht_alias_mult_1(const char *file, int nc_id,
- HostViewInt1D lng_indexer,
- HostView1D pht_alias_mult_1) {
- // NOTE: it seems that the chemical mechanism we're using
- // NOTE: 1. sets pht_alias_lst to a blank string [1]
- // NOTE: 2. sets pht_alias_mult_1 to 1.0 [1]
- // NOTE: 3. sets rxt_tag_lst to ['jh2o2', 'usr_HO2_HO2', 'usr_SO2_OH', 'usr_DMS_OH'] [2]
- // NOTE: References:
- // NOTE: [1] (https://github.com/eagles-project/e3sm_mam4_refactor/blob/refactor-maint-2.0/components/eam/src/chemistry/pp_linoz_mam4_resus_mom_soag/mo_sim_dat.F90#L117)
- // NOTE: [2] (https://github.com/eagles-project/e3sm_mam4_refactor/blob/refactor-maint-2.0/components/eam/src/chemistry/pp_linoz_mam4_resus_mom_soag/mo_sim_dat.F90#L99)
-
- // populate lng_indexer (see https://github.com/eagles-project/e3sm_mam4_refactor/blob/refactor-maint-2.0/components/eam/src/chemistry/mozart/mo_jlong.F90#L180)
- static const char *var_names[4] = {"jh2o2", "usr_HO2_HO2", "usr_SO2_OH", "usr_DMS_OH"};
- for (int m = 0; m < mam4::mo_photo::phtcnt; ++m) {
- int var_id;
- int result = nc_inq_varid(nc_id, var_names[m], &var_id);
- EKAT_REQUIRE_MSG(result == 0, "Error! Couldn't fetch ID for variable '"
- << var_names[m] << "' from NetCDF file '" << file << "'\n");
- lng_indexer(m) = var_id;
- }
-
- // set pht_alias_mult_1 to 1
- Kokkos::deep_copy(pht_alias_mult_1, 1.0);
-}
-
-// ON HOST (MPI root only), populates the etfphot view using rebinned
-// solar data from our solar_data_file
-void populate_etfphot(HostView1D we, HostView1D etfphot) {
- // FIXME: It looks like EAM is relying on a piece of infrastructure that
- // FIXME: we just don't have in EAMxx (eam/src/chemistry/utils/solar_data.F90).
- // FIXME: I have no idea whether EAMxx has a plan for supporting this
- // FIXME: solar irradiance / photon flux data, and I'm not going to recreate
- // FIXME: that capability here. So this is an unplugged hole.
- // FIXME:
- // FIXME: If we are going to do this the way EAM does it, the relevant logic
- // FIXME: is the call to rebin() in eam/src/chemistry/mozart/mo_jlong.F90,
- // FIXME: around line 104.
-
- // FIXME: zero the photon flux for now
- Kokkos::deep_copy(etfphot, 0);
-}
-
-// ON HOST, reads the photolysis table (used for gas phase chemistry) from the
-// files with the given names
-mam4::mo_photo::PhotoTableData read_photo_table(const ekat::Comm& comm,
- const char *rsf_file,
- const char* xs_long_file) {
- // NOTE: at the time of development, SCREAM's SCORPIO interface seems intended
- // NOTE: for domain-decomposed grid data. The files we're reading here are not
- // NOTE: spatial data, and should be the same everywhere, so we read them
- // NOTE: using serial NetCDF calls on MPI rank 0 and broadcast to other ranks.
- const int mpi_root = 0;
- int rsf_id, xs_long_id; // NetCDF file IDs (used only on MPI root)
- int nw, nump, numsza, numcolo3, numalb, nt, np_xs; // table dimensions
- if (comm.rank() == mpi_root) { // read dimension data from files and broadcast
- // open files
- int result = nc_open(rsf_file, NC_NOWRITE, &rsf_id);
- EKAT_REQUIRE_MSG(result == 0, "Error! Couldn't open rsf_file '" << rsf_file << "'\n");
- result = nc_open(xs_long_file, NC_NOWRITE, &xs_long_id);
- EKAT_REQUIRE_MSG(result == 0, "Error! Couldn't open xs_long_file '" << xs_long_file << "'\n");
-
- // read and broadcast dimension data
- nump = nc_dimension(rsf_file, rsf_id, "numz");
- numsza = nc_dimension(rsf_file, rsf_id, "numsza");
- numalb = nc_dimension(rsf_file, rsf_id, "numalb");
- numcolo3 = nc_dimension(rsf_file, rsf_id, "numcolo3fact");
- nt = nc_dimension(xs_long_file, xs_long_id, "numtemp");
- nw = nc_dimension(xs_long_file, xs_long_id, "numwl");
- np_xs = nc_dimension(xs_long_file, xs_long_id, "numprs");
-
- int dim_data[7] = {nump, numsza, numcolo3, numalb, nt, nw, np_xs};
- comm.broadcast(dim_data, 7, mpi_root);
- } else { // receive broadcasted dimension data from root rank
- int dim_data[7];
- comm.broadcast(dim_data, 7, mpi_root);
- nump = dim_data[0];
- numsza = dim_data[1];
- numcolo3 = dim_data[2];
- numalb = dim_data[3];
- nt = dim_data[4];
- nw = dim_data[5];
- np_xs = dim_data[6];
- }
-
- // set up the lng_indexer and pht_alias_mult_1 views based on our
- // (hardwired) chemical mechanism
- HostViewInt1D lng_indexer_h("lng_indexer(host)", mam4::mo_photo::phtcnt);
- HostView1D pht_alias_mult_1_h("pht_alias_mult_1(host)", 2);
- if (comm.rank() == mpi_root) {
- set_lng_indexer_and_pht_alias_mult_1(xs_long_file, xs_long_id,
- lng_indexer_h, pht_alias_mult_1_h);
- }
- comm.broadcast(lng_indexer_h.data(), mam4::mo_photo::phtcnt, mpi_root);
- comm.broadcast(pht_alias_mult_1_h.data(), 2, mpi_root);
-
- // compute the size of the foremost dimension of xsqy using lng_indexer
- int numj = 0;
- for (int m = 0; m < mam4::mo_photo::phtcnt; ++m) {
- if (lng_indexer_h(m) > 0) {
- for (int mm = 0; mm < m; ++mm) {
- if (lng_indexer_h(mm) == lng_indexer_h(m)) {
- break;
- }
- ++numj;
- }
- }
- }
-
- // allocate the photolysis table
- auto table = mam4::mo_photo::create_photo_table_data(nw, nt, np_xs, numj,
- nump, numsza, numcolo3,
- numalb);
-
- // allocate host views for table data
- auto rsf_tab_h = Kokkos::create_mirror_view(table.rsf_tab);
- auto xsqy_h = Kokkos::create_mirror_view(table.xsqy);
- auto sza_h = Kokkos::create_mirror_view(table.sza);
- auto alb_h = Kokkos::create_mirror_view(table.alb);
- auto press_h = Kokkos::create_mirror_view(table.press);
- auto colo3_h = Kokkos::create_mirror_view(table.colo3);
- auto o3rat_h = Kokkos::create_mirror_view(table.o3rat);
- auto etfphot_h = Kokkos::create_mirror_view(table.etfphot);
- auto prs_h = Kokkos::create_mirror_view(table.prs);
-
- if (comm.rank() == mpi_root) { // read data from files and broadcast
- // read file data into our host views
- read_nc_var(rsf_file, rsf_id, "pm", press_h);
- read_nc_var(rsf_file, rsf_id, "sza", sza_h);
- read_nc_var(rsf_file, rsf_id, "alb", alb_h);
- read_nc_var(rsf_file, rsf_id, "colo3fact", o3rat_h);
- read_nc_var(rsf_file, rsf_id, "colo3", colo3_h);
- read_nc_var(rsf_file, rsf_id, "RSF", rsf_tab_h);
-
- read_nc_var(xs_long_file, xs_long_id, "pressure", prs_h);
-
- // read xsqy data (using lng_indexer_h for the first index)
- int ndx = 0;
- for (int m = 0; m < mam4::mo_photo::phtcnt; ++m) {
- if (lng_indexer_h(m) > 0) {
- auto xsqy_ndx_h = ekat::subview(xsqy_h, ndx);
- read_nc_var(xs_long_file, xs_long_id, lng_indexer_h(m), xsqy_ndx_h);
- ++ndx;
- }
- }
-
- // populate etfphot by rebinning solar data
- HostView1D wc_h("wc", nw), wlintv_h("wlintv", nw), we_h("we", nw+1);
- read_nc_var(rsf_file, rsf_id, "wc", wc_h);
- read_nc_var(rsf_file, rsf_id, "wlintv", wlintv_h);
- for (int i = 0; i < nw; ++i) {
- we_h(i) = wc_h(i) - 0.5 * wlintv_h(i);
- }
- we_h(nw) = wc_h(nw-1) - 0.5 * wlintv_h(nw-1);
- populate_etfphot(we_h, etfphot_h);
-
- // close the files
- nc_close(rsf_id);
- nc_close(xs_long_id);
- }
-
- // broadcast host views from MPI root to others
- comm.broadcast(rsf_tab_h.data(), nw*numalb*numcolo3*numsza*nump, mpi_root);
- comm.broadcast(xsqy_h.data(), numj*nw*nt*np_xs, mpi_root);
- comm.broadcast(sza_h.data(), numsza, mpi_root);
- comm.broadcast(alb_h.data(), numalb, mpi_root);
- comm.broadcast(press_h.data(), nump, mpi_root);
- comm.broadcast(o3rat_h.data(), numcolo3, mpi_root);
- comm.broadcast(colo3_h.data(), nump, mpi_root);
- comm.broadcast(etfphot_h.data(), nw, mpi_root);
- comm.broadcast(prs_h.data(), np_xs, mpi_root);
-
- // copy host photolysis table into place on device
- Kokkos::deep_copy(table.rsf_tab, rsf_tab_h);
- Kokkos::deep_copy(table.xsqy, xsqy_h);
- Kokkos::deep_copy(table.sza, sza_h);
- Kokkos::deep_copy(table.alb, alb_h);
- Kokkos::deep_copy(table.press, press_h);
- Kokkos::deep_copy(table.colo3, colo3_h);
- Kokkos::deep_copy(table.o3rat, o3rat_h);
- Kokkos::deep_copy(table.etfphot, etfphot_h);
- Kokkos::deep_copy(table.prs, prs_h);
- Kokkos::deep_copy(table.pht_alias_mult_1, pht_alias_mult_1_h);
- Kokkos::deep_copy(table.lng_indexer, lng_indexer_h);
-
- // compute gradients (on device)
- Kokkos::parallel_for("del_p", nump-1, KOKKOS_LAMBDA(int i) {
- table.del_p(i) = 1.0/::abs(table.press(i)- table.press(i+1));
- });
- Kokkos::parallel_for("del_sza", numsza-1, KOKKOS_LAMBDA(int i) {
- table.del_sza(i) = 1.0/(table.sza(i+1) - table.sza(i));
- });
- Kokkos::parallel_for("del_alb", numalb-1, KOKKOS_LAMBDA(int i) {
- table.del_alb(i) = 1.0/(table.alb(i+1) - table.alb(i));
- });
- Kokkos::parallel_for("del_o3rat", numcolo3-1, KOKKOS_LAMBDA(int i) {
- table.del_o3rat(i) = 1.0/(table.o3rat(i+1) - table.o3rat(i));
- });
- Kokkos::parallel_for("dprs", np_xs-1, KOKKOS_LAMBDA(int i) {
- table.dprs(i) = 1.0/(table.prs(i) - table.prs(i+1));
- });
-
- return table;
-}
-
-// performs gas phase chemistry calculations on a single level of a single
-// atmospheric column
-KOKKOS_INLINE_FUNCTION
-void gas_phase_chemistry(Real zm, Real zi, Real phis, Real temp, Real pmid, Real pdel, Real dt,
- const Real photo_rates[mam4::mo_photo::phtcnt], // in
- const Real extfrc[mam4::gas_chemistry::extcnt], // in
- Real q[mam4::gas_chemistry::gas_pcnst], // VMRs, inout
- Real invariants[mam4::gas_chemistry::nfs]) { // out
- // constexpr Real rga = 1.0/haero::Constants::gravity;
- // constexpr Real m2km = 0.01; // converts m -> km
-
- // The following things are chemical mechanism dependent! See mam4xx/src/mam4xx/gas_chem_mechanism.hpp)
- constexpr int gas_pcnst = mam4::gas_chemistry::gas_pcnst; // number of gas phase species
- constexpr int rxntot = mam4::gas_chemistry::rxntot; // number of chemical reactions
- constexpr int extcnt = mam4::gas_chemistry::extcnt; // number of species with external forcing
- constexpr int indexm = 0; // FIXME: index of total atm density in invariants array
-
- constexpr int phtcnt = mam4::mo_photo::phtcnt; // number of photolysis reactions
-
- constexpr int itermax = mam4::gas_chemistry::itermax;
- constexpr int clscnt4 = mam4::gas_chemistry::clscnt4;
-
- // NOTE: vvv these arrays were copied from mam4xx/gas_chem_mechanism.hpp vvv
- constexpr int permute_4[gas_pcnst] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
- 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
- 20, 21, 22, 23, 24, 25, 26, 27, 28, 29};
- constexpr int clsmap_4[gas_pcnst] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
- 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
- 21, 22, 23, 24, 25, 26, 27, 28, 29, 30};
-
- // These indices for species are fixed by the chemical mechanism
- // std::string solsym[] = {"O3", "H2O2", "H2SO4", "SO2", "DMS", "SOAG",
- // "so4_a1", "pom_a1", "soa_a1", "bc_a1", "dst_a1",
- // "ncl_a1", "mom_a1", "num_a1", "so4_a2", "soa_a2",
- // "ncl_a2", "mom_a2", "num_a2", "dst_a3", "ncl_a3",
- // "so4_a3", "bc_a3", "pom_a3", "soa_a3", "mom_a3",
- // "num_a3", "pom_a4", "bc_a4", "mom_a4", "num_a4"};
- constexpr int ndx_h2so4 = 2;
- // std::string extfrc_list[] = {"SO2", "so4_a1", "so4_a2", "pom_a4", "bc_a4",
- // "num_a1", "num_a2", "num_a3", "num_a4", "SOAG"};
- constexpr int synoz_ndx = -1;
-
- // fetch the zenith angle (not its cosine!) in degrees for this column.
- // FIXME: For now, we fix the zenith angle. At length, we need to compute it
- // FIXME: from EAMxx's current set of orbital parameters, which requires some
- // FIXME: conversation with the EAMxx team.
-
- // xform geopotential height from m to km and pressure from Pa to mb
- // Real zsurf = rga * phis;
- // Real zmid = m2km * (zm + zsurf);
-
- // ... compute the column's invariants
- // Real h2ovmr = q[0];
- // setinv(invariants, temp, h2ovmr, q, pmid); FIXME: not ported yet
-
- // ... set rates for "tabular" and user specified reactions
- Real reaction_rates[rxntot];
- mam4::gas_chemistry::setrxt(reaction_rates, temp);
-
- // set reaction rates based on chemical invariants
- // (indices (ndxes?) are taken from mam4 validation data and translated from
- // 1-based indices to 0-based indices)
- int usr_HO2_HO2_ndx = 1, usr_DMS_OH_ndx = 5,
- usr_SO2_OH_ndx = 3, inv_h2o_ndx = 3;
- mam4::gas_chemistry::usrrxt(reaction_rates, temp, invariants, invariants[indexm],
- usr_HO2_HO2_ndx, usr_DMS_OH_ndx,
- usr_SO2_OH_ndx, inv_h2o_ndx);
- mam4::gas_chemistry::adjrxt(reaction_rates, invariants, invariants[indexm]);
-
- //===================================
- // Photolysis rates at time = t(n+1)
- //===================================
-
- // compute the rate of change from forcing
- Real extfrc_rates[extcnt]; // [1/cm^3/s]
- for (int mm = 0; mm < extcnt; ++mm) {
- if (mm != synoz_ndx) {
- extfrc_rates[mm] = extfrc[mm] / invariants[indexm];
- }
- }
-
- // ... Form the washout rates
- Real het_rates[gas_pcnst];
- // FIXME: not ported yet
- //sethet(het_rates, pmid, zmid, phis, temp, cmfdqr, prain, nevapr, delt,
- // invariants[indexm], q);
-
-
- // save h2so4 before gas phase chem (for later new particle nucleation)
- Real del_h2so4_gasprod = q[ndx_h2so4];
-
- //===========================
- // Class solution algorithms
- //===========================
-
- // copy photolysis rates into reaction_rates (assumes photolysis rates come first)
- for (int i = 0; i < phtcnt; ++i) {
- reaction_rates[i] = photo_rates[i];
- }
-
- // ... solve for "Implicit" species
- bool factor[itermax];
- for (int i = 0; i < itermax; ++i) {
- factor[i] = true;
- }
-
- // initialize error tolerances
- Real epsilon[clscnt4];
- mam4::gas_chemistry::imp_slv_inti(epsilon);
-
- // solve chemical system implicitly
- Real prod_out[clscnt4], loss_out[clscnt4];
- mam4::gas_chemistry::imp_sol(q, reaction_rates, het_rates, extfrc_rates, dt,
- permute_4, clsmap_4, factor, epsilon, prod_out, loss_out);
-
- // save h2so4 change by gas phase chem (for later new particle nucleation)
- if (ndx_h2so4 > 0) {
- del_h2so4_gasprod = q[ndx_h2so4] - del_h2so4_gasprod;
- }
-}
-
-} // namespace scream::impl
diff --git a/components/eamxx/src/physics/mam/impl/mam4_amicphys.cpp b/components/eamxx/src/physics/mam/impl/mam4_amicphys.cpp
deleted file mode 100644
index 71cee1eeb40..00000000000
--- a/components/eamxx/src/physics/mam/impl/mam4_amicphys.cpp
+++ /dev/null
@@ -1,1769 +0,0 @@
-#include
-#include
-#include
-#include
-#include