diff --git a/components/eamxx/cime_config/namelist_defaults_scream.xml b/components/eamxx/cime_config/namelist_defaults_scream.xml index 51b79e5935a..3e277f751b1 100644 --- a/components/eamxx/cime_config/namelist_defaults_scream.xml +++ b/components/eamxx/cime_config/namelist_defaults_scream.xml @@ -252,18 +252,28 @@ be lost if SCREAM_HACK_XML is not enabled. + + true + true + true + true + + 172800.0 + 3.0E-008 + 4 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 + ${DIN_LOC_ROOT}/atm/scream/mam4xx/linoz/ne30pg2/linoz1850-2015_2010JPL_CMIP6_10deg_58km_ne30pg2_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 - + ${DIN_LOC_ROOT}/atm/scream/mam4xx/invariants/ne30pg2/oxid_1.9x2.5_L26_1850-2015_ne30pg2_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 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 86f3100616a..2cc611c0ed6 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,37 +1,20 @@ -#include // for serial NetCDF file reads on MPI root - -#include -#include #include -#include -#include -#include - -#include "scream_config.h" // for SCREAM_CIME_BUILD -#include "share/util/scream_common_physics_functions.hpp" -// NOTE: see the impl/ directory for the contents of the impl namespace +// impl namespace for some driver level functions for microphysics #include "impl/compute_o3_column_density.cpp" #include "impl/gas_phase_chemistry.cpp" #include "physics/rrtmgp/shr_orb_mod_c2f.hpp" -// For EKAT units package -#include "ekat/util/ekat_units.hpp" - -// For EKAT subview -#include -#include - -/* When the preprocessor definition ENABLE_OUTPUT_TRACER_FIELDS is -enabled, the fields oxi_fields, linoz_fields, and -vertical_emission_fields will be saved. -These fields, which are the output of the tracer reader, - have been stored in the FM for evaluation purposes. - There are 33 fields (ncolxnlev) in total. - Therefore, it is recommended to disable - the ENABLE_OUTPUT_TRACER_FIELDS preprocessor - definition once the evaluation of the microphysics interface - is completed. */ +// When the preprocessor definition ENABLE_OUTPUT_TRACER_FIELDS is +// enabled, the fields oxi_fields, linoz_fields, and +// vertical_emission_fields will be saved. +// These fields, which are the output of the tracer reader, +// have been stored in the FM for evaluation purposes. +// There are 33 fields (ncolxnlev) in total. +// Therefore, it is recommended to disable +// the ENABLE_OUTPUT_TRACER_FIELDS preprocessor +// definition once the evaluation of the microphysics interface +// is completed. #define ENABLE_OUTPUT_TRACER_FIELDS namespace scream { @@ -39,33 +22,50 @@ namespace scream { MAMMicrophysics::MAMMicrophysics(const ekat::Comm &comm, const ekat::ParameterList ¶ms) : AtmosphereProcess(comm, params), aero_config_() { - configure(params); -} + // Asserts for the runtime or namelist options + // ----- Aerosol Microphysics processes on/off switches ------- + EKAT_REQUIRE_MSG(m_params.isParameter("mam4_do_cond"), + "ERROR: mam4_do_cond is missing from mam4_aero_microphys " + "parameter list."); -AtmosphereProcessType MAMMicrophysics::type() const { - return AtmosphereProcessType::Physics; -} + EKAT_REQUIRE_MSG( + m_params.isParameter("mam4_do_rename"), + "ERROR: mam4_do_rename is missing from mam4_aero_microphys " + "parameter list."); -namespace { + EKAT_REQUIRE_MSG(m_params.isParameter("mam4_do_newnuc"), + "ERROR: mam4_do_newnuc is missing from mam4_aero_microphys " + " parameter list."); -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); -} + EKAT_REQUIRE_MSG(m_params.isParameter("mam4_do_coag"), + "ERROR: mam4_do_coag is missing from mam4_aero_microphys " + "parameter list."); + // ----- LINOZ namelist parameters ------- + + EKAT_REQUIRE_MSG( + m_params.isParameter("mam4_o3_tau"), + "ERROR: mam4_o3_tau is missing from mam4_aero_microphys parameter list."); + + EKAT_REQUIRE_MSG(m_params.isParameter("mam4_o3_sfc"), + "ERROR: mam4_o3_sfc is missing from mam4_aero_microphys " + "parameter list."); -} // namespace + EKAT_REQUIRE_MSG(m_params.isParameter("mam4_o3_lbl"), + "ERROR: mam4_o3_lbl is missing from mam4_aero_microphys " + "parameter list."); -#define set_file_location(data_file, path) \ - set_data_file(#data_file, path, data_file) + set_namelist_params_(); +} + +AtmosphereProcessType MAMMicrophysics::type() const { + return AtmosphereProcessType::Physics; +} -void MAMMicrophysics::set_defaults_() { - config_.amicphys.do_cond = true; - config_.amicphys.do_rename = true; - config_.amicphys.do_newnuc = true; - config_.amicphys.do_coag = true; +void MAMMicrophysics::set_namelist_params_() { + 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"); // these parameters guide the coupling between parameterizations // NOTE: mam4xx was ported with these parameters fixed, so it's probably not @@ -73,41 +73,19 @@ void MAMMicrophysics::set_defaults_() { config_.amicphys.gaexch_h2so4_uptake_optaa = 2; config_.amicphys.newnuc_h2so4_conc_optaa = 2; - //=========================================================== - // default data file locations (relative to SCREAM_DATA_DIR) - //=========================================================== - - // many of these paths were extracted from - // e3smv2/bld/namelist_files/namelist_defaults_eam.xml - - // 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"); - - // stratospheric chemistry - // set_file_location(config_.linoz.chlorine_loading_file, - // "../cam/chem/trop_mozart/ub/Linoz_Chlorine_Loading_CMIP6_0003-2017_c20171114.nc"); - - // dry deposition - set_file_location(config_.drydep.srf_file, - "../cam/chem/trop_mam/atmsrf_ne4pg2_200527.nc"); -} - -void MAMMicrophysics::configure(const ekat::ParameterList ¶ms) { - set_defaults_(); + // LINOZ namelist parameters + o3_lbl_ = m_params.get("mam4_o3_lbl"); + o3_tau_ = m_params.get("mam4_o3_tau"); + o3_sfc_ = m_params.get("mam4_o3_sfc"); } +// ================================================================ +// SET_GRIDS +// ================================================================ void MAMMicrophysics::set_grids( const std::shared_ptr grids_manager) { - using namespace ekat::units; - - 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); - + // set grid for all the inputs and outputs + // use physics grid grid_ = grids_manager->get_grid("Physics"); const auto &grid_name = grid_->name(); @@ -116,118 +94,142 @@ void MAMMicrophysics::set_grids( // get column geometry and locations col_latitudes_ = grid_->get_geometry_data("lat").get_view(); - col_longitudes_ = grid_->get_geometry_data("lon").get_view(); // define the different field layouts that will be used for this process using namespace ShortFieldTagsNames; - // layout for 2D (1d horiz X 1d vertical) variable - FieldLayout scalar2d_layout_col{{COL}, {ncol_}}; + // Layout for 2D (2d horiz) variable + const FieldLayout scalar2d = grid_->get_2d_scalar_layout(); - // layout for 3D (2d horiz X 1d vertical) variables - FieldLayout scalar3d_layout_mid{{COL, LEV}, {ncol_, nlev_}}; - // At interfaces - FieldLayout scalar3d_layout_int{{COL, ILEV}, {ncol_, nlev_ + 1}}; + // 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); + + 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 variables are "Required" or pure inputs for the process + // -------------------------------------------------------------------------- + + // ----------- Atmospheric quantities ------------- + + // Specific humidity [kg/kg](Require only for building DS) + add_field("qv", scalar3d_mid, q_unit, grid_name, "tracers"); + + // Cloud liquid mass mixing ratio [kg/kg](Require only for building DS) + add_field("qc", scalar3d_mid, q_unit, grid_name, "tracers"); + + // Cloud ice mass mixing ratio [kg/kg](Require only for building DS) + add_field("qi", scalar3d_mid, q_unit, grid_name, "tracers"); + + // Cloud liquid number mixing ratio [1/kg](Require only for building DS) + add_field("nc", scalar3d_mid, n_unit, grid_name, "tracers"); + + // Cloud ice number mixing ratio [1/kg](Require only for building DS) + add_field("ni", scalar3d_mid, n_unit, grid_name, "tracers"); + + // Temperature[K] at midpoints + add_field("T_mid", scalar3d_mid, K, grid_name); + + // Vertical pressure velocity [Pa/s] at midpoints (Require only for building + // DS) + add_field("omega", scalar3d_mid, Pa / s, grid_name); + + // Total pressure [Pa] at midpoints + add_field("p_mid", scalar3d_mid, Pa, grid_name); + + // Total pressure [Pa] at interfaces + add_field("p_int", scalar3d_int, Pa, grid_name); + + // Layer thickness(pdel) [Pa] at midpoints + add_field("pseudo_density", scalar3d_mid, Pa, grid_name); + + // Planetary boundary layer height [m] + add_field("pbl_height", scalar2d, m, grid_name); + + constexpr auto m2 = pow(m, 2); + constexpr auto s2 = pow(s, 2); + + // Surface geopotential [m2/s2] + add_field("phis", scalar2d, m2 / s2, grid_name); + + //----------- Variables from microphysics scheme ------------- + constexpr auto nondim = ekat::units::Units::nondimensional(); + // Total cloud fraction [fraction] + add_field("cldfrac_liq", scalar3d_mid, nondim, grid_name); + + //----------- Variables from other mam4xx processes ------------ + // Number of modes + constexpr int nmodes = mam4::AeroConfig::num_modes(); - const int nmodes = mam4::AeroConfig::num_modes(); // Number of modes // layout for 3D (ncol, nmodes, nlevs) FieldLayout scalar3d_mid_nmodes = grid_->get_3d_vector_layout(true, nmodes, "nmodes"); - static constexpr auto m3 = m * m * m; - // Aerosol dry particle diameter [m] + // 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); - // Wet aerosol density [kg/m3] + constexpr auto m3 = pow(m, 3); + // Wet density of interstitial aerosol [kg/m3] add_field("wetdens", scalar3d_mid_nmodes, kg / m3, grid_name); - // Wet Required diameter [m] - add_field("dgnumwet", scalar3d_mid_nmodes, m, grid_name); + //----------- Variables from coupler (land component)--------- + // surface albedo shortwave, direct + add_field("sfc_alb_dir_vis", scalar2d, nondim, grid_name); - // define fields needed in mam4xx + // --------------------------------------------------------------------- + // These variables are "updated" or inputs/outputs for the process + // --------------------------------------------------------------------- - // 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 - // Total pressure [Pa] at interfaces - add_field("p_int", scalar3d_layout_int, Pa, grid_name); - add_field("qv", scalar3d_layout_mid, kg / kg, grid_name, - "tracers"); // specific humidity - add_field("qi", scalar3d_layout_mid, kg / kg, grid_name, - "tracers"); // ice wet mixing ratio - add_field("ni", scalar3d_layout_mid, n_unit, grid_name, - "tracers"); // ice number mixing ratio - 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_field("sfc_alb_dir_vis", scalar2d_layout_col, nondim, - grid_name); // surface albedo shortwave, direct - - // droplet activation can alter cloud liquid and number mixing ratios - add_field("qc", scalar3d_layout_mid, kg / kg, grid_name, - "tracers"); // cloud liquid wet mixing ratio - add_field("nc", scalar3d_layout_mid, n_unit, grid_name, - "tracers"); // cloud liquid wet number mixing ratio - - // 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 + // (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); - add_field(int_nmr_field_name, scalar3d_layout_mid, n_unit, - grid_name, "tracers"); - - // cloudborne aerosol tracers of interest: number (n) mixing ratios - // NOTE: DO NOT add cld borne aerosols to the "tracer" group as these are - // NOT advected - const char *cld_nmr_field_name = mam_coupling::cld_aero_nmr_field_name(m); - add_field(cld_nmr_field_name, scalar3d_layout_mid, n_unit, - grid_name); + add_field(int_nmr_field_name, scalar3d_mid, n_unit, grid_name, + "tracers"); 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) { - add_field(int_mmr_field_name, scalar3d_layout_mid, kg / kg, - grid_name, "tracers"); + add_field(int_mmr_field_name, scalar3d_mid, q_unit, grid_name, + "tracers"); } + } // 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); - // (cloudborne) aerosol tracers of interest: mass (q) mixing ratios - // NOTE: DO NOT add cld borne aerosols to the "tracer" group as these are - // NOT advected + 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_layout_mid, kg / kg, - grid_name); + add_field(cld_mmr_field_name, scalar3d_mid, q_unit, grid_name); } - } // end for loop for num species - } // end for loop for num modes + } // 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); - add_field(gas_mmr_field_name, scalar3d_layout_mid, kg / kg, - grid_name, "tracers"); + add_field(gas_mmr_field_name, scalar3d_mid, q_unit, grid_name, + "tracers"); } - // 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); // 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"); - std::string spa_map_file = ""; + std::string linoz_map_file = ""; std::vector var_names{"o3_clim", "o3col_clim", "t_clim", "PmL_clim", "dPmL_dO3", "dPmL_dT", "dPmL_dO3col", "cariolle_pscs"}; @@ -237,7 +239,7 @@ void MAMMicrophysics::set_grids( 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_, spa_map_file, var_names, linoz_data_); + grid_, linoz_file_name_, linoz_map_file, var_names, linoz_data_); LinozDataReader_ = scream::mam_coupling::create_tracer_data_reader( LinozHorizInterp_, linoz_file_name_); @@ -266,11 +268,11 @@ void MAMMicrophysics::set_grids( linoz_cariolle_psc Cariolle parameter for PSC loss of ozone [1/s] */ add_field("linoz_fields", scalar3d_mid_linoz, nondim, grid_name); #endif - } + } // LINOZ reader { oxid_file_name_ = m_params.get("mam4_oxid_file_name"); - std::string spa_map_file = ""; + std::string oxid_map_file = ""; // NOTE: order matches mam4xx: std::vector var_names{"O3", "OH", "NO3", "HO2"}; @@ -279,7 +281,7 @@ void MAMMicrophysics::set_grids( scream::mam_coupling::setup_tracer_data(tracer_data_, oxid_file_name_, oxid_ymd); TracerHorizInterp_ = scream::mam_coupling::create_horiz_remapper( - grid_, oxid_file_name_, spa_map_file, var_names, tracer_data_); + grid_, oxid_file_name_, oxid_map_file, var_names, tracer_data_); TracerDataReader_ = scream::mam_coupling::create_tracer_data_reader( TracerHorizInterp_, oxid_file_name_); @@ -302,11 +304,11 @@ void MAMMicrophysics::set_grids( // NOTE: Assuming nondim for units. add_field("oxi_fields", scalar3d_mid_oxi, nondim, grid_name); #endif - } + } // oxid file reader { // FIXME: I will need to add this file per forcing file. - std::string spa_map_file = ""; + std::string extfrc_map_file = ""; // NOTE: order of forcing species is important. // extfrc_lst(: 9) = {'SO2 ','so4_a1 ','so4_a2 // ','pom_a4 ','bc_a4 ', 'num_a1 ','num_a2 @@ -347,7 +349,7 @@ void MAMMicrophysics::set_grids( 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, spa_map_file, var_names, data_tracer); + 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); @@ -395,35 +397,27 @@ void MAMMicrophysics::set_grids( add_field("vertical_emission_fields", scalar3d_mid_emis_ver, nondim, grid_name); #endif - } -} - -// 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 = 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 " - << group.m_info->size() << " " << num_tracers << " " - << mam_coupling::num_aero_modes() << " " - << mam_coupling::num_aero_tracers() << " " - << mam_coupling::num_aero_gases() << "aerosol tracers."); -} + } // 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_); } +// ================================================================ +// 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. + void MAMMicrophysics::init_buffers(const ATMBufferManager &buffer_manager) { EKAT_REQUIRE_MSG( buffer_manager.allocated_bytes() >= requested_buffer_size_in_bytes(), @@ -436,6 +430,9 @@ void MAMMicrophysics::init_buffers(const ATMBufferManager &buffer_manager) { "Error! Used memory != requested memory for MAMMicrophysics."); } +// ================================================================ +// INITIALIZE_IMPL +// ================================================================ void MAMMicrophysics::initialize_impl(const RunType run_type) { // Determine orbital year. If orbital_year is negative, use current year // from timestamp for orbital year; if positive, use provided orbital year @@ -447,11 +444,16 @@ void MAMMicrophysics::initialize_impl(const RunType run_type) { m_orbital_obliq = m_params.get("orbital_obliquity", -9999); m_orbital_mvelp = m_params.get("orbital_mvelp", -9999); - // populate the wet and dry atmosphere states with views from fields and - // the buffer + // --------------------------------------------------------------- + // 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_out("qc").get_view(); - wet_atm_.nc = get_field_out("nc").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(); @@ -459,8 +461,7 @@ void MAMMicrophysics::initialize_impl(const RunType run_type) { 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_tot") - .get_view(); // FIXME: tot or liq? + 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(); @@ -478,10 +479,6 @@ void MAMMicrophysics::initialize_impl(const RunType run_type) { // get surface albedo: shortwave, direct d_sfc_alb_dir_vis_ = get_field_in("sfc_alb_dir_vis").get_view(); - // perform any initialization work - if(run_type == RunType::Initial) { - } - // 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) { @@ -515,8 +512,8 @@ void MAMMicrophysics::initialize_impl(const RunType run_type) { 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) { @@ -531,11 +528,6 @@ void MAMMicrophysics::initialize_impl(const RunType run_type) { m_params.get("mam4_xs_long_file"); photo_table_ = impl::read_photo_table(rsf_file, 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_); // set field property checks for the fields in this process /* e.g. @@ -547,14 +539,6 @@ 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] @@ -573,9 +557,6 @@ void MAMMicrophysics::initialize_impl(const RunType run_type) { auto linoz_cariolle_pscs = buffer_.scratch[7]; // Cariolle parameter for PSC loss of ozone [1/s] - // const std::string linoz_chlorine_file = - // "Linoz_Chlorine_Loading_CMIP6_0003-2017_c20171114.nc"; auto ts = - // timestamp(); int chlorine_loading_ymd=20100101; auto ts = timestamp(); std::string linoz_chlorine_file = m_params.get("mam4_linoz_chlorine_file"); @@ -583,7 +564,7 @@ void MAMMicrophysics::initialize_impl(const RunType run_type) { 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); @@ -591,10 +572,9 @@ void MAMMicrophysics::initialize_impl(const RunType run_type) { // 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 spa_end. - // Note: At the first time step, the data will be moved into spa_beg, - // and spa_end will be reloaded from file with the new month. + // 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( @@ -618,12 +598,20 @@ void MAMMicrophysics::initialize_impl(const RunType run_type) { // 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 // ================================================================ // 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_); @@ -641,20 +629,20 @@ void MAMMicrophysics::run_impl(const double dt) { 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)] + // 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] - 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] + // 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; @@ -679,8 +667,12 @@ void MAMMicrophysics::run_impl(const double dt) { // addition of dt */ trace_time_state_.t_now = ts.frac_of_year_in_days(); scream::mam_coupling::advance_tracer_data( - TracerDataReader_, *TracerHorizInterp_, ts, trace_time_state_, - tracer_data_, dry_atm_.p_mid, dry_atm_.z_iface, cnst_offline_); + 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(); #if defined(ENABLE_OUTPUT_TRACER_FIELDS) @@ -694,8 +686,12 @@ void MAMMicrophysics::run_impl(const double dt) { #endif scream::mam_coupling::advance_tracer_data( - LinozDataReader_, *LinozHorizInterp_, ts, linoz_time_state_, linoz_data_, - dry_atm_.p_mid, dry_atm_.z_iface, linoz_output); + 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(); #if defined(ENABLE_OUTPUT_TRACER_FIELDS) @@ -738,7 +734,6 @@ void MAMMicrophysics::run_impl(const double dt) { #endif const_view_1d &col_latitudes = col_latitudes_; - // const_view_1d &col_longitudes = col_longitudes_; const_view_1d &d_sfc_alb_dir_vis = d_sfc_alb_dir_vis_; mam_coupling::DryAtmosphere &dry_atm = dry_atm_; @@ -779,8 +774,8 @@ void MAMMicrophysics::run_impl(const double dt) { // Use the orbital parameters to calculate the solar declination and // eccentricity factor Real delta, eccf; - auto calday = ts2.frac_of_year_in_days() + - 1; // Want day + fraction; calday 1 == Jan 1 0Z + // 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 { @@ -791,8 +786,7 @@ void MAMMicrophysics::run_impl(const double dt) { // 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. + // 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++) { @@ -830,44 +824,40 @@ void MAMMicrophysics::run_impl(const double dt) { clsmap_4[i] = mam4::gas_chemistry::clsmap_4[i]; permute_4[i] = mam4::gas_chemistry::permute_4[i]; } + // LINOZ parameters from the namelist + const int o3_lbl = o3_lbl_; + const Real o3_tau = o3_tau_; + const Real o3_sfc = o3_sfc_; + // 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?) - Real col_lat = col_latitudes(icol); // column latitude (degrees?) - // Real col_lon = col_longitudes(icol); // column longitude - - Real rlats = - col_lat * M_PI / 180.0; // convert column latitude to radians - // Real rlons = - // col_lon * M_PI / 180.0; // convert column longitude to radians + // convert column latitude to radians + const Real rlats = col_lat * M_PI / 180.0; // 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); - - auto wet_diameter_icol = + const auto atm = mam_coupling::atmosphere_for_column(dry_atm, icol); + const auto wet_diameter_icol = ekat::subview(wet_geometric_mean_diameter_i, icol); - auto dry_diameter_icol = + const auto dry_diameter_icol = ekat::subview(dry_geometric_mean_diameter_i, icol); - auto wetdens_icol = ekat::subview(wetdens, 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); - // set up diagnostics - mam4::Diagnostics diags(nlev); const auto invariants_icol = ekat::subview(invariants, icol); mam4::mo_setext::Forcing forcings_in[extcnt]; for(int i = 0; i < extcnt; ++i) { - int nsectors = forcings[i].nsectors; - int frc_ndx = forcings[i].frc_ndx; - auto file_alt_data = forcings[i].file_alt_data; + 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; @@ -887,16 +877,18 @@ void MAMMicrophysics::run_impl(const double dt) { for(int i = 0; i < mam4::mo_setinv::num_tracer_cnst; ++i) { cnst_offline_icol[i] = ekat::subview(cnst_offline[i], icol); } - - mam4::mo_setinv::setinv(team, invariants_icol, atm.temperature, - atm.vapor_mixing_ratio, cnst_offline_icol, - atm.pressure); + mam4::mo_setinv::setinv(team, // in + invariants_icol, // out + atm.temperature, atm.vapor_mixing_ratio, // in + cnst_offline_icol, atm.pressure); // in // 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, invariants_icol, - adv_mass_kg_per_moles, o3_col_dens_i); + impl::compute_o3_column_density(team, atm, progs, // in + invariants_icol, // in + adv_mass_kg_per_moles, // in + o3_col_dens_i); // out // set up photolysis work arrays for this column. mam4::mo_photo::PhotoTableWorkArrays photo_work_arrays_icol; @@ -906,14 +898,18 @@ void MAMMicrophysics::run_impl(const double dt) { // set work view using 1D photo_work_arrays_icol // Note: We are not allocating views here. mam4::mo_photo::set_photo_table_work_arrays( - photo_table, work_photo_table_icol, photo_work_arrays_icol); + photo_table, work_photo_table_icol, // in + photo_work_arrays_icol); // out const auto &photo_rates_icol = ekat::subview(photo_rates, icol); mam4::mo_photo::table_photo( - photo_rates_icol, atm.pressure, atm.hydrostatic_dp, atm.temperature, - o3_col_dens_i, zenith_angle(icol), d_sfc_alb_dir_vis(icol), - atm.liquid_mixing_ratio, atm.cloud_fraction, eccf, photo_table, - photo_work_arrays_icol); + photo_rates_icol, // out + atm.pressure, atm.hydrostatic_dp, // in + atm.temperature, o3_col_dens_i, // in + zenith_angle(icol), d_sfc_alb_dir_vis(icol), // in + atm.liquid_mixing_ratio, atm.cloud_fraction, // in + eccf, photo_table, // in + photo_work_arrays_icol); // out // compute aerosol microphysics on each vertical level within this // column @@ -924,7 +920,6 @@ void MAMMicrophysics::run_impl(const double dt) { Real pmid = atm.pressure(kk); Real pdel = atm.hydrostatic_dp(kk); Real zm = atm.height(kk); - Real zi = z_iface(kk); Real pblh = atm.planetary_boundary_layer_height; Real qv = atm.vapor_mixing_ratio(kk); Real cldfrac = atm.cloud_fraction(kk); @@ -1016,8 +1011,6 @@ void MAMMicrophysics::run_impl(const double dt) { } // do aerosol microphysics (gas-aerosol exchange, nucleation, // coagulation) - // FIXME: Verify cldfrac is the right one by looking at EAM - // variable impl::modal_aero_amicphys_intr( // in config.amicphys, dt, temp, pmid, pdel, zm, pblh, qv, cldfrac, @@ -1027,6 +1020,8 @@ void MAMMicrophysics::run_impl(const double dt) { vmr0, vmr_pregas, vmr_precld, dgncur_a_kk, dgncur_awet_kk, wetdens_kk); + mam_coupling::vmr2mmr(vmrcw, adv_mass_kg_per_moles, qqcw); + //----------------- // LINOZ chemistry //----------------- @@ -1036,25 +1031,38 @@ void MAMMicrophysics::run_impl(const double dt) { Real do3_linoz, do3_linoz_psc, ss_o3, o3col_du_diag, o3clim_linoz_diag, zenith_angle_degrees; - int o3_ndx = 0; // index of "O3" in solsym array (in EAM) + // index of "O3" in solsym array (in EAM) + constexpr int o3_ndx = static_cast(mam4::GasId::O3); mam4::lin_strat_chem::lin_strat_chem_solve_kk( + // in o3_col_dens_i(kk), temp, zenith_angle(icol), pmid, dt, rlats, linoz_o3_clim(icol, kk), linoz_t_clim(icol, kk), linoz_o3col_clim(icol, kk), linoz_PmL_clim(icol, kk), linoz_dPmL_dO3(icol, kk), linoz_dPmL_dT(icol, kk), linoz_dPmL_dO3col(icol, kk), linoz_cariolle_pscs(icol, kk), - 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 (kk > 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); - }*/ - - // ... check for negative values and reset to zero + chlorine_loading, config.linoz.psc_T, + // out + vmr[o3_ndx], + // outputs that are not used + 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(kk >= nlev - o3_lbl) { + Real o3l_vmr, do3mass; + // initial O3 vmr + o3l_vmr = vmr[o3_ndx]; + // get new value of O3 vmr + o3l_vmr = + mam4::lin_strat_chem::lin_strat_sfcsink_kk(dt, pdel, // in + o3l_vmr, // out + o3_sfc, // in + o3_tau, // in + do3mass); // out + // Update the mixing ratio (vmr) for O3 + vmr[o3_ndx] = o3l_vmr; + } + // 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; } @@ -1063,11 +1071,10 @@ void MAMMicrophysics::run_impl(const double dt) { // Dry deposition (gas) //---------------------- - // FIXME: C++ port in progress! + // FIXME: drydep integration in progress! // mam4::drydep::drydep_xactive(...); mam_coupling::vmr2mmr(vmr, adv_mass_kg_per_moles, qq); - mam_coupling::vmr2mmr(vmrcw, adv_mass_kg_per_moles, qqcw); for(int i = offset_aerosol; i < pcnst; ++i) { state_q[i] = qq[i - offset_aerosol]; 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 a628cb9fada..a5209521a15 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 @@ -48,9 +48,6 @@ class MAMMicrophysics final : public scream::AtmosphereProcess { // The name of the subcomponent std::string name() const { return "mam_aero_microphysics"; } - // set aerosol microphysics configuration parameters (called by constructor) - void configure(const ekat::ParameterList ¶ms); - // grid void set_grids( const std::shared_ptr grids_manager) override; @@ -68,13 +65,14 @@ class MAMMicrophysics final : public scream::AtmosphereProcess { // Finalize void finalize_impl(){/*Do nothing*/}; - // performs some checks on the tracers group - void set_computed_group_impl(const FieldGroup &group) override; - private: // number of horizontal columns and vertical levels int ncol_, nlev_; + // Namelist for LINOZ + int o3_lbl_; + Real o3_tau_, o3_sfc_; + // 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 @@ -228,7 +226,7 @@ class MAMMicrophysics final : public scream::AtmosphereProcess { std::shared_ptr grid_; // sets defaults for "namelist parameters" - void set_defaults_(); + void set_namelist_params_(); mam_coupling::TracerTimeState linoz_time_state_; view_2d work_photo_table_; 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 index d3c4e172b58..50c98220abd 100644 --- a/components/eamxx/src/physics/mam/impl/compute_o3_column_density.cpp +++ b/components/eamxx/src/physics/mam/impl/compute_o3_column_density.cpp @@ -18,7 +18,7 @@ void compute_o3_column_density( // NOTE: if we need o2 column densities, set_ub_col and setcol must be changed Kokkos::parallel_for( Kokkos::TeamThreadRange(team, mam4::nlev), [&](const int k) { - Real pdel = atm.hydrostatic_dp(k); + const Real pdel = atm.hydrostatic_dp(k); // extract aerosol state variables into "working arrays" (mass // mixing ratios) (in EAM, this is done in the gas_phase_chemdr // subroutine defined within @@ -41,11 +41,12 @@ void compute_o3_column_density( invariants_k[i] = invariants(k, i); } // 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_k, - pdel); + mam4::mo_photo::set_ub_col(o3_col_deltas[k + 1], // out + vmr, invariants_k, pdel); // out }); // sum the o3 column deltas to densities - mam4::mo_photo::setcol(o3_col_deltas, o3_col_dens); + mam4::mo_photo::setcol(o3_col_deltas, // in + o3_col_dens); // out } } // 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 index d977340b995..fa700e868c8 100644 --- a/components/eamxx/src/physics/mam/impl/gas_phase_chemistry.cpp +++ b/components/eamxx/src/physics/mam/impl/gas_phase_chemistry.cpp @@ -253,9 +253,10 @@ void gas_phase_chemistry( // solve chemical system implicitly Real prod_out[clscnt4], loss_out[clscnt4]; - mam4::gas_chemistry::imp_sol(qq, reaction_rates, het_rates, extfrc_rates, dt, - permute_4, clsmap_4, factor, epsilon, prod_out, - loss_out); + mam4::gas_chemistry::imp_sol(qq, // out + reaction_rates, het_rates, extfrc_rates, // in + dt, permute_4, clsmap_4, factor, // in + epsilon, prod_out, loss_out); // out // save h2so4 change by gas phase chem (for later new particle nucleation) if(ndx_h2so4 > 0) { diff --git a/components/eamxx/src/physics/mam/impl/helper_micro.hpp b/components/eamxx/src/physics/mam/impl/helper_micro.hpp index be989971541..c8e1c0cc831 100644 --- a/components/eamxx/src/physics/mam/impl/helper_micro.hpp +++ b/components/eamxx/src/physics/mam/impl/helper_micro.hpp @@ -395,15 +395,19 @@ inline std::shared_ptr create_horiz_remapper( horiz_interp_tgt_grid, IdentityRemapper::SrcAliasTgt); } else { EKAT_REQUIRE_MSG(tracer_data.ncols_data <= ncols_model, - "Error! We do not allow to coarsen spa data to fit the " + "Error! We do not allow to coarsen tracer external " + "forcing data to fit the " "model. We only allow\n" - " spa data to be at the same or coarser resolution " + " tracer external forcing data to be at the same or " + "coarser resolution " "as the model.\n"); // We must have a valid map file EKAT_REQUIRE_MSG( map_file != "", - "ERROR: Spa data is on a different grid than the model one,\n" - " but spa_remap_file is missing from SPA parameter list."); + "ERROR: tracer external forcing data is on a different grid than the " + "model one,\n" + " but tracer external forcing data remap file is missing from " + "tracer external forcing data parameter list."); remapper = std::make_shared(horiz_interp_tgt_grid, map_file); @@ -447,13 +451,13 @@ inline std::shared_ptr create_tracer_data_reader( } // create_tracer_data_reader inline void update_tracer_data_from_file( - std::shared_ptr &scorpio_reader, + const std::shared_ptr &scorpio_reader, const int time_index, // zero-based AbstractRemapper &tracer_horiz_interp, TracerData &tracer_data) { // 1. read from field scorpio_reader->read_variables(time_index); - // 2. Run the horiz remapper (it is a do-nothing op if spa data is on same - // grid as model) + // 2. Run the horiz remapper (it is a do-nothing op if tracer external forcing + // data is on same grid as model) tracer_horiz_interp.remap(/*forward = */ true); // const int nvars = tracer_data.nvars_; @@ -472,11 +476,12 @@ inline void update_tracer_data_from_file( } // update_tracer_data_from_file inline void update_tracer_timestate( - std::shared_ptr &scorpio_reader, const util::TimeStamp &ts, - AbstractRemapper &tracer_horiz_interp, TracerTimeState &time_state, - TracerData &data_tracer) { + const std::shared_ptr &scorpio_reader, + const util::TimeStamp &ts, AbstractRemapper &tracer_horiz_interp, + TracerTimeState &time_state, TracerData &data_tracer) { // Now we check if we have to update the data that changes monthly - // NOTE: This means that SPA assumes monthly data to update. Not + // NOTE: This means that tracer external forcing assumes monthly data to + // update. Not // any other frequency. const auto month = ts.get_month() - 1; // Make it 0-based if(month != time_state.current_month) { @@ -484,7 +489,7 @@ inline void update_tracer_timestate( const int nvars = data_tracer.nvars_; const auto ps = data_tracer.ps; - // Update the SPA time state information + // Update the tracer external forcing time state information time_state.current_month = month; time_state.t_beg_month = util::TimeStamp({ts.get_year(), month + 1, 1}, {0, 0, 0}) @@ -505,7 +510,7 @@ inline void update_tracer_timestate( // Assume the data is saved monthly and cycles in one year // Add offset_time_index to support cases where data is saved // from other periods of time. - // Update the SPA forcing data for this month and next month + // Update the tracer external forcing data for this month and next month // Start by copying next months data to this months data structure. // NOTE: If the timestep is bigger than monthly this could cause the wrong // values @@ -685,11 +690,12 @@ inline void perform_vertical_interpolation(const const_view_1d &altitude_int, } inline void advance_tracer_data( - std::shared_ptr &scorpio_reader, - AbstractRemapper &tracer_horiz_interp, const util::TimeStamp &ts, - TracerTimeState &time_state, TracerData &data_tracer, - const const_view_2d &p_tgt, const const_view_2d &zi_tgt, - const view_2d output[]) { + const std::shared_ptr &scorpio_reader, // in + AbstractRemapper &tracer_horiz_interp, // out + const util::TimeStamp &ts, // in + TracerTimeState &time_state, TracerData &data_tracer, // out + const const_view_2d &p_tgt, const const_view_2d &zi_tgt, // in + const view_2d output[]) { // out /* Update the TracerTimeState to reflect the current time, note the addition * of dt */ time_state.t_now = ts.frac_of_year_in_days(); diff --git a/components/eamxx/tests/single-process/mam/aero_microphys/input.yaml b/components/eamxx/tests/single-process/mam/aero_microphys/input.yaml index 738d88e06da..44bb9ee687e 100644 --- a/components/eamxx/tests/single-process/mam/aero_microphys/input.yaml +++ b/components/eamxx/tests/single-process/mam/aero_microphys/input.yaml @@ -10,6 +10,15 @@ time_stepping: atmosphere_processes: atm_procs_list: [mam4_aero_microphys] + mam4_aero_microphys: + mam4_do_cond: true + mam4_do_newnuc: true + mam4_do_coag: true + mam4_do_rename: true + mam4_o3_tau: 172800.0 + mam4_o3_sfc: 3.0E-008 + mam4_o3_lbl: 4 + mam4_aero_microphys: mam4_linoz_ymd : 20100101 mam4_linoz_file_name : ${SCREAM_DATA_DIR}/mam4xx/linoz/ne2np4/linoz1850-2015_2010JPL_CMIP6_10deg_58km_ne2np4_c20240724.nc @@ -46,10 +55,10 @@ initial_conditions: topography_filename: ${TOPO_DATA_DIR}/${EAMxx_tests_TOPO_FILE} phis : 1.0 pbl_height: 1.0 - dgnum : [1.37146e-07 ,3.45899e-08 ,1.00000e-06 ,9.99601e-08] - dgnumwet : [1.37452e-07 ,3.46684e-08 ,1.00900e-06 ,9.99601e-08] - wetdens : [5.08262e-12 ,1.54035e-13 ,3.09018e-13 ,9.14710e-22] #These should come from the input file + dgnum: [1.246662106183775E-007, 4.081134799487888E-008, 1.103139143795796E-006, 1.000000011686097E-007] + dgnumwet: [2.367209731605067E-007, 6.780643470563889E-008, 3.028011448344027E-006, 1.000000096285154E-007] + wetdens: [1038.67760516297, 1046.20002003441, 1031.74623165457, 1086.79731859184] # The parameters for I/O control Scorpio: diff --git a/externals/mam4xx b/externals/mam4xx index d39e7558cbf..6a39ba72b88 160000 --- a/externals/mam4xx +++ b/externals/mam4xx @@ -1 +1 @@ -Subproject commit d39e7558cbff54064d80880ba62c1a39fff7bd3f +Subproject commit 6a39ba72b880c5700bc0f5542f5a394eaa70333f