From aca99090b43e6bdf509a1eb19f021e2030977a18 Mon Sep 17 00:00:00 2001 From: Francis Vitt Date: Wed, 10 Jun 2026 06:31:39 -0600 Subject: [PATCH 1/8] Implement tuvx CAM configure option modified: bld/build-namelist modified: bld/config_files/definition.xml modified: bld/configure modified: cime_config/buildlib modified: cime_config/buildnml modified: cime_config/config_component.xml modified: cime_config/testdefs/testmods_dirs/cam/carma_mixed_sulfate/shell_commands modified: cime_config/testdefs/testmods_dirs/cam/outfrq9s_tuvx_waccm_ma_mam5/shell_commands modified: cime_config/testdefs/testmods_dirs/cam/outfrq9s_waccm_ma_mam4/shell_commands modified: src/chemistry/mozart/mo_tuvx.F90 --- bld/build-namelist | 9 ++ bld/config_files/definition.xml | 3 + bld/configure | 15 ++- cime_config/buildlib | 116 ++++++++++-------- cime_config/buildnml | 12 +- cime_config/config_component.xml | 2 +- .../cam/carma_mixed_sulfate/shell_commands | 2 +- .../shell_commands | 1 + .../cam/outfrq9s_waccm_ma_mam4/shell_commands | 2 +- src/chemistry/mozart/mo_tuvx.F90 | 87 +++++++------ 10 files changed, 147 insertions(+), 102 deletions(-) diff --git a/bld/build-namelist b/bld/build-namelist index dbc6adfc43..84eabe98cb 100755 --- a/bld/build-namelist +++ b/bld/build-namelist @@ -750,6 +750,15 @@ if (not $waccm_phys) { add_default($nl, 'p_bot_for_radmrg'); } +# Check TUV-X configuration +my $tuvx_active = $nl->get_value('tuvx_active'); +if ($tuvx_active =~ /$TRUE/) { + my $tuvx_config = $cfg->get('tuvx'); + if (!$tuvx_config) { + die "$ProgName - ERROR: must use -tuvx CAM configure flag \n"; + } +} + # Solar irradiance # First check that solar_const and solar_irrad_data_file are not both defined diff --git a/bld/config_files/definition.xml b/bld/config_files/definition.xml index 7b552abd0a..75b8147078 100644 --- a/bld/config_files/definition.xml +++ b/bld/config_files/definition.xml @@ -59,6 +59,9 @@ Option to turn on waccmx thermosphere/ionosphere extension: 0 => no, 1 => yes Option to turn on extended non-LTE CO2 cooling: 0 => no, 1 => yes + +Option to build TUV-X library: 0 => no, 1 => yes + Ionosphere model used in WACCMX. diff --git a/bld/configure b/bld/configure index f1447e2829..b91355f2ab 100755 --- a/bld/configure +++ b/bld/configure @@ -107,6 +107,7 @@ OPTIONS -psubcols Maximum number of sub-columns in a run - set to 1 if not using sub-columns (default) -rad Specify the radiation package [rrtmg | rrtmgp | rrtmgp_gpu | camrt] -silhs Switch on SILHS. + -tuvx Switch to build TUV-X library -usr_mech_infile Path and file name of the user supplied chemistry mechanism file. -waccm_phys Switch enables the use of WACCM physics in any chemistry configuration. The user does not need to set this if one of the waccm chemistry options @@ -305,6 +306,7 @@ GetOptions( "waccm_phys" => \$opts{'waccm_phys'}, "waccmx" => \$opts{'waccmx'}, "ext_co2_cool" => \$opts{'ext_co2_cool'}, + "tuvx" => \$opts{'tuvx'}, ) or usage(); # Give usage message. @@ -717,6 +719,12 @@ if (defined $opts{'ext_co2_cool'}) { } my $ext_co2_cool = $cfg_ref->get('ext_co2_cool'); +# TUV-x option +if (defined $opts{'tuvx'}) { + $cfg_ref->set('tuvx', $opts{'tuvx'}); +} +my $tuvx = $cfg_ref->get('tuvx'); + #----------------------------------------------------------------------------------------------- # Prognostic species package(s) @@ -1788,6 +1796,11 @@ if ($ext_co2_cool) { $cfg_cppdefs .= ' -DEXT_CO2_COOL'; } +# TUV-X +if ($tuvx) { + $cfg_cppdefs .= ' -DTUVX'; +} + # PIO my $pio2_build = (defined $opts{'pio2'}) ? 1 : 0; @@ -1864,7 +1877,7 @@ if ($cosp) { $ldflags .= " -L$cosp_libdir -lrttov13_wrapper -lrttov13_mw_scatt -lrttov13_brdf_atlas -lrttov13_emis_atlas -lrttov13_other -lrttov13_parallel -lrttov13_coef_io -lrttov13_hdf -lrttov13_main "; $cfg_ref->set('ldflags', $ldflags); print "Adding rttov libraries as dependencies in ldflags.\n"; - } + } # Create the COSP Makefile from a template and copy it into the cosp bld directory if ($print) { print "creating $cosp_libdir/Makefile\n"; } write_cosp_makefile("$cfgdir/../src/physics/cosp2/Makefile.in", "$cosp_libdir/Makefile"); diff --git a/cime_config/buildlib b/cime_config/buildlib index 49a196b53d..cb1c4f4de3 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -232,60 +232,68 @@ def _build_tuvx(caseroot, libroot, bldroot): # Builds the TUV-x library and updates the case variables used to set the # include paths and linked libraries - with Case(caseroot) as case: - bldpath = os.path.join(bldroot, "tuv-x") - if not os.path.exists(bldpath): - os.makedirs(bldpath) - srcpath = os.path.abspath(os.path.join(case.get_value("SRCROOT"), \ - "libraries", "tuv-x", "")) - logger.info("Building TUV-x in {} from source in {}\n".format(bldpath, srcpath)) - - arg_dict = _cmake_default_args(caseroot) - cmake_args = "-DCMAKE_VERBOSE_MAKEFILE=ON " - if case.get_value("MPILIB") != "mpi-serial": - cmake_args += "-DTUVX_ENABLE_MPI:BOOL=TRUE " - cmake_args += "-DCMAKE_BUILD_TYPE=Debug " - cmake_args += "-DCMAKE_Fortran_COMPILER=mpif90 " - cmake_args += "-DCMAKE_C_COMPILER=mpicc " - cmake_args += "-DCMAKE_CXX_COMPILER=mpicxx " - cmake_args += "-DCMAKE_Fortran_COMPILER_WORKS=1 " - cmake_args += "-DCMAKE_C_COMPILER_WORKS=1 " - cmake_args += "-DCMAKE_CXX_COMPILER_WORKS=1 " - if (case.get_value("MACH") == "izumi") : - cmake_args += f"-DCMAKE_PREFIX_PATH={arg_dict['NETCDF_PATH']} " - cmake_args += f"-DCMAKE_IGNORE_PATH={os.environ.get('PYTHONHOME')} " - if (case.get_value("MACH") == "izumi") and (case.get_value('COMPILER') == "nag") : - cmake_args += "-DCMAKE_Fortran_FLAGS='-C=all -g ' " - else : - cmake_args += f"-DCMAKE_Fortran_FLAGS='{arg_dict['FFLAGS']}' " - cmake_args += f"-DCMAKE_INSTALL_PREFIX='{libroot}' " - cmake_args += "-DTUVX_ENABLE_TESTS=OFF " - cmake_args += "-DTUVX_ENABLE_COVERAGE=OFF " - cmake_args += "-DTUVX_BUILD_CLI=OFF " - cmake_args += f"-DTUVX_INSTALL_INCLUDE_DIR='{_tuvx_include_dir(libroot)}' " - cmake_args += f"-DTUVX_INSTALL_MOD_DIR='{_tuvx_include_dir(libroot)}' " - cmake_args += srcpath - - _run_cmd(f"cmake {cmake_args}", bldpath) - _run_cmd(case.get_value('GMAKE'), bldpath) - _run_cmd(f"{case.get_value('GMAKE')} install", bldpath) - - # add TUV-x to include paths - incldir = os.environ.get('USER_INCLDIR') - if incldir is None: - incldir = '' - os.environ['USER_INCLDIR'] = incldir + \ - f" -I{_tuvx_include_dir(libroot)} " - - # create symlink to library in folder CIME expects libraries to be in - dst = os.path.join(libroot, "libtuvx.a") - if os.path.isfile(dst): - os.remove(dst) - os.symlink(_tuvx_lib_path(libroot), dst) - dst = os.path.join(libroot, "libyaml-cpp.a") - if os.path.isfile(dst): - os.remove(dst) - os.symlink(_yaml_cpp_lib_path(libroot), dst) + with Case(caseroot, read_only=False) as case: + config_opts = case.get_value("CAM_CONFIG_OPTS") + if "-tuvx" in config_opts: + bldpath = os.path.join(bldroot, "tuv-x") + if not os.path.exists(bldpath): + os.makedirs(bldpath) + srcpath = os.path.abspath(os.path.join(case.get_value("SRCROOT"), \ + "libraries", "tuv-x", "")) + logger.info("Building TUV-x in {} from source in {}\n".format(bldpath, srcpath)) + + arg_dict = _cmake_default_args(caseroot) + cmake_args = "-DCMAKE_VERBOSE_MAKEFILE=ON " + if case.get_value("MPILIB") != "mpi-serial": + cmake_args += "-DTUVX_ENABLE_MPI:BOOL=TRUE " + cmake_args += "-DCMAKE_BUILD_TYPE=Debug " + cmake_args += "-DCMAKE_Fortran_COMPILER=mpif90 " + cmake_args += "-DCMAKE_C_COMPILER=mpicc " + cmake_args += "-DCMAKE_CXX_COMPILER=mpicxx " + cmake_args += "-DCMAKE_Fortran_COMPILER_WORKS=1 " + cmake_args += "-DCMAKE_C_COMPILER_WORKS=1 " + cmake_args += "-DCMAKE_CXX_COMPILER_WORKS=1 " + if (case.get_value("MACH") == "izumi") : + cmake_args += f"-DCMAKE_PREFIX_PATH={arg_dict['NETCDF_PATH']} " + cmake_args += f"-DCMAKE_IGNORE_PATH={os.environ.get('PYTHONHOME')} " + if (case.get_value("MACH") == "izumi") and (case.get_value('COMPILER') == "nag") : + cmake_args += "-DCMAKE_Fortran_FLAGS='-C=all -g ' " + else : + cmake_args += f"-DCMAKE_Fortran_FLAGS='{arg_dict['FFLAGS']}' " + cmake_args += f"-DCMAKE_INSTALL_PREFIX='{libroot}' " + cmake_args += "-DTUVX_ENABLE_TESTS=OFF " + cmake_args += "-DTUVX_ENABLE_COVERAGE=OFF " + cmake_args += "-DTUVX_BUILD_CLI=OFF " + cmake_args += f"-DTUVX_INSTALL_INCLUDE_DIR='{_tuvx_include_dir(libroot)}' " + cmake_args += f"-DTUVX_INSTALL_MOD_DIR='{_tuvx_include_dir(libroot)}' " + cmake_args += srcpath + + _run_cmd(f"cmake {cmake_args}", bldpath) + _run_cmd(case.get_value('GMAKE'), bldpath) + _run_cmd(f"{case.get_value('GMAKE')} install", bldpath) + + # add TUV-x to include paths + incldir = os.environ.get('USER_INCLDIR') + if incldir is None: + incldir = '' + os.environ['USER_INCLDIR'] = incldir + \ + f" -I{_tuvx_include_dir(libroot)} " + + # create symlink to library in folder CIME expects libraries to be in + dst = os.path.join(libroot, "libtuvx.a") + if os.path.isfile(dst): + os.remove(dst) + os.symlink(_tuvx_lib_path(libroot), dst) + dst = os.path.join(libroot, "libyaml-cpp.a") + if os.path.isfile(dst): + os.remove(dst) + os.symlink(_yaml_cpp_lib_path(libroot), dst) + + # Add TUVX to CAM linked library list: + linked_libs = case.get_value("CAM_LINKED_LIBS", + subgroup="build_component_cam") + tuvx_link_opts = " -ltuvx -lyaml-cpp" + case.set_value("CAM_LINKED_LIBS", linked_libs + tuvx_link_opts) ############################################################################### def _tuvx_include_dir(libroot): diff --git a/cime_config/buildnml b/cime_config/buildnml index 44238cf6e5..71ae4a44d1 100755 --- a/cime_config/buildnml +++ b/cime_config/buildnml @@ -65,7 +65,8 @@ def buildnml(case, caseroot, compname): "Gregorian calendar (modifier _cG) cannot be used with spinup compset \n {}".format(COMPSET) ) - tuvx_data_src = os.path.join(srcroot, "libraries", "tuv-x", "data") + if '-tuvx' in CAM_CONFIG_OPTS: + tuvx_data_src = os.path.join(srcroot, "libraries", "tuv-x", "data") testsrc = os.path.join(srcroot, "components", "cam") if os.path.exists(testsrc): @@ -237,10 +238,11 @@ def buildnml(case, caseroot, compname): shutil.copy(file1,file2) # Copy TUV-x data to rundir - dest_data = os.path.join(rundir, "data") - if os.path.exists(dest_data): - shutil.rmtree(dest_data) - shutil.copytree(tuvx_data_src, dest_data) + if '-tuvx' in CAM_CONFIG_OPTS: + dest_data = os.path.join(rundir, "data") + if os.path.exists(dest_data): + shutil.rmtree(dest_data) + shutil.copytree(tuvx_data_src, dest_data) ############################################################################### def _main_func(): diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index d2ce3c980d..b15bc59949 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -230,7 +230,7 @@ char - -ltuvx -lyaml-cpp -lstdc++ + -lstdc++ build_component_cam env_build.xml diff --git a/cime_config/testdefs/testmods_dirs/cam/carma_mixed_sulfate/shell_commands b/cime_config/testdefs/testmods_dirs/cam/carma_mixed_sulfate/shell_commands index e3d9ef9950..ed92990dcb 100644 --- a/cime_config/testdefs/testmods_dirs/cam/carma_mixed_sulfate/shell_commands +++ b/cime_config/testdefs/testmods_dirs/cam/carma_mixed_sulfate/shell_commands @@ -1,4 +1,4 @@ -./xmlchange CAM_CONFIG_OPTS=" -phys cam4 -rad rrtmg -chem waccm_ma_sulfur -carma mixed_sulfate" +./xmlchange CAM_CONFIG_OPTS=" -phys cam4 -rad rrtmg -chem waccm_ma_sulfur -carma mixed_sulfate -tuvx" ./xmlchange ROF_NCPL=\$ATM_NCPL ./xmlchange GLC_NCPL=\$ATM_NCPL ./xmlchange RUN_STARTDATE="0001-01-01" diff --git a/cime_config/testdefs/testmods_dirs/cam/outfrq9s_tuvx_waccm_ma_mam5/shell_commands b/cime_config/testdefs/testmods_dirs/cam/outfrq9s_tuvx_waccm_ma_mam5/shell_commands index eb40ad83e0..f064e3e999 100644 --- a/cime_config/testdefs/testmods_dirs/cam/outfrq9s_tuvx_waccm_ma_mam5/shell_commands +++ b/cime_config/testdefs/testmods_dirs/cam/outfrq9s_tuvx_waccm_ma_mam5/shell_commands @@ -1,2 +1,3 @@ +./xmlchange --append CAM_CONFIG_OPTS="-tuvx" ./xmlchange ROF_NCPL=\$ATM_NCPL ./xmlchange GLC_NCPL=\$ATM_NCPL diff --git a/cime_config/testdefs/testmods_dirs/cam/outfrq9s_waccm_ma_mam4/shell_commands b/cime_config/testdefs/testmods_dirs/cam/outfrq9s_waccm_ma_mam4/shell_commands index 8cf20f57e4..25d6b327a1 100644 --- a/cime_config/testdefs/testmods_dirs/cam/outfrq9s_waccm_ma_mam4/shell_commands +++ b/cime_config/testdefs/testmods_dirs/cam/outfrq9s_waccm_ma_mam4/shell_commands @@ -1,2 +1,2 @@ ./xmlchange ROF_NCPL=\$ATM_NCPL -./xmlchange CAM_CONFIG_OPTS="-phys cam6 -age_of_air_trcs -chem waccm_ma_mam4 -ext_co2_cool" +./xmlchange CAM_CONFIG_OPTS="-phys cam6 -age_of_air_trcs -chem waccm_ma_mam4 -ext_co2_cool -tuvx" diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 7644277f39..29c7d800eb 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -2,12 +2,18 @@ ! Wrapper for TUV-x photolysis rate constant calculator !---------------------------------------------------------------------- module mo_tuvx - + use shr_kind_mod, only : r8 => shr_kind_r8, cl=>shr_kind_cl + use spmd_utils, only : is_main_task => masterproc + use spmd_utils, only : main_task_id => masterprocid + use cam_abortutils, only : endrun + use cam_logfile, only : iulog ! log info output unit + use spmd_utils, only : mpicom, mpi_character, mpi_integer, mpi_logical, mpi_success + use ppgrid, only : pver, & ! number of vertical layers + pverp, & ! number of vertical interfaces (pver + 1) + pcols ! maximum number of columns +#ifdef TUVX use musica_map, only : map_t use musica_string, only : string_t - use ppgrid, only : pver, & ! number of vertical layers - pverp ! number of vertical interfaces (pver + 1) - use shr_kind_mod, only : r8 => shr_kind_r8, cl=>shr_kind_cl use tuvx_core, only : core_t use tuvx_grid_from_host, only : grid_updater_t use tuvx_profile_from_host, only : profile_updater_t @@ -19,13 +25,8 @@ module mo_tuvx use ppgrid, only : pcols ! maximum number of columns use radconstants, only : get_sw_spectral_boundaries use cam_history, only : fieldname_len, horiz_only, addfld, outfld !, add_default - use spmd_utils, only : is_main_task => masterproc - use spmd_utils, only : main_task_id => masterprocid - use spmd_utils, only : mpicom, mpi_character, mpi_integer, mpi_logical, mpi_success - use cam_abortutils, only : endrun - use cam_logfile, only : iulog ! log info output unit use shr_const_mod, only : pi => shr_const_pi - +#endif implicit none private @@ -38,6 +39,7 @@ module mo_tuvx public :: tuvx_finalize public :: tuvx_active +#ifdef TUVX ! Inidices for grid updaters integer, parameter :: NUM_GRIDS = 2 ! number of grids that CAM will update at runtime integer, parameter :: GRID_INDEX_HEIGHT = 1 ! Height grid index @@ -140,21 +142,21 @@ module mo_tuvx integer :: index_ ! index of the photolysis rate constant from TUV-x end type diagnostic_t type(diagnostic_t), allocatable :: diagnostics(:) - +#endif ! namelist options character(len=cl) :: tuvx_config_path = 'NONE' ! absolute path to TUVX configuration file logical, protected :: tuvx_active = .false. - - integer :: swaertau_idx = -1 ! shortwave aerosol extinction optical depth. tau - integer :: swaertauw_idx = -1 ! shortwave aerosol extinction optical depth * single scattering albedo. tau*w - integer :: swaertauwg_idx = -1 ! shortwave aerosol extinction optical depth * single scattering albedo * asymmetry parameter. tau*w*g - integer :: swcldtau_idx = -1 ! shortwave cloud extinction optical depth. tau - integer :: swcldtauw_idx = -1 ! shortwave cloud extinction optical depth * single scattering albedo. tau*w - integer :: swcldtauwg_idx = -1 ! shortwave cloud extinction optical depth * single scattering albedo * asymmetry parameter. tau*w*g - type (interp_type) :: interp_wgts - real(r8) :: rrtmg_wavelength(nswbands-1) - integer :: nwave - +#ifdef TUVX + integer :: swaertau_idx = -1 ! shortwave aerosol extinction optical depth. tau + integer :: swaertauw_idx = -1 ! shortwave aerosol extinction optical depth * single scattering albedo. tau*w + integer :: swaertauwg_idx = -1 ! shortwave aerosol extinction optical depth * single scattering albedo * asymmetry parameter. tau*w*g + integer :: swcldtau_idx = -1 ! shortwave cloud extinction optical depth. tau + integer :: swcldtauw_idx = -1 ! shortwave cloud extinction optical depth * single scattering albedo. tau*w + integer :: swcldtauwg_idx = -1 ! shortwave cloud extinction optical depth * single scattering albedo * asymmetry parameter. tau*w*g + type (interp_type) :: interp_wgts + real(r8) :: rrtmg_wavelength(nswbands-1) + integer :: nwave +#endif !================================================================================================ contains !================================================================================================ @@ -163,7 +165,7 @@ module mo_tuvx ! registers fields in the physics buffer !----------------------------------------------------------------------- subroutine tuvx_register( ) - +#ifdef TUVX use mo_jeuv, only : nIonRates use physics_buffer, only : pbuf_add_field, dtype_r8 use ppgrid, only : pcols ! maximum number of columns @@ -190,7 +192,7 @@ subroutine tuvx_register( ) call pbuf_add_field('SWCLDTAU', 'global',dtype_r8,(/pcols,pver,nswbands/), swcldtau_idx) ! shortwave tau call pbuf_add_field('SWCLDTAUW', 'global',dtype_r8,(/pcols,pver,nswbands/), swcldtauw_idx) ! shortwave tau * w call pbuf_add_field('SWCLDTAUWG', 'global',dtype_r8,(/pcols,pver,nswbands/), swcldtauwg_idx) ! shortwave tau * w * g - +#endif end subroutine tuvx_register !================================================================================================ @@ -200,7 +202,7 @@ end subroutine tuvx_register !----------------------------------------------------------------------- subroutine tuvx_readnl(nlfile) - use namelist_utils, only : find_group_name + use namelist_utils, only : find_group_name character(len=*), intent(in) :: nlfile ! filepath for file containing namelist input @@ -235,6 +237,7 @@ subroutine tuvx_readnl(nlfile) call mpi_bcast(tuvx_active, 1, mpi_logical, main_task_id, mpicom, ierr) if (ierr /= mpi_success) call endrun(subname//': mpi_bcast error : tuvx_active') +#ifdef TUVX if (tuvx_active .and. tuvx_config_path == 'NONE') then call endrun(subname // ' : must set tuvx_config_path when TUV-X is active') end if @@ -243,7 +246,11 @@ subroutine tuvx_readnl(nlfile) write(iulog,*) 'tuvx_readnl: tuvx_config_path = ', trim(tuvx_config_path) write(iulog,*) 'tuvx_readnl: tuvx_active = ', tuvx_active end if - +#else + if (tuvx_active .or. tuvx_config_path /= 'NONE') then + call endrun(subname // ' : use -tuvx configure CAM option to build TUV-X library') + end if +#endif end subroutine tuvx_readnl !================================================================================================ @@ -252,7 +259,10 @@ end subroutine tuvx_readnl ! Initializes TUV-x for photolysis calculations !----------------------------------------------------------------------- subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle, pbuf2d ) - + use physics_buffer, only : physics_buffer_desc + use physics_buffer, only : pbuf_set_field + use ppgrid, only : pcols ! maximum number of columns +#ifdef TUVX use infnan, only : nan, assignment(=) use mo_chem_utls, only : get_spc_ndx, get_inv_ndx use mo_jeuv, only : neuv ! number of extreme-UV rates @@ -262,9 +272,6 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle, pbuf2d musica_mpi_pack, & musica_mpi_unpack use musica_string, only : string_t, to_char - use physics_buffer, only : physics_buffer_desc - use physics_buffer, only : pbuf_set_field - use ppgrid, only : pcols ! maximum number of columns use solar_irrad_data, only : has_spectrum use tuvx_grid, only : grid_t use tuvx_grid_warehouse, only : grid_warehouse_t @@ -273,13 +280,14 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle, pbuf2d use time_manager, only : is_first_step use physics_buffer, only: pbuf_get_index - +#endif character(len=*), intent(in) :: photon_file ! photon file used in extended-UV module setup character(len=*), intent(in) :: electron_file ! electron file used in extended-UV module setup real(r8), intent(in) :: max_solar_zenith_angle ! cutoff solar zenith angle for ! photo rate calculations [degrees] type(physics_buffer_desc), pointer :: pbuf2d(:,:) ! Physics buffer +#ifdef TUVX character(len=*), parameter :: my_name = "TUV-x wrapper initialization" class(core_t), pointer :: core character, allocatable :: buffer(:) @@ -551,7 +559,7 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle, pbuf2d deallocate(wc) if( is_main_task ) call log_initialization( labels ) - +#endif end subroutine tuvx_init !================================================================================================ @@ -564,13 +572,13 @@ subroutine tuvx_timestep_init( ) integer :: i_thread if( .not. tuvx_active ) return - +#ifdef TUVX do i_thread = 1, size( tuvx_ptrs ) associate( tuvx => tuvx_ptrs( i_thread ) ) call set_et_flux( tuvx ) end associate end do - +#endif end subroutine tuvx_timestep_init !================================================================================================ @@ -614,6 +622,7 @@ subroutine tuvx_get_photo_rates( pbuf, ncol, lchnk, height_mid, & real(r8), intent(in) :: liquid_water_content(ncol,pver) ! liquid water content (kg/kg) real(r8), intent(inout) :: photolysis_rates(ncol,pver,phtcnt) ! photolysis rate ! constants (1/s) +#ifdef TUVX integer :: ipht, k, idose integer :: i_col ! column index integer :: i_level ! vertical level index @@ -797,7 +806,7 @@ subroutine tuvx_get_photo_rates( pbuf, ncol, lchnk, height_mid, & end do call outfld('CPE_jO3b', cpe_jo3_b(:ncol,:), ncol, lchnk ) end if - +#endif end subroutine tuvx_get_photo_rates !================================================================================================ @@ -806,7 +815,7 @@ end subroutine tuvx_get_photo_rates ! Cleans up memory associated with TUV-x calculators !----------------------------------------------------------------------- subroutine tuvx_finalize( ) - +#ifdef TUVX integer :: i_core, i_diag if( allocated( tuvx_ptrs ) ) then @@ -828,9 +837,9 @@ subroutine tuvx_finalize( ) if (allocated(dose_rate_hist_name)) then deallocate(dose_rate_hist_name) end if - +#endif end subroutine tuvx_finalize - +#ifdef TUVX !================================================================================================ !================================================================================================ ! @@ -2028,7 +2037,7 @@ subroutine calculate_jno( solar_zenith_angle, et_flux, fixed_species_conc, speci jno(:pver) = work_jno(:pver) end subroutine calculate_jno - +#endif !================================================================================================ end module mo_tuvx From f1cefc90826a2309e5be5eb4acb33cade8e843bd Mon Sep 17 00:00:00 2001 From: Francis Vitt Date: Wed, 10 Jun 2026 10:48:05 -0600 Subject: [PATCH 2/8] Fix for building TUV-X in conda environment modified: cime_config/buildlib --- cime_config/buildlib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cime_config/buildlib b/cime_config/buildlib index cb1c4f4de3..ad85215fb6 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -253,9 +253,9 @@ def _build_tuvx(caseroot, libroot, bldroot): cmake_args += "-DCMAKE_Fortran_COMPILER_WORKS=1 " cmake_args += "-DCMAKE_C_COMPILER_WORKS=1 " cmake_args += "-DCMAKE_CXX_COMPILER_WORKS=1 " + cmake_args += "-DCMAKE_DISABLE_FIND_PACKAGE_yaml-cpp=1 " if (case.get_value("MACH") == "izumi") : cmake_args += f"-DCMAKE_PREFIX_PATH={arg_dict['NETCDF_PATH']} " - cmake_args += f"-DCMAKE_IGNORE_PATH={os.environ.get('PYTHONHOME')} " if (case.get_value("MACH") == "izumi") and (case.get_value('COMPILER') == "nag") : cmake_args += "-DCMAKE_Fortran_FLAGS='-C=all -g ' " else : From ce748bc2dccdaecd1b00110561d9c1895972b672 Mon Sep 17 00:00:00 2001 From: Francis Vitt Date: Thu, 11 Jun 2026 16:12:07 -0600 Subject: [PATCH 3/8] Update cime_config/buildlib Haipeng's suggestion Co-authored-by: Haipeng Lin --- cime_config/buildlib | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cime_config/buildlib b/cime_config/buildlib index ad85215fb6..673345b55a 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -234,6 +234,14 @@ def _build_tuvx(caseroot, libroot, bldroot): with Case(caseroot, read_only=False) as case: config_opts = case.get_value("CAM_CONFIG_OPTS") + + # Remove any TUV-x link options set by a previous build, so that + # re-building the case does not accumulate duplicate or stale + # options in CAM_LINKED_LIBS: + linked_libs = case.get_value("CAM_LINKED_LIBS", + subgroup="build_component_cam") + tuvx_link_opts = " -ltuvx -lyaml-cpp" + new_linked_libs = re.sub(r"\s*-ltuvx -lyaml-cpp", "", linked_libs) if "-tuvx" in config_opts: bldpath = os.path.join(bldroot, "tuv-x") if not os.path.exists(bldpath): From 088f04e549344db4d0762b95415e17b25abff48f Mon Sep 17 00:00:00 2001 From: Francis Vitt Date: Thu, 11 Jun 2026 16:12:57 -0600 Subject: [PATCH 4/8] Update cime_config/buildlib Haipeng's suggestion Co-authored-by: Haipeng Lin --- cime_config/buildlib | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cime_config/buildlib b/cime_config/buildlib index 673345b55a..9ccac06e1a 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -298,10 +298,10 @@ def _build_tuvx(caseroot, libroot, bldroot): os.symlink(_yaml_cpp_lib_path(libroot), dst) # Add TUVX to CAM linked library list: - linked_libs = case.get_value("CAM_LINKED_LIBS", - subgroup="build_component_cam") - tuvx_link_opts = " -ltuvx -lyaml-cpp" - case.set_value("CAM_LINKED_LIBS", linked_libs + tuvx_link_opts) + new_linked_libs = tuvx_link_opts.strip() + " " + new_linked_libs + + if new_linked_libs != linked_libs: + case.set_value("CAM_LINKED_LIBS", new_linked_libs) ############################################################################### def _tuvx_include_dir(libroot): From e455fd6f7c1ad7961ad824e6ddd955e245114d40 Mon Sep 17 00:00:00 2001 From: Francis Vitt Date: Thu, 11 Jun 2026 16:13:29 -0600 Subject: [PATCH 5/8] Update bld/build-namelist Haipeng's suggestion Co-authored-by: Haipeng Lin --- bld/build-namelist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bld/build-namelist b/bld/build-namelist index 84eabe98cb..6ce53e55b3 100755 --- a/bld/build-namelist +++ b/bld/build-namelist @@ -752,7 +752,7 @@ if (not $waccm_phys) { # Check TUV-X configuration my $tuvx_active = $nl->get_value('tuvx_active'); -if ($tuvx_active =~ /$TRUE/) { +if ($tuvx_active =~ /$TRUE/io) { my $tuvx_config = $cfg->get('tuvx'); if (!$tuvx_config) { die "$ProgName - ERROR: must use -tuvx CAM configure flag \n"; From e86ec1031ffb4d5d9e0fcc012f87ecdd02f068c1 Mon Sep 17 00:00:00 2001 From: Francis Vitt Date: Thu, 11 Jun 2026 16:34:44 -0600 Subject: [PATCH 6/8] need to import re --- cime_config/buildlib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cime_config/buildlib b/cime_config/buildlib index 9ccac06e1a..3ff27782b1 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -6,7 +6,7 @@ create the cam library # pylint: disable=multiple-imports, wrong-import-position, wildcard-import # pylint: disable=unused-wildcard-import, bad-whitespace, too-many-locals # pylint: disable=invalid-name -import sys, os, filecmp, shutil +import sys, os, filecmp, shutil, re from glob import glob _CIMEROOT = os.environ.get("CIMEROOT") From a6fda0947ea5e1cf0fd6295ce864c3eca10cfb01 Mon Sep 17 00:00:00 2001 From: Francis Vitt Date: Tue, 16 Jun 2026 05:28:50 -0600 Subject: [PATCH 7/8] ChangeLog --- doc/ChangeLog | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index ee91c52211..794e40edcd 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,79 @@ +=============================================================== + +Tag name: cam6_4_182 +Originator(s): fvitt +Date: 16 Jun 2026 +One-line Summary: Updates to CAM's build of TUV-X library +Github PR URL: https://github.com/ESCOMP/CAM/pull/1577 + +Purpose of changes (include the issue number and title text for each relevant GitHub issue): + + Build TUV-X library only in cases where TUV-X is used. Configure + switch -tuvx has been added to CAM_CONFIG_OPTS which activates + the build of the TUV-X library. + + Address issues: + #1537: TUV-x build issue when users have Conda environments active + https://github.com/ESCOMP/CAM/issues/1570 + + #1570: TUV-x copies input files to run directory regardless of whether TUV-x is active + https://github.com/ESCOMP/CAM/issues/1570 + +Describe any changes made to build system: + Changes TUV-X library build as described above + +Describe any changes made to the namelist: N/A + +List any changes to the defaults for the boundary datasets: N/A + +Describe any substantial timing or memory changes: N/A + +Code reviewed by: jimmielin cacraigucar + +List all files eliminated: N/A + +List all files added and what they do: N/A + +List all existing files that have been modified, and describe the changes: + +If there were any failures reported from running test_driver.sh on any test +platform, and checkin with these failures has been OK'd by the gatekeeper, +then copy the lines from the td.*.status files for the failed tests to the +appropriate machine below. All failed tests must be justified. + +derecho/intel/aux_cam: +derecho/nvhpc/aux_cam: + +izumi/nag/aux_cam: + +izumi/gnu/aux_cam: + +CAM tag used for the baseline comparison tests if different than previous +tag: + +Summarize any changes to answers, i.e., +- what code configurations: +- what platforms/compilers: +- nature of change (roundoff; larger than roundoff but same climate; new + climate): + +If bitwise differences were observed, how did you show they were no worse +than roundoff? + +If this tag changes climate describe the run(s) done to evaluate the new +climate in enough detail that it(they) could be reproduced, i.e., +- source tag (all code used must be in the repository): +- platform/compilers: +- configure commandline: +- build-namelist command (or complete namelist): +- MSS location of output: + +MSS location of control simulations used to validate new climate: + +URL for AMWG diagnostics output used to validate new climate: + +=============================================================== =============================================================== Tag name: cam6_4_181 From 4525eef65cde4716eca34b0c77aa1cf0c8839649 Mon Sep 17 00:00:00 2001 From: Francis Vitt Date: Tue, 16 Jun 2026 07:58:26 -0600 Subject: [PATCH 8/8] ChangeLog update --- doc/ChangeLog | 57 ++++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index 794e40edcd..c3cc2ea0fa 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -36,42 +36,47 @@ List all files added and what they do: N/A List all existing files that have been modified, and describe the changes: -If there were any failures reported from running test_driver.sh on any test -platform, and checkin with these failures has been OK'd by the gatekeeper, -then copy the lines from the td.*.status files for the failed tests to the -appropriate machine below. All failed tests must be justified. +M bld/build-namelist + - error out if tuvx_active is TRUE and not configured for TUV-X -derecho/intel/aux_cam: +M bld/config_files/definition.xml +M bld/configure + - add "-tuvx" configure switch -- define "TUVX" CPP def -derecho/nvhpc/aux_cam: +M cime_config/buildlib + - build TUV-X lib only if "-tuvx" is included in CAM_CONFIG_OPTS -izumi/nag/aux_cam: +M cime_config/buildnml + - copy TUV-X input data to run directory only if "-tuvx" is included in CAM_CONFIG_OPTS -izumi/gnu/aux_cam: +M cime_config/config_component.xml + - remove tuvx and yaml-cpp libs from default setting of CAM_LINKED_LIBS + -- buildlib will add these only only if cam is configured for tuvx -CAM tag used for the baseline comparison tests if different than previous -tag: +M cime_config/testdefs/testmods_dirs/cam/carma_mixed_sulfate/shell_commands +M cime_config/testdefs/testmods_dirs/cam/outfrq9s_tuvx_waccm_ma_mam5/shell_commands +M cime_config/testdefs/testmods_dirs/cam/outfrq9s_waccm_ma_mam4/shell_commands + -- append "-tuvx" to CAM_CONFIG_OPTS -Summarize any changes to answers, i.e., -- what code configurations: -- what platforms/compilers: -- nature of change (roundoff; larger than roundoff but same climate; new - climate): +M src/chemistry/mozart/mo_tuvx.F90 + - use ifdefs to stub out the module for cases where TUV-X lib is not available -If bitwise differences were observed, how did you show they were no worse -than roundoff? +If there were any failures reported from running test_driver.sh on any test +platform, and checkin with these failures has been OK'd by the gatekeeper, +then copy the lines from the td.*.status files for the failed tests to the +appropriate machine below. All failed tests must be justified. -If this tag changes climate describe the run(s) done to evaluate the new -climate in enough detail that it(they) could be reproduced, i.e., -- source tag (all code used must be in the repository): -- platform/compilers: -- configure commandline: -- build-namelist command (or complete namelist): -- MSS location of output: +derecho/intel/aux_cam: All PASS -MSS location of control simulations used to validate new climate: +derecho/nvhpc/aux_cam: All PASS -URL for AMWG diagnostics output used to validate new climate: +izumi/nag/aux_cam: + FAIL ERC_D_Ln9.f10_f10_mt232.FHIST_C5.izumi_nag.cam-outfrq3s_subcol COMPARE_base_rest + - pre-existing failure -- see https://github.com/ESCOMP/CAM/issues/1514 + +izumi/gnu/aux_cam: All PASS + +Summarize any changes to answers: bit-for-bit =============================================================== ===============================================================