From 452b0fc1904bd80e8516e43e7275e7e7243e3744 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Thu, 6 Oct 2022 10:02:11 -0700 Subject: [PATCH 01/84] add tuv-x to externals --- Externals_CAM.cfg | 7 +++++++ libs/README.md | 1 + 2 files changed, 8 insertions(+) create mode 100644 libs/README.md diff --git a/Externals_CAM.cfg b/Externals_CAM.cfg index dfeab9a555..496d5e5780 100644 --- a/Externals_CAM.cfg +++ b/Externals_CAM.cfg @@ -69,5 +69,12 @@ sparse = ../.mpas_sparse_checkout hash = ff76a231 required = True +[tuv-x] +local_path = libs/tuv-x +protocol = git +repo_url = https://github.com/NCAR/tuv-x.git +branch = develop-160-cesm-build +required = True + [externals_description] schema_version = 1.0.0 diff --git a/libs/README.md b/libs/README.md new file mode 100644 index 0000000000..ed70c2235f --- /dev/null +++ b/libs/README.md @@ -0,0 +1 @@ +External libraries used by CAM From fdf16a367b678a37fe44f050ceab1ddfc9c88179 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Mon, 10 Oct 2022 13:31:21 -0600 Subject: [PATCH 02/84] update externals for tuv-x and json-fortran --- .gitignore | 2 ++ Externals.cfg | 14 ++++++++++++++ Externals_CAM.cfg | 9 ++++++++- libs/README.md | 1 - 4 files changed, 24 insertions(+), 2 deletions(-) delete mode 100644 libs/README.md diff --git a/.gitignore b/.gitignore index afe4662783..42d902494c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,8 @@ src/dynamics/fv3/atmos_cubed_sphere libraries/FMS libraries/mct libraries/parallelio +libraries/tuv-x +libraries/json-fortran src/atmos_phys src/dynamics/mpas/dycore share diff --git a/Externals.cfg b/Externals.cfg index b29291a7da..21d82894f3 100644 --- a/Externals.cfg +++ b/Externals.cfg @@ -109,6 +109,20 @@ repo_url = https://github.com/ESCOMP/RTM local_path = components/rtm required = True +[tuv-x] +local_path = libraries/tuv-x +protocol = git +repo_url = https://github.com/NCAR/tuv-x.git +branch = develop-160-cesm-build +required = True + +[json-fortran] +local_path = libraries/json-fortran +protocol = git +repo_url = https://github.com/jacobwilliams/json-fortran.git +tag = 8.2.1 +required = True + [cam] local_path = . protocol = externals_only diff --git a/Externals_CAM.cfg b/Externals_CAM.cfg index 496d5e5780..00932e324c 100644 --- a/Externals_CAM.cfg +++ b/Externals_CAM.cfg @@ -70,11 +70,18 @@ hash = ff76a231 required = True [tuv-x] -local_path = libs/tuv-x +local_path = libraries/tuv-x protocol = git repo_url = https://github.com/NCAR/tuv-x.git branch = develop-160-cesm-build required = True +[json-fortran] +local_path = libraries/json-fortran +protocol = git +repo_url = https://github.com/jacobwilliams/json-fortran.git +tag = 8.2.1 +required = True + [externals_description] schema_version = 1.0.0 diff --git a/libs/README.md b/libs/README.md deleted file mode 100644 index ed70c2235f..0000000000 --- a/libs/README.md +++ /dev/null @@ -1 +0,0 @@ -External libraries used by CAM From 95074390f6eb8264c47790b53ae1945de7a48b5b Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Wed, 12 Oct 2022 17:45:33 -0600 Subject: [PATCH 03/84] add tuv-x to cam build --- cime_config/buildlib | 89 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/cime_config/buildlib b/cime_config/buildlib index 12c1c4f9b9..f55434df16 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -20,6 +20,7 @@ from CIME.case import Case from CIME.utils import run_cmd, expect from CIME.buildlib import parse_input from CIME.build import get_standard_makefile_args +from CIME.XML.env_build import EnvBuild logger = logging.getLogger(__name__) @@ -108,10 +109,98 @@ def _build_cam(caseroot, libroot, bldroot): logger.info("%s: \n\n output:\n %s \n\n err:\n\n%s\n", cmd, out, err) expect(rc == 0, "Command %s failed with rc=%s" % (cmd, rc)) +############################################################################### +def _run_cmd(command, working_dir): +############################################################################### + + rc, out, err = run_cmd(command, from_dir=working_dir, verbose=True) + expect(rc == 0, "Command {} failed with rc={}".format(command, rc)) + +############################################################################### +def _cmake_default_args(caseroot): +############################################################################### +# Returns a dictionary of CMake variables based on the Macros.cmake file for +# the build. + + build = EnvBuild(case_root=caseroot) + with Case(caseroot) as case: + macro_path = os.path.abspath(os.path.join(caseroot, "cmake_macros", "")) + args = "-DCONVERT_TO_MAKE=ON " + args += "-DCASEROOT={} ".format(caseroot) + args += "-DCOMPILER={} ".format(build.get_value("COMPILER")) + args += "-DOS={} ".format(build.get_value("OS")) + args += "-DMACH={} ".format(case.get_value("MACH")) + cmd = "cmake {} .".format(args) + rc, out, err = run_cmd(cmd, combine_output=True, from_dir=macro_path) + expect(rc == 0, "Command {} failed with rc={}".format(cmd, rc)) + + arg_dict = {} + for line in out.splitlines(): + if ":=" in line: + key, val = line.split(":=") + arg_dict[key.replace('CIME_SET_MAKEFILE_VAR','').strip()] = val.strip() + + return arg_dict + +############################################################################### +def _build_json_fortran(caseroot, libroot, bldroot): +############################################################################### + + with Case(caseroot) as case: + bldpath = os.path.join(bldroot, "json-fortran") + if not os.path.exists(bldpath): + os.makedirs(bldpath) + srcpath = os.path.abspath(os.path.join(case.get_value("COMP_ROOT_DIR_ATM"), \ + "libraries", "json-fortran", "")) + logger.info("Building json-fortran {} from source in {}\n".format(bldpath, srcpath)) + + arg_dict = _cmake_default_args(caseroot) + cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["SFC"]) + cmake_args += "-DCMAKE_BUILD_TYPE=Release " + cmake_args += "-DSKIP_DOC_GEN:BOOL=TRUE " + cmake_args += srcpath + + _run_cmd("cmake {}".format(cmake_args), bldpath) + _run_cmd(case.get_value("GMAKE"), bldpath) + +############################################################################### +def _build_tuvx(caseroot, libroot, bldroot): +############################################################################### + + with Case(caseroot) as case: + bldpath = os.path.join(bldroot, "tuv-x") + if not os.path.exists(bldpath): + os.makedirs(bldpath) + jsoninc = os.path.join(bldroot, "json-fortran", "include", "") + expect(os.path.exists(jsoninc), \ + "JSON-Fortran include folder for TUV-x build not found at {}".format(jsoninc)) + jsonlib = os.path.join(bldroot, "json-fortran", "lib", "libjsonfortran.a") + expect(os.path.exists(jsonlib), \ + "JSON-Fortran library for TUV-x build not found at {}".format(jsonlib)) + srcpath = os.path.abspath(os.path.join(case.get_value("COMP_ROOT_DIR_ATM"), \ + "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_Fortran_COMPILER={} ".format(arg_dict["MPIFC"]) + cmake_args += "-DCMAKE_BUILD_TYPE=Release " + cmake_args += "-DENABLE_COVERAGE=OFF " + cmake_args += "-DJSON_INCLUDE_DIR={} ".format(jsoninc) + cmake_args += "-DJSON_LIB={} ".format(jsonlib) + # Is MPI always enabled? If not, how to know if it is being used? + cmake_args += "-DENABLE_MPI:BOOL=TRUE " + cmake_args += "-DCMAKE_Fortran_FLAGS='{}' ".format(arg_dict["FFLAGS"]) + cmake_args += srcpath + + _run_cmd("cmake {}".format(cmake_args), bldpath) + _run_cmd(case.get_value("GMAKE"), bldpath) + ############################################################################### def _main_func(): caseroot, libroot, bldroot = parse_input(sys.argv) + _build_json_fortran(caseroot, libroot, bldroot) + _build_tuvx(caseroot, libroot, bldroot) _build_fms(caseroot, libroot, bldroot) _build_cam(caseroot, libroot, bldroot) From 6fa3038a4a564db5381dc02b3a0b91ade4b95563 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Thu, 13 Oct 2022 08:02:57 -0600 Subject: [PATCH 04/84] compile in MPI support only if needed --- cime_config/buildlib | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cime_config/buildlib b/cime_config/buildlib index f55434df16..4359cb6aad 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -167,6 +167,7 @@ def _build_json_fortran(caseroot, libroot, bldroot): def _build_tuvx(caseroot, libroot, bldroot): ############################################################################### + build = EnvBuild(case_root=caseroot) with Case(caseroot) as case: bldpath = os.path.join(bldroot, "tuv-x") if not os.path.exists(bldpath): @@ -182,13 +183,15 @@ def _build_tuvx(caseroot, libroot, bldroot): logger.info("Building TUV-x in {} from source in {}\n".format(bldpath, srcpath)) arg_dict = _cmake_default_args(caseroot) - cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["MPIFC"]) + if build.get_value("MPILIB") == "mpi-serial": + cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["SFC"]) + else: + cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["MPIFC"]) + cmake_args += "-DENABLE_MPI:BOOL=TRUE " cmake_args += "-DCMAKE_BUILD_TYPE=Release " cmake_args += "-DENABLE_COVERAGE=OFF " cmake_args += "-DJSON_INCLUDE_DIR={} ".format(jsoninc) cmake_args += "-DJSON_LIB={} ".format(jsonlib) - # Is MPI always enabled? If not, how to know if it is being used? - cmake_args += "-DENABLE_MPI:BOOL=TRUE " cmake_args += "-DCMAKE_Fortran_FLAGS='{}' ".format(arg_dict["FFLAGS"]) cmake_args += srcpath From 840307612dc6122516706aac4cecec3d604b3a63 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Tue, 18 Oct 2022 14:00:53 -0600 Subject: [PATCH 05/84] draft TUV-x wrapper; link TUV-x libs --- Externals.cfg | 18 +-- cime_config/buildlib | 35 +++++- cime_config/config_component.xml | 12 ++ src/chemistry/mozart/mo_chemini.F90 | 8 ++ src/chemistry/mozart/mo_tuvx.F90 | 167 ++++++++++++++++++++++++++++ 5 files changed, 222 insertions(+), 18 deletions(-) create mode 100644 src/chemistry/mozart/mo_tuvx.F90 diff --git a/Externals.cfg b/Externals.cfg index 21d82894f3..faa1be51d0 100644 --- a/Externals.cfg +++ b/Externals.cfg @@ -21,9 +21,9 @@ externals = Externals.cfg required = True [cmeps] -tag = cmeps0.13.70 +branch = develop-add-linked-libs protocol = git -repo_url = https://github.com/ESCOMP/CMEPS.git +repo_url = https://github.com/mattldawson/CMEPS.git local_path = components/cmeps required = True @@ -109,20 +109,6 @@ repo_url = https://github.com/ESCOMP/RTM local_path = components/rtm required = True -[tuv-x] -local_path = libraries/tuv-x -protocol = git -repo_url = https://github.com/NCAR/tuv-x.git -branch = develop-160-cesm-build -required = True - -[json-fortran] -local_path = libraries/json-fortran -protocol = git -repo_url = https://github.com/jacobwilliams/json-fortran.git -tag = 8.2.1 -required = True - [cam] local_path = . protocol = externals_only diff --git a/cime_config/buildlib b/cime_config/buildlib index 4359cb6aad..e90cf5000d 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -145,6 +145,8 @@ def _cmake_default_args(caseroot): ############################################################################### def _build_json_fortran(caseroot, libroot, bldroot): ############################################################################### +# Builds the json-fortran 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, "json-fortran") @@ -152,7 +154,7 @@ def _build_json_fortran(caseroot, libroot, bldroot): os.makedirs(bldpath) srcpath = os.path.abspath(os.path.join(case.get_value("COMP_ROOT_DIR_ATM"), \ "libraries", "json-fortran", "")) - logger.info("Building json-fortran {} from source in {}\n".format(bldpath, srcpath)) + logger.info("Building json-fortran in {} from source in {}\n".format(bldpath, srcpath)) arg_dict = _cmake_default_args(caseroot) cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["SFC"]) @@ -163,9 +165,23 @@ def _build_json_fortran(caseroot, libroot, bldroot): _run_cmd("cmake {}".format(cmake_args), bldpath) _run_cmd(case.get_value("GMAKE"), bldpath) + # add json-fortran to include paths + incldir = os.environ.get('USER_INCLDIR') + if incldir is None: + incldir = '' + os.environ['USER_INCLDIR'] = incldir + \ + " -I{} ".format(os.path.join(bldroot, "json-fortran", "include")) + + # The built library is included in the CAM_LINKED_LIBS entry in + # config_component.xml and is staged here to the lib folder + os.rename(os.path.join(bldroot, "json-fortran", "lib", "libjsonfortran.a"), \ + os.path.join(libroot, "libjsonfortran.a")) + ############################################################################### 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 build = EnvBuild(case_root=caseroot) with Case(caseroot) as case: @@ -175,7 +191,7 @@ def _build_tuvx(caseroot, libroot, bldroot): jsoninc = os.path.join(bldroot, "json-fortran", "include", "") expect(os.path.exists(jsoninc), \ "JSON-Fortran include folder for TUV-x build not found at {}".format(jsoninc)) - jsonlib = os.path.join(bldroot, "json-fortran", "lib", "libjsonfortran.a") + jsonlib = os.path.join(libroot, "libjsonfortran.a") expect(os.path.exists(jsonlib), \ "JSON-Fortran library for TUV-x build not found at {}".format(jsonlib)) srcpath = os.path.abspath(os.path.join(case.get_value("COMP_ROOT_DIR_ATM"), \ @@ -190,6 +206,7 @@ def _build_tuvx(caseroot, libroot, bldroot): cmake_args += "-DENABLE_MPI:BOOL=TRUE " cmake_args += "-DCMAKE_BUILD_TYPE=Release " cmake_args += "-DENABLE_COVERAGE=OFF " + cmake_args += "-DENABLE_TESTS=OFF " cmake_args += "-DJSON_INCLUDE_DIR={} ".format(jsoninc) cmake_args += "-DJSON_LIB={} ".format(jsonlib) cmake_args += "-DCMAKE_Fortran_FLAGS='{}' ".format(arg_dict["FFLAGS"]) @@ -198,6 +215,20 @@ def _build_tuvx(caseroot, libroot, bldroot): _run_cmd("cmake {}".format(cmake_args), bldpath) _run_cmd(case.get_value("GMAKE"), bldpath) + # add TUV-x to include paths + incldir = os.environ.get('USER_INCLDIR') + if incldir is None: + incldir = '' + os.environ['USER_INCLDIR'] = incldir + \ + " -I{} ".format(os.path.join(bldroot, "tuv-x", "include")) + + # The built libraries are included in the CAM_LINKED_LIBS entry in + # config_component.xml and are staged here to the lib folder + os.rename(os.path.join(bldroot, "tuv-x", "lib", "libmusica.a"), \ + os.path.join(libroot, "libmusica.a")) + os.rename(os.path.join(bldroot, "tuv-x", "lib", "libtuvx.a"), \ + os.path.join(libroot, "libtuvx.a")) + ############################################################################### def _main_func(): diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index 5cf95a6e4a..195a7821e7 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -190,6 +190,18 @@ + + char + + -ltuvx -lmusica -ljsonfortran + build_component_cam + env_build.xml + + CAM linked libraries. The libraries are built by CAM's buildlib script and should be included + during linking of the model executable. + + + char diff --git a/src/chemistry/mozart/mo_chemini.F90 b/src/chemistry/mozart/mo_chemini.F90 index d66458e8fc..46ba96c933 100644 --- a/src/chemistry/mozart/mo_chemini.F90 +++ b/src/chemistry/mozart/mo_chemini.F90 @@ -48,6 +48,7 @@ subroutine chemini & use mo_srf_emissions, only : srf_emissions_inti use mo_sulf, only : sulf_inti use mo_photo, only : photo_inti + use mo_tuvx, only : tuvx_init use mo_lightning, only : lightning_inti use mo_drydep, only : drydep_inti use mo_imp_sol, only : imp_slv_inti @@ -200,6 +201,13 @@ subroutine chemini & if (masterproc) write(iulog,*) 'chemini: after photo_inti on node ',iam + !----------------------------------------------------------------------- + ! ... initialize the TUV-x photolysis rate constant calculator + !----------------------------------------------------------------------- + + call tuvx_init( ) + if (masterproc) write(iulog,*) 'chemini: after tuvx_init on node ',iam + !----------------------------------------------------------------------- ! ... initialize ion production !----------------------------------------------------------------------- diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 new file mode 100644 index 0000000000..9485641718 --- /dev/null +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -0,0 +1,167 @@ +module mo_tuvx + !---------------------------------------------------------------------- + ! ... wrapper for TUV-x photolysis rate constant calculator + !---------------------------------------------------------------------- + +#ifdef _MPI + use mpi +#endif + use tuvx_core, only : core_t + + implicit none + + private + + public :: tuvx_init + public :: tuvx_finalize + + ! TUV-x calculator for each OMP thread + type :: tuvx_ptr + type(core_t), pointer :: core_ => null( ) + end type tuvx_ptr + type(tuvx_ptr), allocatable :: tuvx_ptrs(:) + + ! TODO where should this path be stored? + character(len=*), parameter :: tuvx_config_path = "tuvx_config.json" + ! TODO how to know what MPI communicator to use? +#ifdef _MPI + integer, parameter :: tuvx_comm = MPI_COMM_WORLD +#else + integer, parameter :: tuvx_comm = 0 +#endif + +!================================================================================================ +contains +!================================================================================================ + + subroutine tuvx_init( ) +!----------------------------------------------------------------------- +! +! Purpose: initialize TUV-x for photolysis calculations +! +!----------------------------------------------------------------------- + + use cam_logfile, only : iulog + use musica_string, only : string_t, to_char + use spmd_utils, only : main_task => masterprocid, & + is_main_task => masterproc + +!----------------------------------------------------------------------- +! Local variables +!----------------------------------------------------------------------- + class(core_t), pointer :: core + character, allocatable :: buffer(:) + type(string_t) :: config_path + integer :: pack_size, pos, i_core, i_err + character(len=255) :: cwd + + call getcwd(cwd) + config_path = tuvx_config_path + if( is_main_task ) then + write(iulog,*) "Initializing TUV-x on MPI Task "//trim( to_char( main_task ) ) & + //" and OpenMP thread "//trim( to_char( thread_id( ) ) ) & + //" for "//trim( to_char( max_threads( ) ) )//" threads." + write(iulog,*) "TUV-x working dir: '"//trim(cwd) & + //"' config path: '"//config_path//"'" + end if + +#if 0 + ! construct a core on the primary process and pack it onto an MPI buffer + if( is_main_task ) then + core => core_t( config_path ) + pack_size = core%pack_size( tuvx_comm ) + allocate( buffer( pack_size ) ) + pos = 0 + call core%mpi_pack( buffer, pos, tuvx_comm ) + deallocate( core ) + end if + + ! broadcast the core data to all MPI processes +#ifdef _MPI + call mpi_bcast( pack_size, 1, MPI_INTEGER, main_task, tuvx_comm, i_err ) + if( .not. is_main_task ) allocate( buffer( pack_size ) ) + call mpi_bcast( buffer, pack_size, MPI_CHARACTER, main_task, tuvx_comm, i_err ) +#endif + + ! unpack the core for each OMP thread on every MPI process + allocate( tuvx_ptrs( max_threads( ) ) ) + do i_core = 1, size( tuvx_ptrs ) + associate( tuvx => tuvx_ptrs( i_core ) ) + allocate( tuvx%core_ ) + pos = 0 + call tuvx%core_%mpi_unpack( buffer, pos, tuvx_comm ) + end associate + end do +#endif + + end subroutine tuvx_init + +!================================================================================================ + + subroutine tuvx_finalize( ) +!----------------------------------------------------------------------- +! +! Purpose: clean up memory associated with TUV-x calculators +! +!----------------------------------------------------------------------- + +!----------------------------------------------------------------------- +! Local variables +!----------------------------------------------------------------------- + integer :: i_core + + if( allocated( tuvx_ptrs ) ) then + do i_core = 1, size( tuvx_ptrs ) + if( associated( tuvx_ptrs( i_core )%core_ ) ) then + deallocate( tuvx_ptrs( i_core )%core_ ) + end if + end do + end if + + end subroutine tuvx_finalize + +!================================================================================================ + + integer function thread_id( ) +!----------------------------------------------------------------------- +! +! Purpose: returns the id of the current OpenMP thread, or 1 if not +! using OpenMP +! +!----------------------------------------------------------------------- +#ifdef _OPENMP + use omp_lib, only : omp_get_thread_num +#endif + +#ifdef _OPENMP + thread_id = omp_get_thread_num( ) +#else + thread_id = 1 +#endif + + end function thread_id + +!================================================================================================ + + integer function max_threads( ) +!----------------------------------------------------------------------- +! +! Purpose: returns the number of threads available for calculations at +! runtime +! +!----------------------------------------------------------------------- +#ifdef _OPENMP + use omp_lib, only : omp_get_max_threads +#endif + +#ifdef _OPENMP + max_threads = omp_get_max_threads( ) +#else + max_threads = 1 +#endif + + end function max_threads + +!================================================================================================ + +end module mo_tuvx From dd397947fc75f4f8c869ba268fb6c17cef3c2229 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Wed, 19 Oct 2022 08:54:49 -0600 Subject: [PATCH 06/84] add MPI/OpenMP support to TUV-x wrapper --- cime_config/buildnml | 8 +++ src/chemistry/mozart/mo_tuvx.F90 | 87 ++++++++++++++++++++------------ 2 files changed, 63 insertions(+), 32 deletions(-) diff --git a/cime_config/buildnml b/cime_config/buildnml index 9393269f84..ed8794b0ab 100755 --- a/cime_config/buildnml +++ b/cime_config/buildnml @@ -199,6 +199,14 @@ def buildnml(case, caseroot, compname): if (os.path.isfile(file1)) and (not os.path.isfile(file2)): shutil.copy(file1,file2) + # Temporary copy of TUV-x data for development + dest_data = os.path.join(rundir, "data") + if os.path.exists(dest_data): + shutil.rmtree(dest_data) + shutil.copytree(os.path.join(srcroot, "libraries", "tuv-x", "data"), dest_data) + shutil.copy2(os.path.join(srcroot, "libraries", "tuv-x", "examples", "full_config.json"), \ + os.path.join(rundir, "tuvx_config.json")) + ############################################################################### def _main_func(): diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 9485641718..f6b6c67b54 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -1,11 +1,8 @@ module mo_tuvx - !---------------------------------------------------------------------- - ! ... wrapper for TUV-x photolysis rate constant calculator - !---------------------------------------------------------------------- +!---------------------------------------------------------------------- +! ... wrapper for TUV-x photolysis rate constant calculator +!---------------------------------------------------------------------- -#ifdef _MPI - use mpi -#endif use tuvx_core, only : core_t implicit none @@ -21,14 +18,9 @@ module mo_tuvx end type tuvx_ptr type(tuvx_ptr), allocatable :: tuvx_ptrs(:) - ! TODO where should this path be stored? + ! TODO where should this file and other TUV-x data be stored? + ! TODO how should this path be set and communicated to this wrapper? character(len=*), parameter :: tuvx_config_path = "tuvx_config.json" - ! TODO how to know what MPI communicator to use? -#ifdef _MPI - integer, parameter :: tuvx_comm = MPI_COMM_WORLD -#else - integer, parameter :: tuvx_comm = 0 -#endif !================================================================================================ contains @@ -41,10 +33,14 @@ subroutine tuvx_init( ) ! !----------------------------------------------------------------------- - use cam_logfile, only : iulog +#ifdef HAVE_MPI + use mpi +#endif + use musica_assert, only : assert_msg use musica_string, only : string_t, to_char use spmd_utils, only : main_task => masterprocid, & - is_main_task => masterproc + is_main_task => masterproc, & + mpicom !----------------------------------------------------------------------- ! Local variables @@ -53,34 +49,30 @@ subroutine tuvx_init( ) character, allocatable :: buffer(:) type(string_t) :: config_path integer :: pack_size, pos, i_core, i_err - character(len=255) :: cwd - call getcwd(cwd) config_path = tuvx_config_path - if( is_main_task ) then - write(iulog,*) "Initializing TUV-x on MPI Task "//trim( to_char( main_task ) ) & - //" and OpenMP thread "//trim( to_char( thread_id( ) ) ) & - //" for "//trim( to_char( max_threads( ) ) )//" threads." - write(iulog,*) "TUV-x working dir: '"//trim(cwd) & - //"' config path: '"//config_path//"'" - end if + if( is_main_task ) call log_initialization( ) + +#ifndef HAVE_MPI + call assert_msg( 113937299, is_main_task, "Multiple tasks present without " & + //"MPI support enabled for TUV-x") +#endif -#if 0 ! construct a core on the primary process and pack it onto an MPI buffer if( is_main_task ) then core => core_t( config_path ) - pack_size = core%pack_size( tuvx_comm ) + pack_size = core%pack_size( mpicom ) allocate( buffer( pack_size ) ) pos = 0 - call core%mpi_pack( buffer, pos, tuvx_comm ) + call core%mpi_pack( buffer, pos, mpicom ) deallocate( core ) end if ! broadcast the core data to all MPI processes -#ifdef _MPI - call mpi_bcast( pack_size, 1, MPI_INTEGER, main_task, tuvx_comm, i_err ) +#ifdef HAVE_MPI + call mpi_bcast( pack_size, 1, MPI_INTEGER, main_task, mpicom, i_err ) if( .not. is_main_task ) allocate( buffer( pack_size ) ) - call mpi_bcast( buffer, pack_size, MPI_CHARACTER, main_task, tuvx_comm, i_err ) + call mpi_bcast( buffer, pack_size, MPI_CHARACTER, main_task, mpicom, i_err ) #endif ! unpack the core for each OMP thread on every MPI process @@ -89,10 +81,9 @@ subroutine tuvx_init( ) associate( tuvx => tuvx_ptrs( i_core ) ) allocate( tuvx%core_ ) pos = 0 - call tuvx%core_%mpi_unpack( buffer, pos, tuvx_comm ) + call tuvx%core_%mpi_unpack( buffer, pos, mpicom ) end associate end do -#endif end subroutine tuvx_init @@ -164,4 +155,36 @@ end function max_threads !================================================================================================ + subroutine log_initialization( ) +!----------------------------------------------------------------------- +! +! Purpose: prints initialization conditions to the log file +! +!----------------------------------------------------------------------- + + use cam_logfile, only : iulog + use musica_string, only : to_char + use spmd_utils, only : main_task => masterprocid, & + is_main_task => masterproc + + if( is_main_task ) then + write(iulog,*) "Initializing TUV-x" +#ifdef HAVE_MPI + write(iulog,*) " - with MPI support on task "//trim( to_char( main_task ) ) +#else + write(iulog,*) " - without MPI support" +#endif +#ifdef _OPENMP + write(iulog,*) " - with OpenMP support for "// & + trim( to_char( max_threads( ) ) )//" threads, on thread" & + //trim( to_char( thread_id( ) ) ) +#else + write(iulog,*) " - without OpenMP support" +#endif + write(iulog,*) " - with configuration file: '"//tuvx_config_path//"'" + end if + + end subroutine log_initialization +!================================================================================================ + end module mo_tuvx From 9f6b6d24a967c45606a4a194d94049c73ad4eca8 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Wed, 19 Oct 2022 11:32:35 -0700 Subject: [PATCH 07/84] offset OMP thread for use with array of TUV-x cores --- src/chemistry/mozart/mo_tuvx.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index f6b6c67b54..d7f4496b96 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -117,7 +117,7 @@ integer function thread_id( ) !----------------------------------------------------------------------- ! ! Purpose: returns the id of the current OpenMP thread, or 1 if not -! using OpenMP +! using OpenMP (1 <= id <= max_threads()) ! !----------------------------------------------------------------------- #ifdef _OPENMP @@ -125,7 +125,7 @@ integer function thread_id( ) #endif #ifdef _OPENMP - thread_id = omp_get_thread_num( ) + thread_id = 1 + omp_get_thread_num( ) #else thread_id = 1 #endif From 5f1378cb6a0ad50e6b2cbbbf3380cc4e415a55f6 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Thu, 20 Oct 2022 16:56:01 -0600 Subject: [PATCH 08/84] add TUV-x run function --- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 6 ++ src/chemistry/mozart/mo_tuvx.F90 | 66 +++++++++++++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index 323d9bbdaf..57f9910755 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -249,6 +249,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & use chem_mods, only : nabscol, nfs, indexm, clscnt4 use physconst, only : rga use mo_photo, only : set_ub_col, setcol, table_photo + use mo_tuvx, only : tuvx_get_photo_rates use mo_exp_sol, only : exp_sol use mo_imp_sol, only : imp_sol use mo_setrxt, only : setrxt @@ -788,6 +789,11 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & col_dens, zen_angle, asdir, cwat, cldfr, & esfact, vmr, invariants, ncol, lchnk, pbuf ) + !----------------------------------------------------------------- + ! ... get calculated photolysis rates from TUV-x + !----------------------------------------------------------------- + call tuvx_get_photo_rates( ncol ) + do i = 1,phtcnt call outfld( tag_names(i), reaction_rates(:ncol,:,rxt_tag_map(i)), ncol, lchnk ) enddo diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index d7f4496b96..7322d2e0c4 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -3,6 +3,8 @@ module mo_tuvx ! ... wrapper for TUV-x photolysis rate constant calculator !---------------------------------------------------------------------- + use musica_string, only : string_t + use shr_kind_mod, only : r8 => shr_kind_r8 use tuvx_core, only : core_t implicit none @@ -10,15 +12,18 @@ module mo_tuvx private public :: tuvx_init + public :: tuvx_get_photo_rates public :: tuvx_finalize ! TUV-x calculator for each OMP thread type :: tuvx_ptr - type(core_t), pointer :: core_ => null( ) + type(core_t), pointer :: core_ => null( ) ! TUV-x calculator + integer :: n_photo_rates_ ! number of photo reactions in TUV-x + real(r8), allocatable :: photo_rates_(:,:) ! photolysis rate constants + ! (vertical level, reaction) [s-1] end type tuvx_ptr type(tuvx_ptr), allocatable :: tuvx_ptrs(:) - ! TODO where should this file and other TUV-x data be stored? ! TODO how should this path be set and communicated to this wrapper? character(len=*), parameter :: tuvx_config_path = "tuvx_config.json" @@ -36,8 +41,11 @@ subroutine tuvx_init( ) #ifdef HAVE_MPI use mpi #endif + use cam_logfile, only : iulog use musica_assert, only : assert_msg + use musica_mpi, only : musica_mpi_rank use musica_string, only : string_t, to_char + use tuvx_grid, only : grid_t use spmd_utils, only : main_task => masterprocid, & is_main_task => masterproc, & mpicom @@ -48,7 +56,8 @@ subroutine tuvx_init( ) class(core_t), pointer :: core character, allocatable :: buffer(:) type(string_t) :: config_path - integer :: pack_size, pos, i_core, i_err + class(grid_t), pointer :: height + integer :: pack_size, pos, i_core, i_err, i_thread config_path = tuvx_config_path if( is_main_task ) call log_initialization( ) @@ -71,8 +80,16 @@ subroutine tuvx_init( ) ! broadcast the core data to all MPI processes #ifdef HAVE_MPI call mpi_bcast( pack_size, 1, MPI_INTEGER, main_task, mpicom, i_err ) + if( i_err /= MPI_SUCCESS ) then + write(iulog,*) "TUV-x MPI int bcast error" + call mpi_abort( mpicom, 1, i_err ) + end if if( .not. is_main_task ) allocate( buffer( pack_size ) ) call mpi_bcast( buffer, pack_size, MPI_CHARACTER, main_task, mpicom, i_err ) + if( i_err /= MPI_SUCCESS ) then + write(iulog,*) "TUV-x MPI char array bcast error" + call mpi_abort( mpicom, 1, i_err ) + end if #endif ! unpack the core for each OMP thread on every MPI process @@ -85,8 +102,51 @@ subroutine tuvx_init( ) end associate end do + ! Set up map between CAM and TUV-x photolysis reactions for each thread + do i_thread = 1, max_threads( ) + associate( tuvx => tuvx_ptrs( i_thread ) ) + tuvx%n_photo_rates_ = tuvx%core_%number_of_photolysis_reactions( ) + height => tuvx%core_%get_grid( "height", "km" ) + ! Temporary for development + allocate( tuvx%photo_rates_( height%ncells_ + 1, tuvx%n_photo_rates_ ) ) + end associate + end do + end subroutine tuvx_init +!================================================================================================ + + subroutine tuvx_get_photo_rates( ncol ) +!----------------------------------------------------------------------- +! +! Purpose: calculate and return photolysis rate constants +! +!----------------------------------------------------------------------- + + use cam_logfile, only : iulog + use spmd_utils, only : main_task => masterprocid, & + is_main_task => masterproc, & + mpicom +!----------------------------------------------------------------------- +! Dummy arguments +!----------------------------------------------------------------------- + + integer, intent(in) :: ncol ! Number of colums to calculated photolysis for + +!----------------------------------------------------------------------- +! Local variables +!----------------------------------------------------------------------- + integer :: i_col ! column index + + associate( tuvx => tuvx_ptrs( thread_id( ) ) ) + do i_col = 1, ncol + ! Temporary fix SZA for development + call tuvx%core_%run( 45.0_r8, photolysis_rate_constants = tuvx%photo_rates_ ) + end do + end associate + + end subroutine tuvx_get_photo_rates + !================================================================================================ subroutine tuvx_finalize( ) From ab18e8212005810d89f24cf278c72acb44a5161c Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 21 Oct 2022 13:28:54 -0700 Subject: [PATCH 09/84] create temporary TUV-x for CAM (#3) --- cime_config/buildnml | 2 +- cime_config/temp_tuvx_config.json | 156 ++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 cime_config/temp_tuvx_config.json diff --git a/cime_config/buildnml b/cime_config/buildnml index ed8794b0ab..e256f2d070 100755 --- a/cime_config/buildnml +++ b/cime_config/buildnml @@ -204,7 +204,7 @@ def buildnml(case, caseroot, compname): if os.path.exists(dest_data): shutil.rmtree(dest_data) shutil.copytree(os.path.join(srcroot, "libraries", "tuv-x", "data"), dest_data) - shutil.copy2(os.path.join(srcroot, "libraries", "tuv-x", "examples", "full_config.json"), \ + shutil.copy2(os.path.join(srcroot, "cime_config", "temp_tuvx_config.json"), \ os.path.join(rundir, "tuvx_config.json")) ############################################################################### diff --git a/cime_config/temp_tuvx_config.json b/cime_config/temp_tuvx_config.json new file mode 100644 index 0000000000..58b0fa6fa0 --- /dev/null +++ b/cime_config/temp_tuvx_config.json @@ -0,0 +1,156 @@ +{ + "__description": "This example configuration includes photolysis and dose rate calculations", + "O2 absorption" : { + "cross section parameters file": "data/cross_sections/O2_parameters.txt" + }, + "grids": { + "height": { + "type": "equal interval", + "units": "km", + "begins at" : 0.0, + "ends at" : 120.0, + "cell delta" : 1.0 + }, + "wavelength": { + "type": "from csv file", + "units": "nm", + "file path": "data/grids/wavelength/combined.grid" + }, + "time": { + "type": "from config file", + "units": "hours", + "values": [ 14.0 ] + } + }, + "profiles": { + "O3": { + "type": "O3", + "units": "molecule cm-3", + "file path": "data/profiles/atmosphere/ussa.ozone" + }, + "air": { + "type": "air", + "units": "molecule cm-3", + "file path": "data/profiles/atmosphere/ussa.dens" + }, + "O2": { + "type": "O2", + "units": "molecule cm-3", + "file path": "data/profiles/atmosphere/ussa.dens" + }, + "temperature": { + "type": "from csv file", + "units": "K", + "file path": "data/profiles/atmosphere/ussa.temp", + "grid": { + "name": "height", + "units": "km" + } + }, + "solar zenith angle": { + "type": "solar zenith angle", + "units": "degrees", + "year" : 2002, + "month": 3, + "day": 21, + "longitude": 0.0, + "latitude": 0.0 + }, + "Earth-Sun distance": { + "type": "Earth-Sun distance", + "units": "AU", + "year" : 2002, + "month": 3, + "day": 21 + }, + "surface albedo": { + "type": "from config file", + "units": "none", + "uniform value": 0.10, + "grid": { + "name": "wavelength", + "units": "nm" + } + }, + "extraterrestrial flux": { + "type": "extraterrestrial flux", + "units": "photon cm-2 s-1", + "file path": ["data/profiles/solar/susim_hi.flx", + "data/profiles/solar/atlas3_1994_317_a.dat", + "data/profiles/solar/sao2010.solref.converted", + "data/profiles/solar/neckel.flx"], + "interpolator": ["","","","fractional target"] + } + }, + "radiative transfer": { + "cross sections": [ + { + "name": "air", + "type": "air" + }, + { + "name": "O3", + "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "type": "O3" + }, + { + "name": "O2", + "netcdf files": ["data/cross_sections/O2_1.nc"], + "type": "base", + "lower extrapolation": { "type": "boundary" } + } + ], + "radiators": [ + { + "name": "air", + "type": "base", + "cross section": "air", + "vertical profile": "air", + "vertical profile units": "molecule cm-3" + }, + { + "name": "O2", + "type": "base", + "cross section": "O2", + "vertical profile": "O2", + "vertical profile units": "molecule cm-3" + }, + { + "name": "O3", + "type": "base", + "cross section": "O3", + "vertical profile": "O3", + "vertical profile units": "molecule cm-3" + }, + { + "name": "aerosols", + "type": "aerosol", + "optical depths": [2.40e-01, 1.06e-01, 4.56e-02, 1.91e-02, 1.01e-02, 7.63e-03, + 5.38e-03, 5.00e-03, 5.15e-03, 4.94e-03, 4.82e-03, 4.51e-03, + 4.74e-03, 4.37e-03, 4.28e-03, 4.03e-03, 3.83e-03, 3.78e-03, + 3.88e-03, 3.08e-03, 2.26e-03, 1.64e-03, 1.23e-03, 9.45e-04, + 7.49e-04, 6.30e-04, 5.50e-04, 4.21e-04, 3.22e-04, 2.48e-04, + 1.90e-04, 1.45e-04, 1.11e-04, 8.51e-05, 6.52e-05, 5.00e-05, + 3.83e-05, 2.93e-05, 2.25e-05, 1.72e-05, 1.32e-05, 1.01e-05, + 7.72e-06, 5.91e-06, 4.53e-06, 3.46e-06, 2.66e-06, 2.04e-06, + 1.56e-06, 1.19e-06, 9.14e-07], + "single scattering albedo": 0.99, + "asymmetry factor": 0.61, + "550 nm optical depth": 0.235 + } + ] + }, + "photolysis reactions": { + "O2+hv->O+O": { + "cross section": { + "netcdf files": ["data/cross_sections/O2_1.nc"], + "type": "base", + "lower extrapolation": { "type": "boundary" } + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + } +} From 5da13c7cec8b363ac2e574a4b14461c4980951d6 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Tue, 1 Nov 2022 13:03:31 -0600 Subject: [PATCH 10/84] add height and temperature updaters to tuv-x wrapper --- cime_config/buildlib | 5 +- cime_config/temp_tuvx_config.json | 28 +--- src/chemistry/mozart/mo_tuvx.F90 | 264 ++++++++++++++++++++++++++---- 3 files changed, 240 insertions(+), 57 deletions(-) diff --git a/cime_config/buildlib b/cime_config/buildlib index e90cf5000d..6c22917a32 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -204,7 +204,10 @@ def _build_tuvx(caseroot, libroot, bldroot): else: cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["MPIFC"]) cmake_args += "-DENABLE_MPI:BOOL=TRUE " - cmake_args += "-DCMAKE_BUILD_TYPE=Release " + if case.get_value("DEBUG"): + cmake_args += "-DCMAKE_BUILD_TYPE=Debug " + else: + cmake_args += "-DCMAKE_BUILD_TYPE=Release " cmake_args += "-DENABLE_COVERAGE=OFF " cmake_args += "-DENABLE_TESTS=OFF " cmake_args += "-DJSON_INCLUDE_DIR={} ".format(jsoninc) diff --git a/cime_config/temp_tuvx_config.json b/cime_config/temp_tuvx_config.json index 58b0fa6fa0..b4ff8f10b0 100644 --- a/cime_config/temp_tuvx_config.json +++ b/cime_config/temp_tuvx_config.json @@ -4,13 +4,6 @@ "cross section parameters file": "data/cross_sections/O2_parameters.txt" }, "grids": { - "height": { - "type": "equal interval", - "units": "km", - "begins at" : 0.0, - "ends at" : 120.0, - "cell delta" : 1.0 - }, "wavelength": { "type": "from csv file", "units": "nm", @@ -38,24 +31,6 @@ "units": "molecule cm-3", "file path": "data/profiles/atmosphere/ussa.dens" }, - "temperature": { - "type": "from csv file", - "units": "K", - "file path": "data/profiles/atmosphere/ussa.temp", - "grid": { - "name": "height", - "units": "km" - } - }, - "solar zenith angle": { - "type": "solar zenith angle", - "units": "degrees", - "year" : 2002, - "month": 3, - "day": 21, - "longitude": 0.0, - "latitude": 0.0 - }, "Earth-Sun distance": { "type": "Earth-Sun distance", "units": "AU", @@ -83,6 +58,9 @@ } }, "radiative transfer": { + "solver": { + "type": "delta eddington" + }, "cross sections": [ { "name": "air", diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 7322d2e0c4..d591debe26 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -3,9 +3,11 @@ module mo_tuvx ! ... wrapper for TUV-x photolysis rate constant calculator !---------------------------------------------------------------------- - use musica_string, only : string_t - use shr_kind_mod, only : r8 => shr_kind_r8 - use tuvx_core, only : core_t + use musica_string, only : string_t + use shr_kind_mod, only : r8 => shr_kind_r8 + use tuvx_core, only : core_t + use tuvx_grid_from_host, only : grid_updater_t + use tuvx_profile_from_host, only : profile_updater_t implicit none @@ -15,18 +17,30 @@ module mo_tuvx public :: tuvx_get_photo_rates public :: tuvx_finalize + ! Inidices for grid updaters + integer, parameter :: NUM_GRIDS = 1 ! number of grids that CAM will update at runtime + integer, parameter :: GRID_INDEX_HEIGHT = 1 ! Height grid index + + ! Indices for profile updaters + integer, parameter :: NUM_PROFILES = 1 ! number of profiles that CAM will update at runtime + integer, parameter :: PROFILE_INDEX_TEMPERATURE = 1 ! Temperature profile index + + ! TODO how should this path be set and communicated to this wrapper? + character(len=*), parameter :: tuvx_config_path = "tuvx_config.json" + ! TUV-x calculator for each OMP thread type :: tuvx_ptr - type(core_t), pointer :: core_ => null( ) ! TUV-x calculator - integer :: n_photo_rates_ ! number of photo reactions in TUV-x - real(r8), allocatable :: photo_rates_(:,:) ! photolysis rate constants - ! (vertical level, reaction) [s-1] + type(core_t), pointer :: core_ => null( ) ! TUV-x calculator + integer :: n_photo_rates_ ! number of photo reactions in TUV-x + real(r8), allocatable :: photo_rates_(:,:) ! photolysis rate constants + ! (vertical level, reaction) [s-1] + type(grid_updater_t) :: grids_(NUM_GRIDS) ! grid updaters + type(profile_updater_t) :: profiles_(NUM_PROFILES) ! profile updaters + real(r8), allocatable :: heights_(:) ! TEMPORARY FOR DEVELOPMENT + real(r8), allocatable :: temperatures_(:) ! TEMPORARY FOR DEVELOPMENT end type tuvx_ptr type(tuvx_ptr), allocatable :: tuvx_ptrs(:) - ! TODO how should this path be set and communicated to this wrapper? - character(len=*), parameter :: tuvx_config_path = "tuvx_config.json" - !================================================================================================ contains !================================================================================================ @@ -41,14 +55,16 @@ subroutine tuvx_init( ) #ifdef HAVE_MPI use mpi #endif - use cam_logfile, only : iulog - use musica_assert, only : assert_msg - use musica_mpi, only : musica_mpi_rank - use musica_string, only : string_t, to_char - use tuvx_grid, only : grid_t - use spmd_utils, only : main_task => masterprocid, & - is_main_task => masterproc, & - mpicom + use cam_logfile, only : iulog + use musica_assert, only : assert_msg + use musica_mpi, only : musica_mpi_rank + use musica_string, only : string_t, to_char + use tuvx_grid, only : grid_t + use tuvx_grid_warehouse, only : grid_warehouse_t + use tuvx_profile_warehouse, only : profile_warehouse_t + use spmd_utils, only : main_task => masterprocid, & + is_main_task => masterproc, & + mpicom !----------------------------------------------------------------------- ! Local variables @@ -57,7 +73,9 @@ subroutine tuvx_init( ) character, allocatable :: buffer(:) type(string_t) :: config_path class(grid_t), pointer :: height - integer :: pack_size, pos, i_core, i_err, i_thread + class(grid_warehouse_t), pointer :: cam_grids + class(profile_warehouse_t), pointer :: cam_profiles + integer :: pack_size, pos, i_core, i_err config_path = tuvx_config_path if( is_main_task ) call log_initialization( ) @@ -67,9 +85,13 @@ subroutine tuvx_init( ) //"MPI support enabled for TUV-x") #endif + ! Create the set of TUV-x grids and profiles that CAM will update at runtime + cam_grids => get_cam_grids( ) + cam_profiles => get_cam_profiles( cam_grids ) + ! construct a core on the primary process and pack it onto an MPI buffer if( is_main_task ) then - core => core_t( config_path ) + core => core_t( config_path, cam_grids, cam_profiles ) pack_size = core%pack_size( mpicom ) allocate( buffer( pack_size ) ) pos = 0 @@ -99,19 +121,22 @@ subroutine tuvx_init( ) allocate( tuvx%core_ ) pos = 0 call tuvx%core_%mpi_unpack( buffer, pos, mpicom ) - end associate - end do - ! Set up map between CAM and TUV-x photolysis reactions for each thread - do i_thread = 1, max_threads( ) - associate( tuvx => tuvx_ptrs( i_thread ) ) + ! Set up map between CAM and TUV-x photolysis reactions for each thread + call create_updaters( tuvx, cam_grids, cam_profiles ) + + ! TEMPORARY FOR DEVELOPMENT tuvx%n_photo_rates_ = tuvx%core_%number_of_photolysis_reactions( ) height => tuvx%core_%get_grid( "height", "km" ) - ! Temporary for development allocate( tuvx%photo_rates_( height%ncells_ + 1, tuvx%n_photo_rates_ ) ) + deallocate( height ) + end associate end do + deallocate( cam_grids ) + deallocate( cam_profiles ) + end subroutine tuvx_init !================================================================================================ @@ -140,8 +165,16 @@ subroutine tuvx_get_photo_rates( ncol ) associate( tuvx => tuvx_ptrs( thread_id( ) ) ) do i_col = 1, ncol - ! Temporary fix SZA for development + + ! set conditions for this column in TUV-x + call set_heights( tuvx, i_col ) + call set_temperatures( tuvx, i_col ) + ! TODO Add remaining input conditions + + ! Calculate photolysis rate constants for this column + ! TEMPORARY FOR DEVELOPMENT - fix SZA call tuvx%core_%run( 45.0_r8, photolysis_rate_constants = tuvx%photo_rates_ ) + end do end associate @@ -163,14 +196,20 @@ subroutine tuvx_finalize( ) if( allocated( tuvx_ptrs ) ) then do i_core = 1, size( tuvx_ptrs ) - if( associated( tuvx_ptrs( i_core )%core_ ) ) then - deallocate( tuvx_ptrs( i_core )%core_ ) - end if + associate( tuvx => tuvx_ptrs( i_core ) ) + if( associated( tuvx%core_ ) ) deallocate( tuvx%core_ ) + end associate end do end if end subroutine tuvx_finalize +!================================================================================================ +!================================================================================================ +! +! Support functions +! +!================================================================================================ !================================================================================================ integer function thread_id( ) @@ -236,7 +275,7 @@ subroutine log_initialization( ) #endif #ifdef _OPENMP write(iulog,*) " - with OpenMP support for "// & - trim( to_char( max_threads( ) ) )//" threads, on thread" & + trim( to_char( max_threads( ) ) )//" threads, on thread " & //trim( to_char( thread_id( ) ) ) #else write(iulog,*) " - without OpenMP support" @@ -245,6 +284,169 @@ subroutine log_initialization( ) end if end subroutine log_initialization + +!================================================================================================ + + function get_cam_grids( ) result( grids ) +!----------------------------------------------------------------------- +! +! Purpose: creates and loads a grid warehouse with grids that CAM will +! update at runtime +! +!----------------------------------------------------------------------- + + use ppgrid, only : pver ! number of vertical levels + use tuvx_grid_from_host, only : grid_from_host_t + use tuvx_grid_warehouse, only : grid_warehouse_t + + class(grid_warehouse_t), pointer :: grids ! collection of grids to be updated by CAM + +!----------------------------------------------------------------------- +! Local variables +!----------------------------------------------------------------------- + class(grid_from_host_t), pointer :: host_grid + + grids => grid_warehouse_t( ) + + ! Height grid will be ... \todo figure out how height grid should translate + ! to CAM vertical grid + host_grid => grid_from_host_t( "height", "km", pver ) + call grids%add( host_grid ) + deallocate( host_grid ) + + end function get_cam_grids + +!================================================================================================ + + function get_cam_profiles( grids ) result( profiles ) +!----------------------------------------------------------------------- +! +! Purpose: creates and loads a profile warehouse with profiles that CAM +! will update at runtime +! +!----------------------------------------------------------------------- + + use tuvx_grid, only : grid_t + use tuvx_grid_warehouse, only : grid_warehouse_t + use tuvx_profile_from_host, only : profile_from_host_t + use tuvx_profile_warehouse, only : profile_warehouse_t + + class(profile_warehouse_t), pointer :: profiles ! collection of profiles to be updated by CAM + class(grid_warehouse_t), intent(in) :: grids ! collection of grids to be updated by CAM + +!----------------------------------------------------------------------- +! Local variables +!----------------------------------------------------------------------- + class(profile_from_host_t), pointer :: host_profile + class(grid_t), pointer :: height + + height => grids%get_grid( "height", "km" ) + profiles => profile_warehouse_t( ) + + ! Temperature profile on height grid + host_profile => profile_from_host_t( "temperature", "K", height%size( ) ) + call profiles%add( host_profile ) + deallocate( host_profile ) + + deallocate( height ) + + end function get_cam_profiles + +!================================================================================================ + + subroutine create_updaters( this, grids, profiles ) +!----------------------------------------------------------------------- +! +! Purpose: creates updaters for each grid and profile that CAM will use +! to update TUV-x at each timestep +! +!----------------------------------------------------------------------- + + use musica_assert, only : assert + use tuvx_grid_from_host, only : grid_updater_t + use tuvx_grid, only : grid_t + use tuvx_grid_warehouse, only : grid_warehouse_t + use tuvx_profile, only : profile_t + use tuvx_profile_from_host, only : profile_updater_t + use tuvx_profile_warehouse, only : profile_warehouse_t + + class(tuvx_ptr), intent(inout) :: this + class(grid_warehouse_t), intent(in) :: grids + class(profile_warehouse_t), intent(in) :: profiles + +!----------------------------------------------------------------------- +! Local variables +!----------------------------------------------------------------------- + class(grid_t), pointer :: host_grid + class(profile_t), pointer :: host_profile + logical :: found + + host_grid => grids%get_grid( "height", "km" ) + this%grids_( GRID_INDEX_HEIGHT ) = this%core_%get_updater( host_grid, found ) + call assert( 213798815, found ) + write(*,*) "allocating height array to ", host_grid%size( ) + 1, " values", & + " on thread ", thread_id( ) + allocate( this%heights_( host_grid%size( ) + 1 ) ) ! TEMPORARY FOR DEVELOPMENT + deallocate( host_grid ) + + host_profile => profiles%get_profile( "temperature", "K" ) + this%profiles_( PROFILE_INDEX_TEMPERATURE ) = & + this%core_%get_updater( host_profile, found ) + call assert( 418735162, found ) + write(*,*) "allocating temperature array to ", host_profile%size( ) + 1, " values", & + " on thread ", thread_id( ) + allocate( this%temperatures_( host_profile%size( ) + 1 ) ) ! TEMPORARY FOR DEVELOPMENT + deallocate( host_profile ) + + end subroutine create_updaters + +!================================================================================================ + + subroutine set_heights( this, i_col ) +!----------------------------------------------------------------------- +! +! Purpose: sets the height values in TUV-x for the given column +! +! TODO: Describe how CAM vertical profile is mapped to TUV-x heights +! +!----------------------------------------------------------------------- + + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! Column to set conditions for + + ! TEMPORARY FOR DEVELOPMENT + integer :: i_level + do i_level = 1, size( this%heights_ ) + this%heights_( i_level ) = i_level * 1.0_r8 - 1.0_r8 + end do + call this%grids_( GRID_INDEX_HEIGHT )%update( & + mid_points = this%heights_(1:size(this%heights_)-1), & + edges = this%heights_(:) ) + + end subroutine set_heights + +!================================================================================================ + + subroutine set_temperatures( this, i_col ) +!----------------------------------------------------------------------- +! +! Purpose: sets the temperatures in TUV-x for the given column +! +! TODO: Describe how CAM temperature profile is mapped to TUV-x heights +! +!----------------------------------------------------------------------- + + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! Column to set conditions for + + ! TEMPORARY FOR DEVELOPMENT + this%temperatures_(:) = 298.15_r8 + call this%profiles_( PROFILE_INDEX_TEMPERATURE )%update( & + mid_point_values = this%temperatures_(1:size(this%temperatures_)-1), & + edge_values = this%temperatures_(:) ) + + end subroutine set_temperatures + !================================================================================================ end module mo_tuvx From 7a592415703e1b8a31dce1edfdacd13e9678953a Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Wed, 2 Nov 2022 15:55:10 -0600 Subject: [PATCH 11/84] set up remaining profile updaters --- cime_config/temp_tuvx_config.json | 45 ------- src/chemistry/mozart/mo_tuvx.F90 | 199 +++++++++++++++++++++++++----- 2 files changed, 171 insertions(+), 73 deletions(-) diff --git a/cime_config/temp_tuvx_config.json b/cime_config/temp_tuvx_config.json index b4ff8f10b0..9b6a22322e 100644 --- a/cime_config/temp_tuvx_config.json +++ b/cime_config/temp_tuvx_config.json @@ -8,54 +8,9 @@ "type": "from csv file", "units": "nm", "file path": "data/grids/wavelength/combined.grid" - }, - "time": { - "type": "from config file", - "units": "hours", - "values": [ 14.0 ] } }, "profiles": { - "O3": { - "type": "O3", - "units": "molecule cm-3", - "file path": "data/profiles/atmosphere/ussa.ozone" - }, - "air": { - "type": "air", - "units": "molecule cm-3", - "file path": "data/profiles/atmosphere/ussa.dens" - }, - "O2": { - "type": "O2", - "units": "molecule cm-3", - "file path": "data/profiles/atmosphere/ussa.dens" - }, - "Earth-Sun distance": { - "type": "Earth-Sun distance", - "units": "AU", - "year" : 2002, - "month": 3, - "day": 21 - }, - "surface albedo": { - "type": "from config file", - "units": "none", - "uniform value": 0.10, - "grid": { - "name": "wavelength", - "units": "nm" - } - }, - "extraterrestrial flux": { - "type": "extraterrestrial flux", - "units": "photon cm-2 s-1", - "file path": ["data/profiles/solar/susim_hi.flx", - "data/profiles/solar/atlas3_1994_317_a.dat", - "data/profiles/solar/sao2010.solref.converted", - "data/profiles/solar/neckel.flx"], - "interpolator": ["","","","fractional target"] - } }, "radiative transfer": { "solver": { diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index d591debe26..70f274e869 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -17,13 +17,22 @@ module mo_tuvx public :: tuvx_get_photo_rates public :: tuvx_finalize + integer, parameter :: NUM_WAVELENGTHS = 157 ! TEMPORARY FOR DEVELOPMENT + ! Inidices for grid updaters - integer, parameter :: NUM_GRIDS = 1 ! number of grids that CAM will update at runtime - integer, parameter :: GRID_INDEX_HEIGHT = 1 ! Height grid index + integer, parameter :: NUM_GRIDS = 1 ! number of grids that CAM will update at runtime + integer, parameter :: GRID_INDEX_HEIGHT = 1 ! Height grid index ! Indices for profile updaters - integer, parameter :: NUM_PROFILES = 1 ! number of profiles that CAM will update at runtime - integer, parameter :: PROFILE_INDEX_TEMPERATURE = 1 ! Temperature profile index + integer, parameter :: NUM_PROFILES = 8 ! number of profiles that CAM will update at runtime + integer, parameter :: PROFILE_INDEX_TEMPERATURE = 1 ! Temperature profile index + integer, parameter :: PROFILE_INDEX_ALBEDO = 2 ! Surface albedo profile index + integer, parameter :: PROFILE_INDEX_ET_FLUX = 3 ! Extraterrestrial flux profile index + integer, parameter :: PROFILE_INDEX_AIR = 4 ! Air density profile index + integer, parameter :: PROFILE_INDEX_O3 = 5 ! Ozone profile index + integer, parameter :: PROFILE_INDEX_O2 = 6 ! Molecular oxygen profile index + integer, parameter :: PROFILE_INDEX_SO2 = 7 ! Sulfur dioxide profile index + integer, parameter :: PROFILE_INDEX_NO2 = 8 ! Nitrogen dioxide profile index ! TODO how should this path be set and communicated to this wrapper? character(len=*), parameter :: tuvx_config_path = "tuvx_config.json" @@ -37,7 +46,9 @@ module mo_tuvx type(grid_updater_t) :: grids_(NUM_GRIDS) ! grid updaters type(profile_updater_t) :: profiles_(NUM_PROFILES) ! profile updaters real(r8), allocatable :: heights_(:) ! TEMPORARY FOR DEVELOPMENT - real(r8), allocatable :: temperatures_(:) ! TEMPORARY FOR DEVELOPMENT + real(r8), allocatable :: height_values_(:) ! TEMPORARY FOR DEVELOPMENT + real(r8), allocatable :: height_mid_values_(:) ! TEMPORARY FOR DEVELOPMENT + real(r8), allocatable :: wavelength_values_(:) ! TEMPORARY FOR DEVELOPMENT end type tuvx_ptr type(tuvx_ptr), allocatable :: tuvx_ptrs(:) @@ -87,7 +98,7 @@ subroutine tuvx_init( ) ! Create the set of TUV-x grids and profiles that CAM will update at runtime cam_grids => get_cam_grids( ) - cam_profiles => get_cam_profiles( cam_grids ) + cam_profiles => get_cam_profiles( ) ! construct a core on the primary process and pack it onto an MPI buffer if( is_main_task ) then @@ -169,11 +180,14 @@ subroutine tuvx_get_photo_rates( ncol ) ! set conditions for this column in TUV-x call set_heights( tuvx, i_col ) call set_temperatures( tuvx, i_col ) - ! TODO Add remaining input conditions + call set_surface_albedo( tuvx, i_col ) + call set_et_flux( tuvx, i_col ) + call set_radiator_profiles( tuvx, i_col ) ! Calculate photolysis rate constants for this column ! TEMPORARY FOR DEVELOPMENT - fix SZA - call tuvx%core_%run( 45.0_r8, photolysis_rate_constants = tuvx%photo_rates_ ) + call tuvx%core_%run( solar_zenith_angle = 45.0_r8, & + photolysis_rate_constants = tuvx%photo_rates_ ) end do end associate @@ -318,7 +332,7 @@ end function get_cam_grids !================================================================================================ - function get_cam_profiles( grids ) result( profiles ) + function get_cam_profiles( ) result( profiles ) !----------------------------------------------------------------------- ! ! Purpose: creates and loads a profile warehouse with profiles that CAM @@ -326,29 +340,50 @@ function get_cam_profiles( grids ) result( profiles ) ! !----------------------------------------------------------------------- - use tuvx_grid, only : grid_t - use tuvx_grid_warehouse, only : grid_warehouse_t + use ppgrid, only : pver ! number of vertical levels use tuvx_profile_from_host, only : profile_from_host_t use tuvx_profile_warehouse, only : profile_warehouse_t class(profile_warehouse_t), pointer :: profiles ! collection of profiles to be updated by CAM - class(grid_warehouse_t), intent(in) :: grids ! collection of grids to be updated by CAM !----------------------------------------------------------------------- ! Local variables !----------------------------------------------------------------------- class(profile_from_host_t), pointer :: host_profile - class(grid_t), pointer :: height - height => grids%get_grid( "height", "km" ) profiles => profile_warehouse_t( ) ! Temperature profile on height grid - host_profile => profile_from_host_t( "temperature", "K", height%size( ) ) + host_profile => profile_from_host_t( "temperature", "K", pver ) + call profiles%add( host_profile ) + deallocate( host_profile ) + + ! Surface albedo on wavelength grid + host_profile => profile_from_host_t( "surface albedo", "none", NUM_WAVELENGTHS ) + call profiles%add( host_profile ) + deallocate( host_profile ) + + ! Extraterrestrial flux on wavelength grid + host_profile => profile_from_host_t( "extraterrestrial flux", "photon cm-2 s-1", & + NUM_WAVELENGTHS ) call profiles%add( host_profile ) deallocate( host_profile ) - deallocate( height ) + ! Air profile + host_profile => profile_from_host_t( "air", "molecule cm-3", pver ) + call profiles%add( host_profile ) + deallocate( host_profile ) + + ! O3 profile + ! TODO optionally include if available + host_profile => profile_from_host_t( "O3", "molecule cm-3", pver ) + call profiles%add( host_profile ) + deallocate( host_profile ) + + ! O2 profile + host_profile => profile_from_host_t( "O2", "molecule cm-3", pver ) + call profiles%add( host_profile ) + deallocate( host_profile ) end function get_cam_profiles @@ -381,21 +416,48 @@ subroutine create_updaters( this, grids, profiles ) class(profile_t), pointer :: host_profile logical :: found + ! Grid updaters + host_grid => grids%get_grid( "height", "km" ) this%grids_( GRID_INDEX_HEIGHT ) = this%core_%get_updater( host_grid, found ) call assert( 213798815, found ) - write(*,*) "allocating height array to ", host_grid%size( ) + 1, " values", & - " on thread ", thread_id( ) - allocate( this%heights_( host_grid%size( ) + 1 ) ) ! TEMPORARY FOR DEVELOPMENT + allocate( this%heights_( host_grid%size( ) + 1 ) ) ! TEMPORARY FOR DEVELOPMENT + allocate( this%height_values_( host_grid%size( ) + 1 ) ) ! TEMPORARY FOR DEVELOPMENT + allocate( this%height_mid_values_( host_grid%size( ) ) ) ! TEMPORARY FOR DEVELOPMENT deallocate( host_grid ) + allocate( this%wavelength_values_( NUM_WAVELENGTHS + 1 ) ) ! TEMPORARY FOR DEVELOPMENT + + ! Profile updaters + host_profile => profiles%get_profile( "temperature", "K" ) - this%profiles_( PROFILE_INDEX_TEMPERATURE ) = & - this%core_%get_updater( host_profile, found ) + this%profiles_( PROFILE_INDEX_TEMPERATURE ) = this%core_%get_updater( host_profile, found ) call assert( 418735162, found ) - write(*,*) "allocating temperature array to ", host_profile%size( ) + 1, " values", & - " on thread ", thread_id( ) - allocate( this%temperatures_( host_profile%size( ) + 1 ) ) ! TEMPORARY FOR DEVELOPMENT + deallocate( host_profile ) + + host_profile => profiles%get_profile( "surface albedo", "none" ) + this%profiles_( PROFILE_INDEX_ALBEDO ) = this%core_%get_updater( host_profile, found ) + call assert( 720785186, found ) + deallocate( host_profile ) + + host_profile => profiles%get_profile( "extraterrestrial flux", "photon cm-2 s-1" ) + this%profiles_( PROFILE_INDEX_ET_FLUX ) = this%core_%get_updater( host_profile, found ) + call assert( 550628282, found ) + deallocate( host_profile ) + + host_profile => profiles%get_profile( "air", "molecule cm-3" ) + this%profiles_( PROFILE_INDEX_AIR ) = this%core_%get_updater( host_profile, found ) + call assert( 380471378, found ) + deallocate( host_profile ) + + host_profile => profiles%get_profile( "O3", "molecule cm-3" ) + this%profiles_( PROFILE_INDEX_O3 ) = this%core_%get_updater( host_profile, found ) + call assert( 210314474, found ) + deallocate( host_profile ) + + host_profile => profiles%get_profile( "O2", "molecule cm-3" ) + this%profiles_( PROFILE_INDEX_O2 ) = this%core_%get_updater( host_profile, found ) + call assert( 105165970, found ) deallocate( host_profile ) end subroutine create_updaters @@ -420,7 +482,6 @@ subroutine set_heights( this, i_col ) this%heights_( i_level ) = i_level * 1.0_r8 - 1.0_r8 end do call this%grids_( GRID_INDEX_HEIGHT )%update( & - mid_points = this%heights_(1:size(this%heights_)-1), & edges = this%heights_(:) ) end subroutine set_heights @@ -440,13 +501,95 @@ subroutine set_temperatures( this, i_col ) integer, intent(in) :: i_col ! Column to set conditions for ! TEMPORARY FOR DEVELOPMENT - this%temperatures_(:) = 298.15_r8 + this%height_values_(:) = 298.15_r8 call this%profiles_( PROFILE_INDEX_TEMPERATURE )%update( & - mid_point_values = this%temperatures_(1:size(this%temperatures_)-1), & - edge_values = this%temperatures_(:) ) + edge_values = this%height_values_(:) ) end subroutine set_temperatures +!================================================================================================ + + subroutine set_surface_albedo( this, i_col ) +!----------------------------------------------------------------------- +! +! Purpose: sets the surface albedo in TUV-x for the given column +! +! TODO: Describe how CAM surface albedo profile is mapped to TUV-x wavelengths +! +!----------------------------------------------------------------------- + + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! Column to set conditions for + + ! TEMPORARY FOR DEVELOPMENT + this%wavelength_values_(:) = 0.1_r8 + call this%profiles_( PROFILE_INDEX_ALBEDO )%update( & + edge_values = this%wavelength_values_(:) ) + + end subroutine set_surface_albedo + +!================================================================================================ + + subroutine set_et_flux( this, i_col ) +!----------------------------------------------------------------------- +! +! Purpose: sets the extraterrestrial flux in TUV-x for the given column +! +! TODO: Describe how CAM extraterrestrial flux profile is mapped to TUV-x wavelengths +! +!----------------------------------------------------------------------- + + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! Column to set conditions for + + ! TEMPORARY FOR DEVELOPMENT + this%wavelength_values_(:) = 1000.0_r8 + call this%profiles_( PROFILE_INDEX_ET_FLUX )%update( & + edge_values = this%wavelength_values_(:) ) + + end subroutine set_et_flux + +!================================================================================================ + + subroutine set_radiator_profiles( this, i_col ) +!----------------------------------------------------------------------- +! +! Purpose: sets the profiles of optically active atmospheric constituents +! in TUV-x for the given column +! +! TODO: Describe how CAM profiles are mapped to TUV-x heights +! +!----------------------------------------------------------------------- + + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! Column to set conditions for + + ! TEMPORARY FOR DEVELOPMENT - air + this%height_values_(:) = 2.54e19_r8 + this%height_mid_values_(:) = 2.54e21_r8 + call this%profiles_( PROFILE_INDEX_AIR )%update( & + mid_point_values = this%height_values_(1:size(this%height_values_)-1), & + edge_values = this%height_values_(:), & + layer_densities = this%height_mid_values_(:) ) + + ! TEMPORARY FOR DEVELOPMENT - O2 + this%height_values_(:) = 1.0e17_r8 + this%height_mid_values_(:) = 1.0e19_r8 + call this%profiles_( PROFILE_INDEX_O2 )%update( & + mid_point_values = this%height_values_(1:size(this%height_values_)-1), & + edge_values = this%height_values_(:), & + layer_densities = this%height_mid_values_(:) ) + + ! TEMPORARY FOR DEVELOPMENT - O3 + this%height_values_(:) = 1.0e13_r8 + this%height_mid_values_(:) = 1.0e15_r8 + call this%profiles_( PROFILE_INDEX_O3 )%update( & + mid_point_values = this%height_values_(1:size(this%height_values_)-1), & + edge_values = this%height_values_(:), & + layer_densities = this%height_mid_values_(:) ) + + end subroutine set_radiator_profiles + !================================================================================================ end module mo_tuvx From ffb0f5e0fe9a493dc73cb93923114e74aee907d0 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Thu, 3 Nov 2022 16:30:58 -0600 Subject: [PATCH 12/84] send wavelength grid to TUV-x --- cime_config/temp_tuvx_config.json | 5 ----- src/chemistry/mozart/mo_tuvx.F90 | 18 +++++++++++++++++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/cime_config/temp_tuvx_config.json b/cime_config/temp_tuvx_config.json index 9b6a22322e..0152c50a88 100644 --- a/cime_config/temp_tuvx_config.json +++ b/cime_config/temp_tuvx_config.json @@ -4,11 +4,6 @@ "cross section parameters file": "data/cross_sections/O2_parameters.txt" }, "grids": { - "wavelength": { - "type": "from csv file", - "units": "nm", - "file path": "data/grids/wavelength/combined.grid" - } }, "profiles": { }, diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 70f274e869..1aae4a3db5 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -20,8 +20,9 @@ module mo_tuvx integer, parameter :: NUM_WAVELENGTHS = 157 ! TEMPORARY FOR DEVELOPMENT ! Inidices for grid updaters - integer, parameter :: NUM_GRIDS = 1 ! number of grids that CAM will update at runtime + integer, parameter :: NUM_GRIDS = 2 ! number of grids that CAM will update at runtime integer, parameter :: GRID_INDEX_HEIGHT = 1 ! Height grid index + integer, parameter :: GRID_INDEX_WAVELENGTH = 2 ! Wavelength grid index ! Indices for profile updaters integer, parameter :: NUM_PROFILES = 8 ! number of profiles that CAM will update at runtime @@ -319,6 +320,9 @@ function get_cam_grids( ) result( grids ) ! Local variables !----------------------------------------------------------------------- class(grid_from_host_t), pointer :: host_grid + type(grid_updater_t) :: updater + real(r8) :: wavelengths(121) ! TEMPORARY FOR DEVELOPMENT + integer :: i_wavelength grids => grid_warehouse_t( ) @@ -328,6 +332,17 @@ function get_cam_grids( ) result( grids ) call grids%add( host_grid ) deallocate( host_grid ) + ! Wavelength grid wil be ... /todo figure out where to get wavelength grid + ! from (wavelengths must be set prior to construction of the TUV-x core) + do i_wavelength = 1, size( wavelengths ) + wavelengths( i_wavelength ) = 199.0_r8 + i_wavelength + end do + host_grid => grid_from_host_t( "wavelength", "nm", 120 ) + updater = grid_updater_t( host_grid ) + call updater%update( edges = wavelengths ) + call grids%add( host_grid ) + deallocate( host_grid ) + end function get_cam_grids !================================================================================================ @@ -426,6 +441,7 @@ subroutine create_updaters( this, grids, profiles ) allocate( this%height_mid_values_( host_grid%size( ) ) ) ! TEMPORARY FOR DEVELOPMENT deallocate( host_grid ) + ! wavelength grid cannot be updated at runtime allocate( this%wavelength_values_( NUM_WAVELENGTHS + 1 ) ) ! TEMPORARY FOR DEVELOPMENT ! Profile updaters From 9cebe0d8341b69bb9b83e191641915085fc6a0a3 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Fri, 4 Nov 2022 15:15:42 -0600 Subject: [PATCH 13/84] add radiator updater for aerosols --- cime_config/temp_tuvx_config.json | 16 --- src/chemistry/mozart/mo_tuvx.F90 | 204 +++++++++++++++++++++--------- 2 files changed, 141 insertions(+), 79 deletions(-) diff --git a/cime_config/temp_tuvx_config.json b/cime_config/temp_tuvx_config.json index 0152c50a88..70a3767370 100644 --- a/cime_config/temp_tuvx_config.json +++ b/cime_config/temp_tuvx_config.json @@ -49,22 +49,6 @@ "cross section": "O3", "vertical profile": "O3", "vertical profile units": "molecule cm-3" - }, - { - "name": "aerosols", - "type": "aerosol", - "optical depths": [2.40e-01, 1.06e-01, 4.56e-02, 1.91e-02, 1.01e-02, 7.63e-03, - 5.38e-03, 5.00e-03, 5.15e-03, 4.94e-03, 4.82e-03, 4.51e-03, - 4.74e-03, 4.37e-03, 4.28e-03, 4.03e-03, 3.83e-03, 3.78e-03, - 3.88e-03, 3.08e-03, 2.26e-03, 1.64e-03, 1.23e-03, 9.45e-04, - 7.49e-04, 6.30e-04, 5.50e-04, 4.21e-04, 3.22e-04, 2.48e-04, - 1.90e-04, 1.45e-04, 1.11e-04, 8.51e-05, 6.52e-05, 5.00e-05, - 3.83e-05, 2.93e-05, 2.25e-05, 1.72e-05, 1.32e-05, 1.01e-05, - 7.72e-06, 5.91e-06, 4.53e-06, 3.46e-06, 2.66e-06, 2.04e-06, - 1.56e-06, 1.19e-06, 9.14e-07], - "single scattering albedo": 0.99, - "asymmetry factor": 0.61, - "550 nm optical depth": 0.235 } ] }, diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 1aae4a3db5..e5d123768b 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -3,11 +3,12 @@ module mo_tuvx ! ... wrapper for TUV-x photolysis rate constant calculator !---------------------------------------------------------------------- - use musica_string, only : string_t - use shr_kind_mod, only : r8 => shr_kind_r8 - use tuvx_core, only : core_t - use tuvx_grid_from_host, only : grid_updater_t - use tuvx_profile_from_host, only : profile_updater_t + use musica_string, only : string_t + use shr_kind_mod, only : r8 => shr_kind_r8 + use tuvx_core, only : core_t + use tuvx_grid_from_host, only : grid_updater_t + use tuvx_profile_from_host, only : profile_updater_t + use tuvx_radiator_from_host, only : radiator_updater_t implicit none @@ -17,8 +18,6 @@ module mo_tuvx public :: tuvx_get_photo_rates public :: tuvx_finalize - integer, parameter :: NUM_WAVELENGTHS = 157 ! TEMPORARY FOR DEVELOPMENT - ! 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 @@ -35,21 +34,27 @@ module mo_tuvx integer, parameter :: PROFILE_INDEX_SO2 = 7 ! Sulfur dioxide profile index integer, parameter :: PROFILE_INDEX_NO2 = 8 ! Nitrogen dioxide profile index + ! Indices for radiator updaters + integer, parameter :: NUM_RADIATORS = 1 ! number of radiators that CAM will update at runtime + integer, parameter :: RADIATOR_INDEX_AEROSOL = 1 ! Aerosol radiator index + ! TODO how should this path be set and communicated to this wrapper? character(len=*), parameter :: tuvx_config_path = "tuvx_config.json" ! TUV-x calculator for each OMP thread type :: tuvx_ptr - type(core_t), pointer :: core_ => null( ) ! TUV-x calculator - integer :: n_photo_rates_ ! number of photo reactions in TUV-x - real(r8), allocatable :: photo_rates_(:,:) ! photolysis rate constants - ! (vertical level, reaction) [s-1] - type(grid_updater_t) :: grids_(NUM_GRIDS) ! grid updaters - type(profile_updater_t) :: profiles_(NUM_PROFILES) ! profile updaters - real(r8), allocatable :: heights_(:) ! TEMPORARY FOR DEVELOPMENT - real(r8), allocatable :: height_values_(:) ! TEMPORARY FOR DEVELOPMENT - real(r8), allocatable :: height_mid_values_(:) ! TEMPORARY FOR DEVELOPMENT - real(r8), allocatable :: wavelength_values_(:) ! TEMPORARY FOR DEVELOPMENT + type(core_t), pointer :: core_ => null( ) ! TUV-x calculator + integer :: n_photo_rates_ ! number of photo reactions in TUV-x + real(r8), allocatable :: photo_rates_(:,:) ! photolysis rate constants + ! (vertical level, reaction) [s-1] + type(grid_updater_t) :: grids_(NUM_GRIDS) ! grid updaters + type(profile_updater_t) :: profiles_(NUM_PROFILES) ! profile updaters + type(radiator_updater_t) :: radiators_(NUM_RADIATORS) ! radiator updaters + real(r8), allocatable :: heights_(:) ! TEMPORARY FOR DEVELOPMENT + real(r8), allocatable :: height_values_(:) ! TEMPORARY FOR DEVELOPMENT + real(r8), allocatable :: height_mid_values_(:) ! TEMPORARY FOR DEVELOPMENT + real(r8), allocatable :: wavelength_values_(:) ! TEMPORARY FOR DEVELOPMENT + real(r8), allocatable :: optics_values_(:,:) ! TEMPORARY FOR DEVELOPMENT end type tuvx_ptr type(tuvx_ptr), allocatable :: tuvx_ptrs(:) @@ -67,16 +72,17 @@ subroutine tuvx_init( ) #ifdef HAVE_MPI use mpi #endif - use cam_logfile, only : iulog - use musica_assert, only : assert_msg - use musica_mpi, only : musica_mpi_rank - use musica_string, only : string_t, to_char - use tuvx_grid, only : grid_t - use tuvx_grid_warehouse, only : grid_warehouse_t - use tuvx_profile_warehouse, only : profile_warehouse_t - use spmd_utils, only : main_task => masterprocid, & - is_main_task => masterproc, & - mpicom + use cam_logfile, only : iulog + use musica_assert, only : assert_msg + use musica_mpi, only : musica_mpi_rank + use musica_string, only : string_t, to_char + use tuvx_grid, only : grid_t + use tuvx_grid_warehouse, only : grid_warehouse_t + use tuvx_profile_warehouse, only : profile_warehouse_t + use tuvx_radiator_warehouse, only : radiator_warehouse_t + use spmd_utils, only : main_task => masterprocid, & + is_main_task => masterproc, & + mpicom !----------------------------------------------------------------------- ! Local variables @@ -87,6 +93,7 @@ subroutine tuvx_init( ) class(grid_t), pointer :: height class(grid_warehouse_t), pointer :: cam_grids class(profile_warehouse_t), pointer :: cam_profiles + class(radiator_warehouse_t), pointer :: cam_radiators integer :: pack_size, pos, i_core, i_err config_path = tuvx_config_path @@ -99,11 +106,12 @@ subroutine tuvx_init( ) ! Create the set of TUV-x grids and profiles that CAM will update at runtime cam_grids => get_cam_grids( ) - cam_profiles => get_cam_profiles( ) + cam_profiles => get_cam_profiles( cam_grids ) + cam_radiators => get_cam_radiators( cam_grids ) ! construct a core on the primary process and pack it onto an MPI buffer if( is_main_task ) then - core => core_t( config_path, cam_grids, cam_profiles ) + core => core_t( config_path, cam_grids, cam_profiles, cam_radiators ) pack_size = core%pack_size( mpicom ) allocate( buffer( pack_size ) ) pos = 0 @@ -135,7 +143,7 @@ subroutine tuvx_init( ) call tuvx%core_%mpi_unpack( buffer, pos, mpicom ) ! Set up map between CAM and TUV-x photolysis reactions for each thread - call create_updaters( tuvx, cam_grids, cam_profiles ) + call create_updaters( tuvx, cam_grids, cam_profiles, cam_radiators ) ! TEMPORARY FOR DEVELOPMENT tuvx%n_photo_rates_ = tuvx%core_%number_of_photolysis_reactions( ) @@ -146,8 +154,9 @@ subroutine tuvx_init( ) end associate end do - deallocate( cam_grids ) - deallocate( cam_profiles ) + deallocate( cam_grids ) + deallocate( cam_profiles ) + deallocate( cam_radiators ) end subroutine tuvx_init @@ -347,7 +356,7 @@ end function get_cam_grids !================================================================================================ - function get_cam_profiles( ) result( profiles ) + function get_cam_profiles( grids ) result( profiles ) !----------------------------------------------------------------------- ! ! Purpose: creates and loads a profile warehouse with profiles that CAM @@ -355,56 +364,102 @@ function get_cam_profiles( ) result( profiles ) ! !----------------------------------------------------------------------- - use ppgrid, only : pver ! number of vertical levels + use tuvx_grid, only : grid_t + use tuvx_grid_warehouse, only : grid_warehouse_t use tuvx_profile_from_host, only : profile_from_host_t use tuvx_profile_warehouse, only : profile_warehouse_t - class(profile_warehouse_t), pointer :: profiles ! collection of profiles to be updated by CAM + class(grid_warehouse_t), intent(in) :: grids ! CAM grids used in TUV-x + class(profile_warehouse_t), pointer :: profiles ! collection of profiles to be updated by CAM !----------------------------------------------------------------------- ! Local variables !----------------------------------------------------------------------- class(profile_from_host_t), pointer :: host_profile + class(grid_t), pointer :: height, wavelength - profiles => profile_warehouse_t( ) + profiles => profile_warehouse_t( ) + height => grids%get_grid( "height", "km" ) + wavelength => grids%get_grid( "wavelength", "nm" ) ! Temperature profile on height grid - host_profile => profile_from_host_t( "temperature", "K", pver ) + host_profile => profile_from_host_t( "temperature", "K", height%size( ) ) call profiles%add( host_profile ) deallocate( host_profile ) ! Surface albedo on wavelength grid - host_profile => profile_from_host_t( "surface albedo", "none", NUM_WAVELENGTHS ) + host_profile => profile_from_host_t( "surface albedo", "none", wavelength%size( ) ) call profiles%add( host_profile ) deallocate( host_profile ) ! Extraterrestrial flux on wavelength grid host_profile => profile_from_host_t( "extraterrestrial flux", "photon cm-2 s-1", & - NUM_WAVELENGTHS ) + wavelength%size( ) ) call profiles%add( host_profile ) deallocate( host_profile ) ! Air profile - host_profile => profile_from_host_t( "air", "molecule cm-3", pver ) + host_profile => profile_from_host_t( "air", "molecule cm-3", height%size( ) ) call profiles%add( host_profile ) deallocate( host_profile ) ! O3 profile ! TODO optionally include if available - host_profile => profile_from_host_t( "O3", "molecule cm-3", pver ) + host_profile => profile_from_host_t( "O3", "molecule cm-3", height%size( ) ) call profiles%add( host_profile ) deallocate( host_profile ) ! O2 profile - host_profile => profile_from_host_t( "O2", "molecule cm-3", pver ) + host_profile => profile_from_host_t( "O2", "molecule cm-3", height%size( ) ) call profiles%add( host_profile ) deallocate( host_profile ) + deallocate( height ) + deallocate( wavelength ) + end function get_cam_profiles !================================================================================================ - subroutine create_updaters( this, grids, profiles ) + function get_cam_radiators( grids ) result( radiators ) +!----------------------------------------------------------------------- +! +! Purpose: creates and loads a radiator warehouse with radiators that CAM +! will update at runtime +! +!----------------------------------------------------------------------- + + use tuvx_grid, only : grid_t + use tuvx_grid_warehouse, only : grid_warehouse_t + use tuvx_radiator_from_host, only : radiator_from_host_t + use tuvx_radiator_warehouse, only : radiator_warehouse_t + + type(grid_warehouse_t), intent(in) :: grids ! CAM grids used in TUV-x + class(radiator_warehouse_t), pointer :: radiators ! collection of radiators to be updated by CAM + +!----------------------------------------------------------------------- +! Local variables +!----------------------------------------------------------------------- + class(radiator_from_host_t), pointer :: host_radiator + class(grid_t), pointer :: height, wavelength + + radiators => radiator_warehouse_t( ) + height => grids%get_grid( "height", "km" ) + wavelength => grids%get_grid( "wavelength", "nm" ) + + ! Aerosol radiator + host_radiator => radiator_from_host_t( "aerosol", height, wavelength ) + call radiators%add( host_radiator ) + deallocate( host_radiator ) + + deallocate( height ) + deallocate( wavelength ) + + end function get_cam_radiators + +!================================================================================================ + + subroutine create_updaters( this, grids, profiles, radiators ) !----------------------------------------------------------------------- ! ! Purpose: creates updaters for each grid and profile that CAM will use @@ -412,37 +467,48 @@ subroutine create_updaters( this, grids, profiles ) ! !----------------------------------------------------------------------- - use musica_assert, only : assert - use tuvx_grid_from_host, only : grid_updater_t - use tuvx_grid, only : grid_t - use tuvx_grid_warehouse, only : grid_warehouse_t - use tuvx_profile, only : profile_t - use tuvx_profile_from_host, only : profile_updater_t - use tuvx_profile_warehouse, only : profile_warehouse_t + use musica_assert, only : assert + use tuvx_grid, only : grid_t + use tuvx_grid_from_host, only : grid_updater_t + use tuvx_grid_warehouse, only : grid_warehouse_t + use tuvx_profile, only : profile_t + use tuvx_profile_from_host, only : profile_updater_t + use tuvx_profile_warehouse, only : profile_warehouse_t + use tuvx_radiator, only : radiator_t + use tuvx_radiator_from_host, only : radiator_updater_t + use tuvx_radiator_warehouse, only : radiator_warehouse_t - class(tuvx_ptr), intent(inout) :: this - class(grid_warehouse_t), intent(in) :: grids - class(profile_warehouse_t), intent(in) :: profiles + class(tuvx_ptr), intent(inout) :: this + class(grid_warehouse_t), intent(in) :: grids + class(profile_warehouse_t), intent(in) :: profiles + class(radiator_warehouse_t), intent(in) :: radiators !----------------------------------------------------------------------- ! Local variables !----------------------------------------------------------------------- - class(grid_t), pointer :: host_grid - class(profile_t), pointer :: host_profile - logical :: found + class(grid_t), pointer :: height, wavelength + class(profile_t), pointer :: host_profile + class(radiator_t), pointer :: host_radiator + logical :: found ! Grid updaters - host_grid => grids%get_grid( "height", "km" ) - this%grids_( GRID_INDEX_HEIGHT ) = this%core_%get_updater( host_grid, found ) + height => grids%get_grid( "height", "km" ) + this%grids_( GRID_INDEX_HEIGHT ) = this%core_%get_updater( height, found ) call assert( 213798815, found ) - allocate( this%heights_( host_grid%size( ) + 1 ) ) ! TEMPORARY FOR DEVELOPMENT - allocate( this%height_values_( host_grid%size( ) + 1 ) ) ! TEMPORARY FOR DEVELOPMENT - allocate( this%height_mid_values_( host_grid%size( ) ) ) ! TEMPORARY FOR DEVELOPMENT - deallocate( host_grid ) + allocate( this%heights_( height%size( ) + 1 ) ) ! TEMPORARY FOR DEVELOPMENT + allocate( this%height_values_( height%size( ) + 1 ) ) ! TEMPORARY FOR DEVELOPMENT + allocate( this%height_mid_values_( height%size( ) ) ) ! TEMPORARY FOR DEVELOPMENT ! wavelength grid cannot be updated at runtime - allocate( this%wavelength_values_( NUM_WAVELENGTHS + 1 ) ) ! TEMPORARY FOR DEVELOPMENT + wavelength => grids%get_grid( "wavelength", "nm" ) + allocate( this%wavelength_values_( wavelength%size( ) + 1 ) ) ! TEMPORARY FOR DEVELOPMENT + + ! optical property working array + allocate( this%optics_values_( height%size( ), wavelength%size( ) ) ) + + deallocate( height ) + deallocate( wavelength ) ! Profile updaters @@ -476,6 +542,13 @@ subroutine create_updaters( this, grids, profiles ) call assert( 105165970, found ) deallocate( host_profile ) + ! radiator updaters + + host_radiator => radiators%get_radiator( "aerosol" ) + this%radiators_( RADIATOR_INDEX_AEROSOL ) = this%core_%get_updater( host_radiator, found ) + call assert( 675200430, found ) + nullify( host_radiator ) + end subroutine create_updaters !================================================================================================ @@ -604,6 +677,11 @@ subroutine set_radiator_profiles( this, i_col ) edge_values = this%height_values_(:), & layer_densities = this%height_mid_values_(:) ) + ! TEMPORARY FOR DEVELOPMENT - aerosols + this%optics_values_(:,:) = 0.01_r8 + call this%radiators_( RADIATOR_INDEX_AEROSOL )%update( & + optical_depths = this%optics_values_ ) + end subroutine set_radiator_profiles !================================================================================================ From 3241b31c9151bcbe8b7468f6eef094bd6b589961 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Fri, 4 Nov 2022 16:58:58 -0600 Subject: [PATCH 14/84] send CAM height and temperature data to TUV-x --- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 2 +- src/chemistry/mozart/mo_tuvx.F90 | 123 +++++++++++++++---- 2 files changed, 99 insertions(+), 26 deletions(-) diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index 57f9910755..dbcefc090a 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -792,7 +792,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & !----------------------------------------------------------------- ! ... get calculated photolysis rates from TUV-x !----------------------------------------------------------------- - call tuvx_get_photo_rates( ncol ) + call tuvx_get_photo_rates( ncol, zmid, zint, tfld, ts ) do i = 1,phtcnt call outfld( tag_names(i), reaction_rates(:ncol,:,rxt_tag_map(i)), ncol, lchnk ) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index e5d123768b..0a8b213c3f 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -162,22 +162,27 @@ end subroutine tuvx_init !================================================================================================ - subroutine tuvx_get_photo_rates( ncol ) + subroutine tuvx_get_photo_rates( ncol, zmid, zint, tfld, ts ) !----------------------------------------------------------------------- ! ! Purpose: calculate and return photolysis rate constants ! !----------------------------------------------------------------------- - use cam_logfile, only : iulog + use cam_logfile, only : iulog ! log info output unit + use ppgrid, only : pcols, & ! maximum number of columns + pver ! number of vertical levels use spmd_utils, only : main_task => masterprocid, & is_main_task => masterproc, & mpicom !----------------------------------------------------------------------- ! Dummy arguments !----------------------------------------------------------------------- - - integer, intent(in) :: ncol ! Number of colums to calculated photolysis for + integer, intent(in) :: ncol ! Number of colums to calculated photolysis for + real(r8), intent(in) :: zmid(ncol,pver) ! height at mid-points (km) + real(r8), intent(in) :: zint(ncol,pver) ! height at interfaces (km) + real(r8), intent(in) :: tfld(pcols,pver) ! midpoint temperature (K) + real(r8), intent(in) :: ts(pcols) ! surface temperature (K) !----------------------------------------------------------------------- ! Local variables @@ -188,8 +193,8 @@ subroutine tuvx_get_photo_rates( ncol ) do i_col = 1, ncol ! set conditions for this column in TUV-x - call set_heights( tuvx, i_col ) - call set_temperatures( tuvx, i_col ) + call set_heights( tuvx, i_col, ncol, zmid, zint ) + call set_temperatures( tuvx, i_col, tfld, ts ) call set_surface_albedo( tuvx, i_col ) call set_et_flux( tuvx, i_col ) call set_radiator_profiles( tuvx, i_col ) @@ -285,7 +290,7 @@ subroutine log_initialization( ) ! !----------------------------------------------------------------------- - use cam_logfile, only : iulog + use cam_logfile, only : iulog ! log info output unit use musica_string, only : to_char use spmd_utils, only : main_task => masterprocid, & is_main_task => masterproc @@ -323,6 +328,9 @@ function get_cam_grids( ) result( grids ) use tuvx_grid_from_host, only : grid_from_host_t use tuvx_grid_warehouse, only : grid_warehouse_t +!----------------------------------------------------------------------- +! Dummy arguments +!----------------------------------------------------------------------- class(grid_warehouse_t), pointer :: grids ! collection of grids to be updated by CAM !----------------------------------------------------------------------- @@ -369,6 +377,9 @@ function get_cam_profiles( grids ) result( profiles ) use tuvx_profile_from_host, only : profile_from_host_t use tuvx_profile_warehouse, only : profile_warehouse_t +!----------------------------------------------------------------------- +! Dummy arguments +!----------------------------------------------------------------------- class(grid_warehouse_t), intent(in) :: grids ! CAM grids used in TUV-x class(profile_warehouse_t), pointer :: profiles ! collection of profiles to be updated by CAM @@ -434,6 +445,9 @@ function get_cam_radiators( grids ) result( radiators ) use tuvx_radiator_from_host, only : radiator_from_host_t use tuvx_radiator_warehouse, only : radiator_warehouse_t +!----------------------------------------------------------------------- +! Dummy arguments +!----------------------------------------------------------------------- type(grid_warehouse_t), intent(in) :: grids ! CAM grids used in TUV-x class(radiator_warehouse_t), pointer :: radiators ! collection of radiators to be updated by CAM @@ -478,6 +492,9 @@ subroutine create_updaters( this, grids, profiles, radiators ) use tuvx_radiator_from_host, only : radiator_updater_t use tuvx_radiator_warehouse, only : radiator_warehouse_t +!----------------------------------------------------------------------- +! Dummy arguments +!----------------------------------------------------------------------- class(tuvx_ptr), intent(inout) :: this class(grid_warehouse_t), intent(in) :: grids class(profile_warehouse_t), intent(in) :: profiles @@ -553,46 +570,93 @@ end subroutine create_updaters !================================================================================================ - subroutine set_heights( this, i_col ) + subroutine set_heights( this, i_col, ncol, zmid, zint ) !----------------------------------------------------------------------- ! ! Purpose: sets the height values in TUV-x for the given column ! -! TODO: Describe how CAM vertical profile is mapped to TUV-x heights +! CAM to TUV-x height grid mapping +! +! TUV-x heights are "bottom-up" and require atmospheric constituent +! concentrations at interfaces. Therefore, CAM mid-points are used as +! TUV-x grid interfaces, with an additional layer introduced between +! the surface and the lowest CAM mid-point. +! +! ---- (interface) ===== (mid-point) +! +! CAM TUV-x +! ------(top)------ i_int = 1 (exo values) +! ================= i_mid = 1 -------(top)------ i_int = pver + 1 +! ----------------- i_int = 2 ================== i_mid = pver +! ------------------ i_int = pver +! || +! || || +! || +! ----------------- i_int = pver +! ================= i_imd = pver ------------------ i_int = 2 +! ================== i_mid = 1 +! -----(ground)---- i_int = pver+1 -----(ground)----- i_int = 1 ! !----------------------------------------------------------------------- - class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator - integer, intent(in) :: i_col ! Column to set conditions for + use ppgrid, only : pver ! number of vertical levels - ! TEMPORARY FOR DEVELOPMENT +!----------------------------------------------------------------------- +! Dummy arguments +!----------------------------------------------------------------------- + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! column to set conditions for + integer, intent(in) :: ncol ! number of colums to calculated photolysis for + real(r8), intent(in) :: zmid(ncol,pver) ! height at mid-points (km) + real(r8), intent(in) :: zint(ncol,pver) ! height at interfaces (km) + +!----------------------------------------------------------------------- +! Local variables +!----------------------------------------------------------------------- integer :: i_level - do i_level = 1, size( this%heights_ ) - this%heights_( i_level ) = i_level * 1.0_r8 - 1.0_r8 - end do - call this%grids_( GRID_INDEX_HEIGHT )%update( & - edges = this%heights_(:) ) + real(r8) :: edges(pver+1) + real(r8) :: mid_points(pver) + + edges(1) = 0.0_r8 + edges(2:pver+1) = zmid(i_col,pver:1:-1) + mid_points(1) = zmid(i_col,pver) * 0.5_r8 + mid_points(2:pver) = zint(i_col,pver:2:-1) + call this%grids_( GRID_INDEX_HEIGHT )%update( edges = edges, mid_points = mid_points ) end subroutine set_heights !================================================================================================ - subroutine set_temperatures( this, i_col ) + subroutine set_temperatures( this, i_col, tfld, ts ) !----------------------------------------------------------------------- ! ! Purpose: sets the temperatures in TUV-x for the given column ! -! TODO: Describe how CAM temperature profile is mapped to TUV-x heights +! See description of `set_heights` for CAM <-> TUV-x vertical grid +! mapping. ! !----------------------------------------------------------------------- - class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator - integer, intent(in) :: i_col ! Column to set conditions for + use ppgrid, only : pcols, & ! maximum number of columns + pver ! number of vertical levels - ! TEMPORARY FOR DEVELOPMENT - this%height_values_(:) = 298.15_r8 - call this%profiles_( PROFILE_INDEX_TEMPERATURE )%update( & - edge_values = this%height_values_(:) ) +!----------------------------------------------------------------------- +! Dummy arguments +!----------------------------------------------------------------------- + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! column to set conditions for + real(r8), intent(in) :: tfld(pcols,pver) ! midpoint temperature (K) + real(r8), intent(in) :: ts(pcols) ! surface temperature (K) + +!----------------------------------------------------------------------- +! Local variables +!----------------------------------------------------------------------- + integer :: i_level + real(r8) :: edges(pver+1) + + edges(1) = ts(i_col) + edges(2:pver+1) = tfld(i_col,pver:1:-1) + call this%profiles_( PROFILE_INDEX_TEMPERATURE )%update( edge_values = edges ) end subroutine set_temperatures @@ -607,6 +671,9 @@ subroutine set_surface_albedo( this, i_col ) ! !----------------------------------------------------------------------- +!----------------------------------------------------------------------- +! Dummy arguments +!----------------------------------------------------------------------- class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator integer, intent(in) :: i_col ! Column to set conditions for @@ -628,6 +695,9 @@ subroutine set_et_flux( this, i_col ) ! !----------------------------------------------------------------------- +!----------------------------------------------------------------------- +! Dummy arguments +!----------------------------------------------------------------------- class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator integer, intent(in) :: i_col ! Column to set conditions for @@ -650,6 +720,9 @@ subroutine set_radiator_profiles( this, i_col ) ! !----------------------------------------------------------------------- +!----------------------------------------------------------------------- +! Dummy arguments +!----------------------------------------------------------------------- class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator integer, intent(in) :: i_col ! Column to set conditions for From 8e100d5d7d133ede4c99d3b6026e9c2dc7080fd7 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Mon, 7 Nov 2022 08:43:14 -0700 Subject: [PATCH 15/84] fix height variables sent to tuvx --- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 2 +- src/chemistry/mozart/mo_tuvx.F90 | 29 ++++++++++---------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index dbcefc090a..7af0657d2b 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -792,7 +792,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & !----------------------------------------------------------------- ! ... get calculated photolysis rates from TUV-x !----------------------------------------------------------------- - call tuvx_get_photo_rates( ncol, zmid, zint, tfld, ts ) + call tuvx_get_photo_rates( ncol, zm, zi, tfld, ts ) do i = 1,phtcnt call outfld( tag_names(i), reaction_rates(:ncol,:,rxt_tag_map(i)), ncol, lchnk ) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 0a8b213c3f..a42be67fac 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -162,7 +162,7 @@ end subroutine tuvx_init !================================================================================================ - subroutine tuvx_get_photo_rates( ncol, zmid, zint, tfld, ts ) + subroutine tuvx_get_photo_rates( ncol, zm, zi, tfld, ts ) !----------------------------------------------------------------------- ! ! Purpose: calculate and return photolysis rate constants @@ -179,8 +179,8 @@ subroutine tuvx_get_photo_rates( ncol, zmid, zint, tfld, ts ) ! Dummy arguments !----------------------------------------------------------------------- integer, intent(in) :: ncol ! Number of colums to calculated photolysis for - real(r8), intent(in) :: zmid(ncol,pver) ! height at mid-points (km) - real(r8), intent(in) :: zint(ncol,pver) ! height at interfaces (km) + real(r8), intent(in) :: zm(pcols,pver) ! height at mid-points (km) + real(r8), intent(in) :: zi(pcols,pver) ! height at interfaces (km) real(r8), intent(in) :: tfld(pcols,pver) ! midpoint temperature (K) real(r8), intent(in) :: ts(pcols) ! surface temperature (K) @@ -193,7 +193,7 @@ subroutine tuvx_get_photo_rates( ncol, zmid, zint, tfld, ts ) do i_col = 1, ncol ! set conditions for this column in TUV-x - call set_heights( tuvx, i_col, ncol, zmid, zint ) + call set_heights( tuvx, i_col, ncol, zm, zi ) call set_temperatures( tuvx, i_col, tfld, ts ) call set_surface_albedo( tuvx, i_col ) call set_et_flux( tuvx, i_col ) @@ -570,7 +570,7 @@ end subroutine create_updaters !================================================================================================ - subroutine set_heights( this, i_col, ncol, zmid, zint ) + subroutine set_heights( this, i_col, ncol, zm, zi) !----------------------------------------------------------------------- ! ! Purpose: sets the height values in TUV-x for the given column @@ -599,16 +599,17 @@ subroutine set_heights( this, i_col, ncol, zmid, zint ) ! !----------------------------------------------------------------------- - use ppgrid, only : pver ! number of vertical levels + use ppgrid, only : pcols, & ! maximum number of columns + pver ! number of vertical levels !----------------------------------------------------------------------- ! Dummy arguments !----------------------------------------------------------------------- - class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator - integer, intent(in) :: i_col ! column to set conditions for - integer, intent(in) :: ncol ! number of colums to calculated photolysis for - real(r8), intent(in) :: zmid(ncol,pver) ! height at mid-points (km) - real(r8), intent(in) :: zint(ncol,pver) ! height at interfaces (km) + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! column to set conditions for + integer, intent(in) :: ncol ! number of colums to calculated photolysis for + real(r8), intent(in) :: zm(pcols,pver) ! height at mid-points (km) + real(r8), intent(in) :: zi(pcols,pver) ! height at interfaces (km) !----------------------------------------------------------------------- ! Local variables @@ -618,9 +619,9 @@ subroutine set_heights( this, i_col, ncol, zmid, zint ) real(r8) :: mid_points(pver) edges(1) = 0.0_r8 - edges(2:pver+1) = zmid(i_col,pver:1:-1) - mid_points(1) = zmid(i_col,pver) * 0.5_r8 - mid_points(2:pver) = zint(i_col,pver:2:-1) + edges(2:pver+1) = zm(i_col,pver:1:-1) + mid_points(1) = zm(i_col,pver) * 0.5_r8 + mid_points(2:pver) = zi(i_col,pver:2:-1) call this%grids_( GRID_INDEX_HEIGHT )%update( edges = edges, mid_points = mid_points ) end subroutine set_heights From 64f9a3b27e14514bd1be3b944a27118092222550 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Mon, 7 Nov 2022 11:00:11 -0700 Subject: [PATCH 16/84] send surface albedos to TUV-x --- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 2 +- src/chemistry/mozart/mo_tuvx.F90 | 66 +++++++++++--------- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index 7af0657d2b..439eee9cb5 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -792,7 +792,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & !----------------------------------------------------------------- ! ... get calculated photolysis rates from TUV-x !----------------------------------------------------------------- - call tuvx_get_photo_rates( ncol, zm, zi, tfld, ts ) + call tuvx_get_photo_rates( ncol, zm, zi, tfld, ts, asdir ) do i = 1,phtcnt call outfld( tag_names(i), reaction_rates(:ncol,:,rxt_tag_map(i)), ncol, lchnk ) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index a42be67fac..a495396809 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -162,7 +162,8 @@ end subroutine tuvx_init !================================================================================================ - subroutine tuvx_get_photo_rates( ncol, zm, zi, tfld, ts ) + subroutine tuvx_get_photo_rates( ncol, height_mid, height_int, temperature_mid, & + surface_temperature, surface_albedo ) !----------------------------------------------------------------------- ! ! Purpose: calculate and return photolysis rate constants @@ -178,11 +179,12 @@ subroutine tuvx_get_photo_rates( ncol, zm, zi, tfld, ts ) !----------------------------------------------------------------------- ! Dummy arguments !----------------------------------------------------------------------- - integer, intent(in) :: ncol ! Number of colums to calculated photolysis for - real(r8), intent(in) :: zm(pcols,pver) ! height at mid-points (km) - real(r8), intent(in) :: zi(pcols,pver) ! height at interfaces (km) - real(r8), intent(in) :: tfld(pcols,pver) ! midpoint temperature (K) - real(r8), intent(in) :: ts(pcols) ! surface temperature (K) + integer, intent(in) :: ncol ! Number of colums to calculated photolysis for + real(r8), intent(in) :: height_mid(pcols,pver) ! height at mid-points (km) + real(r8), intent(in) :: height_int(pcols,pver) ! height at interfaces (km) + real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) + real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) + real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) !----------------------------------------------------------------------- ! Local variables @@ -193,9 +195,9 @@ subroutine tuvx_get_photo_rates( ncol, zm, zi, tfld, ts ) do i_col = 1, ncol ! set conditions for this column in TUV-x - call set_heights( tuvx, i_col, ncol, zm, zi ) - call set_temperatures( tuvx, i_col, tfld, ts ) - call set_surface_albedo( tuvx, i_col ) + call set_heights( tuvx, i_col, ncol, height_mid, height_int ) + call set_temperatures( tuvx, i_col, temperature_mid, surface_temperature ) + call set_surface_albedo( tuvx, i_col, surface_albedo ) call set_et_flux( tuvx, i_col ) call set_radiator_profiles( tuvx, i_col ) @@ -570,7 +572,7 @@ end subroutine create_updaters !================================================================================================ - subroutine set_heights( this, i_col, ncol, zm, zi) + subroutine set_heights( this, i_col, ncol, height_mid, height_int) !----------------------------------------------------------------------- ! ! Purpose: sets the height values in TUV-x for the given column @@ -605,11 +607,11 @@ subroutine set_heights( this, i_col, ncol, zm, zi) !----------------------------------------------------------------------- ! Dummy arguments !----------------------------------------------------------------------- - class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator - integer, intent(in) :: i_col ! column to set conditions for - integer, intent(in) :: ncol ! number of colums to calculated photolysis for - real(r8), intent(in) :: zm(pcols,pver) ! height at mid-points (km) - real(r8), intent(in) :: zi(pcols,pver) ! height at interfaces (km) + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! column to set conditions for + integer, intent(in) :: ncol ! number of colums to calculated photolysis for + real(r8), intent(in) :: height_mid(pcols,pver) ! height above the surface at mid-points (km) + real(r8), intent(in) :: height_int(pcols,pver) ! height above the surface at interfaces (km) !----------------------------------------------------------------------- ! Local variables @@ -619,16 +621,16 @@ subroutine set_heights( this, i_col, ncol, zm, zi) real(r8) :: mid_points(pver) edges(1) = 0.0_r8 - edges(2:pver+1) = zm(i_col,pver:1:-1) - mid_points(1) = zm(i_col,pver) * 0.5_r8 - mid_points(2:pver) = zi(i_col,pver:2:-1) + edges(2:pver+1) = height_mid(i_col,pver:1:-1) + mid_points(1) = height_mid(i_col,pver) * 0.5_r8 + mid_points(2:pver) = height_int(i_col,pver:2:-1) call this%grids_( GRID_INDEX_HEIGHT )%update( edges = edges, mid_points = mid_points ) end subroutine set_heights !================================================================================================ - subroutine set_temperatures( this, i_col, tfld, ts ) + subroutine set_temperatures( this, i_col, temperature_mid, surface_temperature ) !----------------------------------------------------------------------- ! ! Purpose: sets the temperatures in TUV-x for the given column @@ -644,10 +646,10 @@ subroutine set_temperatures( this, i_col, tfld, ts ) !----------------------------------------------------------------------- ! Dummy arguments !----------------------------------------------------------------------- - class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator - integer, intent(in) :: i_col ! column to set conditions for - real(r8), intent(in) :: tfld(pcols,pver) ! midpoint temperature (K) - real(r8), intent(in) :: ts(pcols) ! surface temperature (K) + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! column to set conditions for + real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) + real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) !----------------------------------------------------------------------- ! Local variables @@ -655,31 +657,33 @@ subroutine set_temperatures( this, i_col, tfld, ts ) integer :: i_level real(r8) :: edges(pver+1) - edges(1) = ts(i_col) - edges(2:pver+1) = tfld(i_col,pver:1:-1) + edges(1) = surface_temperature(i_col) + edges(2:pver+1) = temperature_mid(i_col,pver:1:-1) call this%profiles_( PROFILE_INDEX_TEMPERATURE )%update( edge_values = edges ) end subroutine set_temperatures !================================================================================================ - subroutine set_surface_albedo( this, i_col ) + subroutine set_surface_albedo( this, i_col, surface_albedo ) !----------------------------------------------------------------------- ! ! Purpose: sets the surface albedo in TUV-x for the given column ! -! TODO: Describe how CAM surface albedo profile is mapped to TUV-x wavelengths +! CAM uses a single value for surface albedo at all wavelengths ! !----------------------------------------------------------------------- + use ppgrid, only : pcols ! maximum number of columns + !----------------------------------------------------------------------- ! Dummy arguments !----------------------------------------------------------------------- - class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator - integer, intent(in) :: i_col ! Column to set conditions for + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! column to set conditions for + real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) - ! TEMPORARY FOR DEVELOPMENT - this%wavelength_values_(:) = 0.1_r8 + this%wavelength_values_(:) = surface_albedo(i_col) call this%profiles_( PROFILE_INDEX_ALBEDO )%update( & edge_values = this%wavelength_values_(:) ) From 49d323bb1185b80d96fc26f177e92a11c8ea7cb4 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Mon, 7 Nov 2022 15:45:41 -0700 Subject: [PATCH 17/84] send air density to TUV-x --- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 3 +- src/chemistry/mozart/mo_tuvx.F90 | 76 +++++++++++++------- 2 files changed, 52 insertions(+), 27 deletions(-) diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index 439eee9cb5..0d1d2d4704 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -792,7 +792,8 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & !----------------------------------------------------------------- ! ... get calculated photolysis rates from TUV-x !----------------------------------------------------------------- - call tuvx_get_photo_rates( ncol, zm, zi, tfld, ts, asdir ) + call tuvx_get_photo_rates( ncol, zm, zi, tfld, ts, invariants, & + vmr, asdir ) do i = 1,phtcnt call outfld( tag_names(i), reaction_rates(:ncol,:,rxt_tag_map(i)), ncol, lchnk ) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index a495396809..ed9f587e16 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -4,6 +4,7 @@ module mo_tuvx !---------------------------------------------------------------------- use musica_string, only : string_t + use ppgrid, only : pver ! number of vertical layers use shr_kind_mod, only : r8 => shr_kind_r8 use tuvx_core, only : core_t use tuvx_grid_from_host, only : grid_updater_t @@ -50,6 +51,8 @@ module mo_tuvx type(grid_updater_t) :: grids_(NUM_GRIDS) ! grid updaters type(profile_updater_t) :: profiles_(NUM_PROFILES) ! profile updaters type(radiator_updater_t) :: radiators_(NUM_RADIATORS) ! radiator updaters + real(r8) :: height_delta_(pver) ! change in height in each + ! vertical layer (km) real(r8), allocatable :: heights_(:) ! TEMPORARY FOR DEVELOPMENT real(r8), allocatable :: height_values_(:) ! TEMPORARY FOR DEVELOPMENT real(r8), allocatable :: height_mid_values_(:) ! TEMPORARY FOR DEVELOPMENT @@ -163,16 +166,17 @@ end subroutine tuvx_init !================================================================================================ subroutine tuvx_get_photo_rates( ncol, height_mid, height_int, temperature_mid, & - surface_temperature, surface_albedo ) + surface_temperature, fixed_species_conc, species_vmr, surface_albedo ) !----------------------------------------------------------------------- ! ! Purpose: calculate and return photolysis rate constants ! !----------------------------------------------------------------------- - use cam_logfile, only : iulog ! log info output unit - use ppgrid, only : pcols, & ! maximum number of columns - pver ! number of vertical levels + use cam_logfile, only : iulog ! log info output unit + use chem_mods, only : gas_pcnst, & ! number of non-fixed species + nfs ! number of fixed species + use ppgrid, only : pcols ! maximum number of columns use spmd_utils, only : main_task => masterprocid, & is_main_task => masterproc, & mpicom @@ -181,9 +185,13 @@ subroutine tuvx_get_photo_rates( ncol, height_mid, height_int, temperature_mid, !----------------------------------------------------------------------- integer, intent(in) :: ncol ! Number of colums to calculated photolysis for real(r8), intent(in) :: height_mid(pcols,pver) ! height at mid-points (km) - real(r8), intent(in) :: height_int(pcols,pver) ! height at interfaces (km) + real(r8), intent(in) :: height_int(pcols,pver+1) ! height at interfaces (km) real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) + real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities + ! (molecule cm-3) + real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) !----------------------------------------------------------------------- @@ -194,12 +202,14 @@ subroutine tuvx_get_photo_rates( ncol, height_mid, height_int, temperature_mid, associate( tuvx => tuvx_ptrs( thread_id( ) ) ) do i_col = 1, ncol - ! set conditions for this column in TUV-x + ! update grid heights call set_heights( tuvx, i_col, ncol, height_mid, height_int ) + + ! set conditions for this column in TUV-x call set_temperatures( tuvx, i_col, temperature_mid, surface_temperature ) call set_surface_albedo( tuvx, i_col, surface_albedo ) call set_et_flux( tuvx, i_col ) - call set_radiator_profiles( tuvx, i_col ) + call set_radiator_profiles( tuvx, i_col, ncol, fixed_species_conc, species_vmr ) ! Calculate photolysis rate constants for this column ! TEMPORARY FOR DEVELOPMENT - fix SZA @@ -326,7 +336,6 @@ function get_cam_grids( ) result( grids ) ! !----------------------------------------------------------------------- - use ppgrid, only : pver ! number of vertical levels use tuvx_grid_from_host, only : grid_from_host_t use tuvx_grid_warehouse, only : grid_warehouse_t @@ -577,6 +586,8 @@ subroutine set_heights( this, i_col, ncol, height_mid, height_int) ! ! Purpose: sets the height values in TUV-x for the given column ! +! NOTE: This function must be called before updating any profile data. +! ! CAM to TUV-x height grid mapping ! ! TUV-x heights are "bottom-up" and require atmospheric constituent @@ -601,17 +612,16 @@ subroutine set_heights( this, i_col, ncol, height_mid, height_int) ! !----------------------------------------------------------------------- - use ppgrid, only : pcols, & ! maximum number of columns - pver ! number of vertical levels + use ppgrid, only : pcols ! maximum number of columns !----------------------------------------------------------------------- ! Dummy arguments !----------------------------------------------------------------------- - class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator - integer, intent(in) :: i_col ! column to set conditions for - integer, intent(in) :: ncol ! number of colums to calculated photolysis for - real(r8), intent(in) :: height_mid(pcols,pver) ! height above the surface at mid-points (km) - real(r8), intent(in) :: height_int(pcols,pver) ! height above the surface at interfaces (km) + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! column to set conditions for + integer, intent(in) :: ncol ! number of colums to calculated photolysis for + real(r8), intent(in) :: height_mid(pcols,pver) ! height above the surface at mid-points (km) + real(r8), intent(in) :: height_int(pcols,pver+1) ! height above the surface at interfaces (km) !----------------------------------------------------------------------- ! Local variables @@ -625,6 +635,7 @@ subroutine set_heights( this, i_col, ncol, height_mid, height_int) mid_points(1) = height_mid(i_col,pver) * 0.5_r8 mid_points(2:pver) = height_int(i_col,pver:2:-1) call this%grids_( GRID_INDEX_HEIGHT )%update( edges = edges, mid_points = mid_points ) + this%height_delta_(1:pver) = edges(2:pver+1) - edges(1:pver) end subroutine set_heights @@ -640,8 +651,7 @@ subroutine set_temperatures( this, i_col, temperature_mid, surface_temperature ) ! !----------------------------------------------------------------------- - use ppgrid, only : pcols, & ! maximum number of columns - pver ! number of vertical levels + use ppgrid, only : pcols ! maximum number of columns !----------------------------------------------------------------------- ! Dummy arguments @@ -654,7 +664,6 @@ subroutine set_temperatures( this, i_col, temperature_mid, surface_temperature ) !----------------------------------------------------------------------- ! Local variables !----------------------------------------------------------------------- - integer :: i_level real(r8) :: edges(pver+1) edges(1) = surface_temperature(i_col) @@ -715,7 +724,7 @@ end subroutine set_et_flux !================================================================================================ - subroutine set_radiator_profiles( this, i_col ) + subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species_vmr ) !----------------------------------------------------------------------- ! ! Purpose: sets the profiles of optically active atmospheric constituents @@ -725,19 +734,34 @@ subroutine set_radiator_profiles( this, i_col ) ! !----------------------------------------------------------------------- + use chem_mods, only : gas_pcnst, & ! number of non-fixed species + nfs, & ! number of fixed species + indexm ! index for air density in fixed species array + !----------------------------------------------------------------------- ! Dummy arguments !----------------------------------------------------------------------- class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator - integer, intent(in) :: i_col ! Column to set conditions for + integer, intent(in) :: i_col ! column to set conditions for + integer, intent(in) :: ncol ! number of columns + real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities + ! (molecule cm-3) + real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) + +!----------------------------------------------------------------------- +! Local variables +!----------------------------------------------------------------------- + real(r8) :: edges(pver+1), densities(pver) + real(r8) :: km2cm = 1.0e5 ! conversion from km to cm - ! TEMPORARY FOR DEVELOPMENT - air - this%height_values_(:) = 2.54e19_r8 - this%height_mid_values_(:) = 2.54e21_r8 + ! air + edges(1) = fixed_species_conc(i_col,pver,indexm) + edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,indexm) + densities(1:pver) = this%height_delta_(1:pver) * km2cm * & + sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) call this%profiles_( PROFILE_INDEX_AIR )%update( & - mid_point_values = this%height_values_(1:size(this%height_values_)-1), & - edge_values = this%height_values_(:), & - layer_densities = this%height_mid_values_(:) ) + edge_values = edges, layer_densities = densities ) ! TEMPORARY FOR DEVELOPMENT - O2 this%height_values_(:) = 1.0e17_r8 From 0a0b3ff3fba352b69db26df6659744799c8980bb Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Mon, 7 Nov 2022 16:50:23 -0700 Subject: [PATCH 18/84] send O2 and O3 concentrations to TUV-x --- src/chemistry/mozart/mo_tuvx.F90 | 58 +++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index ed9f587e16..106aa63d01 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -39,6 +39,12 @@ module mo_tuvx integer, parameter :: NUM_RADIATORS = 1 ! number of radiators that CAM will update at runtime integer, parameter :: RADIATOR_INDEX_AEROSOL = 1 ! Aerosol radiator index + ! Information needed to access CAM species state data + logical :: is_fixed_O2 = .false. ! indicates whether O2 concentrations are fixed + logical :: is_fixed_O3 = .false. ! indicates whether O3 concentrations are fixed + integer :: index_O2 = 0 ! index for O2 in concentration array + integer :: index_O3 = 0 ! index for O3 in concentration array + ! TODO how should this path be set and communicated to this wrapper? character(len=*), parameter :: tuvx_config_path = "tuvx_config.json" @@ -76,6 +82,7 @@ subroutine tuvx_init( ) use mpi #endif use cam_logfile, only : iulog + use mo_chem_utls, only : get_spc_ndx, get_inv_ndx use musica_assert, only : assert_msg use musica_mpi, only : musica_mpi_rank use musica_string, only : string_t, to_char @@ -161,6 +168,15 @@ subroutine tuvx_init( ) deallocate( cam_profiles ) deallocate( cam_radiators ) + ! Get index info for CAM species concentrations + index_O2 = get_inv_ndx( 'O2' ) + is_fixed_O2 = index_O2 > 0 + if( .not. is_fixed_O2 ) index_O2 = get_spc_ndx( 'O2' ) + index_O3 = get_inv_ndx( 'O3' ) + is_fixed_O3 = index_O3 > 0 + if( .not. is_fixed_O3 ) index_O3 = get_spc_ndx( 'O3' ) + + end subroutine tuvx_init !================================================================================================ @@ -763,21 +779,39 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species call this%profiles_( PROFILE_INDEX_AIR )%update( & edge_values = edges, layer_densities = densities ) - ! TEMPORARY FOR DEVELOPMENT - O2 - this%height_values_(:) = 1.0e17_r8 - this%height_mid_values_(:) = 1.0e19_r8 + ! O2 + if( is_fixed_O2 ) then + edges(1) = fixed_species_conc(i_col,pver,index_O2) + edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,index_O2) + else if( index_O2 > 0 ) then + edges(1) = species_vmr(i_col,pver,index_O2) * & + fixed_species_conc(i_col,pver,indexm) + edges(2:pver+1) = species_vmr(i_col,pver:1:-1,index_O2) * & + fixed_species_conc(i_col,pver:1:-1,indexm) + else + edges(:) = 0.0_r8 + end if + densities(1:pver) = this%height_delta_(1:pver) * km2cm * & + sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) call this%profiles_( PROFILE_INDEX_O2 )%update( & - mid_point_values = this%height_values_(1:size(this%height_values_)-1), & - edge_values = this%height_values_(:), & - layer_densities = this%height_mid_values_(:) ) + edge_values = edges, layer_densities = densities ) - ! TEMPORARY FOR DEVELOPMENT - O3 - this%height_values_(:) = 1.0e13_r8 - this%height_mid_values_(:) = 1.0e15_r8 + ! O3 + if( is_fixed_O3 ) then + edges(1) = fixed_species_conc(i_col,pver,index_O3) + edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,index_O3) + else if( index_O3 > 0 ) then + edges(1) = species_vmr(i_col,pver,index_O3) * & + fixed_species_conc(i_col,pver,indexm) + edges(2:pver+1) = species_vmr(i_col,pver:1:-1,index_O3) * & + fixed_species_conc(i_col,pver:1:-1,indexm) + else + edges(:) = 0.0_r8 + end if + densities(1:pver) = this%height_delta_(1:pver) * km2cm * & + sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) call this%profiles_( PROFILE_INDEX_O3 )%update( & - mid_point_values = this%height_values_(1:size(this%height_values_)-1), & - edge_values = this%height_values_(:), & - layer_densities = this%height_mid_values_(:) ) + edge_values = edges, layer_densities = densities ) ! TEMPORARY FOR DEVELOPMENT - aerosols this%optics_values_(:,:) = 0.01_r8 From 0562e9d3ea845abe13109fcd67712bb4ba839e3f Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Mon, 7 Nov 2022 17:44:57 -0700 Subject: [PATCH 19/84] sent solar zenith angle to TUV-x --- cime_config/buildlib | 2 +- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 2 +- src/chemistry/mozart/mo_tuvx.F90 | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/cime_config/buildlib b/cime_config/buildlib index 6c22917a32..5b14ba4959 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -132,7 +132,7 @@ def _cmake_default_args(caseroot): args += "-DMACH={} ".format(case.get_value("MACH")) cmd = "cmake {} .".format(args) rc, out, err = run_cmd(cmd, combine_output=True, from_dir=macro_path) - expect(rc == 0, "Command {} failed with rc={}".format(cmd, rc)) + expect(rc == 0, "Command {} failed with rc={} out={} err={}".format(cmd, rc, out, err)) arg_dict = {} for line in out.splitlines(): diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index 0d1d2d4704..965e3c9dcf 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -793,7 +793,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & ! ... get calculated photolysis rates from TUV-x !----------------------------------------------------------------- call tuvx_get_photo_rates( ncol, zm, zi, tfld, ts, invariants, & - vmr, asdir ) + vmr, asdir, zen_angle ) do i = 1,phtcnt call outfld( tag_names(i), reaction_rates(:ncol,:,rxt_tag_map(i)), ncol, lchnk ) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 106aa63d01..48e2738dbd 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -182,7 +182,8 @@ end subroutine tuvx_init !================================================================================================ subroutine tuvx_get_photo_rates( ncol, height_mid, height_int, temperature_mid, & - surface_temperature, fixed_species_conc, species_vmr, surface_albedo ) + surface_temperature, fixed_species_conc, species_vmr, surface_albedo, & + solar_zenith_angle ) !----------------------------------------------------------------------- ! ! Purpose: calculate and return photolysis rate constants @@ -193,6 +194,7 @@ subroutine tuvx_get_photo_rates( ncol, height_mid, height_int, temperature_mid, use chem_mods, only : gas_pcnst, & ! number of non-fixed species nfs ! number of fixed species use ppgrid, only : pcols ! maximum number of columns + use shr_const_mod, only : pi => shr_const_pi use spmd_utils, only : main_task => masterprocid, & is_main_task => masterproc, & mpicom @@ -209,6 +211,7 @@ subroutine tuvx_get_photo_rates( ncol, height_mid, height_int, temperature_mid, real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing ! ratios (mol mol-1) real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) + real(r8), intent(in) :: solar_zenith_angle(ncol) ! solar zenith angle (radians) !----------------------------------------------------------------------- ! Local variables @@ -229,7 +232,8 @@ subroutine tuvx_get_photo_rates( ncol, height_mid, height_int, temperature_mid, ! Calculate photolysis rate constants for this column ! TEMPORARY FOR DEVELOPMENT - fix SZA - call tuvx%core_%run( solar_zenith_angle = 45.0_r8, & + call tuvx%core_%run( solar_zenith_angle = & + solar_zenith_angle(i_col) * 180.0_r8 / pi, & photolysis_rate_constants = tuvx%photo_rates_ ) end do From 69a589715caa49716564347c8ba774aadc07ad90 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Tue, 8 Nov 2022 13:45:51 -0700 Subject: [PATCH 20/84] send ET flux to tuv-x --- cime_config/buildlib | 8 ++++ src/chemistry/mozart/mo_tuvx.F90 | 76 +++++++++++++++++++++----------- 2 files changed, 58 insertions(+), 26 deletions(-) diff --git a/cime_config/buildlib b/cime_config/buildlib index 5b14ba4959..e18fafb9e6 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -130,6 +130,9 @@ def _cmake_default_args(caseroot): args += "-DCOMPILER={} ".format(build.get_value("COMPILER")) args += "-DOS={} ".format(build.get_value("OS")) args += "-DMACH={} ".format(case.get_value("MACH")) + args += "-DCMAKE_C_COMPILER_WORKS=1 " + args += "-DCMAKE_Fortran_COMPILER_WORKS=1 " + args += "-DCMAKE_CXX_COMPILER_WORKS=1 " cmd = "cmake {} .".format(args) rc, out, err = run_cmd(cmd, combine_output=True, from_dir=macro_path) expect(rc == 0, "Command {} failed with rc={} out={} err={}".format(cmd, rc, out, err)) @@ -139,6 +142,7 @@ def _cmake_default_args(caseroot): if ":=" in line: key, val = line.split(":=") arg_dict[key.replace('CIME_SET_MAKEFILE_VAR','').strip()] = val.strip() + logger.info("{}: {}".format(key.replace('CIME_SET_MAKEFILE_VAR','').strip(), val.strip())) return arg_dict @@ -158,6 +162,8 @@ def _build_json_fortran(caseroot, libroot, bldroot): arg_dict = _cmake_default_args(caseroot) cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["SFC"]) + cmake_args += "-DCMAKE_C_COMPILER_WORKS=1 " + cmake_args += "-DCMAKE_CXX_COMPILER_WORKS=1 " cmake_args += "-DCMAKE_BUILD_TYPE=Release " cmake_args += "-DSKIP_DOC_GEN:BOOL=TRUE " cmake_args += srcpath @@ -208,6 +214,8 @@ def _build_tuvx(caseroot, libroot, bldroot): cmake_args += "-DCMAKE_BUILD_TYPE=Debug " else: cmake_args += "-DCMAKE_BUILD_TYPE=Release " + cmake_args += "-DCMAKE_C_COMPILER_WORKS=1 " + cmake_args += "-DCMAKE_CXX_COMPILER_WORKS=1 " cmake_args += "-DENABLE_COVERAGE=OFF " cmake_args += "-DENABLE_TESTS=OFF " cmake_args += "-DJSON_INCLUDE_DIR={} ".format(jsoninc) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 48e2738dbd..aab5725f2e 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -45,6 +45,11 @@ module mo_tuvx integer :: index_O2 = 0 ! index for O2 in concentration array integer :: index_O3 = 0 ! index for O3 in concentration array + ! Information needed to translate the CAM wavelength grid to TUV-x + integer :: n_wavelength_bins = 0 ! number of ET Flux wavelength bins to use + integer :: wavelength_grid_start_index = 0 ! starting index in ET Flux wavelength grid to use + integer :: wavelength_grid_end_index = 0 ! ending index in ET Flux wavelength grid to use + ! TODO how should this path be set and communicated to this wrapper? character(len=*), parameter :: tuvx_config_path = "tuvx_config.json" @@ -190,14 +195,14 @@ subroutine tuvx_get_photo_rates( ncol, height_mid, height_int, temperature_mid, ! !----------------------------------------------------------------------- - use cam_logfile, only : iulog ! log info output unit - use chem_mods, only : gas_pcnst, & ! number of non-fixed species - nfs ! number of fixed species - use ppgrid, only : pcols ! maximum number of columns - use shr_const_mod, only : pi => shr_const_pi - use spmd_utils, only : main_task => masterprocid, & - is_main_task => masterproc, & - mpicom + use cam_logfile, only : iulog ! log info output unit + use chem_mods, only : gas_pcnst, & ! number of non-fixed species + nfs ! number of fixed species + use ppgrid, only : pcols ! maximum number of columns + use shr_const_mod, only : pi => shr_const_pi + use spmd_utils, only : main_task => masterprocid, & + is_main_task => masterproc, & + mpicom !----------------------------------------------------------------------- ! Dummy arguments !----------------------------------------------------------------------- @@ -354,8 +359,15 @@ function get_cam_grids( ) result( grids ) ! Purpose: creates and loads a grid warehouse with grids that CAM will ! update at runtime ! +! NOTE: The wavelength grid is restricted to bins >= 200 nm to avoid +! the Lyman-alpha and Shumann-Runge bands. Photolysis rate +! constant contributions from < 200 nm are calculated separately. +! !----------------------------------------------------------------------- + use musica_assert, only : assert_msg + use solar_irrad_data, only : nbins, & ! number of wavelength bins + we ! wavelength bin edges (nm) use tuvx_grid_from_host, only : grid_from_host_t use tuvx_grid_warehouse, only : grid_warehouse_t @@ -367,27 +379,32 @@ function get_cam_grids( ) result( grids ) !----------------------------------------------------------------------- ! Local variables !----------------------------------------------------------------------- + integer :: i_bin class(grid_from_host_t), pointer :: host_grid type(grid_updater_t) :: updater - real(r8) :: wavelengths(121) ! TEMPORARY FOR DEVELOPMENT - integer :: i_wavelength grids => grid_warehouse_t( ) - ! Height grid will be ... \todo figure out how height grid should translate - ! to CAM vertical grid + ! heights above the surface host_grid => grid_from_host_t( "height", "km", pver ) call grids%add( host_grid ) deallocate( host_grid ) - ! Wavelength grid wil be ... /todo figure out where to get wavelength grid - ! from (wavelengths must be set prior to construction of the TUV-x core) - do i_wavelength = 1, size( wavelengths ) - wavelengths( i_wavelength ) = 199.0_r8 + i_wavelength + ! wavelength bins + do i_bin = 1, nbins + if( we( i_bin ) >= 200.0_r8 ) then + wavelength_grid_start_index = i_bin + exit + end if end do - host_grid => grid_from_host_t( "wavelength", "nm", 120 ) + call assert_msg( 830083933, wavelength_grid_start_index > 0, & + "No wavelength bins available for TUV-x calculations" ) + wavelength_grid_end_index = nbins + n_wavelength_bins = wavelength_grid_end_index - wavelength_grid_start_index + 1 + host_grid => grid_from_host_t( "wavelength", "nm", n_wavelength_bins ) updater = grid_updater_t( host_grid ) - call updater%update( edges = wavelengths ) + call updater%update(edges = & + we(wavelength_grid_start_index:wavelength_grid_end_index+1)) call grids%add( host_grid ) deallocate( host_grid ) @@ -725,20 +742,21 @@ subroutine set_et_flux( this, i_col ) ! ! Purpose: sets the extraterrestrial flux in TUV-x for the given column ! -! TODO: Describe how CAM extraterrestrial flux profile is mapped to TUV-x wavelengths +! Extraterrestrial flux is read from data files and copied to TUV-x ! !----------------------------------------------------------------------- + ! TODO are these units correct? if so, they don't match expected (photo cm-2 s-1) + use solar_irrad_data, only : sol_etf ! extraterrestrial flux (photon cm-2 s-1 nm-1) + !----------------------------------------------------------------------- ! Dummy arguments !----------------------------------------------------------------------- class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator integer, intent(in) :: i_col ! Column to set conditions for - ! TEMPORARY FOR DEVELOPMENT - this%wavelength_values_(:) = 1000.0_r8 - call this%profiles_( PROFILE_INDEX_ET_FLUX )%update( & - edge_values = this%wavelength_values_(:) ) + call this%profiles_( PROFILE_INDEX_ET_FLUX )%update( edge_values = & + sol_etf(wavelength_grid_start_index:wavelength_grid_end_index+1) ) end subroutine set_et_flux @@ -750,7 +768,11 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species ! Purpose: sets the profiles of optically active atmospheric constituents ! in TUV-x for the given column ! -! TODO: Describe how CAM profiles are mapped to TUV-x heights +! See `set_height` for a description of the CAM <-> TUV-x vertical grid +! mapping. +! +! Above layer densities are calculated using a scale height for air +! and ... TODO fill in description for O2, O3 ! !----------------------------------------------------------------------- @@ -798,7 +820,8 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species densities(1:pver) = this%height_delta_(1:pver) * km2cm * & sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) call this%profiles_( PROFILE_INDEX_O2 )%update( & - edge_values = edges, layer_densities = densities ) + edge_values = edges, layer_densities = densities, & + exo_density = 0.0_r8 ) ! TODO how should this be calculated? ! O3 if( is_fixed_O3 ) then @@ -815,7 +838,8 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species densities(1:pver) = this%height_delta_(1:pver) * km2cm * & sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) call this%profiles_( PROFILE_INDEX_O3 )%update( & - edge_values = edges, layer_densities = densities ) + edge_values = edges, layer_densities = densities, & + exo_density = 0.0_r8 ) ! TODO how should this be calculated? ! TEMPORARY FOR DEVELOPMENT - aerosols this%optics_values_(:,:) = 0.01_r8 From e3cc2ab0ad9897a384d34506894bedeee8c94cb3 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Wed, 9 Nov 2022 13:29:54 -0700 Subject: [PATCH 21/84] use native TUV-x grid for photo calcs --- src/chemistry/mozart/chemistry.F90 | 6 ++ src/chemistry/mozart/mo_tuvx.F90 | 128 ++++++++++++++++------------- 2 files changed, 79 insertions(+), 55 deletions(-) diff --git a/src/chemistry/mozart/chemistry.F90 b/src/chemistry/mozart/chemistry.F90 index 5b590163e4..584156a629 100644 --- a/src/chemistry/mozart/chemistry.F90 +++ b/src/chemistry/mozart/chemistry.F90 @@ -992,6 +992,7 @@ subroutine chem_timestep_init(phys_state,pbuf2d) use mo_aurora, only : aurora_timestep_init use mo_photo, only : photo_timestep_init + use mo_tuvx, only : tuvx_timestep_init use cfc11star, only : update_cfc11star use physics_buffer, only : physics_buffer_desc @@ -1063,6 +1064,11 @@ subroutine chem_timestep_init(phys_state,pbuf2d) !----------------------------------------------------------------------------- call photo_timestep_init( calday ) + !----------------------------------------------------------------------------- + ! ... setup the TUV-x profiles for this timestep + !----------------------------------------------------------------------------- + call tuvx_timestep_init( ) + call update_cfc11star( pbuf2d, phys_state ) ! Galatic Cosmic Rays ... diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index aab5725f2e..58adb111e3 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -16,6 +16,7 @@ module mo_tuvx private public :: tuvx_init + public :: tuvx_timestep_init public :: tuvx_get_photo_rates public :: tuvx_finalize @@ -45,14 +46,10 @@ module mo_tuvx integer :: index_O2 = 0 ! index for O2 in concentration array integer :: index_O3 = 0 ! index for O3 in concentration array - ! Information needed to translate the CAM wavelength grid to TUV-x - integer :: n_wavelength_bins = 0 ! number of ET Flux wavelength bins to use - integer :: wavelength_grid_start_index = 0 ! starting index in ET Flux wavelength grid to use - integer :: wavelength_grid_end_index = 0 ! ending index in ET Flux wavelength grid to use - - ! TODO how should this path be set and communicated to this wrapper? + ! TODO how should these paths be set and communicated to this wrapper? character(len=*), parameter :: tuvx_config_path = "tuvx_config.json" - + character(len=*), parameter :: wavelength_config_path = & + "data/grids/wavelength/combined.grid" ! TUV-x calculator for each OMP thread type :: tuvx_ptr type(core_t), pointer :: core_ => null( ) ! TUV-x calculator @@ -64,10 +61,11 @@ module mo_tuvx type(radiator_updater_t) :: radiators_(NUM_RADIATORS) ! radiator updaters real(r8) :: height_delta_(pver) ! change in height in each ! vertical layer (km) - real(r8), allocatable :: heights_(:) ! TEMPORARY FOR DEVELOPMENT - real(r8), allocatable :: height_values_(:) ! TEMPORARY FOR DEVELOPMENT - real(r8), allocatable :: height_mid_values_(:) ! TEMPORARY FOR DEVELOPMENT - real(r8), allocatable :: wavelength_values_(:) ! TEMPORARY FOR DEVELOPMENT + real(r8), allocatable :: wavelength_edges_(:) ! TUV-x wavelength bin edges (nm) + real(r8), allocatable :: wavelength_values_(:) ! Working array for interface values + ! on the TUV-x wavelength grid + real(r8), allocatable :: wavelength_mid_values_(:) ! Working array for mid-point values + ! on the TUV-x wavelength grid real(r8), allocatable :: optics_values_(:,:) ! TEMPORARY FOR DEVELOPMENT end type tuvx_ptr type(tuvx_ptr), allocatable :: tuvx_ptrs(:) @@ -120,7 +118,7 @@ subroutine tuvx_init( ) #endif ! Create the set of TUV-x grids and profiles that CAM will update at runtime - cam_grids => get_cam_grids( ) + cam_grids => get_cam_grids( wavelength_config_path ) cam_profiles => get_cam_profiles( cam_grids ) cam_radiators => get_cam_radiators( cam_grids ) @@ -184,6 +182,28 @@ subroutine tuvx_init( ) end subroutine tuvx_init +!================================================================================================ + + subroutine tuvx_timestep_init( ) +!----------------------------------------------------------------------- +! +! Purpose: updates TUV-x profiles that depend on time but not space +! +!----------------------------------------------------------------------- + +!----------------------------------------------------------------------- +! Local variables +!----------------------------------------------------------------------- + integer :: i_thread + + do i_thread = 1, size( tuvx_ptrs ) + associate( tuvx => tuvx_ptrs( i_thread ) ) + call set_et_flux( tuvx ) + end associate + end do + + end subroutine tuvx_timestep_init + !================================================================================================ subroutine tuvx_get_photo_rates( ncol, height_mid, height_int, temperature_mid, & @@ -232,11 +252,9 @@ subroutine tuvx_get_photo_rates( ncol, height_mid, height_int, temperature_mid, ! set conditions for this column in TUV-x call set_temperatures( tuvx, i_col, temperature_mid, surface_temperature ) call set_surface_albedo( tuvx, i_col, surface_albedo ) - call set_et_flux( tuvx, i_col ) call set_radiator_profiles( tuvx, i_col, ncol, fixed_species_conc, species_vmr ) ! Calculate photolysis rate constants for this column - ! TEMPORARY FOR DEVELOPMENT - fix SZA call tuvx%core_%run( solar_zenith_angle = & solar_zenith_angle(i_col) * 180.0_r8 / pi, & photolysis_rate_constants = tuvx%photo_rates_ ) @@ -353,35 +371,33 @@ end subroutine log_initialization !================================================================================================ - function get_cam_grids( ) result( grids ) + function get_cam_grids( wavelength_path ) result( grids ) !----------------------------------------------------------------------- ! ! Purpose: creates and loads a grid warehouse with grids that CAM will ! update at runtime ! -! NOTE: The wavelength grid is restricted to bins >= 200 nm to avoid -! the Lyman-alpha and Shumann-Runge bands. Photolysis rate -! constant contributions from < 200 nm are calculated separately. -! !----------------------------------------------------------------------- use musica_assert, only : assert_msg - use solar_irrad_data, only : nbins, & ! number of wavelength bins - we ! wavelength bin edges (nm) + use musica_config, only : config_t + use tuvx_grid, only : grid_t + use tuvx_grid_factory, only : grid_builder use tuvx_grid_from_host, only : grid_from_host_t use tuvx_grid_warehouse, only : grid_warehouse_t !----------------------------------------------------------------------- ! Dummy arguments !----------------------------------------------------------------------- - class(grid_warehouse_t), pointer :: grids ! collection of grids to be updated by CAM + character(len=*), intent(in) :: wavelength_path ! path to the wavelength data file + class(grid_warehouse_t), pointer :: grids ! collection of grids to be updated by CAM !----------------------------------------------------------------------- ! Local variables !----------------------------------------------------------------------- - integer :: i_bin - class(grid_from_host_t), pointer :: host_grid - type(grid_updater_t) :: updater + character(len=*), parameter :: my_name = "CAM grid creator" + class(grid_t), pointer :: host_grid + type(config_t) :: config grids => grid_warehouse_t( ) @@ -390,21 +406,13 @@ function get_cam_grids( ) result( grids ) call grids%add( host_grid ) deallocate( host_grid ) - ! wavelength bins - do i_bin = 1, nbins - if( we( i_bin ) >= 200.0_r8 ) then - wavelength_grid_start_index = i_bin - exit - end if - end do - call assert_msg( 830083933, wavelength_grid_start_index > 0, & - "No wavelength bins available for TUV-x calculations" ) - wavelength_grid_end_index = nbins - n_wavelength_bins = wavelength_grid_end_index - wavelength_grid_start_index + 1 - host_grid => grid_from_host_t( "wavelength", "nm", n_wavelength_bins ) - updater = grid_updater_t( host_grid ) - call updater%update(edges = & - we(wavelength_grid_start_index:wavelength_grid_end_index+1)) + ! wavelengths (will not be updated at runtime) + call config%empty( ) + call config%add( "type", "from csv file", my_name ) + call config%add( "name", "wavelength", my_name ) + call config%add( "units", "nm", my_name ) + call config%add( "file path", wavelength_path, my_name ) + host_grid => grid_builder( config ) call grids%add( host_grid ) deallocate( host_grid ) @@ -428,8 +436,8 @@ function get_cam_profiles( grids ) result( profiles ) !----------------------------------------------------------------------- ! Dummy arguments !----------------------------------------------------------------------- - class(grid_warehouse_t), intent(in) :: grids ! CAM grids used in TUV-x - class(profile_warehouse_t), pointer :: profiles ! collection of profiles to be updated by CAM + class(grid_warehouse_t), intent(in) :: grids ! CAM grids used in TUV-x + class(profile_warehouse_t), pointer :: profiles ! collection of profiles to be updated by CAM !----------------------------------------------------------------------- ! Local variables @@ -463,7 +471,6 @@ function get_cam_profiles( grids ) result( profiles ) deallocate( host_profile ) ! O3 profile - ! TODO optionally include if available host_profile => profile_from_host_t( "O3", "molecule cm-3", height%size( ) ) call profiles%add( host_profile ) deallocate( host_profile ) @@ -561,13 +568,12 @@ subroutine create_updaters( this, grids, profiles, radiators ) height => grids%get_grid( "height", "km" ) this%grids_( GRID_INDEX_HEIGHT ) = this%core_%get_updater( height, found ) call assert( 213798815, found ) - allocate( this%heights_( height%size( ) + 1 ) ) ! TEMPORARY FOR DEVELOPMENT - allocate( this%height_values_( height%size( ) + 1 ) ) ! TEMPORARY FOR DEVELOPMENT - allocate( this%height_mid_values_( height%size( ) ) ) ! TEMPORARY FOR DEVELOPMENT ! wavelength grid cannot be updated at runtime wavelength => grids%get_grid( "wavelength", "nm" ) - allocate( this%wavelength_values_( wavelength%size( ) + 1 ) ) ! TEMPORARY FOR DEVELOPMENT + allocate( this%wavelength_edges_( wavelength%size( ) + 1 ) ) + allocate( this%wavelength_values_( wavelength%size( ) + 1 ) ) + allocate( this%wavelength_mid_values_( wavelength%size( ) ) ) ! optical property working array allocate( this%optics_values_( height%size( ), wavelength%size( ) ) ) @@ -737,26 +743,38 @@ end subroutine set_surface_albedo !================================================================================================ - subroutine set_et_flux( this, i_col ) + subroutine set_et_flux( this ) !----------------------------------------------------------------------- ! ! Purpose: sets the extraterrestrial flux in TUV-x for the given column ! -! Extraterrestrial flux is read from data files and copied to TUV-x +! Extraterrestrial flux is read from data files and interpolated to the +! TUV-x wavelength grid. +! +! NOTE: TUV-x only uses mid-point values for ET Flux ! !----------------------------------------------------------------------- - ! TODO are these units correct? if so, they don't match expected (photo cm-2 s-1) - use solar_irrad_data, only : sol_etf ! extraterrestrial flux (photon cm-2 s-1 nm-1) + use mo_util, only : rebin + use solar_irrad_data, only : nbins, & ! number of wavelength bins + we, & ! wavelength bin edges + ! TODO are these units correct? if so, they don't match expected (photon cm-2 s-1) + sol_etf ! solar extraterrestrial flux (photon cm-2 nm-1 s-1) !----------------------------------------------------------------------- ! Dummy arguments !----------------------------------------------------------------------- class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator - integer, intent(in) :: i_col ! Column to set conditions for - - call this%profiles_( PROFILE_INDEX_ET_FLUX )%update( edge_values = & - sol_etf(wavelength_grid_start_index:wavelength_grid_end_index+1) ) + integer :: n_tuvx_bins + + n_tuvx_bins = size( this%wavelength_mid_values_ ) + call rebin( nbins, n_tuvx_bins, we, this%wavelength_edges_, sol_etf, & + this%wavelength_mid_values_ ) + this%wavelength_values_(1) = this%wavelength_mid_values_(1) + this%wavelength_values_(2:n_tuvx_bins+1) = this%wavelength_mid_values_(1:n_tuvx_bins) + call this%profiles_( PROFILE_INDEX_ET_FLUX )%update( & + mid_point_values = this%wavelength_mid_values_, & + edge_values = this%wavelength_values_ ) end subroutine set_et_flux From 1e05ffe4d181e7f6ad565897e61b62dbe771bb00 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Thu, 10 Nov 2022 12:23:46 -0700 Subject: [PATCH 22/84] send aerosol optical properties to tuv-x --- cime_config/buildlib | 1 - src/chemistry/mozart/chemistry.F90 | 2 +- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 9 +- src/chemistry/mozart/mo_tuvx.F90 | 217 ++++++++++++++++--- src/physics/cam/modal_aer_opt.F90 | 6 + 5 files changed, 202 insertions(+), 33 deletions(-) diff --git a/cime_config/buildlib b/cime_config/buildlib index e18fafb9e6..4bb47915d8 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -142,7 +142,6 @@ def _cmake_default_args(caseroot): if ":=" in line: key, val = line.split(":=") arg_dict[key.replace('CIME_SET_MAKEFILE_VAR','').strip()] = val.strip() - logger.info("{}: {}".format(key.replace('CIME_SET_MAKEFILE_VAR','').strip(), val.strip())) return arg_dict diff --git a/src/chemistry/mozart/chemistry.F90 b/src/chemistry/mozart/chemistry.F90 index 584156a629..d9c67324bd 100644 --- a/src/chemistry/mozart/chemistry.F90 +++ b/src/chemistry/mozart/chemistry.F90 @@ -1222,7 +1222,7 @@ subroutine chem_timestep_tend( state, ptend, cam_in, cam_out, dt, pbuf, fh2o) fsds, cam_in%ts, cam_in%asdir, cam_in%ocnfrac, cam_in%icefrac, & cam_out%precc, cam_out%precl, cam_in%snowhland, ghg_chem, state%latmapback, & drydepflx, wetdepflx, cam_in%cflx, cam_in%fireflx, cam_in%fireztop, & - nhx_nitrogen_flx, noy_nitrogen_flx, ptend%q, pbuf ) + nhx_nitrogen_flx, noy_nitrogen_flx, ptend%q, pbuf, state ) if (associated(cam_out%nhx_nitrogen_flx)) then cam_out%nhx_nitrogen_flx(:ncol) = nhx_nitrogen_flx(:ncol) endif diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index 965e3c9dcf..8f2e3209ad 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -238,7 +238,8 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & delt, ps, & fsds, ts, asdir, ocnfrac, icefrac, & precc, precl, snowhland, ghg_chem, latmapback, & - drydepflx, wetdepflx, cflx, fire_sflx, fire_ztop, nhx_nitrogen_flx, noy_nitrogen_flx, qtend, pbuf) + drydepflx, wetdepflx, cflx, fire_sflx, fire_ztop, & + nhx_nitrogen_flx, noy_nitrogen_flx, qtend, pbuf, state) !----------------------------------------------------------------------- ! ... Chem_solver advances the volumetric mixing ratio @@ -282,6 +283,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & use mo_chm_diags, only : chm_diags, het_diags use perf_mod, only : t_startf, t_stopf use gas_wetdep_opts, only : gas_wetdep_method + use physics_types, only : physics_state use physics_buffer, only : physics_buffer_desc, pbuf_get_field, pbuf_old_tim_idx use infnan, only : nan, assignment(=) use rate_diags, only : rate_diags_calc, rate_diags_o3s_loss @@ -337,6 +339,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & real(r8), intent(out) :: noy_nitrogen_flx(pcols) type(physics_buffer_desc), pointer :: pbuf(:) + type(physics_state), target, intent(in) :: state !----------------------------------------------------------------------- ! ... Local variables @@ -792,8 +795,8 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & !----------------------------------------------------------------- ! ... get calculated photolysis rates from TUV-x !----------------------------------------------------------------- - call tuvx_get_photo_rates( ncol, zm, zi, tfld, ts, invariants, & - vmr, asdir, zen_angle ) + call tuvx_get_photo_rates( state, pbuf, ncol, zm, zi, tfld, ts, & + invariants, vmr, asdir, zen_angle ) do i = 1,phtcnt call outfld( tag_names(i), reaction_rates(:ncol,:,rxt_tag_map(i)), ncol, lchnk ) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 58adb111e3..0a1e41045a 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -46,6 +46,9 @@ module mo_tuvx integer :: index_O2 = 0 ! index for O2 in concentration array integer :: index_O3 = 0 ! index for O3 in concentration array + ! Information needed to access aerosol optical properties + logical :: aerosol_exists = .false. ! indicates whether aerosol optical properties are available + ! TODO how should these paths be set and communicated to this wrapper? character(len=*), parameter :: tuvx_config_path = "tuvx_config.json" character(len=*), parameter :: wavelength_config_path = & @@ -66,7 +69,9 @@ module mo_tuvx ! on the TUV-x wavelength grid real(r8), allocatable :: wavelength_mid_values_(:) ! Working array for mid-point values ! on the TUV-x wavelength grid - real(r8), allocatable :: optics_values_(:,:) ! TEMPORARY FOR DEVELOPMENT + real(r8), allocatable :: optical_depth_(:,:,:) ! (column, vertical level, wavelength) [unitless] + real(r8), allocatable :: single_scattering_albedo_(:,:,:) ! (column, vertical level, wavelength) [unitless] + real(r8), allocatable :: asymmetry_factor_(:,:,:) ! (column, vertical level, wavelength) [unitless] end type tuvx_ptr type(tuvx_ptr), allocatable :: tuvx_ptrs(:) @@ -179,7 +184,6 @@ subroutine tuvx_init( ) is_fixed_O3 = index_O3 > 0 if( .not. is_fixed_O3 ) index_O3 = get_spc_ndx( 'O3' ) - end subroutine tuvx_init !================================================================================================ @@ -206,9 +210,9 @@ end subroutine tuvx_timestep_init !================================================================================================ - subroutine tuvx_get_photo_rates( ncol, height_mid, height_int, temperature_mid, & - surface_temperature, fixed_species_conc, species_vmr, surface_albedo, & - solar_zenith_angle ) + subroutine tuvx_get_photo_rates( state, pbuf, ncol, height_mid, height_int, & + temperature_mid, surface_temperature, fixed_species_conc, species_vmr, & + surface_albedo, solar_zenith_angle ) !----------------------------------------------------------------------- ! ! Purpose: calculate and return photolysis rate constants @@ -218,6 +222,8 @@ subroutine tuvx_get_photo_rates( ncol, height_mid, height_int, temperature_mid, use cam_logfile, only : iulog ! log info output unit use chem_mods, only : gas_pcnst, & ! number of non-fixed species nfs ! number of fixed species + use physics_types, only : physics_state + use physics_buffer, only : physics_buffer_desc use ppgrid, only : pcols ! maximum number of columns use shr_const_mod, only : pi => shr_const_pi use spmd_utils, only : main_task => masterprocid, & @@ -226,6 +232,8 @@ subroutine tuvx_get_photo_rates( ncol, height_mid, height_int, temperature_mid, !----------------------------------------------------------------------- ! Dummy arguments !----------------------------------------------------------------------- + type(physics_state), target, intent(in) :: state + type(physics_buffer_desc), pointer, intent(inout) :: pbuf(:) integer, intent(in) :: ncol ! Number of colums to calculated photolysis for real(r8), intent(in) :: height_mid(pcols,pver) ! height at mid-points (km) real(r8), intent(in) :: height_int(pcols,pver+1) ! height at interfaces (km) @@ -244,6 +252,10 @@ subroutine tuvx_get_photo_rates( ncol, height_mid, height_int, temperature_mid, integer :: i_col ! column index associate( tuvx => tuvx_ptrs( thread_id( ) ) ) + + ! get aerosol optical properties for all columns + call get_aerosol_optical_properties( tuvx, state, pbuf ) + do i_col = 1, ncol ! update grid heights @@ -531,11 +543,20 @@ end function get_cam_radiators subroutine create_updaters( this, grids, profiles, radiators ) !----------------------------------------------------------------------- ! -! Purpose: creates updaters for each grid and profile that CAM will use -! to update TUV-x at each timestep +! Purpose: forms connections between CAM and TUV-x data structures +! +! - creates updaters for each grid and profile that CAM will use +! to update TUV-x at each timestep. +! - allocates working arrays when needed for interpolation between +! grids +! - initializes CAM modules if necessary and sets parameters needed +! for runtime access of CAM data ! !----------------------------------------------------------------------- + use modal_aer_opt, only : modal_aer_opt_init + use ppgrid, only : pcols ! maximum number of columns + use rad_constituents, only : rad_cnst_get_info use musica_assert, only : assert use tuvx_grid, only : grid_t use tuvx_grid_from_host, only : grid_updater_t @@ -561,6 +582,7 @@ subroutine create_updaters( this, grids, profiles, radiators ) class(grid_t), pointer :: height, wavelength class(profile_t), pointer :: host_profile class(radiator_t), pointer :: host_radiator + integer :: n_modes logical :: found ! Grid updaters @@ -576,7 +598,9 @@ subroutine create_updaters( this, grids, profiles, radiators ) allocate( this%wavelength_mid_values_( wavelength%size( ) ) ) ! optical property working array - allocate( this%optics_values_( height%size( ), wavelength%size( ) ) ) + allocate( this%optical_depth_( pcols, height%size( ), wavelength%size( ) ) ) + allocate( this%single_scattering_albedo_( pcols, height%size( ), wavelength%size( ) ) ) + allocate( this%asymmetry_factor_( pcols, height%size( ), wavelength%size( ) ) ) deallocate( height ) deallocate( wavelength ) @@ -615,16 +639,29 @@ subroutine create_updaters( this, grids, profiles, radiators ) ! radiator updaters + ! determine if aerosol optical properties will be available, and if so + ! intialize the aerosol optics module + call rad_cnst_get_info(0, nmodes=n_modes) + if (n_modes > 0) then + aerosol_exists = .true. + call modal_aer_opt_init() + else + ! TODO are there default aerosol optical properties that should be used + ! when an aerosol module is not available? + this%optical_depth_(:,:,:) = 0.0_r8 + this%single_scattering_albedo_(:,:,:) = 0.0_r8 + this%asymmetry_factor_(:,:,:) = 0.0_r8 + end if host_radiator => radiators%get_radiator( "aerosol" ) - this%radiators_( RADIATOR_INDEX_AEROSOL ) = this%core_%get_updater( host_radiator, found ) - call assert( 675200430, found ) - nullify( host_radiator ) + this%radiators_(RADIATOR_INDEX_AEROSOL) = this%core_%get_updater(host_radiator, found) + call assert(675200430, found) + nullify(host_radiator) end subroutine create_updaters !================================================================================================ - subroutine set_heights( this, i_col, ncol, height_mid, height_int) + subroutine set_heights(this, i_col, ncol, height_mid, height_int) !----------------------------------------------------------------------- ! ! Purpose: sets the height values in TUV-x for the given column @@ -711,7 +748,7 @@ subroutine set_temperatures( this, i_col, temperature_mid, surface_temperature ) edges(1) = surface_temperature(i_col) edges(2:pver+1) = temperature_mid(i_col,pver:1:-1) - call this%profiles_( PROFILE_INDEX_TEMPERATURE )%update( edge_values = edges ) + call this%profiles_(PROFILE_INDEX_TEMPERATURE)%update(edge_values = edges) end subroutine set_temperatures @@ -736,8 +773,8 @@ subroutine set_surface_albedo( this, i_col, surface_albedo ) real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) this%wavelength_values_(:) = surface_albedo(i_col) - call this%profiles_( PROFILE_INDEX_ALBEDO )%update( & - edge_values = this%wavelength_values_(:) ) + call this%profiles_(PROFILE_INDEX_ALBEDO)%update( & + edge_values = this%wavelength_values_(:)) end subroutine set_surface_albedo @@ -767,14 +804,14 @@ subroutine set_et_flux( this ) class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator integer :: n_tuvx_bins - n_tuvx_bins = size( this%wavelength_mid_values_ ) + n_tuvx_bins = size(this%wavelength_mid_values_) call rebin( nbins, n_tuvx_bins, we, this%wavelength_edges_, sol_etf, & this%wavelength_mid_values_ ) this%wavelength_values_(1) = this%wavelength_mid_values_(1) this%wavelength_values_(2:n_tuvx_bins+1) = this%wavelength_mid_values_(1:n_tuvx_bins) - call this%profiles_( PROFILE_INDEX_ET_FLUX )%update( & + call this%profiles_(PROFILE_INDEX_ET_FLUX)%update( & mid_point_values = this%wavelength_mid_values_, & - edge_values = this%wavelength_values_ ) + edge_values = this%wavelength_values_) end subroutine set_et_flux @@ -820,8 +857,8 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,indexm) densities(1:pver) = this%height_delta_(1:pver) * km2cm * & sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) - call this%profiles_( PROFILE_INDEX_AIR )%update( & - edge_values = edges, layer_densities = densities ) + call this%profiles_(PROFILE_INDEX_AIR)%update( & + edge_values = edges, layer_densities = densities) ! O2 if( is_fixed_O2 ) then @@ -837,9 +874,9 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species end if densities(1:pver) = this%height_delta_(1:pver) * km2cm * & sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) - call this%profiles_( PROFILE_INDEX_O2 )%update( & + call this%profiles_(PROFILE_INDEX_O2)%update( & edge_values = edges, layer_densities = densities, & - exo_density = 0.0_r8 ) ! TODO how should this be calculated? + exo_density = 0.0_r8) ! TODO how should this be calculated? ! O3 if( is_fixed_O3 ) then @@ -855,17 +892,141 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species end if densities(1:pver) = this%height_delta_(1:pver) * km2cm * & sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) - call this%profiles_( PROFILE_INDEX_O3 )%update( & + call this%profiles_(PROFILE_INDEX_O3)%update( & edge_values = edges, layer_densities = densities, & - exo_density = 0.0_r8 ) ! TODO how should this be calculated? + exo_density = 0.0_r8) ! TODO how should this be calculated? - ! TEMPORARY FOR DEVELOPMENT - aerosols - this%optics_values_(:,:) = 0.01_r8 - call this%radiators_( RADIATOR_INDEX_AEROSOL )%update( & - optical_depths = this%optics_values_ ) + ! aerosols + call this%radiators_(RADIATOR_INDEX_AEROSOL)%update( & + optical_depths = this%optical_depth_(i_col,:,:), & + single_scattering_albedos = this%single_scattering_albedo_(i_col,:,:), & + asymmetry_factors = this%asymmetry_factor_(i_col,:,:)) end subroutine set_radiator_profiles +!================================================================================================ + + subroutine get_aerosol_optical_properties( this, state, pbuf ) +!----------------------------------------------------------------------- +! +! Purpose: updates working arrays of aerosol optical properties for all +! columns from the aerosol package +! +!----------------------------------------------------------------------- + + use aer_rad_props, only : aer_rad_props_sw + use mo_util, only : rebin + use physics_types, only : physics_state + use physics_buffer, only : physics_buffer_desc + use ppgrid, only : pcols ! maximum number of columns + use radconstants, only : nswbands, & ! Number of CAM shortwave radiation bands + get_sw_spectral_boundaries +!----------------------------------------------------------------------- +! Dummy arguments +!----------------------------------------------------------------------- + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + type(physics_state), target, intent(in) :: state + type(physics_buffer_desc), pointer, intent(inout) :: pbuf(:) +!----------------------------------------------------------------------- +! Local variables +!----------------------------------------------------------------------- + real(r8) :: wavelength_edges(nswbands+1) ! CAM radiation wavelength grid edges [nm] + real(r8) :: aer_tau (pcols,0:pver,nswbands) ! aerosol extinction optical depth + real(r8) :: aer_tau_w (pcols,0:pver,nswbands) ! aerosol single scattering albedo * tau + real(r8) :: aer_tau_w_g(pcols,0:pver,nswbands) ! aerosol assymetry parameter * w * tau + real(r8) :: aer_tau_w_f(pcols,0:pver,nswbands) ! aerosol forward scattered fraction * w * tau + real(r8) :: low_bound(nswbands) ! lower bound of CAM wavenumber bins + real(r8) :: high_bound(nswbands) ! upper bound of CAM wavenumber bins + integer :: n_night ! number of night columns + integer :: idx_night(pcols) ! indices of night columns + integer :: n_tuvx_bins ! number of TUV-x wavelength bins + integer :: i_col, i_level ! column and level indices + + ! do nothing if no aerosol module is available + if( .not. aerosol_exists ) return + + ! TODO just assume all daylight columns for now + ! can adjust later if necessary + n_night = 0 + idx_night(:) = 0 + + ! get aerosol optical properties on native CAM radiation grid + call aer_rad_props_sw(0, state, pbuf, n_night, idx_night, & + aer_tau, aer_tau_w, aer_tau_w_g, aer_tau_w_f) + + ! Convert CAM wavenumber grid to wavelength grid and re-order optics arrays + ! NOTE: CAM wavenumber grid is continuous and increasing, except that the + ! last bin should be moved to the just before the first bin (!?!) + call get_sw_spectral_boundaries(low_bound, high_bound, 'nm') + wavelength_edges(1:nswbands-1) = high_bound(nswbands-1:1:-1) + wavelength_edges(nswbands ) = high_bound(nswbands ) + wavelength_edges(nswbands+1 ) = low_bound( nswbands ) + call reorder_optics_array( aer_tau) + call reorder_optics_array( aer_tau_w) + call reorder_optics_array(aer_tau_w_g) + + ! regrid optical properties to TUV-x wavelength and height grid + ! TODO is this the correct regridding scheme to use? + ! TODO is this the correct mapping to the TUV-x vertical grid? + n_tuvx_bins = size(this%wavelength_mid_values_) + do i_col = 1, pcols + do i_level = 1, pver + call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & + aer_tau(i_col,pver-i_level,:), this%optical_depth_(i_col,i_level,:)) + call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & + aer_tau_w(i_col,pver-i_level,:), this%single_scattering_albedo_(i_col,i_level,:)) + call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & + aer_tau_w_g(i_col,pver-i_level,:), this%asymmetry_factor_(i_col,i_level,:)) + end do + end do + + ! back-calculate the single scattering albedo and asymmetry factor + associate(tau => this%optical_depth_, & + omega => this%single_scattering_albedo_, & + g => this%asymmetry_factor_) + where(omega > 0.0_r8) + g = g / omega + elsewhere + g = 0.0_r8 + end where + where(tau > 0.0_r8) + omega = omega / tau + elsewhere + omega = 0.0_r8 + end where + end associate + + end subroutine get_aerosol_optical_properties + +!================================================================================================ + + subroutine reorder_optics_array(optics_array) +!----------------------------------------------------------------------- +! +! Purpose: Reorders elements of an optical property array for conversion +! from wavenumber to wavelength grid and out-of-order final +! element in CAM wavenumber grid +! +!----------------------------------------------------------------------- + + use ppgrid, only : pcols ! maximum number of columns + use radconstants, only : nswbands ! Number of CAM shortwave radiation bands + +!----------------------------------------------------------------------- +! Dummy arguments +!----------------------------------------------------------------------- + real(r8), intent(inout) :: optics_array(pcols,0:pver,nswbands) ! optics array to reorder + +!----------------------------------------------------------------------- +! Local variables +!----------------------------------------------------------------------- + real(r8) :: working(pcols,0:pver,nswbands) ! working array + + working(:,:,:) = optics_array(:,:,:) + optics_array(:,:,1:nswbands-1) = working(:,:,nswbands-1:1:-1) + + end subroutine reorder_optics_array + !================================================================================================ end module mo_tuvx diff --git a/src/physics/cam/modal_aer_opt.F90 b/src/physics/cam/modal_aer_opt.F90 index 5c95c17840..9ed61aa1a7 100644 --- a/src/physics/cam/modal_aer_opt.F90 +++ b/src/physics/cam/modal_aer_opt.F90 @@ -128,8 +128,14 @@ subroutine modal_aer_opt_init() character(len=10) :: fldname character(len=128) :: lngname + logical, save :: is_initialized = .false. + !---------------------------------------------------------------------------- + ! Check if module has already been initialized + if (is_initialized) return + is_initialized = .true. + rmmin = 0.01e-6_r8 rmmax = 25.e-6_r8 xrmin = log(rmmin) From 80c3479c5697e2087cc47a7b9fa316d5f1ce0a29 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Thu, 10 Nov 2022 15:36:17 -0700 Subject: [PATCH 23/84] output whether using online aerosols; include air scale height for exo values --- src/chemistry/mozart/mo_tuvx.F90 | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 0a1e41045a..f74884ab47 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -115,7 +115,6 @@ subroutine tuvx_init( ) integer :: pack_size, pos, i_core, i_err config_path = tuvx_config_path - if( is_main_task ) call log_initialization( ) #ifndef HAVE_MPI call assert_msg( 113937299, is_main_task, "Multiple tasks present without " & @@ -184,6 +183,8 @@ subroutine tuvx_init( ) is_fixed_O3 = index_O3 > 0 if( .not. is_fixed_O3 ) index_O3 = get_spc_ndx( 'O3' ) + if( is_main_task ) call log_initialization( ) + end subroutine tuvx_init !================================================================================================ @@ -377,6 +378,11 @@ subroutine log_initialization( ) write(iulog,*) " - without OpenMP support" #endif write(iulog,*) " - with configuration file: '"//tuvx_config_path//"'" + if( aerosol_exists ) then + write(iulog,*) " - with on-line aerosols" + else + write(iulog,*) " - without on-line aerosols" + end if end if end subroutine log_initialization @@ -642,7 +648,7 @@ subroutine create_updaters( this, grids, profiles, radiators ) ! determine if aerosol optical properties will be available, and if so ! intialize the aerosol optics module call rad_cnst_get_info(0, nmodes=n_modes) - if (n_modes > 0) then + if (n_modes > 0 .and. .not. aerosol_exists) then aerosol_exists = .true. call modal_aer_opt_init() else @@ -858,7 +864,8 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species densities(1:pver) = this%height_delta_(1:pver) * km2cm * & sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) call this%profiles_(PROFILE_INDEX_AIR)%update( & - edge_values = edges, layer_densities = densities) + edge_values = edges, layer_densities = densities, & + scale_height = 8.01_r8 ) ! scale height in [km] ! O2 if( is_fixed_O2 ) then From 5d89cb7573ec188584f964e96596281411c6a7d2 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Thu, 10 Nov 2022 16:10:22 -0700 Subject: [PATCH 24/84] send above-column O2 and O3 concentrations to TUV-x --- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 3 +- src/chemistry/mozart/mo_tuvx.F90 | 39 ++++++++++++++------ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index 8f2e3209ad..4252fc8318 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -796,7 +796,8 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & ! ... get calculated photolysis rates from TUV-x !----------------------------------------------------------------- call tuvx_get_photo_rates( state, pbuf, ncol, zm, zi, tfld, ts, & - invariants, vmr, asdir, zen_angle ) + invariants, vmr, col_delta, asdir, & + zen_angle ) do i = 1,phtcnt call outfld( tag_names(i), reaction_rates(:ncol,:,rxt_tag_map(i)), ncol, lchnk ) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index f74884ab47..e05c364e1a 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -213,7 +213,7 @@ end subroutine tuvx_timestep_init subroutine tuvx_get_photo_rates( state, pbuf, ncol, height_mid, height_int, & temperature_mid, surface_temperature, fixed_species_conc, species_vmr, & - surface_albedo, solar_zenith_angle ) + exo_column_conc, surface_albedo, solar_zenith_angle ) !----------------------------------------------------------------------- ! ! Purpose: calculate and return photolysis rate constants @@ -222,7 +222,8 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, height_mid, height_int, & use cam_logfile, only : iulog ! log info output unit use chem_mods, only : gas_pcnst, & ! number of non-fixed species - nfs ! number of fixed species + nfs, & ! number of fixed species + nabscol ! number of absorbing species (radiators) use physics_types, only : physics_state use physics_buffer, only : physics_buffer_desc use ppgrid, only : pcols ! maximum number of columns @@ -240,10 +241,12 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, height_mid, height_int, & real(r8), intent(in) :: height_int(pcols,pver+1) ! height at interfaces (km) real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) - real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities - ! (molecule cm-3) - real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing - ! ratios (mol mol-1) + real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities + ! (molecule cm-3) + real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) + real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! above column densities + ! (molecule cm-3) real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) real(r8), intent(in) :: solar_zenith_angle(ncol) ! solar zenith angle (radians) @@ -265,7 +268,8 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, height_mid, height_int, & ! set conditions for this column in TUV-x call set_temperatures( tuvx, i_col, temperature_mid, surface_temperature ) call set_surface_albedo( tuvx, i_col, surface_albedo ) - call set_radiator_profiles( tuvx, i_col, ncol, fixed_species_conc, species_vmr ) + call set_radiator_profiles( tuvx, i_col, ncol, fixed_species_conc, & + species_vmr, exo_column_conc ) ! Calculate photolysis rate constants for this column call tuvx%core_%run( solar_zenith_angle = & @@ -823,7 +827,8 @@ end subroutine set_et_flux !================================================================================================ - subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species_vmr ) + subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species_vmr, & + exo_column_conc ) !----------------------------------------------------------------------- ! ! Purpose: sets the profiles of optically active atmospheric constituents @@ -833,12 +838,13 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species ! mapping. ! ! Above layer densities are calculated using a scale height for air -! and ... TODO fill in description for O2, O3 +! and pre-calculated values for O2 and O3 ! !----------------------------------------------------------------------- use chem_mods, only : gas_pcnst, & ! number of non-fixed species nfs, & ! number of fixed species + nabscol, & ! number of absorbing species (radiators) indexm ! index for air density in fixed species array !----------------------------------------------------------------------- @@ -851,12 +857,15 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species ! (molecule cm-3) real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing ! ratios (mol mol-1) + real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! above column densities + ! (molecule cm-3) !----------------------------------------------------------------------- ! Local variables !----------------------------------------------------------------------- real(r8) :: edges(pver+1), densities(pver) - real(r8) :: km2cm = 1.0e5 ! conversion from km to cm + real(r8) :: exo_val + real(r8), parameter :: km2cm = 1.0e5 ! conversion from km to cm ! air edges(1) = fixed_species_conc(i_col,pver,indexm) @@ -879,11 +888,12 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species else edges(:) = 0.0_r8 end if + exo_val = exo_column_conc(i_col,0,1) densities(1:pver) = this%height_delta_(1:pver) * km2cm * & sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) call this%profiles_(PROFILE_INDEX_O2)%update( & edge_values = edges, layer_densities = densities, & - exo_density = 0.0_r8) ! TODO how should this be calculated? + exo_density = exo_val) ! O3 if( is_fixed_O3 ) then @@ -897,11 +907,16 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species else edges(:) = 0.0_r8 end if + if( nabscol >= 2 ) then + exo_val = exo_column_conc(i_col,0,2) + else + exo_val = 0.0_r8 + end if densities(1:pver) = this%height_delta_(1:pver) * km2cm * & sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) call this%profiles_(PROFILE_INDEX_O3)%update( & edge_values = edges, layer_densities = densities, & - exo_density = 0.0_r8) ! TODO how should this be calculated? + exo_density = exo_val) ! aerosols call this%radiators_(RADIATOR_INDEX_AEROSOL)%update( & From 422aba71b58279696a539b1eaa39cb709624a46a Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Fri, 11 Nov 2022 16:54:32 -0700 Subject: [PATCH 25/84] add diagnostic TUV-x output; set wavelength edges; fix et flux values --- cime_config/temp_tuvx_config.json | 78 ++++++++++- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 6 +- src/chemistry/mozart/mo_tuvx.F90 | 132 ++++++++++++++++--- 3 files changed, 196 insertions(+), 20 deletions(-) diff --git a/cime_config/temp_tuvx_config.json b/cime_config/temp_tuvx_config.json index 70a3767370..4ebfbf4777 100644 --- a/cime_config/temp_tuvx_config.json +++ b/cime_config/temp_tuvx_config.json @@ -53,7 +53,28 @@ ] }, "photolysis reactions": { - "O2+hv->O+O": { + "jo3_a": { + "__reaction": "O3+hv->O2+O(1D)", + "cross section": { + "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(1D)" + } + }, + "jo3_b": { + "__reaction": "O3+hv->O2+O(3P)", + "cross section": { + "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(3P)" + } + }, + "jo2_b": { + "__reaction": "O2+hv->O+O", "cross section": { "netcdf files": ["data/cross_sections/O2_1.nc"], "type": "base", @@ -64,5 +85,60 @@ "constant value": 1.0 } }, + "jno2": { + "__reaction": "NO2+hv->NO+O(3P)", + "cross section": { + "netcdf files": ["data/cross_sections/NO2_1.nc"], + "type": "NO2 tint" + }, + "quantum yield": { + "netcdf files": ["data/quantum_yields/NO2_1.nc"], + "type": "NO2 tint", + "lower extrapolation": { "type": "boundary" } + } + }, + "jacet": { + "__reaction": "CH3COCH3+hv->CH3CO+CH3", + "cross section": { + "netcdf files": ["data/cross_sections/CH3COCH3_1.nc"], + "type": "CH3COCH3+hv->CH3CO+CH3" + }, + "quantum yield": { + "type": "CH3COCH3+hv->CH3CO+CH3" + } + }, + "jcfcl3": { + "__reaction": "CCl3F+hv->Products", + "cross section": { + "netcdf files": ["data/cross_sections/CFC-11_1.nc"], + "type": "CCl3F+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + "jpan1": { + "__reaction": "PAN+hv->CH3CO(OO)+NO2", + "cross section": { + "netcdf files": ["data/cross_sections/PAN_1.nc"], + "type": "CH3ONO2+hv->CH3O+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 0.7 + } + }, + "jpan2": { + "__reaction": "PAN+hv->CH3CO(O)+NO3", + "cross section": { + "netcdf files": ["data/cross_sections/PAN_1.nc"], + "type": "CH3ONO2+hv->CH3O+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 0.3 + } + } } } diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index 4252fc8318..9afaebf556 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -795,9 +795,9 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & !----------------------------------------------------------------- ! ... get calculated photolysis rates from TUV-x !----------------------------------------------------------------- - call tuvx_get_photo_rates( state, pbuf, ncol, zm, zi, tfld, ts, & - invariants, vmr, col_delta, asdir, & - zen_angle ) + call tuvx_get_photo_rates( state, pbuf, ncol, lchnk, zm, zi, & + tfld, ts, invariants, vmr, col_delta, & + asdir, zen_angle ) do i = 1,phtcnt call outfld( tag_names(i), reaction_rates(:ncol,:,rxt_tag_map(i)), ncol, lchnk ) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index e05c364e1a..4b9b8c91cd 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -53,12 +53,14 @@ module mo_tuvx character(len=*), parameter :: tuvx_config_path = "tuvx_config.json" character(len=*), parameter :: wavelength_config_path = & "data/grids/wavelength/combined.grid" + logical, parameter :: enable_diagnostics = .true. + ! TUV-x calculator for each OMP thread type :: tuvx_ptr type(core_t), pointer :: core_ => null( ) ! TUV-x calculator integer :: n_photo_rates_ ! number of photo reactions in TUV-x - real(r8), allocatable :: photo_rates_(:,:) ! photolysis rate constants - ! (vertical level, reaction) [s-1] + real(r8), allocatable :: photo_rates_(:,:,:) ! photolysis rate constants + ! (column, vertical level, reaction) [s-1] type(grid_updater_t) :: grids_(NUM_GRIDS) ! grid updaters type(profile_updater_t) :: profiles_(NUM_PROFILES) ! profile updaters type(radiator_updater_t) :: radiators_(NUM_RADIATORS) ! radiator updaters @@ -75,6 +77,13 @@ module mo_tuvx end type tuvx_ptr type(tuvx_ptr), allocatable :: tuvx_ptrs(:) + ! Diagnostic photolysis rate constant output + type :: diagnostic_t + character(len=:), allocatable :: name_ ! Name of the output field + integer :: index_ ! index of the photolysis rate constant from TUV-x + end type diagnostic_t + type(diagnostic_t), allocatable :: diagnostics(:) + !================================================================================================ contains !================================================================================================ @@ -89,18 +98,20 @@ subroutine tuvx_init( ) #ifdef HAVE_MPI use mpi #endif - use cam_logfile, only : iulog + use cam_logfile, only : iulog ! log file output unit use mo_chem_utls, only : get_spc_ndx, get_inv_ndx use musica_assert, only : assert_msg use musica_mpi, only : musica_mpi_rank use musica_string, only : string_t, to_char + use ppgrid, only : pcols ! maximum number of columns + use solar_irrad_data, only : has_spectrum + use spmd_utils, only : main_task => masterprocid, & + is_main_task => masterproc, & + mpicom use tuvx_grid, only : grid_t use tuvx_grid_warehouse, only : grid_warehouse_t use tuvx_profile_warehouse, only : profile_warehouse_t use tuvx_radiator_warehouse, only : radiator_warehouse_t - use spmd_utils, only : main_task => masterprocid, & - is_main_task => masterproc, & - mpicom !----------------------------------------------------------------------- ! Local variables @@ -165,7 +176,7 @@ subroutine tuvx_init( ) ! TEMPORARY FOR DEVELOPMENT tuvx%n_photo_rates_ = tuvx%core_%number_of_photolysis_reactions( ) height => tuvx%core_%get_grid( "height", "km" ) - allocate( tuvx%photo_rates_( height%ncells_ + 1, tuvx%n_photo_rates_ ) ) + allocate( tuvx%photo_rates_( pcols, height%ncells_ + 1, tuvx%n_photo_rates_ ) ) deallocate( height ) end associate @@ -183,6 +194,13 @@ subroutine tuvx_init( ) is_fixed_O3 = index_O3 > 0 if( .not. is_fixed_O3 ) index_O3 = get_spc_ndx( 'O3' ) + ! make sure extraterrestrial flux values are available + call assert_msg( 170693514, has_spectrum, & + "Solar irradiance spectrum needed for TUV-x" ) + + ! set up diagnostic output of photolysis rates + call initialize_diagnostics( tuvx_ptrs( 1 ) ) + if( is_main_task ) call log_initialization( ) end subroutine tuvx_init @@ -211,9 +229,9 @@ end subroutine tuvx_timestep_init !================================================================================================ - subroutine tuvx_get_photo_rates( state, pbuf, ncol, height_mid, height_int, & - temperature_mid, surface_temperature, fixed_species_conc, species_vmr, & - exo_column_conc, surface_albedo, solar_zenith_angle ) + subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & + height_int, temperature_mid, surface_temperature, fixed_species_conc, & + species_vmr, exo_column_conc, surface_albedo, solar_zenith_angle ) !----------------------------------------------------------------------- ! ! Purpose: calculate and return photolysis rate constants @@ -236,7 +254,8 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, height_mid, height_int, & !----------------------------------------------------------------------- type(physics_state), target, intent(in) :: state type(physics_buffer_desc), pointer, intent(inout) :: pbuf(:) - integer, intent(in) :: ncol ! Number of colums to calculated photolysis for + integer, intent(in) :: ncol ! number of active columns on this thread + integer, intent(in) :: lchnk ! identifier for this thread real(r8), intent(in) :: height_mid(pcols,pver) ! height at mid-points (km) real(r8), intent(in) :: height_int(pcols,pver+1) ! height at interfaces (km) real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) @@ -274,9 +293,13 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, height_mid, height_int, & ! Calculate photolysis rate constants for this column call tuvx%core_%run( solar_zenith_angle = & solar_zenith_angle(i_col) * 180.0_r8 / pi, & - photolysis_rate_constants = tuvx%photo_rates_ ) + photolysis_rate_constants = & + tuvx%photo_rates_(i_col,:,:) ) end do + + call output_diagnostics( tuvx, ncol, lchnk ) + end associate end subroutine tuvx_get_photo_rates @@ -391,6 +414,80 @@ subroutine log_initialization( ) end subroutine log_initialization +!================================================================================================ + + subroutine initialize_diagnostics( this ) +!----------------------------------------------------------------------- +! +! Purpose: registers fields for diagnostic output +! +!----------------------------------------------------------------------- + + use cam_history, only : addfld + use musica_string, only : string_t + +!----------------------------------------------------------------------- +! Dummy arguments +!----------------------------------------------------------------------- + type(tuvx_ptr), intent(in) :: this + +!----------------------------------------------------------------------- +! Local variables +!----------------------------------------------------------------------- + type(string_t), allocatable :: labels(:) + integer :: i_label + + if( .not. enable_diagnostics ) then + allocate( diagnostics( 0 ) ) + return + end if + + ! add output for specific photolysis reaction rate constants + labels = this%core_%photolysis_reaction_labels( ) + allocate( diagnostics( size( labels ) ) ) + do i_label = 1, size( labels ) + diagnostics( i_label )%name_ = trim( labels( i_label )%to_char( ) ) + diagnostics( i_label )%index_ = i_label + call addfld( "tuvx_"//diagnostics( i_label )%name_, (/ 'lev' /), 'A', 'sec-1', & + 'photolysis rate constant' ) + end do + + end subroutine initialize_diagnostics + +!================================================================================================ + + subroutine output_diagnostics( this, ncol, lchnk ) +!----------------------------------------------------------------------- +! +! Purpose: outputs diagnostic information for the current time step +! +!----------------------------------------------------------------------- + + use cam_history, only : outfld + +!----------------------------------------------------------------------- +! Dummy arguments +!----------------------------------------------------------------------- + type(tuvx_ptr), intent(in) :: this + integer, intent(in) :: ncol ! number of active columns on this thread + integer, intent(in) :: lchnk ! identifier for this thread + +!----------------------------------------------------------------------- +! Local variables +!----------------------------------------------------------------------- + integer :: i_diag + + if( .not. enable_diagnostics ) return + + do i_diag = 1, size( diagnostics ) + associate( diag => diagnostics( i_diag ) ) + call outfld( "tuvx_"//diag%name_, this%photo_rates_(:ncol,:,diag%index_), & + ncol, lchnk ) + end associate + end do + + end subroutine output_diagnostics + !================================================================================================ function get_cam_grids( wavelength_path ) result( grids ) @@ -606,6 +703,7 @@ subroutine create_updaters( this, grids, profiles, radiators ) allocate( this%wavelength_edges_( wavelength%size( ) + 1 ) ) allocate( this%wavelength_values_( wavelength%size( ) + 1 ) ) allocate( this%wavelength_mid_values_( wavelength%size( ) ) ) + this%wavelength_edges_(:) = wavelength%edge_(:) ! optical property working array allocate( this%optical_depth_( pcols, height%size( ), wavelength%size( ) ) ) @@ -796,7 +894,8 @@ subroutine set_et_flux( this ) ! Purpose: sets the extraterrestrial flux in TUV-x for the given column ! ! Extraterrestrial flux is read from data files and interpolated to the -! TUV-x wavelength grid. +! TUV-x wavelength grid. ET flux values are multiplied by wavelength +! bin widths to get units used in TUV-x (photon cm-2 s-1) ! ! NOTE: TUV-x only uses mid-point values for ET Flux ! @@ -805,17 +904,18 @@ subroutine set_et_flux( this ) use mo_util, only : rebin use solar_irrad_data, only : nbins, & ! number of wavelength bins we, & ! wavelength bin edges - ! TODO are these units correct? if so, they don't match expected (photon cm-2 s-1) sol_etf ! solar extraterrestrial flux (photon cm-2 nm-1 s-1) !----------------------------------------------------------------------- ! Dummy arguments !----------------------------------------------------------------------- class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator - integer :: n_tuvx_bins + real(r8) :: et_flux_orig(nbins) + integer :: n_tuvx_bins + et_flux_orig(:) = sol_etf(:) * ( we(2:nbins+1) - we(1:nbins) ) n_tuvx_bins = size(this%wavelength_mid_values_) - call rebin( nbins, n_tuvx_bins, we, this%wavelength_edges_, sol_etf, & + call rebin( nbins, n_tuvx_bins, we, this%wavelength_edges_, et_flux_orig, & this%wavelength_mid_values_ ) this%wavelength_values_(1) = this%wavelength_mid_values_(1) this%wavelength_values_(2:n_tuvx_bins+1) = this%wavelength_mid_values_(1:n_tuvx_bins) From 147e15ac15d4f0136a907507bdc6a3338a06e653 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Mon, 14 Nov 2022 15:02:00 -0700 Subject: [PATCH 26/84] pass earth sun distance to TUV-x --- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 2 +- src/chemistry/mozart/mo_tuvx.F90 | 25 +++++++++++--------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index 9afaebf556..49d0caf027 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -797,7 +797,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & !----------------------------------------------------------------- call tuvx_get_photo_rates( state, pbuf, ncol, lchnk, zm, zi, & tfld, ts, invariants, vmr, col_delta, & - asdir, zen_angle ) + asdir, zen_angle, esfact ) do i = 1,phtcnt call outfld( tag_names(i), reaction_rates(:ncol,:,rxt_tag_map(i)), ncol, lchnk ) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 4b9b8c91cd..cf2823963d 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -231,7 +231,8 @@ end subroutine tuvx_timestep_init subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & height_int, temperature_mid, surface_temperature, fixed_species_conc, & - species_vmr, exo_column_conc, surface_albedo, solar_zenith_angle ) + species_vmr, exo_column_conc, surface_albedo, solar_zenith_angle, & + earth_sun_distance ) !----------------------------------------------------------------------- ! ! Purpose: calculate and return photolysis rate constants @@ -268,7 +269,7 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & ! (molecule cm-3) real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) real(r8), intent(in) :: solar_zenith_angle(ncol) ! solar zenith angle (radians) - + real(r8), intent(in) :: earth_sun_distance ! Earth-Sun distance (AU) !----------------------------------------------------------------------- ! Local variables !----------------------------------------------------------------------- @@ -293,6 +294,7 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & ! Calculate photolysis rate constants for this column call tuvx%core_%run( solar_zenith_angle = & solar_zenith_angle(i_col) * 180.0_r8 / pi, & + earth_sun_distance = earth_sun_distance, & photolysis_rate_constants = & tuvx%photo_rates_(i_col,:,:) ) @@ -894,8 +896,8 @@ subroutine set_et_flux( this ) ! Purpose: sets the extraterrestrial flux in TUV-x for the given column ! ! Extraterrestrial flux is read from data files and interpolated to the -! TUV-x wavelength grid. ET flux values are multiplied by wavelength -! bin widths to get units used in TUV-x (photon cm-2 s-1) +! TUV-x wavelength grid. CAM ET Flux values are multiplied by the +! width of the wavelength bins to get the TUV-x units of photon cm-2 s-1 ! ! NOTE: TUV-x only uses mid-point values for ET Flux ! @@ -904,7 +906,8 @@ subroutine set_et_flux( this ) use mo_util, only : rebin use solar_irrad_data, only : nbins, & ! number of wavelength bins we, & ! wavelength bin edges - sol_etf ! solar extraterrestrial flux (photon cm-2 nm-1 s-1) + sol_etf ! extraterrestrial flux + ! (photon cm-2 nm-1 s-1) !----------------------------------------------------------------------- ! Dummy arguments @@ -953,12 +956,12 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator integer, intent(in) :: i_col ! column to set conditions for integer, intent(in) :: ncol ! number of columns - real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities - ! (molecule cm-3) - real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing - ! ratios (mol mol-1) - real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! above column densities - ! (molecule cm-3) + real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities + ! (molecule cm-3) + real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) + real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! above column densities + ! (molecule cm-3) !----------------------------------------------------------------------- ! Local variables From 7854fdea2c8a019c04c0e4220caaf309c3495b3f Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Mon, 14 Nov 2022 15:18:09 -0700 Subject: [PATCH 27/84] minor clean up --- src/chemistry/mozart/mo_tuvx.F90 | 62 ++++++++++++++++---------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index cf2823963d..e369cb1906 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -129,7 +129,7 @@ subroutine tuvx_init( ) #ifndef HAVE_MPI call assert_msg( 113937299, is_main_task, "Multiple tasks present without " & - //"MPI support enabled for TUV-x") + //"MPI support enabled for TUV-x" ) #endif ! Create the set of TUV-x grids and profiles that CAM will update at runtime @@ -751,10 +751,10 @@ subroutine create_updaters( this, grids, profiles, radiators ) ! determine if aerosol optical properties will be available, and if so ! intialize the aerosol optics module - call rad_cnst_get_info(0, nmodes=n_modes) - if (n_modes > 0 .and. .not. aerosol_exists) then + call rad_cnst_get_info( 0, nmodes = n_modes ) + if( n_modes > 0 .and. .not. aerosol_exists ) then aerosol_exists = .true. - call modal_aer_opt_init() + call modal_aer_opt_init( ) else ! TODO are there default aerosol optical properties that should be used ! when an aerosol module is not available? @@ -763,15 +763,16 @@ subroutine create_updaters( this, grids, profiles, radiators ) this%asymmetry_factor_(:,:,:) = 0.0_r8 end if host_radiator => radiators%get_radiator( "aerosol" ) - this%radiators_(RADIATOR_INDEX_AEROSOL) = this%core_%get_updater(host_radiator, found) - call assert(675200430, found) - nullify(host_radiator) + this%radiators_( RADIATOR_INDEX_AEROSOL ) = & + this%core_%get_updater( host_radiator, found ) + call assert( 675200430, found ) + nullify( host_radiator ) end subroutine create_updaters !================================================================================================ - subroutine set_heights(this, i_col, ncol, height_mid, height_int) + subroutine set_heights( this, i_col, ncol, height_mid, height_int ) !----------------------------------------------------------------------- ! ! Purpose: sets the height values in TUV-x for the given column @@ -858,7 +859,7 @@ subroutine set_temperatures( this, i_col, temperature_mid, surface_temperature ) edges(1) = surface_temperature(i_col) edges(2:pver+1) = temperature_mid(i_col,pver:1:-1) - call this%profiles_(PROFILE_INDEX_TEMPERATURE)%update(edge_values = edges) + call this%profiles_( PROFILE_INDEX_TEMPERATURE )%update( edge_values = edges ) end subroutine set_temperatures @@ -882,9 +883,9 @@ subroutine set_surface_albedo( this, i_col, surface_albedo ) integer, intent(in) :: i_col ! column to set conditions for real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) - this%wavelength_values_(:) = surface_albedo(i_col) - call this%profiles_(PROFILE_INDEX_ALBEDO)%update( & - edge_values = this%wavelength_values_(:)) + this%wavelength_values_(:) = surface_albedo( i_col ) + call this%profiles_( PROFILE_INDEX_ALBEDO )%update( & + edge_values = this%wavelength_values_(:) ) end subroutine set_surface_albedo @@ -922,7 +923,7 @@ subroutine set_et_flux( this ) this%wavelength_mid_values_ ) this%wavelength_values_(1) = this%wavelength_mid_values_(1) this%wavelength_values_(2:n_tuvx_bins+1) = this%wavelength_mid_values_(1:n_tuvx_bins) - call this%profiles_(PROFILE_INDEX_ET_FLUX)%update( & + call this%profiles_( PROFILE_INDEX_ET_FLUX )%update( & mid_point_values = this%wavelength_mid_values_, & edge_values = this%wavelength_values_) @@ -975,7 +976,7 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,indexm) densities(1:pver) = this%height_delta_(1:pver) * km2cm * & sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) - call this%profiles_(PROFILE_INDEX_AIR)%update( & + call this%profiles_( PROFILE_INDEX_AIR )%update( & edge_values = edges, layer_densities = densities, & scale_height = 8.01_r8 ) ! scale height in [km] @@ -994,9 +995,9 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species exo_val = exo_column_conc(i_col,0,1) densities(1:pver) = this%height_delta_(1:pver) * km2cm * & sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) - call this%profiles_(PROFILE_INDEX_O2)%update( & + call this%profiles_( PROFILE_INDEX_O2 )%update( & edge_values = edges, layer_densities = densities, & - exo_density = exo_val) + exo_density = exo_val ) ! O3 if( is_fixed_O3 ) then @@ -1017,15 +1018,15 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species end if densities(1:pver) = this%height_delta_(1:pver) * km2cm * & sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) - call this%profiles_(PROFILE_INDEX_O3)%update( & + call this%profiles_( PROFILE_INDEX_O3 )%update( & edge_values = edges, layer_densities = densities, & - exo_density = exo_val) + exo_density = exo_val ) ! aerosols - call this%radiators_(RADIATOR_INDEX_AEROSOL)%update( & + call this%radiators_( RADIATOR_INDEX_AEROSOL )%update( & optical_depths = this%optical_depth_(i_col,:,:), & single_scattering_albedos = this%single_scattering_albedo_(i_col,:,:), & - asymmetry_factors = this%asymmetry_factor_(i_col,:,:)) + asymmetry_factors = this%asymmetry_factor_(i_col,:,:) ) end subroutine set_radiator_profiles @@ -1076,23 +1077,22 @@ subroutine get_aerosol_optical_properties( this, state, pbuf ) idx_night(:) = 0 ! get aerosol optical properties on native CAM radiation grid - call aer_rad_props_sw(0, state, pbuf, n_night, idx_night, & - aer_tau, aer_tau_w, aer_tau_w_g, aer_tau_w_f) + call aer_rad_props_sw( 0, state, pbuf, n_night, idx_night, & + aer_tau, aer_tau_w, aer_tau_w_g, aer_tau_w_f ) ! Convert CAM wavenumber grid to wavelength grid and re-order optics arrays ! NOTE: CAM wavenumber grid is continuous and increasing, except that the ! last bin should be moved to the just before the first bin (!?!) - call get_sw_spectral_boundaries(low_bound, high_bound, 'nm') + call get_sw_spectral_boundaries( low_bound, high_bound, 'nm' ) wavelength_edges(1:nswbands-1) = high_bound(nswbands-1:1:-1) wavelength_edges(nswbands ) = high_bound(nswbands ) wavelength_edges(nswbands+1 ) = low_bound( nswbands ) - call reorder_optics_array( aer_tau) - call reorder_optics_array( aer_tau_w) - call reorder_optics_array(aer_tau_w_g) + call reorder_optics_array( aer_tau ) + call reorder_optics_array( aer_tau_w ) + call reorder_optics_array( aer_tau_w_g ) ! regrid optical properties to TUV-x wavelength and height grid ! TODO is this the correct regridding scheme to use? - ! TODO is this the correct mapping to the TUV-x vertical grid? n_tuvx_bins = size(this%wavelength_mid_values_) do i_col = 1, pcols do i_level = 1, pver @@ -1106,9 +1106,9 @@ subroutine get_aerosol_optical_properties( this, state, pbuf ) end do ! back-calculate the single scattering albedo and asymmetry factor - associate(tau => this%optical_depth_, & - omega => this%single_scattering_albedo_, & - g => this%asymmetry_factor_) + associate( tau => this%optical_depth_, & + omega => this%single_scattering_albedo_, & + g => this%asymmetry_factor_ ) where(omega > 0.0_r8) g = g / omega elsewhere @@ -1125,7 +1125,7 @@ end subroutine get_aerosol_optical_properties !================================================================================================ - subroutine reorder_optics_array(optics_array) + subroutine reorder_optics_array( optics_array ) !----------------------------------------------------------------------- ! ! Purpose: Reorders elements of an optical property array for conversion From f8be1bb99bd7e32c08886dfee9f97b5b7730189d Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Mon, 14 Nov 2022 15:29:16 -0700 Subject: [PATCH 28/84] update externals file --- Externals_CAM.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Externals_CAM.cfg b/Externals_CAM.cfg index 00932e324c..226be94332 100644 --- a/Externals_CAM.cfg +++ b/Externals_CAM.cfg @@ -73,7 +73,7 @@ required = True local_path = libraries/tuv-x protocol = git repo_url = https://github.com/NCAR/tuv-x.git -branch = develop-160-cesm-build +branch = main required = True [json-fortran] From 13e030fe8f52ed26d3e868bc124ef71fa3a60513 Mon Sep 17 00:00:00 2001 From: Francis Vitt Date: Tue, 15 Nov 2022 11:49:56 -0700 Subject: [PATCH 29/84] Tuvx options (#5) * implement tuvx namelist options modified: Externals_CAM.cfg modified: bld/namelist_files/namelist_definition.xml modified: cime_config/buildlib modified: src/chemistry/mozart/chemistry.F90 modified: src/chemistry/mozart/mo_gas_phase_chemdr.F90 modified: src/chemistry/mozart/mo_tuvx.F90 * update tuvx config script; adjust tuvx build Co-authored-by: Matthew Dawson --- Externals_CAM.cfg | 2 +- bld/namelist_files/namelist_definition.xml | 11 +++ cime_config/buildlib | 13 ++- cime_config/temp_tuvx_config.json | 29 ++++--- src/chemistry/mozart/chemistry.F90 | 6 ++ src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 31 ++++---- src/chemistry/mozart/mo_tuvx.F90 | 84 ++++++++++++++++---- 7 files changed, 133 insertions(+), 43 deletions(-) diff --git a/Externals_CAM.cfg b/Externals_CAM.cfg index 00932e324c..6367169bf2 100644 --- a/Externals_CAM.cfg +++ b/Externals_CAM.cfg @@ -73,7 +73,7 @@ required = True local_path = libraries/tuv-x protocol = git repo_url = https://github.com/NCAR/tuv-x.git -branch = develop-160-cesm-build +hash = c96cf5f required = True [json-fortran] diff --git a/bld/namelist_files/namelist_definition.xml b/bld/namelist_files/namelist_definition.xml index d371f975f9..5558c07d01 100644 --- a/bld/namelist_files/namelist_definition.xml +++ b/bld/namelist_files/namelist_definition.xml @@ -7052,6 +7052,17 @@ Maximum zenith angle (degrees) used for photolysis. Default: set by build-namelist. + +Filepath of TUV-X configuration specification. +Default: NONE + + + +Switch to turn on TUV-X photolysis. +Default: FALSE + diff --git a/cime_config/buildlib b/cime_config/buildlib index e90cf5000d..20333519bc 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -42,7 +42,7 @@ def _build_fms(caseroot, libroot, bldroot): expect(os.path.exists(fmsbuildlib), "FMS external not found") stat, _, err = run_cmd("{} {} {} {}".format(fmsbuildlib, case.get_value("EXEROOT"), fmsbuilddir, caseroot), verbose=True) expect(stat==0, "FMS build Failed {}".format(err)) - + libfms = os.path.join(bldroot,"FMS","libfms.a") if os.path.exists(libfms): shutil.copy(libfms, libroot) @@ -130,9 +130,12 @@ def _cmake_default_args(caseroot): args += "-DCOMPILER={} ".format(build.get_value("COMPILER")) args += "-DOS={} ".format(build.get_value("OS")) args += "-DMACH={} ".format(case.get_value("MACH")) + args += "-DCMAKE_C_COMPILER_WORKS=1 " + args += "-DCMAKE_Fortran_COMPILER_WORKS=1 " + args += "-DCMAKE_CXX_COMPILER_WORKS=1 " cmd = "cmake {} .".format(args) rc, out, err = run_cmd(cmd, combine_output=True, from_dir=macro_path) - expect(rc == 0, "Command {} failed with rc={}".format(cmd, rc)) + expect(rc == 0, "Command {} failed with rc={} out={} err={}".format(cmd, rc, out, err)) arg_dict = {} for line in out.splitlines(): @@ -158,6 +161,8 @@ def _build_json_fortran(caseroot, libroot, bldroot): arg_dict = _cmake_default_args(caseroot) cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["SFC"]) + cmake_args += "-DCMAKE_C_COMPILER_WORKS=1 " + cmake_args += "-DCMAKE_CXX_COMPILER_WORKS=1 " cmake_args += "-DCMAKE_BUILD_TYPE=Release " cmake_args += "-DSKIP_DOC_GEN:BOOL=TRUE " cmake_args += srcpath @@ -201,9 +206,13 @@ def _build_tuvx(caseroot, libroot, bldroot): arg_dict = _cmake_default_args(caseroot) if build.get_value("MPILIB") == "mpi-serial": cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["SFC"]) + cmake_args += "-DCMAKE_C_COMPILER={} ".format(arg_dict["SCC"]) else: cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["MPIFC"]) + cmake_args += "-DCMAKE_C_COMPILER={} ".format(arg_dict["MPICC"]) cmake_args += "-DENABLE_MPI:BOOL=TRUE " + cmake_args += "-DCMAKE_C_COMPILER_WORKS=1 " + cmake_args += "-DCMAKE_CXX_COMPILER_WORKS=1 " cmake_args += "-DCMAKE_BUILD_TYPE=Release " cmake_args += "-DENABLE_COVERAGE=OFF " cmake_args += "-DENABLE_TESTS=OFF " diff --git a/cime_config/temp_tuvx_config.json b/cime_config/temp_tuvx_config.json index 58b0fa6fa0..27113be240 100644 --- a/cime_config/temp_tuvx_config.json +++ b/cime_config/temp_tuvx_config.json @@ -83,6 +83,9 @@ } }, "radiative transfer": { + "solver": { + "type": "delta eddington" + }, "cross sections": [ { "name": "air", @@ -140,17 +143,21 @@ } ] }, - "photolysis reactions": { - "O2+hv->O+O": { - "cross section": { - "netcdf files": ["data/cross_sections/O2_1.nc"], - "type": "base", - "lower extrapolation": { "type": "boundary" } - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 + "photolysis": { + "enable diagnostics": false, + "reactions": [ + { + "name": "O2+hv->O+O", + "cross section": { + "netcdf files": ["data/cross_sections/O2_1.nc"], + "type": "base", + "lower extrapolation": { "type": "boundary" } + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } } - }, + ] } } diff --git a/src/chemistry/mozart/chemistry.F90 b/src/chemistry/mozart/chemistry.F90 index 5b590163e4..a163dfa307 100644 --- a/src/chemistry/mozart/chemistry.F90 +++ b/src/chemistry/mozart/chemistry.F90 @@ -340,6 +340,7 @@ subroutine chem_readnl(nlfile) use mo_sulf, only: sulf_readnl use species_sums_diags,only: species_sums_readnl use ocean_emis, only: ocean_emis_readnl + use mo_tuvx, only: tuvx_readnl ! args @@ -556,6 +557,7 @@ subroutine chem_readnl(nlfile) call sulf_readnl(nlfile) call species_sums_readnl(nlfile) call ocean_emis_readnl(nlfile) + call tuvx_readnl(nlfile) end subroutine chem_readnl @@ -1289,7 +1291,11 @@ end subroutine chem_timestep_tend !------------------------------------------------------------------- subroutine chem_final() use mee_ionization, only: mee_ion_final + use mo_tuvx, only: tuvx_finalize + call mee_ion_final() + call tuvx_finalize() + end subroutine chem_final !------------------------------------------------------------------- diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index 57f9910755..0ed39659ab 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -249,7 +249,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & use chem_mods, only : nabscol, nfs, indexm, clscnt4 use physconst, only : rga use mo_photo, only : set_ub_col, setcol, table_photo - use mo_tuvx, only : tuvx_get_photo_rates + use mo_tuvx, only : tuvx_get_photo_rates, tuvx_active use mo_exp_sol, only : exp_sol use mo_imp_sol, only : imp_sol use mo_setrxt, only : setrxt @@ -779,20 +779,21 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & !----------------------------------------------------------------------- esfact = 1._r8 - call shr_orb_decl( calday, eccen, mvelpp, lambm0, obliqr , & - delta, esfact ) - - !----------------------------------------------------------------- - ! ... lookup the photolysis rates from table - !----------------------------------------------------------------- - call table_photo( reaction_rates, pmid, pdel, tfld, zmid, zint, & - col_dens, zen_angle, asdir, cwat, cldfr, & - esfact, vmr, invariants, ncol, lchnk, pbuf ) - - !----------------------------------------------------------------- - ! ... get calculated photolysis rates from TUV-x - !----------------------------------------------------------------- - call tuvx_get_photo_rates( ncol ) + call shr_orb_decl( calday, eccen, mvelpp, lambm0, obliqr, delta, esfact ) + + !if (tuvx_active) then + !----------------------------------------------------------------- + ! ... get calculated photolysis rates from TUV-x + !----------------------------------------------------------------- + call tuvx_get_photo_rates( ncol ) + !else + !----------------------------------------------------------------- + ! ... lookup the photolysis rates from table + !----------------------------------------------------------------- + call table_photo( reaction_rates, pmid, pdel, tfld, zmid, zint, & + col_dens, zen_angle, asdir, cwat, cldfr, & + esfact, vmr, invariants, ncol, lchnk, pbuf ) + !endif do i = 1,phtcnt call outfld( tag_names(i), reaction_rates(:ncol,:,rxt_tag_map(i)), ncol, lchnk ) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 7322d2e0c4..d186b35909 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -4,16 +4,21 @@ module mo_tuvx !---------------------------------------------------------------------- use musica_string, only : string_t - use shr_kind_mod, only : r8 => shr_kind_r8 + use shr_kind_mod, only : r8 => shr_kind_r8, cl=>shr_kind_cl use tuvx_core, only : core_t + use cam_logfile, only : iulog + use spmd_utils, only : masterproc + use cam_abortutils, only : endrun implicit none private + public :: tuvx_readnl public :: tuvx_init public :: tuvx_get_photo_rates public :: tuvx_finalize + public :: tuvx_active ! TUV-x calculator for each OMP thread type :: tuvx_ptr @@ -24,13 +29,65 @@ module mo_tuvx end type tuvx_ptr type(tuvx_ptr), allocatable :: tuvx_ptrs(:) - ! TODO how should this path be set and communicated to this wrapper? - character(len=*), parameter :: tuvx_config_path = "tuvx_config.json" + ! namelist options + character(len=cl) :: tuvx_config_path = 'NONE' ! absolute path to TUVX configuration file + logical, protected :: tuvx_active = .false. !================================================================================================ contains !================================================================================================ + !----------------------------------------------------------------------- + ! read namelist options + !----------------------------------------------------------------------- + subroutine tuvx_readnl(nlfile) + use namelist_utils, only : find_group_name + use spmd_utils, only : mpicom, masterprocid, mpi_character, mpi_logical + + character(len=*), intent(in) :: nlfile ! filepath for file containing namelist input + + integer :: unitn, ierr + character(len=*), parameter :: subname = 'tuvx_readnl' + + ! =================== + ! Namelist definition + ! =================== + namelist /tuvx_opts/ tuvx_active, tuvx_config_path + + ! ============= + ! Read namelist + ! ============= + if (masterproc) then + open( newunit=unitn, file=trim(nlfile), status='old' ) + call find_group_name(unitn, 'tuvx_opts', status=ierr) + if (ierr == 0) then + read(unitn, tuvx_opts, iostat=ierr) + if (ierr /= 0) then + call endrun(subname // ':: ERROR reading namelist') + end if + end if + close(unitn) + end if + + ! ============================ + ! Broadcast namelist variables + ! ============================ + call mpi_bcast(tuvx_config_path, len(tuvx_config_path), mpi_character, masterprocid, mpicom, ierr) + call mpi_bcast(tuvx_active, 1, mpi_logical, masterprocid, mpicom, ierr) + + if (tuvx_active .and. tuvx_config_path == 'NONE') then + call endrun(subname // ' : must set tuvx_config_path when TUV-X is active') + end if + + if (masterproc) then + write(iulog,*) 'tuvx_readnl: tuvx_config_path = ', trim(tuvx_config_path) + write(iulog,*) 'tuvx_readnl: tuvx_active = ', tuvx_active + end if + + end subroutine tuvx_readnl +!----------------------------------------------------------------------- +!----------------------------------------------------------------------- + subroutine tuvx_init( ) !----------------------------------------------------------------------- ! @@ -38,9 +95,6 @@ subroutine tuvx_init( ) ! !----------------------------------------------------------------------- -#ifdef HAVE_MPI - use mpi -#endif use cam_logfile, only : iulog use musica_assert, only : assert_msg use musica_mpi, only : musica_mpi_rank @@ -48,7 +102,7 @@ subroutine tuvx_init( ) use tuvx_grid, only : grid_t use spmd_utils, only : main_task => masterprocid, & is_main_task => masterproc, & - mpicom + mpicom, mpi_character, mpi_integer, mpi_success !----------------------------------------------------------------------- ! Local variables @@ -59,7 +113,10 @@ subroutine tuvx_init( ) class(grid_t), pointer :: height integer :: pack_size, pos, i_core, i_err, i_thread - config_path = tuvx_config_path + if (.not.tuvx_active) return + + config_path = trim(tuvx_config_path) + if( is_main_task ) call log_initialization( ) #ifndef HAVE_MPI @@ -78,7 +135,6 @@ subroutine tuvx_init( ) end if ! broadcast the core data to all MPI processes -#ifdef HAVE_MPI call mpi_bcast( pack_size, 1, MPI_INTEGER, main_task, mpicom, i_err ) if( i_err /= MPI_SUCCESS ) then write(iulog,*) "TUV-x MPI int bcast error" @@ -90,7 +146,6 @@ subroutine tuvx_init( ) write(iulog,*) "TUV-x MPI char array bcast error" call mpi_abort( mpicom, 1, i_err ) end if -#endif ! unpack the core for each OMP thread on every MPI process allocate( tuvx_ptrs( max_threads( ) ) ) @@ -123,7 +178,6 @@ subroutine tuvx_get_photo_rates( ncol ) ! !----------------------------------------------------------------------- - use cam_logfile, only : iulog use spmd_utils, only : main_task => masterprocid, & is_main_task => masterproc, & mpicom @@ -138,6 +192,8 @@ subroutine tuvx_get_photo_rates( ncol ) !----------------------------------------------------------------------- integer :: i_col ! column index + if (.not.tuvx_active) return + associate( tuvx => tuvx_ptrs( thread_id( ) ) ) do i_col = 1, ncol ! Temporary fix SZA for development @@ -163,9 +219,9 @@ subroutine tuvx_finalize( ) if( allocated( tuvx_ptrs ) ) then do i_core = 1, size( tuvx_ptrs ) - if( associated( tuvx_ptrs( i_core )%core_ ) ) then - deallocate( tuvx_ptrs( i_core )%core_ ) - end if + associate( tuvx => tuvx_ptrs( i_core ) ) + if( associated( tuvx%core_ ) ) deallocate( tuvx%core_ ) + end associate end do end if From 996bd91ea1f26ff8ebe23d76302239a7fa90ffee Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Tue, 15 Nov 2022 11:55:18 -0700 Subject: [PATCH 30/84] update for changes in TUV-x --- .gitignore | 1 + Externals_CAM.cfg | 7 ++ cime_config/buildlib | 68 +++++++++++- cime_config/config_component.xml | 2 +- cime_config/temp_tuvx_config.json | 170 ++++++++++++++++-------------- 5 files changed, 164 insertions(+), 84 deletions(-) diff --git a/.gitignore b/.gitignore index 42d902494c..39fd22862a 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ libraries/FMS libraries/mct libraries/parallelio libraries/tuv-x +libraries/musica-core libraries/json-fortran src/atmos_phys src/dynamics/mpas/dycore diff --git a/Externals_CAM.cfg b/Externals_CAM.cfg index 226be94332..9156837aef 100644 --- a/Externals_CAM.cfg +++ b/Externals_CAM.cfg @@ -76,6 +76,13 @@ repo_url = https://github.com/NCAR/tuv-x.git branch = main required = True +[musica-core] +local_path = libraries/musica-core +protocol = git +repo_url = https://github.com/NCAR/musica-core.git +branch = main +required = True + [json-fortran] local_path = libraries/json-fortran protocol = git diff --git a/cime_config/buildlib b/cime_config/buildlib index 4bb47915d8..be963a88fe 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -182,6 +182,61 @@ def _build_json_fortran(caseroot, libroot, bldroot): os.rename(os.path.join(bldroot, "json-fortran", "lib", "libjsonfortran.a"), \ os.path.join(libroot, "libjsonfortran.a")) +############################################################################### +def _build_musica_core(caseroot, libroot, bldroot): +############################################################################### +# Builds the musica-core support library for TUV-x and updates the case +# variables used to set the include paths and linked libraries + + build = EnvBuild(case_root=caseroot) + with Case(caseroot) as case: + bldpath = os.path.join(bldroot, "musica-core") + if not os.path.exists(bldpath): + os.makedirs(bldpath) + jsoninc = os.path.join(bldroot, "json-fortran", "include", "") + expect(os.path.exists(jsoninc), \ + "JSON-Fortran include folder for musica-core build not found at {}".format(jsoninc)) + jsonlib = os.path.join(libroot, "libjsonfortran.a") + expect(os.path.exists(jsonlib), \ + "JSON-Fortran library for musica-core build not found at {}".format(jsonlib)) + srcpath = os.path.abspath(os.path.join(case.get_value("COMP_ROOT_DIR_ATM"), \ + "libraries", "musica-core", "")) + logger.info("Building musica-core in {} from source in {}\n".format(bldpath, srcpath)) + + arg_dict = _cmake_default_args(caseroot) + if build.get_value("MPILIB") == "mpi-serial": + cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["SFC"]) + else: + cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["MPIFC"]) + cmake_args += "-DENABLE_MPI:BOOL=TRUE " + if case.get_value("DEBUG"): + cmake_args += "-DCMAKE_BUILD_TYPE=Debug " + else: + cmake_args += "-DCMAKE_BUILD_TYPE=Release " + cmake_args += "-DENABLE_UTIL_ONLY=ON " + cmake_args += "-DCMAKE_C_COMPILER_WORKS=1 " + cmake_args += "-DCMAKE_CXX_COMPILER_WORKS=1 " + cmake_args += "-DENABLE_COVERAGE=OFF " + cmake_args += "-DJSON_INCLUDE_DIR={} ".format(jsoninc) + cmake_args += "-DJSON_LIB={} ".format(jsonlib) + cmake_args += "-DCMAKE_Fortran_FLAGS='{}' ".format(arg_dict["FFLAGS"]) + cmake_args += srcpath + + _run_cmd("cmake {}".format(cmake_args), bldpath) + _run_cmd(case.get_value("GMAKE"), bldpath) + + # add musica-core to include paths + incldir = os.environ.get('USER_INCLDIR') + if incldir is None: + incldir = '' + os.environ['USER_INCLDIR'] = incldir + \ + " -I{} ".format(os.path.join(bldroot, "musica-core", "include")) + + # The musica-core library is included in the CAM_LINKED_LIBS entry in + # config_component.xml and are staged here to the lib folder + os.rename(os.path.join(bldroot, "musica-core", "lib", "libmusica_core.a"), \ + os.path.join(libroot, "libmusica_core.a")) + ############################################################################### def _build_tuvx(caseroot, libroot, bldroot): ############################################################################### @@ -199,6 +254,12 @@ def _build_tuvx(caseroot, libroot, bldroot): jsonlib = os.path.join(libroot, "libjsonfortran.a") expect(os.path.exists(jsonlib), \ "JSON-Fortran library for TUV-x build not found at {}".format(jsonlib)) + coreinc = os.path.join(bldroot, "musica-core", "include", "") + expect(os.path.exists(coreinc), \ + "musica-core include folder for TUV-x build not found at {}".format(coreinc)) + corelib = os.path.join(libroot, "libmusica_core.a") + expect(os.path.exists(corelib), \ + "musica-core library for TUV-x build not found at {}".format(corelib)) srcpath = os.path.abspath(os.path.join(case.get_value("COMP_ROOT_DIR_ATM"), \ "libraries", "tuv-x", "")) logger.info("Building TUV-x in {} from source in {}\n".format(bldpath, srcpath)) @@ -219,6 +280,8 @@ def _build_tuvx(caseroot, libroot, bldroot): cmake_args += "-DENABLE_TESTS=OFF " cmake_args += "-DJSON_INCLUDE_DIR={} ".format(jsoninc) cmake_args += "-DJSON_LIB={} ".format(jsonlib) + cmake_args += "-DMUSICA_CORE_INCLUDE_DIR={} ".format(coreinc) + cmake_args += "-DMUSICA_CORE_LIB={} ".format(corelib) cmake_args += "-DCMAKE_Fortran_FLAGS='{}' ".format(arg_dict["FFLAGS"]) cmake_args += srcpath @@ -232,10 +295,8 @@ def _build_tuvx(caseroot, libroot, bldroot): os.environ['USER_INCLDIR'] = incldir + \ " -I{} ".format(os.path.join(bldroot, "tuv-x", "include")) - # The built libraries are included in the CAM_LINKED_LIBS entry in + # The TUV-x library is included in the CAM_LINKED_LIBS entry in # config_component.xml and are staged here to the lib folder - os.rename(os.path.join(bldroot, "tuv-x", "lib", "libmusica.a"), \ - os.path.join(libroot, "libmusica.a")) os.rename(os.path.join(bldroot, "tuv-x", "lib", "libtuvx.a"), \ os.path.join(libroot, "libtuvx.a")) @@ -244,6 +305,7 @@ def _build_tuvx(caseroot, libroot, bldroot): def _main_func(): caseroot, libroot, bldroot = parse_input(sys.argv) _build_json_fortran(caseroot, libroot, bldroot) + _build_musica_core(caseroot, libroot, bldroot) _build_tuvx(caseroot, libroot, bldroot) _build_fms(caseroot, libroot, bldroot) _build_cam(caseroot, libroot, bldroot) diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index 195a7821e7..73f28135cc 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -193,7 +193,7 @@ char - -ltuvx -lmusica -ljsonfortran + -ltuvx -lmusica_core -ljsonfortran build_component_cam env_build.xml diff --git a/cime_config/temp_tuvx_config.json b/cime_config/temp_tuvx_config.json index 4ebfbf4777..6a81dd6875 100644 --- a/cime_config/temp_tuvx_config.json +++ b/cime_config/temp_tuvx_config.json @@ -52,93 +52,103 @@ } ] }, - "photolysis reactions": { - "jo3_a": { - "__reaction": "O3+hv->O2+O(1D)", - "cross section": { - "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], - "type": "O3" - }, - "quantum yield": { - "type": "O3+hv->O2+O(1D)" - } - }, - "jo3_b": { - "__reaction": "O3+hv->O2+O(3P)", - "cross section": { - "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], - "type": "O3" + "photolysis": { + "reactions": [ + { + "name": "jo3_a", + "__reaction": "O3+hv->O2+O(1D)", + "cross section": { + "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(1D)" + } }, - "quantum yield": { - "type": "O3+hv->O2+O(3P)" - } - }, - "jo2_b": { - "__reaction": "O2+hv->O+O", - "cross section": { - "netcdf files": ["data/cross_sections/O2_1.nc"], - "type": "base", - "lower extrapolation": { "type": "boundary" } + { + "name": "jo3_b", + "__reaction": "O3+hv->O2+O(3P)", + "cross section": { + "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(3P)" + } }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - "jno2": { - "__reaction": "NO2+hv->NO+O(3P)", - "cross section": { - "netcdf files": ["data/cross_sections/NO2_1.nc"], - "type": "NO2 tint" + { + "name": "jo2_b", + "__reaction": "O2+hv->O+O", + "cross section": { + "netcdf files": ["data/cross_sections/O2_1.nc"], + "type": "base", + "lower extrapolation": { "type": "boundary" } + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } }, - "quantum yield": { - "netcdf files": ["data/quantum_yields/NO2_1.nc"], - "type": "NO2 tint", - "lower extrapolation": { "type": "boundary" } - } - }, - "jacet": { - "__reaction": "CH3COCH3+hv->CH3CO+CH3", - "cross section": { - "netcdf files": ["data/cross_sections/CH3COCH3_1.nc"], - "type": "CH3COCH3+hv->CH3CO+CH3" + { + "name": "jno2", + "__reaction": "NO2+hv->NO+O(3P)", + "cross section": { + "netcdf files": ["data/cross_sections/NO2_1.nc"], + "type": "NO2 tint" + }, + "quantum yield": { + "netcdf files": ["data/quantum_yields/NO2_1.nc"], + "type": "NO2 tint", + "lower extrapolation": { "type": "boundary" } + } }, - "quantum yield": { - "type": "CH3COCH3+hv->CH3CO+CH3" - } - }, - "jcfcl3": { - "__reaction": "CCl3F+hv->Products", - "cross section": { - "netcdf files": ["data/cross_sections/CFC-11_1.nc"], - "type": "CCl3F+hv->Products" + { + "name": "jacet", + "__reaction": "CH3COCH3+hv->CH3CO+CH3", + "cross section": { + "netcdf files": ["data/cross_sections/CH3COCH3_1.nc"], + "type": "CH3COCH3+hv->CH3CO+CH3" + }, + "quantum yield": { + "type": "CH3COCH3+hv->CH3CO+CH3" + } }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - "jpan1": { - "__reaction": "PAN+hv->CH3CO(OO)+NO2", - "cross section": { - "netcdf files": ["data/cross_sections/PAN_1.nc"], - "type": "CH3ONO2+hv->CH3O+NO2" + { + "name": "jcfcl3", + "__reaction": "CCl3F+hv->Products", + "cross section": { + "netcdf files": ["data/cross_sections/CFC-11_1.nc"], + "type": "CCl3F+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } }, - "quantum yield": { - "type": "base", - "constant value": 0.7 - } - }, - "jpan2": { - "__reaction": "PAN+hv->CH3CO(O)+NO3", - "cross section": { - "netcdf files": ["data/cross_sections/PAN_1.nc"], - "type": "CH3ONO2+hv->CH3O+NO2" + { + "name": "jpan1", + "__reaction": "PAN+hv->CH3CO(OO)+NO2", + "cross section": { + "netcdf files": ["data/cross_sections/PAN_1.nc"], + "type": "CH3ONO2+hv->CH3O+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 0.7 + } }, - "quantum yield": { - "type": "base", - "constant value": 0.3 + { + "name": "jpan2", + "__reaction": "PAN+hv->CH3CO(O)+NO3", + "cross section": { + "netcdf files": ["data/cross_sections/PAN_1.nc"], + "type": "CH3ONO2+hv->CH3O+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 0.3 + } } - } + ] } } From ea8a6544a4ff3cd16c5998682d7e975062f050cc Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Tue, 15 Nov 2022 16:04:28 -0700 Subject: [PATCH 31/84] draft MOZART TUV-x configuration file --- cime_config/buildnml | 6 +- ...{temp_tuvx_config.json => tuvx_BASIC.json} | 0 cime_config/tuvx_MOZART.json | 464 ++++++++++++++++++ 3 files changed, 468 insertions(+), 2 deletions(-) rename cime_config/{temp_tuvx_config.json => tuvx_BASIC.json} (100%) create mode 100644 cime_config/tuvx_MOZART.json diff --git a/cime_config/buildnml b/cime_config/buildnml index e256f2d070..d78d9fcb7e 100755 --- a/cime_config/buildnml +++ b/cime_config/buildnml @@ -204,8 +204,10 @@ def buildnml(case, caseroot, compname): if os.path.exists(dest_data): shutil.rmtree(dest_data) shutil.copytree(os.path.join(srcroot, "libraries", "tuv-x", "data"), dest_data) - shutil.copy2(os.path.join(srcroot, "cime_config", "temp_tuvx_config.json"), \ - os.path.join(rundir, "tuvx_config.json")) + shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_BASIC.json"), \ + os.path.join(rundir, "tuvx_BASIC.json")) + shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_MOZART.json"), \ + os.path.join(rundir, "tuvx_MOZART.json")) ############################################################################### def _main_func(): diff --git a/cime_config/temp_tuvx_config.json b/cime_config/tuvx_BASIC.json similarity index 100% rename from cime_config/temp_tuvx_config.json rename to cime_config/tuvx_BASIC.json diff --git a/cime_config/tuvx_MOZART.json b/cime_config/tuvx_MOZART.json new file mode 100644 index 0000000000..43384a0b31 --- /dev/null +++ b/cime_config/tuvx_MOZART.json @@ -0,0 +1,464 @@ +{ + "__description": "TUV-x configuration for the MOZART chemical mechanism", + "O2 absorption" : { + "cross section parameters file": "data/cross_sections/O2_parameters.txt" + }, + "grids": { + }, + "profiles": { + }, + "radiative transfer": { + "solver": { + "type": "delta eddington" + }, + "cross sections": [ + { + "name": "air", + "type": "air" + }, + { + "name": "O3", + "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "type": "O3" + }, + { + "name": "O2", + "netcdf files": ["data/cross_sections/O2_1.nc"], + "type": "base", + "lower extrapolation": { "type": "boundary" } + } + ], + "radiators": [ + { + "name": "air", + "type": "base", + "cross section": "air", + "vertical profile": "air", + "vertical profile units": "molecule cm-3" + }, + { + "name": "O2", + "type": "base", + "cross section": "O2", + "vertical profile": "O2", + "vertical profile units": "molecule cm-3" + }, + { + "name": "O3", + "type": "base", + "cross section": "O3", + "vertical profile": "O3", + "vertical profile units": "molecule cm-3" + } + ] + }, + "photolysis": { + "reactions": [ + { + "name": "jo2", + "__reaction": "O2 + hv -> O + O", + "cross section": { + "netcdf files": ["data/cross_sections/O2_1.nc"], + "type": "base", + "lower extrapolation": { "type": "boundary" } + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jo1d", + "__reaction": "O3 + hv -> O2 + O(1D)", + "cross section": { + "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(1D)" + } + }, + { + "name": "jo3p", + "__reaction": "O3 + hv -> O2 + O(3P)", + "cross section": { + "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(3P)" + } + }, + { + "name": "jn2o", + "__reaction": "N2O + hv -> N2 + O(1D)", + "cross section": { + "type": "N2O+hv->N2+O(1D)" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jno2", + "__reaction": "NO2 + hv -> NO + O(3P)", + "cross section": { + "netcdf files": ["data/cross_sections/NO2_1.nc"], + "type": "NO2 tint" + }, + "quantum yield": { + "netcdf files": ["data/quantum_yields/NO2_1.nc"], + "type": "NO2 tint", + "lower extrapolation": { "type": "boundary" } + } + }, + { + "name": "jn2o5", + "__reaction": "N2O5 + hv -> NO2 + NO3", + "cross section": { + "netcdf files": [ + "data/cross_sections/N2O5_1.nc", + "data/cross_sections/N2O5_2.nc" + ], + "type": "N2O5+hv->NO2+NO3" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhno3", + "__reaction": "HNO3 + hv -> OH + NO2", + "cross section": { + "netcdf files": [ + "data/cross_sections/HNO3_1.nc" + ], + "type": "HNO3+hv->OH+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jno3_a", + "__reaction": "NO3 + hv -> NO2 + O(3P)", + "cross section": { + "netcdf files": [ + "data/cross_sections/NO3_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/NO3-NO2+O(3P)_1.nc" + ], + "type": "tint", + "lower extrapolation": { + "type": "constant", + "value": 1.0 + } + } + }, + { + "name": "jno3_b", + "__reaction": "NO3 + hv -> NO + O2", + "cross section": { + "netcdf files": [ + "data/cross_sections/NO3_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/NO3-NO+O2_1.nc" + ], + "type": "tint" + } + }, + { + "name": "jch3ooh", + "__reaction": "CH3OOH + hv -> CH3O + OH", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3OOH_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch2o_a", + "__reaction": "CH2O + hv -> H + HCO", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH2O_1.nc" + ], + "type": "CH2O" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH2O_1.nc" + ], + "type": "base", + "lower extrapolation": { + "type": "boundary" + } + } + }, + { + "name": "jch2o_b", + "__reaction": "CH2O + hv -> H2 + CO", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH2O_1.nc" + ], + "type": "CH2O" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH2O_1.nc" + ], + "type": "CH2O", + "lower extrapolation": { + "type": "boundary" + } + } + }, + { + "name": "jh2o2", + "__reaction": "H2O2 + hv -> OH + OH", + "cross section": { + "netcdf files": [ + "data/cross_sections/H2O2_1.nc" + ], + "type": "H2O2+hv->OH+OH" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3cho", + "__reaction": "CH3CHO + hv -> CH3 + HCO", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3CHO_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH3CHO_1.nc" + ], + "type": "CH3CHO+hv->CH3+HCO" + } + }, + { + "name": "jpan", + "__reaction": "PAN + hv -> 0.6*CH3CO3 + 0.6*NO2 + 0.4*CH3O2 + 0.4*NO3 + 0.4*CO2", + "cross section": { + "netcdf files": [ + "data/cross_sections/PAN_1.nc" + ], + "type": "CH3ONO2+hv->CH3O+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jmvk", + "__reaction": "MVK + hv -> 0.7*C3H6 + 0.7*CO + 0.3*CH3O2 + 0.3*CH3CO3", + "cross section": { + "netcdf files": [ + "data/cross_sections/MVK_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "MVK+hv->Products" + } + }, + { + "name": "jacet", + "__reaction": "CH3COCH3 + hv -> CH3CO + CH3", + "cross section": { + "netcdf files": ["data/cross_sections/CH3COCH3_1.nc"], + "type": "CH3COCH3+hv->CH3CO+CH3" + }, + "quantum yield": { + "type": "CH3COCH3+hv->CH3CO+CH3" + } + }, + { + "name": "jmgly", + "__reaction": "CH3COCHO + hv -> CH3CO3 + CO + HO2", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3COCHO_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "CH3COCHO+hv->CH3CO+HCO" + } + }, + { + "name": "jglyald", + "__reaction": "GLYALD + hv -> 2*HO2 + CO + CH2O", + "cross section": { + "netcdf files": [ + "data/cross_sections/HOCH2CHO_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jglyoxal", + "__reaction": "GLYOXAL + hv -> 2*CO + 2*HO2", + "__comments": "NOTE the products of this reaction don't exactly match", + "cross section": { + "netcdf files": [ + "data/cross_sections/CHOCHO_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CHOCHO-H2_CO_CO_1.nc" + ], + "type": "base", + "lower extrapolation": { + "type": "boundary" + } + } + } + ] + }, + "__CAM aliasing": [ + { + "for": "jho2no2_a", + "reaction": "HO2NO2 + hv -> OH + NO3", + "comments": "TODO find this rate", + "use": "jo2", + "scale by": 0.0 + }, + { + "for": "jho2no2_b", + "reaction": "HO2NO2 + hv -> NO2 + HO2", + "comments": "TODO find this rate", + "use": "jo2", + "scale by": 0.0 + }, + { + "for": "jpooh", + "reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", + "use": "jch3ooh" + }, + { + "for": "jch3co3h", + "reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", + "use": "jh2o2", + "scale by": 0.28 + }, + { + "for": "jmpan", + "reaction": "MPAN + hv -> MCO3 + NO2", + "use": "jpan" + }, + { + "for": "jmacr_a", + "reaction": "MACR + hv -> 1.34*HO2 + 0.66*MCO3 + 1.34*CH2O + 1.34*CH3CO3", + "comments": "TODO - find this rate", + "use": "jo2", + "scale by": 0.0 + }, + { + "for": "jmacr_b", + "reaction": "MACR + hv -> 0.66*HO2 + 1.34*CO", + "comments": "TODO - find this rate", + "use": "jo2", + "scale by": 0.0 + }, + { + "for": "jc2h5ooh", + "reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", + "use": "jch3ooh" + }, + { + "for": "jc3h7ooh", + "reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", + "use": "jch3ooh" + }, + { + "for": "jrooh", + "reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", + "use": "jch3ooh" + }, + { + "for": "jxooh", + "reaction": "XOOH + hv -> OH", + "use": "jch3ooh" + }, + { + "for": "jonitr", + "reaction": "ONITR + hv -> NO2", + "use": "jch3cho" + }, + { + "for": "jisopooh", + "reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", + "use": "jch3ooh" + }, + { + "for": "jhyac", + "reaction": "HYAC (CH3COCH2OH) + hv -> CH3CO3 + HO2 + CH2O", + "comments": "TODO - find this rate", + "use": "jo2", + "scale by": 0.0 + }, + { + "for": "jmek", + "reaction": "MEK + hv -> CH3CO3 + C2H5O2", + "use": "jacet" + }, + { + "for": "jbigald", + "reaction": "BIGALD + hv -> .45*CO + .13*GLYOXAL + .56*HO2 + .13*CH3CO3 + .18*CH3COCHO", + "use": "jno2", + "scale by": 0.2 + }, + { + "for": "jalkooh", + "reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", + "use": "jch3ooh" + }, + { + "for": "jmekooh", + "reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", + "use": "jch3ooh" + }, + { + "for": "jtolooh", + "reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", + "use": "jch3ooh" + }, + { + "for": "jterpooh", + "reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", + "use": "jch3ooh" + } + ] +} From 439a0bac3dd3903d89c8c31d66b1d24971f36949 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Wed, 16 Nov 2022 10:59:19 -0800 Subject: [PATCH 32/84] Develop 167 tuvx inputs (#4) * add height and temperature updaters to tuv-x wrapper * set up remaining profile updaters * send wavelength grid to TUV-x * add radiator updater for aerosols * send CAM height and temperature data to TUV-x * fix height variables sent to tuvx * send surface albedos to TUV-x * send air density to TUV-x * send O2 and O3 concentrations to TUV-x * sent solar zenith angle to TUV-x * send ET flux to tuv-x * use native TUV-x grid for photo calcs * send aerosol optical properties to tuv-x * output whether using online aerosols; include air scale height for exo values * send above-column O2 and O3 concentrations to TUV-x * add diagnostic TUV-x output; set wavelength edges; fix et flux values * pass earth sun distance to TUV-x * minor clean up * update externals file * update for changes in TUV-x --- .gitignore | 1 + Externals_CAM.cfg | 9 +- cime_config/buildlib | 73 +- cime_config/config_component.xml | 2 +- cime_config/temp_tuvx_config.json | 197 ++-- src/chemistry/mozart/chemistry.F90 | 8 +- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 31 +- src/chemistry/mozart/mo_tuvx.F90 | 1055 ++++++++++++++++-- src/physics/cam/modal_aer_opt.F90 | 6 + 9 files changed, 1169 insertions(+), 213 deletions(-) diff --git a/.gitignore b/.gitignore index 42d902494c..39fd22862a 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ libraries/FMS libraries/mct libraries/parallelio libraries/tuv-x +libraries/musica-core libraries/json-fortran src/atmos_phys src/dynamics/mpas/dycore diff --git a/Externals_CAM.cfg b/Externals_CAM.cfg index 6367169bf2..9156837aef 100644 --- a/Externals_CAM.cfg +++ b/Externals_CAM.cfg @@ -73,7 +73,14 @@ required = True local_path = libraries/tuv-x protocol = git repo_url = https://github.com/NCAR/tuv-x.git -hash = c96cf5f +branch = main +required = True + +[musica-core] +local_path = libraries/musica-core +protocol = git +repo_url = https://github.com/NCAR/musica-core.git +branch = main required = True [json-fortran] diff --git a/cime_config/buildlib b/cime_config/buildlib index 20333519bc..39f4d6906a 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -182,6 +182,61 @@ def _build_json_fortran(caseroot, libroot, bldroot): os.rename(os.path.join(bldroot, "json-fortran", "lib", "libjsonfortran.a"), \ os.path.join(libroot, "libjsonfortran.a")) +############################################################################### +def _build_musica_core(caseroot, libroot, bldroot): +############################################################################### +# Builds the musica-core support library for TUV-x and updates the case +# variables used to set the include paths and linked libraries + + build = EnvBuild(case_root=caseroot) + with Case(caseroot) as case: + bldpath = os.path.join(bldroot, "musica-core") + if not os.path.exists(bldpath): + os.makedirs(bldpath) + jsoninc = os.path.join(bldroot, "json-fortran", "include", "") + expect(os.path.exists(jsoninc), \ + "JSON-Fortran include folder for musica-core build not found at {}".format(jsoninc)) + jsonlib = os.path.join(libroot, "libjsonfortran.a") + expect(os.path.exists(jsonlib), \ + "JSON-Fortran library for musica-core build not found at {}".format(jsonlib)) + srcpath = os.path.abspath(os.path.join(case.get_value("COMP_ROOT_DIR_ATM"), \ + "libraries", "musica-core", "")) + logger.info("Building musica-core in {} from source in {}\n".format(bldpath, srcpath)) + + arg_dict = _cmake_default_args(caseroot) + if build.get_value("MPILIB") == "mpi-serial": + cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["SFC"]) + else: + cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["MPIFC"]) + cmake_args += "-DENABLE_MPI:BOOL=TRUE " + if case.get_value("DEBUG"): + cmake_args += "-DCMAKE_BUILD_TYPE=Debug " + else: + cmake_args += "-DCMAKE_BUILD_TYPE=Release " + cmake_args += "-DENABLE_UTIL_ONLY=ON " + cmake_args += "-DCMAKE_C_COMPILER_WORKS=1 " + cmake_args += "-DCMAKE_CXX_COMPILER_WORKS=1 " + cmake_args += "-DENABLE_COVERAGE=OFF " + cmake_args += "-DJSON_INCLUDE_DIR={} ".format(jsoninc) + cmake_args += "-DJSON_LIB={} ".format(jsonlib) + cmake_args += "-DCMAKE_Fortran_FLAGS='{}' ".format(arg_dict["FFLAGS"]) + cmake_args += srcpath + + _run_cmd("cmake {}".format(cmake_args), bldpath) + _run_cmd(case.get_value("GMAKE"), bldpath) + + # add musica-core to include paths + incldir = os.environ.get('USER_INCLDIR') + if incldir is None: + incldir = '' + os.environ['USER_INCLDIR'] = incldir + \ + " -I{} ".format(os.path.join(bldroot, "musica-core", "include")) + + # The musica-core library is included in the CAM_LINKED_LIBS entry in + # config_component.xml and are staged here to the lib folder + os.rename(os.path.join(bldroot, "musica-core", "lib", "libmusica_core.a"), \ + os.path.join(libroot, "libmusica_core.a")) + ############################################################################### def _build_tuvx(caseroot, libroot, bldroot): ############################################################################### @@ -199,6 +254,12 @@ def _build_tuvx(caseroot, libroot, bldroot): jsonlib = os.path.join(libroot, "libjsonfortran.a") expect(os.path.exists(jsonlib), \ "JSON-Fortran library for TUV-x build not found at {}".format(jsonlib)) + coreinc = os.path.join(bldroot, "musica-core", "include", "") + expect(os.path.exists(coreinc), \ + "musica-core include folder for TUV-x build not found at {}".format(coreinc)) + corelib = os.path.join(libroot, "libmusica_core.a") + expect(os.path.exists(corelib), \ + "musica-core library for TUV-x build not found at {}".format(corelib)) srcpath = os.path.abspath(os.path.join(case.get_value("COMP_ROOT_DIR_ATM"), \ "libraries", "tuv-x", "")) logger.info("Building TUV-x in {} from source in {}\n".format(bldpath, srcpath)) @@ -211,13 +272,18 @@ def _build_tuvx(caseroot, libroot, bldroot): cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["MPIFC"]) cmake_args += "-DCMAKE_C_COMPILER={} ".format(arg_dict["MPICC"]) cmake_args += "-DENABLE_MPI:BOOL=TRUE " + if case.get_value("DEBUG"): + cmake_args += "-DCMAKE_BUILD_TYPE=Debug " + else: + cmake_args += "-DCMAKE_BUILD_TYPE=Release " cmake_args += "-DCMAKE_C_COMPILER_WORKS=1 " cmake_args += "-DCMAKE_CXX_COMPILER_WORKS=1 " - cmake_args += "-DCMAKE_BUILD_TYPE=Release " cmake_args += "-DENABLE_COVERAGE=OFF " cmake_args += "-DENABLE_TESTS=OFF " cmake_args += "-DJSON_INCLUDE_DIR={} ".format(jsoninc) cmake_args += "-DJSON_LIB={} ".format(jsonlib) + cmake_args += "-DMUSICA_CORE_INCLUDE_DIR={} ".format(coreinc) + cmake_args += "-DMUSICA_CORE_LIB={} ".format(corelib) cmake_args += "-DCMAKE_Fortran_FLAGS='{}' ".format(arg_dict["FFLAGS"]) cmake_args += srcpath @@ -231,10 +297,8 @@ def _build_tuvx(caseroot, libroot, bldroot): os.environ['USER_INCLDIR'] = incldir + \ " -I{} ".format(os.path.join(bldroot, "tuv-x", "include")) - # The built libraries are included in the CAM_LINKED_LIBS entry in + # The TUV-x library is included in the CAM_LINKED_LIBS entry in # config_component.xml and are staged here to the lib folder - os.rename(os.path.join(bldroot, "tuv-x", "lib", "libmusica.a"), \ - os.path.join(libroot, "libmusica.a")) os.rename(os.path.join(bldroot, "tuv-x", "lib", "libtuvx.a"), \ os.path.join(libroot, "libtuvx.a")) @@ -243,6 +307,7 @@ def _build_tuvx(caseroot, libroot, bldroot): def _main_func(): caseroot, libroot, bldroot = parse_input(sys.argv) _build_json_fortran(caseroot, libroot, bldroot) + _build_musica_core(caseroot, libroot, bldroot) _build_tuvx(caseroot, libroot, bldroot) _build_fms(caseroot, libroot, bldroot) _build_cam(caseroot, libroot, bldroot) diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index 195a7821e7..73f28135cc 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -193,7 +193,7 @@ char - -ltuvx -lmusica -ljsonfortran + -ltuvx -lmusica_core -ljsonfortran build_component_cam env_build.xml diff --git a/cime_config/temp_tuvx_config.json b/cime_config/temp_tuvx_config.json index 27113be240..6a81dd6875 100644 --- a/cime_config/temp_tuvx_config.json +++ b/cime_config/temp_tuvx_config.json @@ -4,87 +4,12 @@ "cross section parameters file": "data/cross_sections/O2_parameters.txt" }, "grids": { - "height": { - "type": "equal interval", - "units": "km", - "begins at" : 0.0, - "ends at" : 120.0, - "cell delta" : 1.0 - }, - "wavelength": { - "type": "from csv file", - "units": "nm", - "file path": "data/grids/wavelength/combined.grid" - }, - "time": { - "type": "from config file", - "units": "hours", - "values": [ 14.0 ] - } }, "profiles": { - "O3": { - "type": "O3", - "units": "molecule cm-3", - "file path": "data/profiles/atmosphere/ussa.ozone" - }, - "air": { - "type": "air", - "units": "molecule cm-3", - "file path": "data/profiles/atmosphere/ussa.dens" - }, - "O2": { - "type": "O2", - "units": "molecule cm-3", - "file path": "data/profiles/atmosphere/ussa.dens" - }, - "temperature": { - "type": "from csv file", - "units": "K", - "file path": "data/profiles/atmosphere/ussa.temp", - "grid": { - "name": "height", - "units": "km" - } - }, - "solar zenith angle": { - "type": "solar zenith angle", - "units": "degrees", - "year" : 2002, - "month": 3, - "day": 21, - "longitude": 0.0, - "latitude": 0.0 - }, - "Earth-Sun distance": { - "type": "Earth-Sun distance", - "units": "AU", - "year" : 2002, - "month": 3, - "day": 21 - }, - "surface albedo": { - "type": "from config file", - "units": "none", - "uniform value": 0.10, - "grid": { - "name": "wavelength", - "units": "nm" - } - }, - "extraterrestrial flux": { - "type": "extraterrestrial flux", - "units": "photon cm-2 s-1", - "file path": ["data/profiles/solar/susim_hi.flx", - "data/profiles/solar/atlas3_1994_317_a.dat", - "data/profiles/solar/sao2010.solref.converted", - "data/profiles/solar/neckel.flx"], - "interpolator": ["","","","fractional target"] - } }, "radiative transfer": { "solver": { - "type": "delta eddington" + "type": "delta eddington" }, "cross sections": [ { @@ -124,39 +49,105 @@ "cross section": "O3", "vertical profile": "O3", "vertical profile units": "molecule cm-3" - }, - { - "name": "aerosols", - "type": "aerosol", - "optical depths": [2.40e-01, 1.06e-01, 4.56e-02, 1.91e-02, 1.01e-02, 7.63e-03, - 5.38e-03, 5.00e-03, 5.15e-03, 4.94e-03, 4.82e-03, 4.51e-03, - 4.74e-03, 4.37e-03, 4.28e-03, 4.03e-03, 3.83e-03, 3.78e-03, - 3.88e-03, 3.08e-03, 2.26e-03, 1.64e-03, 1.23e-03, 9.45e-04, - 7.49e-04, 6.30e-04, 5.50e-04, 4.21e-04, 3.22e-04, 2.48e-04, - 1.90e-04, 1.45e-04, 1.11e-04, 8.51e-05, 6.52e-05, 5.00e-05, - 3.83e-05, 2.93e-05, 2.25e-05, 1.72e-05, 1.32e-05, 1.01e-05, - 7.72e-06, 5.91e-06, 4.53e-06, 3.46e-06, 2.66e-06, 2.04e-06, - 1.56e-06, 1.19e-06, 9.14e-07], - "single scattering albedo": 0.99, - "asymmetry factor": 0.61, - "550 nm optical depth": 0.235 } ] }, "photolysis": { - "enable diagnostics": false, "reactions": [ { - "name": "O2+hv->O+O", - "cross section": { - "netcdf files": ["data/cross_sections/O2_1.nc"], - "type": "base", - "lower extrapolation": { "type": "boundary" } - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } + "name": "jo3_a", + "__reaction": "O3+hv->O2+O(1D)", + "cross section": { + "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(1D)" + } + }, + { + "name": "jo3_b", + "__reaction": "O3+hv->O2+O(3P)", + "cross section": { + "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(3P)" + } + }, + { + "name": "jo2_b", + "__reaction": "O2+hv->O+O", + "cross section": { + "netcdf files": ["data/cross_sections/O2_1.nc"], + "type": "base", + "lower extrapolation": { "type": "boundary" } + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jno2", + "__reaction": "NO2+hv->NO+O(3P)", + "cross section": { + "netcdf files": ["data/cross_sections/NO2_1.nc"], + "type": "NO2 tint" + }, + "quantum yield": { + "netcdf files": ["data/quantum_yields/NO2_1.nc"], + "type": "NO2 tint", + "lower extrapolation": { "type": "boundary" } + } + }, + { + "name": "jacet", + "__reaction": "CH3COCH3+hv->CH3CO+CH3", + "cross section": { + "netcdf files": ["data/cross_sections/CH3COCH3_1.nc"], + "type": "CH3COCH3+hv->CH3CO+CH3" + }, + "quantum yield": { + "type": "CH3COCH3+hv->CH3CO+CH3" + } + }, + { + "name": "jcfcl3", + "__reaction": "CCl3F+hv->Products", + "cross section": { + "netcdf files": ["data/cross_sections/CFC-11_1.nc"], + "type": "CCl3F+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jpan1", + "__reaction": "PAN+hv->CH3CO(OO)+NO2", + "cross section": { + "netcdf files": ["data/cross_sections/PAN_1.nc"], + "type": "CH3ONO2+hv->CH3O+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 0.7 + } + }, + { + "name": "jpan2", + "__reaction": "PAN+hv->CH3CO(O)+NO3", + "cross section": { + "netcdf files": ["data/cross_sections/PAN_1.nc"], + "type": "CH3ONO2+hv->CH3O+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 0.3 + } } ] } diff --git a/src/chemistry/mozart/chemistry.F90 b/src/chemistry/mozart/chemistry.F90 index a163dfa307..046b669ddb 100644 --- a/src/chemistry/mozart/chemistry.F90 +++ b/src/chemistry/mozart/chemistry.F90 @@ -994,6 +994,7 @@ subroutine chem_timestep_init(phys_state,pbuf2d) use mo_aurora, only : aurora_timestep_init use mo_photo, only : photo_timestep_init + use mo_tuvx, only : tuvx_timestep_init use cfc11star, only : update_cfc11star use physics_buffer, only : physics_buffer_desc @@ -1065,6 +1066,11 @@ subroutine chem_timestep_init(phys_state,pbuf2d) !----------------------------------------------------------------------------- call photo_timestep_init( calday ) + !----------------------------------------------------------------------------- + ! ... setup the TUV-x profiles for this timestep + !----------------------------------------------------------------------------- + call tuvx_timestep_init( ) + call update_cfc11star( pbuf2d, phys_state ) ! Galatic Cosmic Rays ... @@ -1218,7 +1224,7 @@ subroutine chem_timestep_tend( state, ptend, cam_in, cam_out, dt, pbuf, fh2o) fsds, cam_in%ts, cam_in%asdir, cam_in%ocnfrac, cam_in%icefrac, & cam_out%precc, cam_out%precl, cam_in%snowhland, ghg_chem, state%latmapback, & drydepflx, wetdepflx, cam_in%cflx, cam_in%fireflx, cam_in%fireztop, & - nhx_nitrogen_flx, noy_nitrogen_flx, ptend%q, pbuf ) + nhx_nitrogen_flx, noy_nitrogen_flx, ptend%q, pbuf, state ) if (associated(cam_out%nhx_nitrogen_flx)) then cam_out%nhx_nitrogen_flx(:ncol) = nhx_nitrogen_flx(:ncol) endif diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index 0ed39659ab..7274e4439a 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -238,7 +238,8 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & delt, ps, & fsds, ts, asdir, ocnfrac, icefrac, & precc, precl, snowhland, ghg_chem, latmapback, & - drydepflx, wetdepflx, cflx, fire_sflx, fire_ztop, nhx_nitrogen_flx, noy_nitrogen_flx, qtend, pbuf) + drydepflx, wetdepflx, cflx, fire_sflx, fire_ztop, & + nhx_nitrogen_flx, noy_nitrogen_flx, qtend, pbuf, state) !----------------------------------------------------------------------- ! ... Chem_solver advances the volumetric mixing ratio @@ -282,6 +283,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & use mo_chm_diags, only : chm_diags, het_diags use perf_mod, only : t_startf, t_stopf use gas_wetdep_opts, only : gas_wetdep_method + use physics_types, only : physics_state use physics_buffer, only : physics_buffer_desc, pbuf_get_field, pbuf_old_tim_idx use infnan, only : nan, assignment(=) use rate_diags, only : rate_diags_calc, rate_diags_o3s_loss @@ -337,6 +339,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & real(r8), intent(out) :: noy_nitrogen_flx(pcols) type(physics_buffer_desc), pointer :: pbuf(:) + type(physics_state), target, intent(in) :: state !----------------------------------------------------------------------- ! ... Local variables @@ -779,20 +782,24 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & !----------------------------------------------------------------------- esfact = 1._r8 - call shr_orb_decl( calday, eccen, mvelpp, lambm0, obliqr, delta, esfact ) + call shr_orb_decl( calday, eccen, mvelpp, lambm0, obliqr , & + delta, esfact ) !if (tuvx_active) then - !----------------------------------------------------------------- - ! ... get calculated photolysis rates from TUV-x - !----------------------------------------------------------------- - call tuvx_get_photo_rates( ncol ) + !----------------------------------------------------------------- + ! ... lookup the photolysis rates from table + !----------------------------------------------------------------- + call table_photo( reaction_rates, pmid, pdel, tfld, zmid, zint, & + col_dens, zen_angle, asdir, cwat, cldfr, & + esfact, vmr, invariants, ncol, lchnk, pbuf ) + !else - !----------------------------------------------------------------- - ! ... lookup the photolysis rates from table - !----------------------------------------------------------------- - call table_photo( reaction_rates, pmid, pdel, tfld, zmid, zint, & - col_dens, zen_angle, asdir, cwat, cldfr, & - esfact, vmr, invariants, ncol, lchnk, pbuf ) + !----------------------------------------------------------------- + ! ... get calculated photolysis rates from TUV-x + !----------------------------------------------------------------- + call tuvx_get_photo_rates( state, pbuf, ncol, lchnk, zm, zi, & + tfld, ts, invariants, vmr, col_delta, & + asdir, zen_angle, esfact ) !endif do i = 1,phtcnt diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index d186b35909..64e0e90f3f 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -1,14 +1,15 @@ -module mo_tuvx !---------------------------------------------------------------------- -! ... wrapper for TUV-x photolysis rate constant calculator +! Wrapper for TUV-x photolysis rate constant calculator !---------------------------------------------------------------------- +module mo_tuvx - use musica_string, only : string_t - use shr_kind_mod, only : r8 => shr_kind_r8, cl=>shr_kind_cl - use tuvx_core, only : core_t - use cam_logfile, only : iulog - use spmd_utils, only : masterproc - use cam_abortutils, only : endrun + use musica_string, only : string_t + use ppgrid, only : pver ! number of vertical layers + 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 + use tuvx_radiator_from_host, only : radiator_updater_t implicit none @@ -16,19 +17,74 @@ module mo_tuvx public :: tuvx_readnl public :: tuvx_init + public :: tuvx_timestep_init public :: tuvx_get_photo_rates public :: tuvx_finalize public :: tuvx_active + ! 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 + integer, parameter :: GRID_INDEX_WAVELENGTH = 2 ! Wavelength grid index + + ! Indices for profile updaters + integer, parameter :: NUM_PROFILES = 8 ! number of profiles that CAM will update at runtime + integer, parameter :: PROFILE_INDEX_TEMPERATURE = 1 ! Temperature profile index + integer, parameter :: PROFILE_INDEX_ALBEDO = 2 ! Surface albedo profile index + integer, parameter :: PROFILE_INDEX_ET_FLUX = 3 ! Extraterrestrial flux profile index + integer, parameter :: PROFILE_INDEX_AIR = 4 ! Air density profile index + integer, parameter :: PROFILE_INDEX_O3 = 5 ! Ozone profile index + integer, parameter :: PROFILE_INDEX_O2 = 6 ! Molecular oxygen profile index + integer, parameter :: PROFILE_INDEX_SO2 = 7 ! Sulfur dioxide profile index + integer, parameter :: PROFILE_INDEX_NO2 = 8 ! Nitrogen dioxide profile index + + ! Indices for radiator updaters + integer, parameter :: NUM_RADIATORS = 1 ! number of radiators that CAM will update at runtime + integer, parameter :: RADIATOR_INDEX_AEROSOL = 1 ! Aerosol radiator index + + ! Information needed to access CAM species state data + logical :: is_fixed_O2 = .false. ! indicates whether O2 concentrations are fixed + logical :: is_fixed_O3 = .false. ! indicates whether O3 concentrations are fixed + integer :: index_O2 = 0 ! index for O2 in concentration array + integer :: index_O3 = 0 ! index for O3 in concentration array + + ! Information needed to access aerosol optical properties + logical :: aerosol_exists = .false. ! indicates whether aerosol optical properties are available + + ! TODO how should these paths be set and communicated to this wrapper? + character(len=*), parameter :: wavelength_config_path = & + "data/grids/wavelength/combined.grid" + logical, parameter :: enable_diagnostics = .true. + ! TUV-x calculator for each OMP thread type :: tuvx_ptr - type(core_t), pointer :: core_ => null( ) ! TUV-x calculator - integer :: n_photo_rates_ ! number of photo reactions in TUV-x - real(r8), allocatable :: photo_rates_(:,:) ! photolysis rate constants - ! (vertical level, reaction) [s-1] + type(core_t), pointer :: core_ => null( ) ! TUV-x calculator + integer :: n_photo_rates_ ! number of photo reactions in TUV-x + real(r8), allocatable :: photo_rates_(:,:,:) ! photolysis rate constants + ! (column, vertical level, reaction) [s-1] + type(grid_updater_t) :: grids_(NUM_GRIDS) ! grid updaters + type(profile_updater_t) :: profiles_(NUM_PROFILES) ! profile updaters + type(radiator_updater_t) :: radiators_(NUM_RADIATORS) ! radiator updaters + real(r8) :: height_delta_(pver) ! change in height in each + ! vertical layer (km) + real(r8), allocatable :: wavelength_edges_(:) ! TUV-x wavelength bin edges (nm) + real(r8), allocatable :: wavelength_values_(:) ! Working array for interface values + ! on the TUV-x wavelength grid + real(r8), allocatable :: wavelength_mid_values_(:) ! Working array for mid-point values + ! on the TUV-x wavelength grid + real(r8), allocatable :: optical_depth_(:,:,:) ! (column, vertical level, wavelength) [unitless] + real(r8), allocatable :: single_scattering_albedo_(:,:,:) ! (column, vertical level, wavelength) [unitless] + real(r8), allocatable :: asymmetry_factor_(:,:,:) ! (column, vertical level, wavelength) [unitless] end type tuvx_ptr type(tuvx_ptr), allocatable :: tuvx_ptrs(:) + ! Diagnostic photolysis rate constant output + type :: diagnostic_t + character(len=:), allocatable :: name_ ! Name of the output field + integer :: index_ ! index of the photolysis rate constant from TUV-x + end type diagnostic_t + type(diagnostic_t), allocatable :: diagnostics(:) + ! namelist options character(len=cl) :: tuvx_config_path = 'NONE' ! absolute path to TUVX configuration file logical, protected :: tuvx_active = .false. @@ -41,8 +97,15 @@ module mo_tuvx ! read namelist options !----------------------------------------------------------------------- subroutine tuvx_readnl(nlfile) + +#ifdef HAVE_MPI + use mpi +#endif + use cam_abortutils, only : endrun + use cam_logfile, only : iulog ! log file output unit use namelist_utils, only : find_group_name - use spmd_utils, only : mpicom, masterprocid, mpi_character, mpi_logical + use spmd_utils, only : mpicom, is_main_task => masterproc, & + main_task => masterprocid character(len=*), intent(in) :: nlfile ! filepath for file containing namelist input @@ -57,7 +120,7 @@ subroutine tuvx_readnl(nlfile) ! ============= ! Read namelist ! ============= - if (masterproc) then + if (is_main_task) then open( newunit=unitn, file=trim(nlfile), status='old' ) call find_group_name(unitn, 'tuvx_opts', status=ierr) if (ierr == 0) then @@ -72,46 +135,55 @@ subroutine tuvx_readnl(nlfile) ! ============================ ! Broadcast namelist variables ! ============================ - call mpi_bcast(tuvx_config_path, len(tuvx_config_path), mpi_character, masterprocid, mpicom, ierr) - call mpi_bcast(tuvx_active, 1, mpi_logical, masterprocid, mpicom, ierr) +#ifdef HAVE_MPI + call mpi_bcast(tuvx_config_path, len(tuvx_config_path), mpi_character, main_task, mpicom, ierr) + call mpi_bcast(tuvx_active, 1, mpi_logical, main_task, mpicom, ierr) +#endif if (tuvx_active .and. tuvx_config_path == 'NONE') then call endrun(subname // ' : must set tuvx_config_path when TUV-X is active') end if - if (masterproc) then + if (is_main_task) then write(iulog,*) 'tuvx_readnl: tuvx_config_path = ', trim(tuvx_config_path) write(iulog,*) 'tuvx_readnl: tuvx_active = ', tuvx_active end if end subroutine tuvx_readnl -!----------------------------------------------------------------------- -!----------------------------------------------------------------------- +!================================================================================================ + + !----------------------------------------------------------------------- + ! Initializes TUV-x for photolysis calculations + !----------------------------------------------------------------------- subroutine tuvx_init( ) -!----------------------------------------------------------------------- -! -! Purpose: initialize TUV-x for photolysis calculations -! -!----------------------------------------------------------------------- - use cam_logfile, only : iulog - use musica_assert, only : assert_msg - use musica_mpi, only : musica_mpi_rank - use musica_string, only : string_t, to_char - use tuvx_grid, only : grid_t - use spmd_utils, only : main_task => masterprocid, & - is_main_task => masterproc, & - mpicom, mpi_character, mpi_integer, mpi_success +#ifdef HAVE_MPI + use mpi +#endif + use cam_logfile, only : iulog ! log file output unit + use mo_chem_utls, only : get_spc_ndx, get_inv_ndx + use musica_assert, only : assert_msg + use musica_mpi, only : musica_mpi_rank + use musica_string, only : string_t, to_char + use ppgrid, only : pcols ! maximum number of columns + use solar_irrad_data, only : has_spectrum + use spmd_utils, only : main_task => masterprocid, & + is_main_task => masterproc, & + mpicom + use tuvx_grid, only : grid_t + use tuvx_grid_warehouse, only : grid_warehouse_t + use tuvx_profile_warehouse, only : profile_warehouse_t + use tuvx_radiator_warehouse, only : radiator_warehouse_t -!----------------------------------------------------------------------- -! Local variables -!----------------------------------------------------------------------- class(core_t), pointer :: core character, allocatable :: buffer(:) type(string_t) :: config_path class(grid_t), pointer :: height - integer :: pack_size, pos, i_core, i_err, i_thread + class(grid_warehouse_t), pointer :: cam_grids + class(profile_warehouse_t), pointer :: cam_profiles + class(radiator_warehouse_t), pointer :: cam_radiators + integer :: pack_size, pos, i_core, i_err if (.not.tuvx_active) return @@ -121,12 +193,21 @@ subroutine tuvx_init( ) #ifndef HAVE_MPI call assert_msg( 113937299, is_main_task, "Multiple tasks present without " & - //"MPI support enabled for TUV-x") + //"MPI support enabled for TUV-x" ) #endif + ! ========================================================================== + ! Create the set of TUV-x grids and profiles that CAM will update at runtime + ! ========================================================================== + cam_grids => get_cam_grids( wavelength_config_path ) + cam_profiles => get_cam_profiles( cam_grids ) + cam_radiators => get_cam_radiators( cam_grids ) + + ! ====================================================================== ! construct a core on the primary process and pack it onto an MPI buffer + ! ====================================================================== if( is_main_task ) then - core => core_t( config_path ) + core => core_t( config_path, cam_grids, cam_profiles, cam_radiators ) pack_size = core%pack_size( mpicom ) allocate( buffer( pack_size ) ) pos = 0 @@ -134,7 +215,10 @@ subroutine tuvx_init( ) deallocate( core ) end if +#ifdef HAVE_MPI + ! ============================================ ! broadcast the core data to all MPI processes + ! ============================================ call mpi_bcast( pack_size, 1, MPI_INTEGER, main_task, mpicom, i_err ) if( i_err /= MPI_SUCCESS ) then write(iulog,*) "TUV-x MPI int bcast error" @@ -146,75 +230,166 @@ subroutine tuvx_init( ) write(iulog,*) "TUV-x MPI char array bcast error" call mpi_abort( mpicom, 1, i_err ) end if +#endif + ! ======================================================== ! unpack the core for each OMP thread on every MPI process + ! ======================================================== allocate( tuvx_ptrs( max_threads( ) ) ) do i_core = 1, size( tuvx_ptrs ) associate( tuvx => tuvx_ptrs( i_core ) ) allocate( tuvx%core_ ) pos = 0 call tuvx%core_%mpi_unpack( buffer, pos, mpicom ) - end associate - end do - ! Set up map between CAM and TUV-x photolysis reactions for each thread - do i_thread = 1, max_threads( ) - associate( tuvx => tuvx_ptrs( i_thread ) ) + ! ===================================================================== + ! Set up map between CAM and TUV-x photolysis reactions for each thread + ! ===================================================================== + call create_updaters( tuvx, cam_grids, cam_profiles, cam_radiators ) + + ! TEMPORARY FOR DEVELOPMENT tuvx%n_photo_rates_ = tuvx%core_%number_of_photolysis_reactions( ) height => tuvx%core_%get_grid( "height", "km" ) - ! Temporary for development - allocate( tuvx%photo_rates_( height%ncells_ + 1, tuvx%n_photo_rates_ ) ) + allocate( tuvx%photo_rates_( pcols, height%ncells_ + 1, tuvx%n_photo_rates_ ) ) + deallocate( height ) + end associate end do + deallocate( cam_grids ) + deallocate( cam_profiles ) + deallocate( cam_radiators ) + + ! ============================================= + ! Get index info for CAM species concentrations + ! ============================================= + index_O2 = get_inv_ndx( 'O2' ) + is_fixed_O2 = index_O2 > 0 + if( .not. is_fixed_O2 ) index_O2 = get_spc_ndx( 'O2' ) + index_O3 = get_inv_ndx( 'O3' ) + is_fixed_O3 = index_O3 > 0 + if( .not. is_fixed_O3 ) index_O3 = get_spc_ndx( 'O3' ) + + ! ==================================================== + ! make sure extraterrestrial flux values are available + ! ==================================================== + call assert_msg( 170693514, has_spectrum, & + "Solar irradiance spectrum needed for TUV-x" ) + + ! ============================================ + ! set up diagnostic output of photolysis rates + ! ============================================ + call initialize_diagnostics( tuvx_ptrs( 1 ) ) + end subroutine tuvx_init !================================================================================================ - subroutine tuvx_get_photo_rates( ncol ) -!----------------------------------------------------------------------- -! -! Purpose: calculate and return photolysis rate constants -! -!----------------------------------------------------------------------- + !----------------------------------------------------------------------- + ! Updates TUV-x profiles that depend on time but not space + !----------------------------------------------------------------------- + subroutine tuvx_timestep_init( ) - use spmd_utils, only : main_task => masterprocid, & - is_main_task => masterproc, & - mpicom -!----------------------------------------------------------------------- -! Dummy arguments -!----------------------------------------------------------------------- + integer :: i_thread + + do i_thread = 1, size( tuvx_ptrs ) + associate( tuvx => tuvx_ptrs( i_thread ) ) + call set_et_flux( tuvx ) + end associate + end do + + end subroutine tuvx_timestep_init + +!================================================================================================ + + !----------------------------------------------------------------------- + ! Calculates and returns photolysis rate constants + !----------------------------------------------------------------------- + subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & + height_int, temperature_mid, surface_temperature, fixed_species_conc, & + species_vmr, exo_column_conc, surface_albedo, solar_zenith_angle, & + earth_sun_distance ) - integer, intent(in) :: ncol ! Number of colums to calculated photolysis for + use cam_logfile, only : iulog ! log info output unit + use chem_mods, only : gas_pcnst, & ! number of non-fixed species + nfs, & ! number of fixed species + nabscol ! number of absorbing species (radiators) + use physics_types, only : physics_state + use physics_buffer, only : physics_buffer_desc + use ppgrid, only : pcols ! maximum number of columns + use shr_const_mod, only : pi => shr_const_pi + use spmd_utils, only : main_task => masterprocid, & + is_main_task => masterproc, & + mpicom + + type(physics_state), target, intent(in) :: state + type(physics_buffer_desc), pointer, intent(inout) :: pbuf(:) + integer, intent(in) :: ncol ! number of active columns on this thread + integer, intent(in) :: lchnk ! identifier for this thread + real(r8), intent(in) :: height_mid(pcols,pver) ! height at mid-points (km) + real(r8), intent(in) :: height_int(pcols,pver+1) ! height at interfaces (km) + real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) + real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) + real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities + ! (molecule cm-3) + real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) + real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! above column densities + ! (molecule cm-3) + real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) + real(r8), intent(in) :: solar_zenith_angle(ncol) ! solar zenith angle (radians) + real(r8), intent(in) :: earth_sun_distance ! Earth-Sun distance (AU) -!----------------------------------------------------------------------- -! Local variables -!----------------------------------------------------------------------- integer :: i_col ! column index if (.not.tuvx_active) return associate( tuvx => tuvx_ptrs( thread_id( ) ) ) + + ! ============================================== + ! get aerosol optical properties for all columns + ! ============================================== + call get_aerosol_optical_properties( tuvx, state, pbuf ) + do i_col = 1, ncol - ! Temporary fix SZA for development - call tuvx%core_%run( 45.0_r8, photolysis_rate_constants = tuvx%photo_rates_ ) + + ! =================== + ! update grid heights + ! =================== + call set_heights( tuvx, i_col, ncol, height_mid, height_int ) + + ! ======================================= + ! set conditions for this column in TUV-x + ! ======================================= + call set_temperatures( tuvx, i_col, temperature_mid, surface_temperature ) + call set_surface_albedo( tuvx, i_col, surface_albedo ) + call set_radiator_profiles( tuvx, i_col, ncol, fixed_species_conc, & + species_vmr, exo_column_conc ) + + ! =================================================== + ! Calculate photolysis rate constants for this column + ! =================================================== + call tuvx%core_%run( solar_zenith_angle = & + solar_zenith_angle(i_col) * 180.0_r8 / pi, & + earth_sun_distance = earth_sun_distance, & + photolysis_rate_constants = & + tuvx%photo_rates_(i_col,:,:) ) + end do + + call output_diagnostics( tuvx, ncol, lchnk ) + end associate end subroutine tuvx_get_photo_rates !================================================================================================ + !----------------------------------------------------------------------- + ! Cleans up memory associated with TUV-x calculators + !----------------------------------------------------------------------- subroutine tuvx_finalize( ) -!----------------------------------------------------------------------- -! -! Purpose: clean up memory associated with TUV-x calculators -! -!----------------------------------------------------------------------- -!----------------------------------------------------------------------- -! Local variables -!----------------------------------------------------------------------- integer :: i_core if( allocated( tuvx_ptrs ) ) then @@ -228,14 +403,18 @@ subroutine tuvx_finalize( ) end subroutine tuvx_finalize !================================================================================================ - - integer function thread_id( ) -!----------------------------------------------------------------------- +!================================================================================================ ! -! Purpose: returns the id of the current OpenMP thread, or 1 if not -! using OpenMP (1 <= id <= max_threads()) +! Support functions ! -!----------------------------------------------------------------------- +!================================================================================================ +!================================================================================================ + + !----------------------------------------------------------------------- + ! Returns the id of the current OpenMP thread, or 1 if not + ! using OpenMP (1 <= id <= max_threads()) + !----------------------------------------------------------------------- + integer function thread_id( ) #ifdef _OPENMP use omp_lib, only : omp_get_thread_num #endif @@ -250,13 +429,11 @@ end function thread_id !================================================================================================ + !----------------------------------------------------------------------- + ! Returns the number of threads available for calculations at + ! runtime + !----------------------------------------------------------------------- integer function max_threads( ) -!----------------------------------------------------------------------- -! -! Purpose: returns the number of threads available for calculations at -! runtime -! -!----------------------------------------------------------------------- #ifdef _OPENMP use omp_lib, only : omp_get_max_threads #endif @@ -271,14 +448,12 @@ end function max_threads !================================================================================================ + !----------------------------------------------------------------------- + ! Prints initialization conditions to the log file + !----------------------------------------------------------------------- subroutine log_initialization( ) -!----------------------------------------------------------------------- -! -! Purpose: prints initialization conditions to the log file -! -!----------------------------------------------------------------------- - use cam_logfile, only : iulog + use cam_logfile, only : iulog ! log info output unit use musica_string, only : to_char use spmd_utils, only : main_task => masterprocid, & is_main_task => masterproc @@ -292,15 +467,713 @@ subroutine log_initialization( ) #endif #ifdef _OPENMP write(iulog,*) " - with OpenMP support for "// & - trim( to_char( max_threads( ) ) )//" threads, on thread" & + trim( to_char( max_threads( ) ) )//" threads, on thread " & //trim( to_char( thread_id( ) ) ) #else write(iulog,*) " - without OpenMP support" #endif - write(iulog,*) " - with configuration file: '"//tuvx_config_path//"'" + write(iulog,*) " - with configuration file: '"//trim( tuvx_config_path )//"'" + if( aerosol_exists ) then + write(iulog,*) " - with on-line aerosols" + else + write(iulog,*) " - without on-line aerosols" + end if end if end subroutine log_initialization + +!================================================================================================ + + !----------------------------------------------------------------------- + ! Registers fields for diagnostic output + !----------------------------------------------------------------------- + subroutine initialize_diagnostics( this ) + + use cam_history, only : addfld + use musica_string, only : string_t + + type(tuvx_ptr), intent(in) :: this + + type(string_t), allocatable :: labels(:) + integer :: i_label + + if( .not. enable_diagnostics ) then + allocate( diagnostics( 0 ) ) + return + end if + + ! ========================================================== + ! add output for specific photolysis reaction rate constants + ! ========================================================== + labels = this%core_%photolysis_reaction_labels( ) + allocate( diagnostics( size( labels ) ) ) + do i_label = 1, size( labels ) + diagnostics( i_label )%name_ = trim( labels( i_label )%to_char( ) ) + diagnostics( i_label )%index_ = i_label + call addfld( "tuvx_"//diagnostics( i_label )%name_, (/ 'lev' /), 'A', 'sec-1', & + 'photolysis rate constant' ) + end do + + end subroutine initialize_diagnostics + +!================================================================================================ + + !----------------------------------------------------------------------- + ! Outputs diagnostic information for the current time step + !----------------------------------------------------------------------- + subroutine output_diagnostics( this, ncol, lchnk ) + + use cam_history, only : outfld + + type(tuvx_ptr), intent(in) :: this + integer, intent(in) :: ncol ! number of active columns on this thread + integer, intent(in) :: lchnk ! identifier for this thread + + integer :: i_diag + + if( .not. enable_diagnostics ) return + + do i_diag = 1, size( diagnostics ) + associate( diag => diagnostics( i_diag ) ) + call outfld( "tuvx_"//diag%name_, this%photo_rates_(:ncol,:,diag%index_), & + ncol, lchnk ) + end associate + end do + + end subroutine output_diagnostics + +!================================================================================================ + + !----------------------------------------------------------------------- + ! Creates and loads a grid warehouse with grids that CAM will + ! update at runtime + !----------------------------------------------------------------------- + function get_cam_grids( wavelength_path ) result( grids ) + + use musica_assert, only : assert_msg + use musica_config, only : config_t + use tuvx_grid, only : grid_t + use tuvx_grid_factory, only : grid_builder + use tuvx_grid_from_host, only : grid_from_host_t + use tuvx_grid_warehouse, only : grid_warehouse_t + + character(len=*), intent(in) :: wavelength_path ! path to the wavelength data file + class(grid_warehouse_t), pointer :: grids ! collection of grids to be updated by CAM + + character(len=*), parameter :: my_name = "CAM grid creator" + class(grid_t), pointer :: host_grid + type(config_t) :: config + + grids => grid_warehouse_t( ) + + ! ========================= + ! heights above the surface + ! ========================= + host_grid => grid_from_host_t( "height", "km", pver ) + call grids%add( host_grid ) + deallocate( host_grid ) + + ! ============================================ + ! wavelengths (will not be updated at runtime) + ! ============================================ + call config%empty( ) + call config%add( "type", "from csv file", my_name ) + call config%add( "name", "wavelength", my_name ) + call config%add( "units", "nm", my_name ) + call config%add( "file path", wavelength_path, my_name ) + host_grid => grid_builder( config ) + call grids%add( host_grid ) + deallocate( host_grid ) + + end function get_cam_grids + +!================================================================================================ + + !----------------------------------------------------------------------- + ! Creates and loads a profile warehouse with profiles that CAM + ! will update at runtime + !----------------------------------------------------------------------- + function get_cam_profiles( grids ) result( profiles ) + + use tuvx_grid, only : grid_t + use tuvx_grid_warehouse, only : grid_warehouse_t + use tuvx_profile_from_host, only : profile_from_host_t + use tuvx_profile_warehouse, only : profile_warehouse_t + + class(grid_warehouse_t), intent(in) :: grids ! CAM grids used in TUV-x + class(profile_warehouse_t), pointer :: profiles ! collection of profiles to be updated by CAM + + class(profile_from_host_t), pointer :: host_profile + class(grid_t), pointer :: height, wavelength + + profiles => profile_warehouse_t( ) + height => grids%get_grid( "height", "km" ) + wavelength => grids%get_grid( "wavelength", "nm" ) + + ! ================================== + ! Temperature profile on height grid + ! ================================== + host_profile => profile_from_host_t( "temperature", "K", height%size( ) ) + call profiles%add( host_profile ) + deallocate( host_profile ) + + ! ================================= + ! Surface albedo on wavelength grid + ! ================================= + host_profile => profile_from_host_t( "surface albedo", "none", wavelength%size( ) ) + call profiles%add( host_profile ) + deallocate( host_profile ) + + ! ======================================== + ! Extraterrestrial flux on wavelength grid + ! ======================================== + host_profile => profile_from_host_t( "extraterrestrial flux", "photon cm-2 s-1", & + wavelength%size( ) ) + call profiles%add( host_profile ) + deallocate( host_profile ) + + ! =========== + ! Air profile + ! =========== + host_profile => profile_from_host_t( "air", "molecule cm-3", height%size( ) ) + call profiles%add( host_profile ) + deallocate( host_profile ) + + ! ========== + ! O3 profile + ! ========== + host_profile => profile_from_host_t( "O3", "molecule cm-3", height%size( ) ) + call profiles%add( host_profile ) + deallocate( host_profile ) + + ! ========== + ! O2 profile + ! ========== + host_profile => profile_from_host_t( "O2", "molecule cm-3", height%size( ) ) + call profiles%add( host_profile ) + deallocate( host_profile ) + + deallocate( height ) + deallocate( wavelength ) + + end function get_cam_profiles + +!================================================================================================ + + !----------------------------------------------------------------------- + ! Creates and loads a radiator warehouse with radiators that CAM + ! update at runtime + !----------------------------------------------------------------------- + function get_cam_radiators( grids ) result( radiators ) + + use tuvx_grid, only : grid_t + use tuvx_grid_warehouse, only : grid_warehouse_t + use tuvx_radiator_from_host, only : radiator_from_host_t + use tuvx_radiator_warehouse, only : radiator_warehouse_t + + type(grid_warehouse_t), intent(in) :: grids ! CAM grids used in TUV-x + class(radiator_warehouse_t), pointer :: radiators ! collection of radiators to be updated by CAM + + class(radiator_from_host_t), pointer :: host_radiator + class(grid_t), pointer :: height, wavelength + + radiators => radiator_warehouse_t( ) + height => grids%get_grid( "height", "km" ) + wavelength => grids%get_grid( "wavelength", "nm" ) + + ! ================ + ! Aerosol radiator + ! ================ + host_radiator => radiator_from_host_t( "aerosol", height, wavelength ) + call radiators%add( host_radiator ) + deallocate( host_radiator ) + + deallocate( height ) + deallocate( wavelength ) + + end function get_cam_radiators + +!================================================================================================ + + !----------------------------------------------------------------------- + ! Forms connections between CAM and TUV-x data structures + ! + ! - creates updaters for each grid and profile that CAM will use + ! to update TUV-x at each timestep. + ! - allocates working arrays when needed for interpolation between + ! grids + ! - initializes CAM modules if necessary and sets parameters needed + ! for runtime access of CAM data + ! + !----------------------------------------------------------------------- + subroutine create_updaters( this, grids, profiles, radiators ) + + use modal_aer_opt, only : modal_aer_opt_init + use ppgrid, only : pcols ! maximum number of columns + use rad_constituents, only : rad_cnst_get_info + use musica_assert, only : assert + use tuvx_grid, only : grid_t + use tuvx_grid_from_host, only : grid_updater_t + use tuvx_grid_warehouse, only : grid_warehouse_t + use tuvx_profile, only : profile_t + use tuvx_profile_from_host, only : profile_updater_t + use tuvx_profile_warehouse, only : profile_warehouse_t + use tuvx_radiator, only : radiator_t + use tuvx_radiator_from_host, only : radiator_updater_t + use tuvx_radiator_warehouse, only : radiator_warehouse_t + + class(tuvx_ptr), intent(inout) :: this + class(grid_warehouse_t), intent(in) :: grids + class(profile_warehouse_t), intent(in) :: profiles + class(radiator_warehouse_t), intent(in) :: radiators + + class(grid_t), pointer :: height, wavelength + class(profile_t), pointer :: host_profile + class(radiator_t), pointer :: host_radiator + integer :: n_modes + logical :: found + + ! ============= + ! Grid updaters + ! ============= + + height => grids%get_grid( "height", "km" ) + this%grids_( GRID_INDEX_HEIGHT ) = this%core_%get_updater( height, found ) + call assert( 213798815, found ) + + ! ============================================ + ! wavelength grid cannot be updated at runtime + ! ============================================ + wavelength => grids%get_grid( "wavelength", "nm" ) + allocate( this%wavelength_edges_( wavelength%size( ) + 1 ) ) + allocate( this%wavelength_values_( wavelength%size( ) + 1 ) ) + allocate( this%wavelength_mid_values_( wavelength%size( ) ) ) + this%wavelength_edges_(:) = wavelength%edge_(:) + + ! ============================== + ! optical property working array + ! ============================== + allocate( this%optical_depth_( pcols, height%size( ), wavelength%size( ) ) ) + allocate( this%single_scattering_albedo_( pcols, height%size( ), wavelength%size( ) ) ) + allocate( this%asymmetry_factor_( pcols, height%size( ), wavelength%size( ) ) ) + + deallocate( height ) + deallocate( wavelength ) + + ! ================ + ! Profile updaters + ! ================ + + host_profile => profiles%get_profile( "temperature", "K" ) + this%profiles_( PROFILE_INDEX_TEMPERATURE ) = this%core_%get_updater( host_profile, found ) + call assert( 418735162, found ) + deallocate( host_profile ) + + host_profile => profiles%get_profile( "surface albedo", "none" ) + this%profiles_( PROFILE_INDEX_ALBEDO ) = this%core_%get_updater( host_profile, found ) + call assert( 720785186, found ) + deallocate( host_profile ) + + host_profile => profiles%get_profile( "extraterrestrial flux", "photon cm-2 s-1" ) + this%profiles_( PROFILE_INDEX_ET_FLUX ) = this%core_%get_updater( host_profile, found ) + call assert( 550628282, found ) + deallocate( host_profile ) + + host_profile => profiles%get_profile( "air", "molecule cm-3" ) + this%profiles_( PROFILE_INDEX_AIR ) = this%core_%get_updater( host_profile, found ) + call assert( 380471378, found ) + deallocate( host_profile ) + + host_profile => profiles%get_profile( "O3", "molecule cm-3" ) + this%profiles_( PROFILE_INDEX_O3 ) = this%core_%get_updater( host_profile, found ) + call assert( 210314474, found ) + deallocate( host_profile ) + + host_profile => profiles%get_profile( "O2", "molecule cm-3" ) + this%profiles_( PROFILE_INDEX_O2 ) = this%core_%get_updater( host_profile, found ) + call assert( 105165970, found ) + deallocate( host_profile ) + + ! ================= + ! radiator updaters + ! ================= + + ! ==================================================================== + ! determine if aerosol optical properties will be available, and if so + ! intialize the aerosol optics module + ! ==================================================================== + call rad_cnst_get_info( 0, nmodes = n_modes ) + if( n_modes > 0 .and. .not. aerosol_exists ) then + aerosol_exists = .true. + call modal_aer_opt_init( ) + else + ! TODO are there default aerosol optical properties that should be used + ! when an aerosol module is not available? + this%optical_depth_(:,:,:) = 0.0_r8 + this%single_scattering_albedo_(:,:,:) = 0.0_r8 + this%asymmetry_factor_(:,:,:) = 0.0_r8 + end if + host_radiator => radiators%get_radiator( "aerosol" ) + this%radiators_( RADIATOR_INDEX_AEROSOL ) = & + this%core_%get_updater( host_radiator, found ) + call assert( 675200430, found ) + nullify( host_radiator ) + + end subroutine create_updaters + +!================================================================================================ + + !----------------------------------------------------------------------- + ! Sets the height values in TUV-x for the given column + ! + ! NOTE: This function must be called before updating any profile data. + ! + ! CAM to TUV-x height grid mapping + ! + ! TUV-x heights are "bottom-up" and require atmospheric constituent + ! concentrations at interfaces. Therefore, CAM mid-points are used as + ! TUV-x grid interfaces, with an additional layer introduced between + ! the surface and the lowest CAM mid-point. + ! + ! ---- (interface) ===== (mid-point) + ! + ! CAM TUV-x + ! ------(top)------ i_int = 1 (exo values) + ! ================= i_mid = 1 -------(top)------ i_int = pver + 1 + ! ----------------- i_int = 2 ================== i_mid = pver + ! ------------------ i_int = pver + ! || + ! || || + ! || + ! ----------------- i_int = pver + ! ================= i_imd = pver ------------------ i_int = 2 + ! ================== i_mid = 1 + ! -----(ground)---- i_int = pver+1 -----(ground)----- i_int = 1 + ! + !----------------------------------------------------------------------- + subroutine set_heights( this, i_col, ncol, height_mid, height_int ) + + use ppgrid, only : pcols ! maximum number of columns + + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! column to set conditions for + integer, intent(in) :: ncol ! number of colums to calculated photolysis for + real(r8), intent(in) :: height_mid(pcols,pver) ! height above the surface at mid-points (km) + real(r8), intent(in) :: height_int(pcols,pver+1) ! height above the surface at interfaces (km) + + integer :: i_level + real(r8) :: edges(pver+1) + real(r8) :: mid_points(pver) + + edges(1) = 0.0_r8 + edges(2:pver+1) = height_mid(i_col,pver:1:-1) + mid_points(1) = height_mid(i_col,pver) * 0.5_r8 + mid_points(2:pver) = height_int(i_col,pver:2:-1) + call this%grids_( GRID_INDEX_HEIGHT )%update( edges = edges, mid_points = mid_points ) + this%height_delta_(1:pver) = edges(2:pver+1) - edges(1:pver) + + end subroutine set_heights + +!================================================================================================ + + !----------------------------------------------------------------------- + ! Sets the temperatures in TUV-x for the given column + ! + ! See description of `set_heights` for CAM <-> TUV-x vertical grid + ! mapping. + !----------------------------------------------------------------------- + subroutine set_temperatures( this, i_col, temperature_mid, surface_temperature ) + + use ppgrid, only : pcols ! maximum number of columns + + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! column to set conditions for + real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) + real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) + + real(r8) :: edges(pver+1) + + edges(1) = surface_temperature(i_col) + edges(2:pver+1) = temperature_mid(i_col,pver:1:-1) + call this%profiles_( PROFILE_INDEX_TEMPERATURE )%update( edge_values = edges ) + + end subroutine set_temperatures + +!================================================================================================ + + !----------------------------------------------------------------------- + ! Sets the surface albedo in TUV-x for the given column + ! + ! CAM uses a single value for surface albedo at all wavelengths + !----------------------------------------------------------------------- + subroutine set_surface_albedo( this, i_col, surface_albedo ) + + use ppgrid, only : pcols ! maximum number of columns + + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! column to set conditions for + real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) + + this%wavelength_values_(:) = surface_albedo( i_col ) + call this%profiles_( PROFILE_INDEX_ALBEDO )%update( & + edge_values = this%wavelength_values_(:) ) + + end subroutine set_surface_albedo + +!================================================================================================ + + !----------------------------------------------------------------------- + ! Sets the extraterrestrial flux in TUV-x for the given column + ! + ! Extraterrestrial flux is read from data files and interpolated to the + ! TUV-x wavelength grid. CAM ET Flux values are multiplied by the + ! width of the wavelength bins to get the TUV-x units of photon cm-2 s-1 + ! + ! NOTE: TUV-x only uses mid-point values for ET Flux + !----------------------------------------------------------------------- + subroutine set_et_flux( this ) + + use mo_util, only : rebin + use solar_irrad_data, only : nbins, & ! number of wavelength bins + we, & ! wavelength bin edges + sol_etf ! extraterrestrial flux + ! (photon cm-2 nm-1 s-1) + + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + real(r8) :: et_flux_orig(nbins) + integer :: n_tuvx_bins + + et_flux_orig(:) = sol_etf(:) * ( we(2:nbins+1) - we(1:nbins) ) + n_tuvx_bins = size(this%wavelength_mid_values_) + call rebin( nbins, n_tuvx_bins, we, this%wavelength_edges_, et_flux_orig, & + this%wavelength_mid_values_ ) + this%wavelength_values_(1) = this%wavelength_mid_values_(1) + this%wavelength_values_(2:n_tuvx_bins+1) = this%wavelength_mid_values_(1:n_tuvx_bins) + call this%profiles_( PROFILE_INDEX_ET_FLUX )%update( & + mid_point_values = this%wavelength_mid_values_, & + edge_values = this%wavelength_values_) + + end subroutine set_et_flux + +!================================================================================================ + + !----------------------------------------------------------------------- + ! Sets the profiles of optically active atmospheric constituents + ! in TUV-x for the given column + ! + ! See `set_height` for a description of the CAM <-> TUV-x vertical grid + ! mapping. + ! + ! Above layer densities are calculated using a scale height for air + ! and pre-calculated values for O2 and O3 + !----------------------------------------------------------------------- + subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species_vmr, & + exo_column_conc ) + + use chem_mods, only : gas_pcnst, & ! number of non-fixed species + nfs, & ! number of fixed species + nabscol, & ! number of absorbing species (radiators) + indexm ! index for air density in fixed species array + + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! column to set conditions for + integer, intent(in) :: ncol ! number of columns + real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities + ! (molecule cm-3) + real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) + real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! above column densities + ! (molecule cm-3) + + real(r8) :: edges(pver+1), densities(pver) + real(r8) :: exo_val + real(r8), parameter :: km2cm = 1.0e5 ! conversion from km to cm + + ! =========== + ! air profile + ! =========== + edges(1) = fixed_species_conc(i_col,pver,indexm) + edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,indexm) + densities(1:pver) = this%height_delta_(1:pver) * km2cm * & + sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) + call this%profiles_( PROFILE_INDEX_AIR )%update( & + edge_values = edges, layer_densities = densities, & + scale_height = 8.01_r8 ) ! scale height in [km] + + ! ========== + ! O2 profile + ! ========== + if( is_fixed_O2 ) then + edges(1) = fixed_species_conc(i_col,pver,index_O2) + edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,index_O2) + else if( index_O2 > 0 ) then + edges(1) = species_vmr(i_col,pver,index_O2) * & + fixed_species_conc(i_col,pver,indexm) + edges(2:pver+1) = species_vmr(i_col,pver:1:-1,index_O2) * & + fixed_species_conc(i_col,pver:1:-1,indexm) + else + edges(:) = 0.0_r8 + end if + exo_val = exo_column_conc(i_col,0,1) + densities(1:pver) = this%height_delta_(1:pver) * km2cm * & + sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) + call this%profiles_( PROFILE_INDEX_O2 )%update( & + edge_values = edges, layer_densities = densities, & + exo_density = exo_val ) + + ! ========== + ! O3 profile + ! ========== + if( is_fixed_O3 ) then + edges(1) = fixed_species_conc(i_col,pver,index_O3) + edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,index_O3) + else if( index_O3 > 0 ) then + edges(1) = species_vmr(i_col,pver,index_O3) * & + fixed_species_conc(i_col,pver,indexm) + edges(2:pver+1) = species_vmr(i_col,pver:1:-1,index_O3) * & + fixed_species_conc(i_col,pver:1:-1,indexm) + else + edges(:) = 0.0_r8 + end if + if( nabscol >= 2 ) then + exo_val = exo_column_conc(i_col,0,2) + else + exo_val = 0.0_r8 + end if + densities(1:pver) = this%height_delta_(1:pver) * km2cm * & + sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) + call this%profiles_( PROFILE_INDEX_O3 )%update( & + edge_values = edges, layer_densities = densities, & + exo_density = exo_val ) + + ! =============== + ! aerosol profile + ! =============== + call this%radiators_( RADIATOR_INDEX_AEROSOL )%update( & + optical_depths = this%optical_depth_(i_col,:,:), & + single_scattering_albedos = this%single_scattering_albedo_(i_col,:,:), & + asymmetry_factors = this%asymmetry_factor_(i_col,:,:) ) + + end subroutine set_radiator_profiles + +!================================================================================================ + + !----------------------------------------------------------------------- + ! Updates working arrays of aerosol optical properties for all + ! columns from the aerosol package + !----------------------------------------------------------------------- + subroutine get_aerosol_optical_properties( this, state, pbuf ) + + use aer_rad_props, only : aer_rad_props_sw + use mo_util, only : rebin + use physics_types, only : physics_state + use physics_buffer, only : physics_buffer_desc + use ppgrid, only : pcols ! maximum number of columns + use radconstants, only : nswbands, & ! Number of CAM shortwave radiation bands + get_sw_spectral_boundaries + + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + type(physics_state), target, intent(in) :: state + type(physics_buffer_desc), pointer, intent(inout) :: pbuf(:) + + real(r8) :: wavelength_edges(nswbands+1) ! CAM radiation wavelength grid edges [nm] + real(r8) :: aer_tau (pcols,0:pver,nswbands) ! aerosol extinction optical depth + real(r8) :: aer_tau_w (pcols,0:pver,nswbands) ! aerosol single scattering albedo * tau + real(r8) :: aer_tau_w_g(pcols,0:pver,nswbands) ! aerosol assymetry parameter * w * tau + real(r8) :: aer_tau_w_f(pcols,0:pver,nswbands) ! aerosol forward scattered fraction * w * tau + real(r8) :: low_bound(nswbands) ! lower bound of CAM wavenumber bins + real(r8) :: high_bound(nswbands) ! upper bound of CAM wavenumber bins + integer :: n_night ! number of night columns + integer :: idx_night(pcols) ! indices of night columns + integer :: n_tuvx_bins ! number of TUV-x wavelength bins + integer :: i_col, i_level ! column and level indices + + ! ============================================ + ! do nothing if no aerosol module is available + ! ============================================ + if( .not. aerosol_exists ) return + + ! TODO just assume all daylight columns for now + ! can adjust later if necessary + n_night = 0 + idx_night(:) = 0 + + ! =========================================================== + ! get aerosol optical properties on native CAM radiation grid + ! =========================================================== + call aer_rad_props_sw( 0, state, pbuf, n_night, idx_night, & + aer_tau, aer_tau_w, aer_tau_w_g, aer_tau_w_f ) + + ! ========================================================================= + ! Convert CAM wavenumber grid to wavelength grid and re-order optics arrays + ! NOTE: CAM wavenumber grid is continuous and increasing, except that the + ! last bin should be moved to the just before the first bin (!?!) + ! ========================================================================= + call get_sw_spectral_boundaries( low_bound, high_bound, 'nm' ) + wavelength_edges(1:nswbands-1) = high_bound(nswbands-1:1:-1) + wavelength_edges(nswbands ) = high_bound(nswbands ) + wavelength_edges(nswbands+1 ) = low_bound( nswbands ) + call reorder_optics_array( aer_tau ) + call reorder_optics_array( aer_tau_w ) + call reorder_optics_array( aer_tau_w_g ) + + ! ============================================================= + ! regrid optical properties to TUV-x wavelength and height grid + ! ============================================================= + ! TODO is this the correct regridding scheme to use? + n_tuvx_bins = size(this%wavelength_mid_values_) + do i_col = 1, pcols + do i_level = 1, pver + call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & + aer_tau(i_col,pver-i_level,:), this%optical_depth_(i_col,i_level,:)) + call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & + aer_tau_w(i_col,pver-i_level,:), this%single_scattering_albedo_(i_col,i_level,:)) + call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & + aer_tau_w_g(i_col,pver-i_level,:), this%asymmetry_factor_(i_col,i_level,:)) + end do + end do + + ! ================================================================ + ! back-calculate the single scattering albedo and asymmetry factor + ! ================================================================ + associate( tau => this%optical_depth_, & + omega => this%single_scattering_albedo_, & + g => this%asymmetry_factor_ ) + where(omega > 0.0_r8) + g = g / omega + elsewhere + g = 0.0_r8 + end where + where(tau > 0.0_r8) + omega = omega / tau + elsewhere + omega = 0.0_r8 + end where + end associate + + end subroutine get_aerosol_optical_properties + +!================================================================================================ + + !----------------------------------------------------------------------- + ! Reorders elements of an optical property array for conversion + ! from wavenumber to wavelength grid and out-of-order final + ! element in CAM wavenumber grid + !----------------------------------------------------------------------- + subroutine reorder_optics_array( optics_array ) + + use ppgrid, only : pcols ! maximum number of columns + use radconstants, only : nswbands ! Number of CAM shortwave radiation bands + + real(r8), intent(inout) :: optics_array(pcols,0:pver,nswbands) ! optics array to reorder + + real(r8) :: working(pcols,0:pver,nswbands) ! working array + + working(:,:,:) = optics_array(:,:,:) + optics_array(:,:,1:nswbands-1) = working(:,:,nswbands-1:1:-1) + + end subroutine reorder_optics_array + !================================================================================================ end module mo_tuvx diff --git a/src/physics/cam/modal_aer_opt.F90 b/src/physics/cam/modal_aer_opt.F90 index 5c95c17840..9ed61aa1a7 100644 --- a/src/physics/cam/modal_aer_opt.F90 +++ b/src/physics/cam/modal_aer_opt.F90 @@ -128,8 +128,14 @@ subroutine modal_aer_opt_init() character(len=10) :: fldname character(len=128) :: lngname + logical, save :: is_initialized = .false. + !---------------------------------------------------------------------------- + ! Check if module has already been initialized + if (is_initialized) return + is_initialized = .true. + rmmin = 0.01e-6_r8 rmmax = 25.e-6_r8 xrmin = log(rmmin) From 9ded52f2c948362f8bea99ff3ad52fa3f97d1944 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Wed, 16 Nov 2022 12:02:45 -0700 Subject: [PATCH 33/84] draft MOZART-TS1 TUV-x config file --- cime_config/buildnml | 2 + cime_config/tuvx_MOZART_TS1.json | 1193 ++++++++++++++++++++++++++++++ 2 files changed, 1195 insertions(+) create mode 100644 cime_config/tuvx_MOZART_TS1.json diff --git a/cime_config/buildnml b/cime_config/buildnml index d78d9fcb7e..7bec211021 100755 --- a/cime_config/buildnml +++ b/cime_config/buildnml @@ -208,6 +208,8 @@ def buildnml(case, caseroot, compname): os.path.join(rundir, "tuvx_BASIC.json")) shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_MOZART.json"), \ os.path.join(rundir, "tuvx_MOZART.json")) + shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_MOZART_TS1.json"), \ + os.path.join(rundir, "tuvx_MOZART_TS1.json")) ############################################################################### def _main_func(): diff --git a/cime_config/tuvx_MOZART_TS1.json b/cime_config/tuvx_MOZART_TS1.json new file mode 100644 index 0000000000..ba8d218f17 --- /dev/null +++ b/cime_config/tuvx_MOZART_TS1.json @@ -0,0 +1,1193 @@ +{ + "__description": "TUV-x configuration for the MOZART-TS1 chemical mechanism", + "O2 absorption" : { + "cross section parameters file": "data/cross_sections/O2_parameters.txt" + }, + "grids": { + }, + "profiles": { + }, + "radiative transfer": { + "solver": { + "type": "delta eddington" + }, + "cross sections": [ + { + "name": "air", + "type": "air" + }, + { + "name": "O3", + "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "type": "O3" + }, + { + "name": "O2", + "netcdf files": ["data/cross_sections/O2_1.nc"], + "type": "base", + "lower extrapolation": { "type": "boundary" } + } + ], + "radiators": [ + { + "name": "air", + "type": "base", + "cross section": "air", + "vertical profile": "air", + "vertical profile units": "molecule cm-3" + }, + { + "name": "O2", + "type": "base", + "cross section": "O2", + "vertical profile": "O2", + "vertical profile units": "molecule cm-3" + }, + { + "name": "O3", + "type": "base", + "cross section": "O3", + "vertical profile": "O3", + "vertical profile units": "molecule cm-3" + } + ] + }, + "photolysis": { + "reactions": [ + { + "name": "jo2_b", + "__reaction": "O2 + hv -> O + O", + "cross section": { + "netcdf files": ["data/cross_sections/O2_1.nc"], + "type": "base", + "lower extrapolation": { "type": "boundary" } + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jo3_a", + "__reaction": "O3 + hv -> O2 + O(1D)", + "cross section": { + "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(1D)" + } + }, + { + "name": "jo3_b", + "__reaction": "O3 + hv -> O2 + O(3P)", + "cross section": { + "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(3P)" + } + }, + { + "name": "jn2o", + "__reaction": "N2O + hv -> N2 + O(1D)", + "cross section": { + "type": "N2O+hv->N2+O(1D)" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jno2", + "__reaction": "NO2 + hv -> NO + O(3P)", + "cross section": { + "netcdf files": ["data/cross_sections/NO2_1.nc"], + "type": "NO2 tint" + }, + "quantum yield": { + "netcdf files": ["data/quantum_yields/NO2_1.nc"], + "type": "NO2 tint", + "lower extrapolation": { "type": "boundary" } + } + }, + { + "name": "jn2o5_a", + "__reaction": "N2O5 + hv -> NO2 + NO3", + "cross section": { + "netcdf files": [ + "data/cross_sections/N2O5_1.nc", + "data/cross_sections/N2O5_2.nc" + ], + "type": "N2O5+hv->NO2+NO3" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhno3", + "__reaction": "HNO3 + hv -> OH + NO2", + "cross section": { + "netcdf files": [ + "data/cross_sections/HNO3_1.nc" + ], + "type": "HNO3+hv->OH+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jno3_a", + "__reaction": "NO3 + hv -> NO2 + O(3P)", + "cross section": { + "netcdf files": [ + "data/cross_sections/NO3_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/NO3-NO2+O(3P)_1.nc" + ], + "type": "tint", + "lower extrapolation": { + "type": "constant", + "value": 1.0 + } + } + }, + { + "name": "jno3_b", + "__reaction": "NO3 + hv -> NO + O2", + "cross section": { + "netcdf files": [ + "data/cross_sections/NO3_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/NO3-NO+O2_1.nc" + ], + "type": "tint" + } + }, + { + "name": "jch3ooh", + "__reaction": "CH3OOH + hv -> CH3O + OH", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3OOH_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch2o_a", + "__reaction": "CH2O + hv -> H + HCO", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH2O_1.nc" + ], + "type": "CH2O" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH2O_1.nc" + ], + "type": "base", + "lower extrapolation": { + "type": "boundary" + } + } + }, + { + "name": "jch2o_b", + "__reaction": "CH2O + hv -> H2 + CO", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH2O_1.nc" + ], + "type": "CH2O" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH2O_1.nc" + ], + "type": "CH2O", + "lower extrapolation": { + "type": "boundary" + } + } + }, + { + "name": "jh2o2", + "__reaction": "H2O2 + hv -> OH + OH", + "cross section": { + "netcdf files": [ + "data/cross_sections/H2O2_1.nc" + ], + "type": "H2O2+hv->OH+OH" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3cho", + "__reaction": "CH3CHO + hv -> CH3 + HCO", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3CHO_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH3CHO_1.nc" + ], + "type": "CH3CHO+hv->CH3+HCO" + } + }, + { + "name": "jpan", + "__reaction": "PAN + hv -> 0.6*CH3CO3 + 0.6*NO2 + 0.4*CH3O2 + 0.4*NO3 + 0.4*CO2", + "cross section": { + "netcdf files": [ + "data/cross_sections/PAN_1.nc" + ], + "type": "CH3ONO2+hv->CH3O+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jmvk", + "__reaction": "MVK + hv -> 0.7*C3H6 + 0.7*CO + 0.3*CH3O2 + 0.3*CH3CO3", + "cross section": { + "netcdf files": [ + "data/cross_sections/MVK_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "MVK+hv->Products" + } + }, + { + "name": "jacet", + "__reaction": "CH3COCH3 + hv -> CH3CO + CH3", + "cross section": { + "netcdf files": ["data/cross_sections/CH3COCH3_1.nc"], + "type": "CH3COCH3+hv->CH3CO+CH3" + }, + "quantum yield": { + "type": "CH3COCH3+hv->CH3CO+CH3" + } + }, + { + "name": "jmgly", + "__reaction": "CH3COCHO + hv -> CH3CO3 + CO + HO2", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3COCHO_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "CH3COCHO+hv->CH3CO+HCO" + } + }, + { + "name": "jglyald", + "__reaction": "GLYALD + hv -> 2*HO2 + CO + CH2O", + "cross section": { + "netcdf files": [ + "data/cross_sections/HOCH2CHO_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jglyoxal", + "__reaction": "GLYOXAL + hv -> 2*CO + 2*HO2", + "__comments": "NOTE the products of this reaction don't exactly match", + "cross section": { + "netcdf files": [ + "data/cross_sections/CHOCHO_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CHOCHO-H2_CO_CO_1.nc" + ], + "type": "base", + "lower extrapolation": { + "type": "boundary" + } + } + }, + { + "name": "jbrcl", + "__reaction": "BrCl + hv -> Br + Cl", + "cross section": { + "netcdf files": [ + "data/cross_sections/BrCl_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jbro", + "__reaction": "BrO + hv -> Br + O", + "cross section": { + "netcdf files": [ + "data/cross_sections/BrO_1.nc" + ], + "type": "BrO+hv->Br+O" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jbrono2_a", + "__reaction": "BrONO2 + hv -> Br + NO3", + "cross section": { + "netcdf files": [ + "data/cross_sections/BrONO2_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.85 + } + }, + { + "name": "jbrono2_b", + "__reaction": "BrONO2 + hv -> BrO + NO2", + "cross section": { + "netcdf files": [ + "data/cross_sections/BrONO2_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.15 + } + }, + { + "name": "jccl4", + "__reaction": "CCl4 + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CCl4_1.nc" + ], + "type": "CCl4+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcf2clbr", + "__reaction": "CF2BrCl + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CF2BrCl_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcf3br", + "__reaction": "CF3Br + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CF3Br_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcfcl3", + "__reaction": "CCl3F + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CFC-11_1.nc" + ], + "type": "CCl3F+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcfc113", + "__reaction": "CFC-113 + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CFC-113_1.nc" + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcfc114", + "__reaction": "CFC-114 + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CFC-114_1.nc" + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcfc115", + "__reaction": "CFC-115 + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CFC-115_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcf2cl2", + "__reaction": "CCl2F2 + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CFC-12_1.nc" + ], + "type": "CCl3F+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3br", + "__reaction": "CH3Br + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3Br_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3ccl3", + "__reaction": "CH3CCl3+hv->Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3CCl3_1.nc" + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3cl", + "__reaction": "CH3Cl + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3Cl_1.nc" + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jchbr3", + "__reaction": "CHBr3 + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CHBr3_1.nc" + ], + "type": "CHBr3+hv->Products", + "lower extrapolation": { + "type": "boundary" + } + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcl2", + "__reaction": "Cl2 + hv -> Cl + Cl", + "cross section": { + "type": "Cl2+hv->Cl+Cl" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jclo", + "__reaction": "ClO + hv -> Cl + O(1D)", + "cross section": { + "netcdf files": [ + "data/cross_sections/ClO_1.nc" + ], + "type": "tint" + }, + "quantum yield": { + "type": "ClO+hv->Cl+O(1D)" + } + }, + { + "name": "jclono2_a", + "__reaction": "ClONO2 + hv -> Cl + NO3", + "cross section": { + "netcdf files": [ + "data/cross_sections/ClONO2_1.nc" + ], + "type": "ClONO2" + }, + "quantum yield": { + "type": "ClONO2+hv->Cl+NO3" + } + }, + { + "name": "jclono2_b", + "__reaction": "ClONO2 + hv -> ClO + NO2", + "cross section": { + "netcdf files": [ + "data/cross_sections/ClONO2_1.nc" + ], + "type": "ClONO2" + }, + "quantum yield": { + "type": "ClONO2+hv->ClO+NO2" + } + }, + { + "name": "jcof2", + "__reaction": "CF2O + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CF2O_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcofcl", + "__reaction": "CClFO + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CClFO_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhcfc141b", + "__reaction": "HCFC-141b + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3CFCl2_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhcfc142b", + "__reaction": "HCFC-142b + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3CF2Cl_1.nc" + ], + "type": "HCFC+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhcfc22", + "__reaction": "HCFC-22 + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CHClF2_1.nc" + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhcl", + "__reaction": "HCl + hv -> H + Cl", + "cross section": { + "netcdf files": [ + "data/cross_sections/HCl_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhobr", + "__reaction": "HOBr + hv -> OH + Br", + "cross section": { + "type": "HOBr+hv->OH+Br" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhocl", + "__reaction": "HOCl + hv -> HO + Cl", + "cross section": { + "netcdf files": [ + "data/cross_sections/HOCl_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "joclo", + "__reaction": "OClO + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/OClO_1.nc", + "data/cross_sections/OClO_2.nc", + "data/cross_sections/OClO_3.nc" + ], + "type": "OClO+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + } + ] + }, + "__CAM aliasing": [ + { + "for": "jh2o_a", + "reaction": "H2O + hv -> OH + H", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jh2o_b", + "reaction": "H2O + hv -> H2 + O1D", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jh2o_c", + "reaction": "H2O + hv -> 2*H + O", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jo2_a", + "reaction": "O2 + hv -> O + O1D", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jho2no2_a", + "reaction": "HO2NO2 + hv -> OH + NO3", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jho2no2_b", + "reaction": "HO2NO2 + hv -> NO2 + HO2", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jn2o5_b", + "reaction": "N2O5 + hv -> NO + O + NO3", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jno", + "reaction": "NO + hv -> N + O", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jalknit", + "reaction": "ALKNIT + hv -> NO2 + 0.4*CH3CHO + 0.1*CH2O + 0.25*CH3COCH3 + HO2 + 0.8*MEK", + "use": "jch3ooh" + }, + { + "for": "jpooh", + "reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", + "use": "jch3ooh" + }, + { + "for": "jch3co3h", + "reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", + "use": "jh2o2", + "scale by": 0.28 + }, + { + "for": "jmpan", + "reaction": "MPAN + hv -> MCO3 + NO2", + "use": "jpan" + }, + { + "for": "jmacr_a", + "reaction": "MACR + hv -> 1.34*HO2 + 0.66*MCO3 + 1.34*CH2O + 1.34*CH3CO3", + "comments": "TODO - find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jmacr_b", + "reaction": "MACR + hv -> 0.66*HO2 + 1.34*CO", + "comments": "TODO - find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jc2h5ooh", + "reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", + "use": "jch3ooh" + }, + { + "for": "jc3h7ooh", + "reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", + "use": "jch3ooh" + }, + { + "for": "jc6h5ooh", + "reaction": "C6H5OOH + hv -> PHENO + OH", + "use": "jch3ooh" + }, + { + "for": "jch4_a", + "reaction": "CH4 + hv -> H + CH3O2", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jch4_b", + "reaction": "CH4 + hv -> 1.44*H2 + 0.18*CH2O + 0.18*O + 0.33*OH + 0.33*H + 0.44*CO2 + 0.38*CO + 0.05*H2O", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jco2", + "reaction": "CO2 + hv -> CO + O", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jeooh", + "reaction": "EOOH + hv -> EO + OH", + "use": "jch3ooh" + }, + { + "for": "jrooh", + "reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", + "use": "jch3ooh" + }, + { + "for": "jxooh", + "reaction": "XOOH + hv -> OH", + "use": "jch3ooh" + }, + { + "for": "jonitr", + "reaction": "ONITR + hv -> NO2", + "use": "jch3cho" + }, + { + "for": "jisopooh", + "reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", + "use": "jch3ooh" + }, + { + "for": "jhyac", + "reaction": "HYAC (CH3COCH2OH) + hv -> CH3CO3 + HO2 + CH2O", + "comments": "TODO - find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jmek", + "reaction": "MEK + hv -> CH3CO3 + C2H5O2", + "use": "jacet" + }, + { + "for": "jalkooh", + "reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", + "use": "jch3ooh" + }, + { + "for": "jbenzooh", + "reaction": "BENZOOH + hv -> OH + GLYOXAL + 0.5*BIGALD1 + HO2", + "use": "jch3ooh" + }, + { + "for": "jbepomuc", + "reaction": "BEPOMUC + hv -> BIGALD1 + 1.5*HO2 + 1.5*CO", + "use": "jno2", + "scale by": 0.1 + }, + { + "for": "jbigald", + "reaction": "BIGALD + hv -> 0.45*CO + 0.13*GLYOXAL + 0.56*HO2 + 0.13*CH3CO3 + 0.18*CH3COCHO", + "use": "jno2", + "scale by": 0.2 + }, + { + "for": "jbigald1", + "reaction": "BIGALD1 + hv -> 0.6*MALO2 + HO2", + "use": "jno2", + "scale by": 0.14 + }, + { + "for": "jbigald2", + "reaction": "BIGALD2 + hv -> 0.6*HO2 + 0.6*DICARBO2", + "use": "jno2", + "scale by": 0.2 + }, + { + "for": "jbigald3", + "reaction": "BIGALD3 + hv -> 0.6*HO2 + 0.6*CO + 0.6*MDIALO2", + "use": "jno2", + "scale by": 0.2 + }, + { + "for": "jbigald4", + "reaction": "BIGALD4 + hv -> HO2 + CO + CH3COCHO + CH3CO3", + "use": "jno2", + "scale by": 0.006 + }, + { + "for": "jbzooh", + "reaction": "BZOOH + hv -> BZALD + OH + HO2", + "use": "jch3ooh" + }, + { + "for": "jmekooh", + "reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", + "use": "jch3ooh" + }, + { + "for": "jtolooh", + "reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", + "use": "jch3ooh" + }, + { + "for": "jterpooh", + "reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", + "use": "jch3ooh" + }, + { + "for": "jhonitr", + "reaction": "HONITR + hv -> NO2 + 0.67*HO2 + 0.33*CH3CHO + 0.33*CH2O + 0.33*CO + 0.33*GLYALD + 0.33*CH3CO3 + 0.17*HYAC + 0.17*CH3COCH3", + "use": "jch2o_a" + }, + { + "for": "jhpald", + "reaction": "HPALD + hv -> BIGALD3 + OH + HO2", + "use": "jno2", + "scale by": 0.006 + }, + { + "for": "jisopnooh", + "reaction": "ISOPNOOH + hv -> NO2 + HO2 + ISOPOOH", + "use": "jch3ooh" + }, + { + "for": "jnc4cho", + "reaction": "NC4CHO + hv -> BIGALD3 + NO2 + HO2", + "use": "jch2o_a" + }, + { + "for": "jnoa", + "reaction": "NOA + hv -> NO2 + CH2O + CH3CO3", + "use": "jch2o_a" + }, + { + "for": "jnterpooh", + "reaction": "NTERPOOH + hv -> TERPROD1 + NO2 + OH", + "use": "jch3ooh" + }, + { + "for": "jphenooh", + "reaction": "PHENOOH + hv -> OH + HO2 + 0.7*GLYOXAL", + "use": "jch3ooh" + }, + { + "for": "jtepomuc", + "reaction": "TEPOMUC + hv -> 0.5*CH3CO3 + HO2 + 1.5*CO", + "use": "jno2", + "scale by": 0.1 + }, + { + "for": "jterp2ooh", + "reaction": "TERP2OOH + hv -> OH + 0.375*CH2O + 0.3*CH3COCH3 + 0.25*CO + CO2 + TERPROD2 + HO2 + 0.25*GLYALD", + "use": "jch3ooh" + }, + { + "for": "jterpnit", + "reaction": "TERPNIT + hv -> TERPROD1 + NO2 + HO2", + "use": "jch3ooh" + }, + { + "for": "jterprd1", + "reaction": "TERPROD1 + hv -> HO2 + CO + TERPROD2", + "use": "jch3cho" + }, + { + "for": "jterprd2", + "reaction": "TERPROD2 + hv -> 0.15*RO2 + 0.68*CH2O + 0.8*CO2 + 0.5*CH3COCH3 + 0.65*CH3CO3 + 1.2*HO2 + 1.7*CO", + "use": "jch3cho" + }, + { + "for": "jxylenooh", + "reaction": "XYLENOOH + hv -> OH + HO2 + 0.34*GLYOXAL + 0.54*CH3COCHO + 0.06*BIGALD1 + 0.2*BIGALD2 + 0.15*BIGALD3 + 0.21*BIGALD4", + "use": "jch3ooh" + }, + { + "for": "jxylolooh", + "reaction": "XYLOLOOH + hv -> OH + 0.17*GLYOXAL + 0.51*CH3COCHO + HO2", + "use": "jch3ooh" + }, + { + "for": "jch2br2", + "reaction": "CH2BR2 + hv -> 2*BR", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jcl2o2", + "reaction": "CL2O2 + hv -> 2*CL", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jh2402", + "reaction": "H2402 + hv -> 2*BR + 2*COF2", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jhbr", + "reaction": "HBR + hv -> BR + H", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jhf", + "reaction": "HF + hv -> H + F", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jsf6", + "reaction": "SF6 + hv -> sink", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jh2so4", + "reaction": "H2SO4 + hv -> SO3 + H2O", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jocs", + "reaction": "OCS + hv -> S + CO", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jso", + "reaction": "SO + hv -> S + O", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jso2", + "reaction": "SO2 + hv -> SO + O", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jso3", + "reaction": "SO3 + hv -> SO2 + O", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jsoa1_a1", + "reaction": "soa1_a1 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa1_a2", + "reaction": "soa1_a2 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa2_a1", + "reaction": "soa2_a1 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa2_a2", + "reaction": "soa2_a2 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa3_a1", + "reaction": "soa3_a1 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa3_a2", + "reaction": "soa3_a2 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa4_a1", + "reaction": "soa4_a1 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa4_a2", + "reaction": "soa4_a2 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa5_a1", + "reaction": "soa5_a1 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa5_a2", + "reaction": "soa5_a2 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + } + ] +} From a3b66a0d9b2e862b6d5f1f9d78e03efb4ea79d9f Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Wed, 16 Nov 2022 14:16:35 -0700 Subject: [PATCH 34/84] draft MOZART-TSMLT TUV-x config file --- cime_config/buildnml | 2 + cime_config/tuvx_MOZART_TSMLT.json | 1200 ++++++++++++++++++++++++++++ 2 files changed, 1202 insertions(+) create mode 100644 cime_config/tuvx_MOZART_TSMLT.json diff --git a/cime_config/buildnml b/cime_config/buildnml index 7bec211021..9a85ec91bd 100755 --- a/cime_config/buildnml +++ b/cime_config/buildnml @@ -210,6 +210,8 @@ def buildnml(case, caseroot, compname): os.path.join(rundir, "tuvx_MOZART.json")) shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_MOZART_TS1.json"), \ os.path.join(rundir, "tuvx_MOZART_TS1.json")) + shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_MOZART_TSMLT.json"), \ + os.path.join(rundir, "tuvx_MOZART_TSMLT.json")) ############################################################################### def _main_func(): diff --git a/cime_config/tuvx_MOZART_TSMLT.json b/cime_config/tuvx_MOZART_TSMLT.json new file mode 100644 index 0000000000..2c07e5a1bd --- /dev/null +++ b/cime_config/tuvx_MOZART_TSMLT.json @@ -0,0 +1,1200 @@ +{ + "__description": "TUV-x configuration for the MOZART-TSMLT chemical mechanism", + "O2 absorption" : { + "cross section parameters file": "data/cross_sections/O2_parameters.txt" + }, + "grids": { + }, + "profiles": { + }, + "radiative transfer": { + "solver": { + "type": "delta eddington" + }, + "cross sections": [ + { + "name": "air", + "type": "air" + }, + { + "name": "O3", + "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "type": "O3" + }, + { + "name": "O2", + "netcdf files": ["data/cross_sections/O2_1.nc"], + "type": "base", + "lower extrapolation": { "type": "boundary" } + } + ], + "radiators": [ + { + "name": "air", + "type": "base", + "cross section": "air", + "vertical profile": "air", + "vertical profile units": "molecule cm-3" + }, + { + "name": "O2", + "type": "base", + "cross section": "O2", + "vertical profile": "O2", + "vertical profile units": "molecule cm-3" + }, + { + "name": "O3", + "type": "base", + "cross section": "O3", + "vertical profile": "O3", + "vertical profile units": "molecule cm-3" + } + ] + }, + "photolysis": { + "reactions": [ + { + "name": "jo2_b", + "__reaction": "O2 + hv -> O + O", + "cross section": { + "netcdf files": ["data/cross_sections/O2_1.nc"], + "type": "base", + "lower extrapolation": { "type": "boundary" } + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jo3_a", + "__reaction": "O3 + hv -> O2 + O(1D)", + "cross section": { + "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(1D)" + } + }, + { + "name": "jo3_b", + "__reaction": "O3 + hv -> O2 + O(3P)", + "cross section": { + "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(3P)" + } + }, + { + "name": "jn2o", + "__reaction": "N2O + hv -> N2 + O(1D)", + "cross section": { + "type": "N2O+hv->N2+O(1D)" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jno2", + "__reaction": "NO2 + hv -> NO + O(3P)", + "cross section": { + "netcdf files": ["data/cross_sections/NO2_1.nc"], + "type": "NO2 tint" + }, + "quantum yield": { + "netcdf files": ["data/quantum_yields/NO2_1.nc"], + "type": "NO2 tint", + "lower extrapolation": { "type": "boundary" } + } + }, + { + "name": "jn2o5_a", + "__reaction": "N2O5 + hv -> NO2 + NO3", + "cross section": { + "netcdf files": [ + "data/cross_sections/N2O5_1.nc", + "data/cross_sections/N2O5_2.nc" + ], + "type": "N2O5+hv->NO2+NO3" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhno3", + "__reaction": "HNO3 + hv -> OH + NO2", + "cross section": { + "netcdf files": [ + "data/cross_sections/HNO3_1.nc" + ], + "type": "HNO3+hv->OH+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jno3_a", + "__reaction": "NO3 + hv -> NO2 + O(3P)", + "cross section": { + "netcdf files": [ + "data/cross_sections/NO3_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/NO3-NO2+O(3P)_1.nc" + ], + "type": "tint", + "lower extrapolation": { + "type": "constant", + "value": 1.0 + } + } + }, + { + "name": "jno3_b", + "__reaction": "NO3 + hv -> NO + O2", + "cross section": { + "netcdf files": [ + "data/cross_sections/NO3_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/NO3-NO+O2_1.nc" + ], + "type": "tint" + } + }, + { + "name": "jch3ooh", + "__reaction": "CH3OOH + hv -> CH3O + OH", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3OOH_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch2o_a", + "__reaction": "CH2O + hv -> H + HCO", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH2O_1.nc" + ], + "type": "CH2O" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH2O_1.nc" + ], + "type": "base", + "lower extrapolation": { + "type": "boundary" + } + } + }, + { + "name": "jch2o_b", + "__reaction": "CH2O + hv -> H2 + CO", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH2O_1.nc" + ], + "type": "CH2O" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH2O_1.nc" + ], + "type": "CH2O", + "lower extrapolation": { + "type": "boundary" + } + } + }, + { + "name": "jh2o2", + "__reaction": "H2O2 + hv -> OH + OH", + "cross section": { + "netcdf files": [ + "data/cross_sections/H2O2_1.nc" + ], + "type": "H2O2+hv->OH+OH" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3cho", + "__reaction": "CH3CHO + hv -> CH3 + HCO", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3CHO_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH3CHO_1.nc" + ], + "type": "CH3CHO+hv->CH3+HCO" + } + }, + { + "name": "jpan", + "__reaction": "PAN + hv -> 0.6*CH3CO3 + 0.6*NO2 + 0.4*CH3O2 + 0.4*NO3 + 0.4*CO2", + "cross section": { + "netcdf files": [ + "data/cross_sections/PAN_1.nc" + ], + "type": "CH3ONO2+hv->CH3O+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jmvk", + "__reaction": "MVK + hv -> 0.7*C3H6 + 0.7*CO + 0.3*CH3O2 + 0.3*CH3CO3", + "cross section": { + "netcdf files": [ + "data/cross_sections/MVK_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "MVK+hv->Products" + } + }, + { + "name": "jacet", + "__reaction": "CH3COCH3 + hv -> CH3CO + CH3", + "cross section": { + "netcdf files": ["data/cross_sections/CH3COCH3_1.nc"], + "type": "CH3COCH3+hv->CH3CO+CH3" + }, + "quantum yield": { + "type": "CH3COCH3+hv->CH3CO+CH3" + } + }, + { + "name": "jmgly", + "__reaction": "CH3COCHO + hv -> CH3CO3 + CO + HO2", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3COCHO_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "CH3COCHO+hv->CH3CO+HCO" + } + }, + { + "name": "jglyald", + "__reaction": "GLYALD + hv -> 2*HO2 + CO + CH2O", + "cross section": { + "netcdf files": [ + "data/cross_sections/HOCH2CHO_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jglyoxal", + "__reaction": "GLYOXAL + hv -> 2*CO + 2*HO2", + "__comments": "NOTE the products of this reaction don't exactly match", + "cross section": { + "netcdf files": [ + "data/cross_sections/CHOCHO_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CHOCHO-H2_CO_CO_1.nc" + ], + "type": "base", + "lower extrapolation": { + "type": "boundary" + } + } + }, + { + "name": "jbrcl", + "__reaction": "BrCl + hv -> Br + Cl", + "cross section": { + "netcdf files": [ + "data/cross_sections/BrCl_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jbro", + "__reaction": "BrO + hv -> Br + O", + "cross section": { + "netcdf files": [ + "data/cross_sections/BrO_1.nc" + ], + "type": "BrO+hv->Br+O" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jbrono2_a", + "__reaction": "BrONO2 + hv -> Br + NO3", + "cross section": { + "netcdf files": [ + "data/cross_sections/BrONO2_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.85 + } + }, + { + "name": "jbrono2_b", + "__reaction": "BrONO2 + hv -> BrO + NO2", + "cross section": { + "netcdf files": [ + "data/cross_sections/BrONO2_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.15 + } + }, + { + "name": "jccl4", + "__reaction": "CCl4 + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CCl4_1.nc" + ], + "type": "CCl4+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcf2clbr", + "__reaction": "CF2BrCl + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CF2BrCl_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcf3br", + "__reaction": "CF3Br + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CF3Br_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcfcl3", + "__reaction": "CCl3F + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CFC-11_1.nc" + ], + "type": "CCl3F+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcfc113", + "__reaction": "CFC-113 + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CFC-113_1.nc" + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcfc114", + "__reaction": "CFC-114 + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CFC-114_1.nc" + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcfc115", + "__reaction": "CFC-115 + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CFC-115_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcf2cl2", + "__reaction": "CCl2F2 + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CFC-12_1.nc" + ], + "type": "CCl3F+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3br", + "__reaction": "CH3Br + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3Br_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3ccl3", + "__reaction": "CH3CCl3+hv->Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3CCl3_1.nc" + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3cl", + "__reaction": "CH3Cl + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3Cl_1.nc" + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jchbr3", + "__reaction": "CHBr3 + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CHBr3_1.nc" + ], + "type": "CHBr3+hv->Products", + "lower extrapolation": { + "type": "boundary" + } + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcl2", + "__reaction": "Cl2 + hv -> Cl + Cl", + "cross section": { + "type": "Cl2+hv->Cl+Cl" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jclo", + "__reaction": "ClO + hv -> Cl + O(1D)", + "cross section": { + "netcdf files": [ + "data/cross_sections/ClO_1.nc" + ], + "type": "tint" + }, + "quantum yield": { + "type": "ClO+hv->Cl+O(1D)" + } + }, + { + "name": "jclono2_a", + "__reaction": "ClONO2 + hv -> Cl + NO3", + "cross section": { + "netcdf files": [ + "data/cross_sections/ClONO2_1.nc" + ], + "type": "ClONO2" + }, + "quantum yield": { + "type": "ClONO2+hv->Cl+NO3" + } + }, + { + "name": "jclono2_b", + "__reaction": "ClONO2 + hv -> ClO + NO2", + "cross section": { + "netcdf files": [ + "data/cross_sections/ClONO2_1.nc" + ], + "type": "ClONO2" + }, + "quantum yield": { + "type": "ClONO2+hv->ClO+NO2" + } + }, + { + "name": "jcof2", + "__reaction": "CF2O + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CF2O_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcofcl", + "__reaction": "CClFO + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CClFO_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhcfc141b", + "__reaction": "HCFC-141b + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3CFCl2_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhcfc142b", + "__reaction": "HCFC-142b + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CH3CF2Cl_1.nc" + ], + "type": "HCFC+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhcfc22", + "__reaction": "HCFC-22 + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/CHClF2_1.nc" + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhcl", + "__reaction": "HCl + hv -> H + Cl", + "cross section": { + "netcdf files": [ + "data/cross_sections/HCl_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhobr", + "__reaction": "HOBr + hv -> OH + Br", + "cross section": { + "type": "HOBr+hv->OH+Br" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhocl", + "__reaction": "HOCl + hv -> HO + Cl", + "cross section": { + "netcdf files": [ + "data/cross_sections/HOCl_1.nc" + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "joclo", + "__reaction": "OClO + hv -> Products", + "cross section": { + "netcdf files": [ + "data/cross_sections/OClO_1.nc", + "data/cross_sections/OClO_2.nc", + "data/cross_sections/OClO_3.nc" + ], + "type": "OClO+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + } + ] + }, + "__CAM aliasing": [ + { + "for": "jh2o_a", + "reaction": "H2O + hv -> OH + H", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jh2o_b", + "reaction": "H2O + hv -> H2 + O1D", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jh2o_c", + "reaction": "H2O + hv -> 2*H + O", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jo2_a", + "reaction": "O2 + hv -> O + O1D", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jho2no2_a", + "reaction": "HO2NO2 + hv -> OH + NO3", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jho2no2_b", + "reaction": "HO2NO2 + hv -> NO2 + HO2", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jn2o5_b", + "reaction": "N2O5 + hv -> NO + O + NO3", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jno", + "reaction": "NO + hv -> N + O", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jno_i", + "reaction": "NO + hv -> NOp + e", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jalknit", + "reaction": "ALKNIT + hv -> NO2 + 0.4*CH3CHO + 0.1*CH2O + 0.25*CH3COCH3 + HO2 + 0.8*MEK", + "use": "jch3ooh" + }, + { + "for": "jpooh", + "reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", + "use": "jch3ooh" + }, + { + "for": "jch3co3h", + "reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", + "use": "jh2o2", + "scale by": 0.28 + }, + { + "for": "jmpan", + "reaction": "MPAN + hv -> MCO3 + NO2", + "use": "jpan" + }, + { + "for": "jmacr_a", + "reaction": "MACR + hv -> 1.34*HO2 + 0.66*MCO3 + 1.34*CH2O + 1.34*CH3CO3", + "comments": "TODO - find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jmacr_b", + "reaction": "MACR + hv -> 0.66*HO2 + 1.34*CO", + "comments": "TODO - find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jc2h5ooh", + "reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", + "use": "jch3ooh" + }, + { + "for": "jc3h7ooh", + "reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", + "use": "jch3ooh" + }, + { + "for": "jc6h5ooh", + "reaction": "C6H5OOH + hv -> PHENO + OH", + "use": "jch3ooh" + }, + { + "for": "jch4_a", + "reaction": "CH4 + hv -> H + CH3O2", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jch4_b", + "reaction": "CH4 + hv -> 1.44*H2 + 0.18*CH2O + 0.18*O + 0.33*OH + 0.33*H + 0.44*CO2 + 0.38*CO + 0.05*H2O", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jco2", + "reaction": "CO2 + hv -> CO + O", + "comments": "TODO find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jeooh", + "reaction": "EOOH + hv -> EO + OH", + "use": "jch3ooh" + }, + { + "for": "jrooh", + "reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", + "use": "jch3ooh" + }, + { + "for": "jxooh", + "reaction": "XOOH + hv -> OH", + "use": "jch3ooh" + }, + { + "for": "jonitr", + "reaction": "ONITR + hv -> NO2", + "use": "jch3cho" + }, + { + "for": "jisopooh", + "reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", + "use": "jch3ooh" + }, + { + "for": "jhyac", + "reaction": "HYAC (CH3COCH2OH) + hv -> CH3CO3 + HO2 + CH2O", + "comments": "TODO - find this rate", + "use": "jo2_b", + "scale by": 0.0 + }, + { + "for": "jmek", + "reaction": "MEK + hv -> CH3CO3 + C2H5O2", + "use": "jacet" + }, + { + "for": "jalkooh", + "reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", + "use": "jch3ooh" + }, + { + "for": "jbenzooh", + "reaction": "BENZOOH + hv -> OH + GLYOXAL + 0.5*BIGALD1 + HO2", + "use": "jch3ooh" + }, + { + "for": "jbepomuc", + "reaction": "BEPOMUC + hv -> BIGALD1 + 1.5*HO2 + 1.5*CO", + "use": "jno2", + "scale by": 0.1 + }, + { + "for": "jbigald", + "reaction": "BIGALD + hv -> 0.45*CO + 0.13*GLYOXAL + 0.56*HO2 + 0.13*CH3CO3 + 0.18*CH3COCHO", + "use": "jno2", + "scale by": 0.2 + }, + { + "for": "jbigald1", + "reaction": "BIGALD1 + hv -> 0.6*MALO2 + HO2", + "use": "jno2", + "scale by": 0.14 + }, + { + "for": "jbigald2", + "reaction": "BIGALD2 + hv -> 0.6*HO2 + 0.6*DICARBO2", + "use": "jno2", + "scale by": 0.2 + }, + { + "for": "jbigald3", + "reaction": "BIGALD3 + hv -> 0.6*HO2 + 0.6*CO + 0.6*MDIALO2", + "use": "jno2", + "scale by": 0.2 + }, + { + "for": "jbigald4", + "reaction": "BIGALD4 + hv -> HO2 + CO + CH3COCHO + CH3CO3", + "use": "jno2", + "scale by": 0.006 + }, + { + "for": "jbzooh", + "reaction": "BZOOH + hv -> BZALD + OH + HO2", + "use": "jch3ooh" + }, + { + "for": "jmekooh", + "reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", + "use": "jch3ooh" + }, + { + "for": "jtolooh", + "reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", + "use": "jch3ooh" + }, + { + "for": "jterpooh", + "reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", + "use": "jch3ooh" + }, + { + "for": "jhonitr", + "reaction": "HONITR + hv -> NO2 + 0.67*HO2 + 0.33*CH3CHO + 0.33*CH2O + 0.33*CO + 0.33*GLYALD + 0.33*CH3CO3 + 0.17*HYAC + 0.17*CH3COCH3", + "use": "jch2o_a" + }, + { + "for": "jhpald", + "reaction": "HPALD + hv -> BIGALD3 + OH + HO2", + "use": "jno2", + "scale by": 0.006 + }, + { + "for": "jisopnooh", + "reaction": "ISOPNOOH + hv -> NO2 + HO2 + ISOPOOH", + "use": "jch3ooh" + }, + { + "for": "jnc4cho", + "reaction": "NC4CHO + hv -> BIGALD3 + NO2 + HO2", + "use": "jch2o_a" + }, + { + "for": "jnoa", + "reaction": "NOA + hv -> NO2 + CH2O + CH3CO3", + "use": "jch2o_a" + }, + { + "for": "jnterpooh", + "reaction": "NTERPOOH + hv -> TERPROD1 + NO2 + OH", + "use": "jch3ooh" + }, + { + "for": "jphenooh", + "reaction": "PHENOOH + hv -> OH + HO2 + 0.7*GLYOXAL", + "use": "jch3ooh" + }, + { + "for": "jtepomuc", + "reaction": "TEPOMUC + hv -> 0.5*CH3CO3 + HO2 + 1.5*CO", + "use": "jno2", + "scale by": 0.1 + }, + { + "for": "jterp2ooh", + "reaction": "TERP2OOH + hv -> OH + 0.375*CH2O + 0.3*CH3COCH3 + 0.25*CO + CO2 + TERPROD2 + HO2 + 0.25*GLYALD", + "use": "jch3ooh" + }, + { + "for": "jterpnit", + "reaction": "TERPNIT + hv -> TERPROD1 + NO2 + HO2", + "use": "jch3ooh" + }, + { + "for": "jterprd1", + "reaction": "TERPROD1 + hv -> HO2 + CO + TERPROD2", + "use": "jch3cho" + }, + { + "for": "jterprd2", + "reaction": "TERPROD2 + hv -> 0.15*RO2 + 0.68*CH2O + 0.8*CO2 + 0.5*CH3COCH3 + 0.65*CH3CO3 + 1.2*HO2 + 1.7*CO", + "use": "jch3cho" + }, + { + "for": "jxylenooh", + "reaction": "XYLENOOH + hv -> OH + HO2 + 0.34*GLYOXAL + 0.54*CH3COCHO + 0.06*BIGALD1 + 0.2*BIGALD2 + 0.15*BIGALD3 + 0.21*BIGALD4", + "use": "jch3ooh" + }, + { + "for": "jxylolooh", + "reaction": "XYLOLOOH + hv -> OH + 0.17*GLYOXAL + 0.51*CH3COCHO + HO2", + "use": "jch3ooh" + }, + { + "for": "jch2br2", + "reaction": "CH2BR2 + hv -> 2*BR", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jcl2o2", + "reaction": "CL2O2 + hv -> 2*CL", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jh2402", + "reaction": "H2402 + hv -> 2*BR + 2*COF2", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jhbr", + "reaction": "HBR + hv -> BR + H", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jhf", + "reaction": "HF + hv -> H + F", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jsf6", + "reaction": "SF6 + hv -> sink", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jh2so4", + "reaction": "H2SO4 + hv -> SO3 + H2O", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jocs", + "reaction": "OCS + hv -> S + CO", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jso", + "reaction": "SO + hv -> S + O", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jso2", + "reaction": "SO2 + hv -> SO + O", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jso3", + "reaction": "SO3 + hv -> SO2 + O", + "comments": "TODO - find this rate", + "use": "jo2_a", + "scale by": 0.0 + }, + { + "for": "jsoa1_a1", + "reaction": "soa1_a1 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa1_a2", + "reaction": "soa1_a2 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa2_a1", + "reaction": "soa2_a1 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa2_a2", + "reaction": "soa2_a2 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa3_a1", + "reaction": "soa3_a1 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa3_a2", + "reaction": "soa3_a2 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa4_a1", + "reaction": "soa4_a1 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa4_a2", + "reaction": "soa4_a2 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa5_a1", + "reaction": "soa5_a1 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + }, + { + "for": "jsoa5_a2", + "reaction": "soa5_a2 + hv -> Products", + "use": "jno2", + "scale by": 0.0004 + } + ] +} From ee5f4fc272048c0ce0e248d3b94b9abcd08a0268 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Fri, 18 Nov 2022 13:47:06 -0700 Subject: [PATCH 35/84] draft mapping for tuv-x photo rates --- cime_config/tuvx_MOZART.json | 231 ++++++++++--------- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 9 +- src/chemistry/mozart/mo_tuvx.F90 | 126 +++++++--- 3 files changed, 215 insertions(+), 151 deletions(-) diff --git a/cime_config/tuvx_MOZART.json b/cime_config/tuvx_MOZART.json index 43384a0b31..08041a0f6e 100644 --- a/cime_config/tuvx_MOZART.json +++ b/cime_config/tuvx_MOZART.json @@ -347,118 +347,121 @@ } ] }, - "__CAM aliasing": [ - { - "for": "jho2no2_a", - "reaction": "HO2NO2 + hv -> OH + NO3", - "comments": "TODO find this rate", - "use": "jo2", - "scale by": 0.0 - }, - { - "for": "jho2no2_b", - "reaction": "HO2NO2 + hv -> NO2 + HO2", - "comments": "TODO find this rate", - "use": "jo2", - "scale by": 0.0 - }, - { - "for": "jpooh", - "reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", - "use": "jch3ooh" - }, - { - "for": "jch3co3h", - "reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", - "use": "jh2o2", - "scale by": 0.28 - }, - { - "for": "jmpan", - "reaction": "MPAN + hv -> MCO3 + NO2", - "use": "jpan" - }, - { - "for": "jmacr_a", - "reaction": "MACR + hv -> 1.34*HO2 + 0.66*MCO3 + 1.34*CH2O + 1.34*CH3CO3", - "comments": "TODO - find this rate", - "use": "jo2", - "scale by": 0.0 - }, - { - "for": "jmacr_b", - "reaction": "MACR + hv -> 0.66*HO2 + 1.34*CO", - "comments": "TODO - find this rate", - "use": "jo2", - "scale by": 0.0 - }, - { - "for": "jc2h5ooh", - "reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", - "use": "jch3ooh" - }, - { - "for": "jc3h7ooh", - "reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", - "use": "jch3ooh" - }, - { - "for": "jrooh", - "reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", - "use": "jch3ooh" - }, - { - "for": "jxooh", - "reaction": "XOOH + hv -> OH", - "use": "jch3ooh" - }, - { - "for": "jonitr", - "reaction": "ONITR + hv -> NO2", - "use": "jch3cho" - }, - { - "for": "jisopooh", - "reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", - "use": "jch3ooh" - }, - { - "for": "jhyac", - "reaction": "HYAC (CH3COCH2OH) + hv -> CH3CO3 + HO2 + CH2O", - "comments": "TODO - find this rate", - "use": "jo2", - "scale by": 0.0 - }, - { - "for": "jmek", - "reaction": "MEK + hv -> CH3CO3 + C2H5O2", - "use": "jacet" - }, - { - "for": "jbigald", - "reaction": "BIGALD + hv -> .45*CO + .13*GLYOXAL + .56*HO2 + .13*CH3CO3 + .18*CH3COCHO", - "use": "jno2", - "scale by": 0.2 - }, - { - "for": "jalkooh", - "reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", - "use": "jch3ooh" - }, - { - "for": "jmekooh", - "reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", - "use": "jch3ooh" - }, - { - "for": "jtolooh", - "reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", - "use": "jch3ooh" - }, - { - "for": "jterpooh", - "reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", - "use": "jch3ooh" - } - ] + "__CAM aliasing": { + "default matching": "backup", + "pairs": [ + { + "to": "jho2no2_a", + "__reaction": "HO2NO2 + hv -> OH + NO3", + "__comments": "TODO find this rate", + "from": "jo2", + "scale by": 0.0 + }, + { + "to": "jho2no2_b", + "__reaction": "HO2NO2 + hv -> NO2 + HO2", + "__comments": "TODO find this rate", + "from": "jo2", + "scale by": 0.0 + }, + { + "to": "jpooh", + "__reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jch3co3h", + "__reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", + "from": "jh2o2", + "scale by": 0.28 + }, + { + "to": "jmpan", + "__reaction": "MPAN + hv -> MCO3 + NO2", + "from": "jpan" + }, + { + "to": "jmacr_a", + "__reaction": "MACR + hv -> 1.34*HO2 + 0.66*MCO3 + 1.34*CH2O + 1.34*CH3CO3", + "__comments": "TODO - find this rate", + "from": "jo2", + "scale by": 0.0 + }, + { + "to": "jmacr_b", + "__reaction": "MACR + hv -> 0.66*HO2 + 1.34*CO", + "__comments": "TODO - find this rate", + "from": "jo2", + "scale by": 0.0 + }, + { + "to": "jc2h5ooh", + "__reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jc3h7ooh", + "__reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", + "from": "jch3ooh" + }, + { + "to": "jrooh", + "__reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", + "from": "jch3ooh" + }, + { + "to": "jxooh", + "__reaction": "XOOH + hv -> OH", + "from": "jch3ooh" + }, + { + "to": "jonitr", + "__reaction": "ONITR + hv -> NO2", + "from": "jch3cho" + }, + { + "to": "jisopooh", + "__reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", + "from": "jch3ooh" + }, + { + "to": "jhyac", + "__reaction": "HYAC (CH3COCH2OH) + hv -> CH3CO3 + HO2 + CH2O", + "__comments": "TODO - find this rate", + "from": "jo2", + "scale by": 0.0 + }, + { + "to": "jmek", + "__reaction": "MEK + hv -> CH3CO3 + C2H5O2", + "from": "jacet" + }, + { + "to": "jbigald", + "__reaction": "BIGALD + hv -> .45*CO + .13*GLYOXAL + .56*HO2 + .13*CH3CO3 + .18*CH3COCHO", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jalkooh", + "__reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", + "from": "jch3ooh" + }, + { + "to": "jmekooh", + "__reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", + "from": "jch3ooh" + }, + { + "to": "jtolooh", + "__reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", + "from": "jch3ooh" + }, + { + "to": "jterpooh", + "__reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", + "from": "jch3ooh" + } + ] + } } diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index 7274e4439a..c11aed989c 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -785,7 +785,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & call shr_orb_decl( calday, eccen, mvelpp, lambm0, obliqr , & delta, esfact ) - !if (tuvx_active) then + if (tuvx_active) then !----------------------------------------------------------------- ! ... lookup the photolysis rates from table !----------------------------------------------------------------- @@ -793,14 +793,15 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & col_dens, zen_angle, asdir, cwat, cldfr, & esfact, vmr, invariants, ncol, lchnk, pbuf ) - !else + else !----------------------------------------------------------------- ! ... get calculated photolysis rates from TUV-x !----------------------------------------------------------------- call tuvx_get_photo_rates( state, pbuf, ncol, lchnk, zm, zi, & tfld, ts, invariants, vmr, col_delta, & - asdir, zen_angle, esfact ) - !endif + asdir, zen_angle, esfact, & + reaction_rates ) + endif do i = 1,phtcnt call outfld( tag_names(i), reaction_rates(:ncol,:,rxt_tag_map(i)), ncol, lchnk ) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 64e0e90f3f..7005d7dc7b 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -3,6 +3,7 @@ !---------------------------------------------------------------------- module mo_tuvx + use musica_map, only : map_t use musica_string, only : string_t use ppgrid, only : pver ! number of vertical layers use shr_kind_mod, only : r8 => shr_kind_r8, cl=>shr_kind_cl @@ -62,6 +63,8 @@ module mo_tuvx integer :: n_photo_rates_ ! number of photo reactions in TUV-x real(r8), allocatable :: photo_rates_(:,:,:) ! photolysis rate constants ! (column, vertical level, reaction) [s-1] + type(map_t) :: photo_rate_map_ ! map between TUV-x and CAM + ! photo rate constant arrays type(grid_updater_t) :: grids_(NUM_GRIDS) ! grid updaters type(profile_updater_t) :: profiles_(NUM_PROFILES) ! profile updaters type(radiator_updater_t) :: radiators_(NUM_RADIATORS) ! radiator updaters @@ -164,6 +167,7 @@ subroutine tuvx_init( ) use cam_logfile, only : iulog ! log file output unit use mo_chem_utls, only : get_spc_ndx, get_inv_ndx use musica_assert, only : assert_msg + use musica_config, only : config_t use musica_mpi, only : musica_mpi_rank use musica_string, only : string_t, to_char use ppgrid, only : pcols ! maximum number of columns @@ -176,9 +180,12 @@ subroutine tuvx_init( ) use tuvx_profile_warehouse, only : profile_warehouse_t use tuvx_radiator_warehouse, only : radiator_warehouse_t + character(len=*), parameter :: my_name = "TUV-x wrapper initialization" class(core_t), pointer :: core character, allocatable :: buffer(:) type(string_t) :: config_path + type(config_t) :: tuvx_config, map_config + type(map_t) :: map class(grid_t), pointer :: height class(grid_warehouse_t), pointer :: cam_grids class(profile_warehouse_t), pointer :: cam_profiles @@ -203,12 +210,17 @@ subroutine tuvx_init( ) cam_profiles => get_cam_profiles( cam_grids ) cam_radiators => get_cam_radiators( cam_grids ) - ! ====================================================================== - ! construct a core on the primary process and pack it onto an MPI buffer - ! ====================================================================== + ! ================================================================== + ! construct a core and a map between TUV-x and CAM photolysis arrays + ! on the primary process and pack them onto an MPI buffer + ! ================================================================== if( is_main_task ) then + call tuvx_config%from_file( config_path%to_char( ) ) + call tuvx_config%get( "__CAM aliasing", map_config, my_name ) core => core_t( config_path, cam_grids, cam_profiles, cam_radiators ) - pack_size = core%pack_size( mpicom ) + call set_photo_rate_map( core, map_config, map ) + pack_size = core%pack_size( mpicom ) + & + map%pack_size( mpicom ) allocate( buffer( pack_size ) ) pos = 0 call core%mpi_pack( buffer, pos, mpicom ) @@ -216,9 +228,9 @@ subroutine tuvx_init( ) end if #ifdef HAVE_MPI - ! ============================================ - ! broadcast the core data to all MPI processes - ! ============================================ + ! ==================================================== + ! broadcast the core and map data to all MPI processes + ! ==================================================== call mpi_bcast( pack_size, 1, MPI_INTEGER, main_task, mpicom, i_err ) if( i_err /= MPI_SUCCESS ) then write(iulog,*) "TUV-x MPI int bcast error" @@ -232,22 +244,25 @@ subroutine tuvx_init( ) end if #endif - ! ======================================================== - ! unpack the core for each OMP thread on every MPI process - ! ======================================================== + ! ================================================================ + ! unpack the core and map for each OMP thread on every MPI process + ! ================================================================ allocate( tuvx_ptrs( max_threads( ) ) ) do i_core = 1, size( tuvx_ptrs ) associate( tuvx => tuvx_ptrs( i_core ) ) allocate( tuvx%core_ ) pos = 0 call tuvx%core_%mpi_unpack( buffer, pos, mpicom ) + call tuvx%photo_rate_map_%mpi_unpack( buffer, pos, mpicom ) - ! ===================================================================== - ! Set up map between CAM and TUV-x photolysis reactions for each thread - ! ===================================================================== + ! =================================================================== + ! Set up connections between CAM and TUV-x input data for each thread + ! =================================================================== call create_updaters( tuvx, cam_grids, cam_profiles, cam_radiators ) - ! TEMPORARY FOR DEVELOPMENT + ! =============================================================== + ! Create a working array for calculated photolysis rate constants + ! =============================================================== tuvx%n_photo_rates_ = tuvx%core_%number_of_photolysis_reactions( ) height => tuvx%core_%get_grid( "height", "km" ) allocate( tuvx%photo_rates_( pcols, height%ncells_ + 1, tuvx%n_photo_rates_ ) ) @@ -308,10 +323,11 @@ end subroutine tuvx_timestep_init subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & height_int, temperature_mid, surface_temperature, fixed_species_conc, & species_vmr, exo_column_conc, surface_albedo, solar_zenith_angle, & - earth_sun_distance ) + earth_sun_distance, photolysis_rates ) use cam_logfile, only : iulog ! log info output unit - use chem_mods, only : gas_pcnst, & ! number of non-fixed species + use chem_mods, only : phtcnt, & ! number of photolysis reactions + gas_pcnst, & ! number of non-fixed species nfs, & ! number of fixed species nabscol ! number of absorbing species (radiators) use physics_types, only : physics_state @@ -324,23 +340,26 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & type(physics_state), target, intent(in) :: state type(physics_buffer_desc), pointer, intent(inout) :: pbuf(:) - integer, intent(in) :: ncol ! number of active columns on this thread - integer, intent(in) :: lchnk ! identifier for this thread - real(r8), intent(in) :: height_mid(pcols,pver) ! height at mid-points (km) - real(r8), intent(in) :: height_int(pcols,pver+1) ! height at interfaces (km) - real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) - real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) - real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities - ! (molecule cm-3) - real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing - ! ratios (mol mol-1) - real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! above column densities - ! (molecule cm-3) - real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) - real(r8), intent(in) :: solar_zenith_angle(ncol) ! solar zenith angle (radians) - real(r8), intent(in) :: earth_sun_distance ! Earth-Sun distance (AU) - - integer :: i_col ! column index + integer, intent(in) :: ncol ! number of active columns on this thread + integer, intent(in) :: lchnk ! identifier for this thread + real(r8), intent(in) :: height_mid(pcols,pver) ! height at mid-points (km) + real(r8), intent(in) :: height_int(pcols,pver+1) ! height at interfaces (km) + real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) + real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) + real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities + ! (molecule cm-3) + real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) + real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! above column densities + ! (molecule cm-3) + real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) + real(r8), intent(in) :: solar_zenith_angle(ncol) ! solar zenith angle (radians) + real(r8), intent(in) :: earth_sun_distance ! Earth-Sun distance (AU) + real(r8), intent(inout) :: photolysis_rates(ncol,pver,phtcnt) ! photolysis rate + ! constants (1/s) + + integer :: i_col ! column index + integer :: i_level ! vertical level index if (.not.tuvx_active) return @@ -375,6 +394,13 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & photolysis_rate_constants = & tuvx%photo_rates_(i_col,:,:) ) + ! ============================================ + ! Return the photolysis rates on the CAM grids + ! ============================================ + do i_level = 1, pver + call tuvx%photo_rate_map_%apply( tuvx%photo_rates_(i_col,pver-i_level+1,:), & + photolysis_rates(i_col,i_level,:) ) + end do end do call output_diagnostics( tuvx, ncol, lchnk ) @@ -516,6 +542,40 @@ subroutine initialize_diagnostics( this ) end subroutine initialize_diagnostics +!================================================================================================ + + !----------------------------------------------------------------------- + ! Sets up a map between the TUV-x and CAM photolysis rate arrays + !----------------------------------------------------------------------- + subroutine set_photo_rate_map( core, config, map ) + + use cam_logfile, only : iulog ! log info output unit + use chem_mods, only : phtcnt, & ! number of photolysis reactions + rxt_tag_lst ! labels for all chemical reactions + ! NOTE photolysis reactions are + ! expected to appear first + use musica_config, only : config_t + use musica_string, only : string_t + + type(core_t), intent(in) :: core ! TUV-x core + type(config_t), intent(inout) :: config ! CAM<->TUV-x map configuration + type(map_t), intent(out) :: map + + integer :: i_label + type(string_t), allocatable :: tuvx_labels(:), cam_labels(:) + + allocate( cam_labels( phtcnt ) ) + do i_label = 1, phtcnt + cam_labels( i_label ) = trim( rxt_tag_lst( i_label ) ) + end do + tuvx_labels = core%photolysis_reaction_labels( ) + map = map_t( config, tuvx_labels, cam_labels ) + write(iulog,*) + write(iulog,*) "TUV-x <-> CAM-Chem photolysis rate constant map" + call map%print( tuvx_labels, cam_labels, iulog ) + + end subroutine set_photo_rate_map + !================================================================================================ !----------------------------------------------------------------------- From f4fa3dcad58e8dd004bde8edff0626999ef9f767 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Fri, 18 Nov 2022 16:10:31 -0700 Subject: [PATCH 36/84] mix tuv map message passing --- src/chemistry/mozart/mo_tuvx.F90 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 7005d7dc7b..ad41d8f0ed 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -224,6 +224,7 @@ subroutine tuvx_init( ) allocate( buffer( pack_size ) ) pos = 0 call core%mpi_pack( buffer, pos, mpicom ) + call map%mpi_pack( buffer, pos, mpicom ) deallocate( core ) end if From 583c4b5af057f7a3217f4777b934d659edf19075 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Fri, 18 Nov 2022 17:44:23 -0700 Subject: [PATCH 37/84] update TUV-x TS1 config --- cime_config/tuvx_MOZART_TS1.json | 885 ++++++++++++++++--------------- src/chemistry/mozart/mo_tuvx.F90 | 2 + 2 files changed, 446 insertions(+), 441 deletions(-) diff --git a/cime_config/tuvx_MOZART_TS1.json b/cime_config/tuvx_MOZART_TS1.json index ba8d218f17..20bb437026 100644 --- a/cime_config/tuvx_MOZART_TS1.json +++ b/cime_config/tuvx_MOZART_TS1.json @@ -749,445 +749,448 @@ } ] }, - "__CAM aliasing": [ - { - "for": "jh2o_a", - "reaction": "H2O + hv -> OH + H", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jh2o_b", - "reaction": "H2O + hv -> H2 + O1D", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jh2o_c", - "reaction": "H2O + hv -> 2*H + O", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jo2_a", - "reaction": "O2 + hv -> O + O1D", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jho2no2_a", - "reaction": "HO2NO2 + hv -> OH + NO3", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jho2no2_b", - "reaction": "HO2NO2 + hv -> NO2 + HO2", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jn2o5_b", - "reaction": "N2O5 + hv -> NO + O + NO3", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jno", - "reaction": "NO + hv -> N + O", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jalknit", - "reaction": "ALKNIT + hv -> NO2 + 0.4*CH3CHO + 0.1*CH2O + 0.25*CH3COCH3 + HO2 + 0.8*MEK", - "use": "jch3ooh" - }, - { - "for": "jpooh", - "reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", - "use": "jch3ooh" - }, - { - "for": "jch3co3h", - "reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", - "use": "jh2o2", - "scale by": 0.28 - }, - { - "for": "jmpan", - "reaction": "MPAN + hv -> MCO3 + NO2", - "use": "jpan" - }, - { - "for": "jmacr_a", - "reaction": "MACR + hv -> 1.34*HO2 + 0.66*MCO3 + 1.34*CH2O + 1.34*CH3CO3", - "comments": "TODO - find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jmacr_b", - "reaction": "MACR + hv -> 0.66*HO2 + 1.34*CO", - "comments": "TODO - find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jc2h5ooh", - "reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", - "use": "jch3ooh" - }, - { - "for": "jc3h7ooh", - "reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", - "use": "jch3ooh" - }, - { - "for": "jc6h5ooh", - "reaction": "C6H5OOH + hv -> PHENO + OH", - "use": "jch3ooh" - }, - { - "for": "jch4_a", - "reaction": "CH4 + hv -> H + CH3O2", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jch4_b", - "reaction": "CH4 + hv -> 1.44*H2 + 0.18*CH2O + 0.18*O + 0.33*OH + 0.33*H + 0.44*CO2 + 0.38*CO + 0.05*H2O", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jco2", - "reaction": "CO2 + hv -> CO + O", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jeooh", - "reaction": "EOOH + hv -> EO + OH", - "use": "jch3ooh" - }, - { - "for": "jrooh", - "reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", - "use": "jch3ooh" - }, - { - "for": "jxooh", - "reaction": "XOOH + hv -> OH", - "use": "jch3ooh" - }, - { - "for": "jonitr", - "reaction": "ONITR + hv -> NO2", - "use": "jch3cho" - }, - { - "for": "jisopooh", - "reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", - "use": "jch3ooh" - }, - { - "for": "jhyac", - "reaction": "HYAC (CH3COCH2OH) + hv -> CH3CO3 + HO2 + CH2O", - "comments": "TODO - find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jmek", - "reaction": "MEK + hv -> CH3CO3 + C2H5O2", - "use": "jacet" - }, - { - "for": "jalkooh", - "reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", - "use": "jch3ooh" - }, - { - "for": "jbenzooh", - "reaction": "BENZOOH + hv -> OH + GLYOXAL + 0.5*BIGALD1 + HO2", - "use": "jch3ooh" - }, - { - "for": "jbepomuc", - "reaction": "BEPOMUC + hv -> BIGALD1 + 1.5*HO2 + 1.5*CO", - "use": "jno2", - "scale by": 0.1 - }, - { - "for": "jbigald", - "reaction": "BIGALD + hv -> 0.45*CO + 0.13*GLYOXAL + 0.56*HO2 + 0.13*CH3CO3 + 0.18*CH3COCHO", - "use": "jno2", - "scale by": 0.2 - }, - { - "for": "jbigald1", - "reaction": "BIGALD1 + hv -> 0.6*MALO2 + HO2", - "use": "jno2", - "scale by": 0.14 - }, - { - "for": "jbigald2", - "reaction": "BIGALD2 + hv -> 0.6*HO2 + 0.6*DICARBO2", - "use": "jno2", - "scale by": 0.2 - }, - { - "for": "jbigald3", - "reaction": "BIGALD3 + hv -> 0.6*HO2 + 0.6*CO + 0.6*MDIALO2", - "use": "jno2", - "scale by": 0.2 - }, - { - "for": "jbigald4", - "reaction": "BIGALD4 + hv -> HO2 + CO + CH3COCHO + CH3CO3", - "use": "jno2", - "scale by": 0.006 - }, - { - "for": "jbzooh", - "reaction": "BZOOH + hv -> BZALD + OH + HO2", - "use": "jch3ooh" - }, - { - "for": "jmekooh", - "reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", - "use": "jch3ooh" - }, - { - "for": "jtolooh", - "reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", - "use": "jch3ooh" - }, - { - "for": "jterpooh", - "reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", - "use": "jch3ooh" - }, - { - "for": "jhonitr", - "reaction": "HONITR + hv -> NO2 + 0.67*HO2 + 0.33*CH3CHO + 0.33*CH2O + 0.33*CO + 0.33*GLYALD + 0.33*CH3CO3 + 0.17*HYAC + 0.17*CH3COCH3", - "use": "jch2o_a" - }, - { - "for": "jhpald", - "reaction": "HPALD + hv -> BIGALD3 + OH + HO2", - "use": "jno2", - "scale by": 0.006 - }, - { - "for": "jisopnooh", - "reaction": "ISOPNOOH + hv -> NO2 + HO2 + ISOPOOH", - "use": "jch3ooh" - }, - { - "for": "jnc4cho", - "reaction": "NC4CHO + hv -> BIGALD3 + NO2 + HO2", - "use": "jch2o_a" - }, - { - "for": "jnoa", - "reaction": "NOA + hv -> NO2 + CH2O + CH3CO3", - "use": "jch2o_a" - }, - { - "for": "jnterpooh", - "reaction": "NTERPOOH + hv -> TERPROD1 + NO2 + OH", - "use": "jch3ooh" - }, - { - "for": "jphenooh", - "reaction": "PHENOOH + hv -> OH + HO2 + 0.7*GLYOXAL", - "use": "jch3ooh" - }, - { - "for": "jtepomuc", - "reaction": "TEPOMUC + hv -> 0.5*CH3CO3 + HO2 + 1.5*CO", - "use": "jno2", - "scale by": 0.1 - }, - { - "for": "jterp2ooh", - "reaction": "TERP2OOH + hv -> OH + 0.375*CH2O + 0.3*CH3COCH3 + 0.25*CO + CO2 + TERPROD2 + HO2 + 0.25*GLYALD", - "use": "jch3ooh" - }, - { - "for": "jterpnit", - "reaction": "TERPNIT + hv -> TERPROD1 + NO2 + HO2", - "use": "jch3ooh" - }, - { - "for": "jterprd1", - "reaction": "TERPROD1 + hv -> HO2 + CO + TERPROD2", - "use": "jch3cho" - }, - { - "for": "jterprd2", - "reaction": "TERPROD2 + hv -> 0.15*RO2 + 0.68*CH2O + 0.8*CO2 + 0.5*CH3COCH3 + 0.65*CH3CO3 + 1.2*HO2 + 1.7*CO", - "use": "jch3cho" - }, - { - "for": "jxylenooh", - "reaction": "XYLENOOH + hv -> OH + HO2 + 0.34*GLYOXAL + 0.54*CH3COCHO + 0.06*BIGALD1 + 0.2*BIGALD2 + 0.15*BIGALD3 + 0.21*BIGALD4", - "use": "jch3ooh" - }, - { - "for": "jxylolooh", - "reaction": "XYLOLOOH + hv -> OH + 0.17*GLYOXAL + 0.51*CH3COCHO + HO2", - "use": "jch3ooh" - }, - { - "for": "jch2br2", - "reaction": "CH2BR2 + hv -> 2*BR", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jcl2o2", - "reaction": "CL2O2 + hv -> 2*CL", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jh2402", - "reaction": "H2402 + hv -> 2*BR + 2*COF2", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jhbr", - "reaction": "HBR + hv -> BR + H", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jhf", - "reaction": "HF + hv -> H + F", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jsf6", - "reaction": "SF6 + hv -> sink", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jh2so4", - "reaction": "H2SO4 + hv -> SO3 + H2O", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jocs", - "reaction": "OCS + hv -> S + CO", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jso", - "reaction": "SO + hv -> S + O", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jso2", - "reaction": "SO2 + hv -> SO + O", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jso3", - "reaction": "SO3 + hv -> SO2 + O", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jsoa1_a1", - "reaction": "soa1_a1 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa1_a2", - "reaction": "soa1_a2 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa2_a1", - "reaction": "soa2_a1 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa2_a2", - "reaction": "soa2_a2 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa3_a1", - "reaction": "soa3_a1 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa3_a2", - "reaction": "soa3_a2 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa4_a1", - "reaction": "soa4_a1 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa4_a2", - "reaction": "soa4_a2 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa5_a1", - "reaction": "soa5_a1 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa5_a2", - "reaction": "soa5_a2 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - } - ] + "__CAM aliasing": { + "default matching": "backup", + "pairs": [ + { + "to": "jh2o_a", + "__reaction": "H2O + hv -> OH + H", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jh2o_b", + "__reaction": "H2O + hv -> H2 + O1D", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jh2o_c", + "__reaction": "H2O + hv -> 2*H + O", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jo2_a", + "__reaction": "O2 + hv -> O + O1D", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jho2no2_a", + "__reaction": "HO2NO2 + hv -> OH + NO3", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jho2no2_b", + "__reaction": "HO2NO2 + hv -> NO2 + HO2", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jn2o5_b", + "__reaction": "N2O5 + hv -> NO + O + NO3", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jno", + "__reaction": "NO + hv -> N + O", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jalknit", + "__reaction": "ALKNIT + hv -> NO2 + 0.4*CH3CHO + 0.1*CH2O + 0.25*CH3COCH3 + HO2 + 0.8*MEK", + "from": "jch3ooh" + }, + { + "to": "jpooh", + "__reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jch3co3h", + "__reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", + "from": "jh2o2", + "scale by": 0.28 + }, + { + "to": "jmpan", + "__reaction": "MPAN + hv -> MCO3 + NO2", + "from": "jpan" + }, + { + "to": "jmacr_a", + "__reaction": "MACR + hv -> 1.34*HO2 + 0.66*MCO3 + 1.34*CH2O + 1.34*CH3CO3", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jmacr_b", + "__reaction": "MACR + hv -> 0.66*HO2 + 1.34*CO", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jc2h5ooh", + "__reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jc3h7ooh", + "__reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", + "from": "jch3ooh" + }, + { + "to": "jc6h5ooh", + "__reaction": "C6H5OOH + hv -> PHENO + OH", + "from": "jch3ooh" + }, + { + "to": "jch4_a", + "__reaction": "CH4 + hv -> H + CH3O2", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jch4_b", + "__reaction": "CH4 + hv -> 1.44*H2 + 0.18*CH2O + 0.18*O + 0.33*OH + 0.33*H + 0.44*CO2 + 0.38*CO + 0.05*H2O", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jco2", + "__reaction": "CO2 + hv -> CO + O", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jeooh", + "__reaction": "EOOH + hv -> EO + OH", + "from": "jch3ooh" + }, + { + "to": "jrooh", + "__reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", + "from": "jch3ooh" + }, + { + "to": "jxooh", + "__reaction": "XOOH + hv -> OH", + "from": "jch3ooh" + }, + { + "to": "jonitr", + "__reaction": "ONITR + hv -> NO2", + "from": "jch3cho" + }, + { + "to": "jisopooh", + "__reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", + "from": "jch3ooh" + }, + { + "to": "jhyac", + "__reaction": "HYAC (CH3COCH2OH) + hv -> CH3CO3 + HO2 + CH2O", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jmek", + "__reaction": "MEK + hv -> CH3CO3 + C2H5O2", + "from": "jacet" + }, + { + "to": "jalkooh", + "__reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", + "from": "jch3ooh" + }, + { + "to": "jbenzooh", + "__reaction": "BENZOOH + hv -> OH + GLYOXAL + 0.5*BIGALD1 + HO2", + "from": "jch3ooh" + }, + { + "to": "jbepomuc", + "__reaction": "BEPOMUC + hv -> BIGALD1 + 1.5*HO2 + 1.5*CO", + "from": "jno2", + "scale by": 0.1 + }, + { + "to": "jbigald", + "__reaction": "BIGALD + hv -> 0.45*CO + 0.13*GLYOXAL + 0.56*HO2 + 0.13*CH3CO3 + 0.18*CH3COCHO", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald1", + "__reaction": "BIGALD1 + hv -> 0.6*MALO2 + HO2", + "from": "jno2", + "scale by": 0.14 + }, + { + "to": "jbigald2", + "__reaction": "BIGALD2 + hv -> 0.6*HO2 + 0.6*DICARBO2", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald3", + "__reaction": "BIGALD3 + hv -> 0.6*HO2 + 0.6*CO + 0.6*MDIALO2", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald4", + "__reaction": "BIGALD4 + hv -> HO2 + CO + CH3COCHO + CH3CO3", + "from": "jno2", + "scale by": 0.006 + }, + { + "to": "jbzooh", + "__reaction": "BZOOH + hv -> BZALD + OH + HO2", + "from": "jch3ooh" + }, + { + "to": "jmekooh", + "__reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", + "from": "jch3ooh" + }, + { + "to": "jtolooh", + "__reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", + "from": "jch3ooh" + }, + { + "to": "jterpooh", + "__reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", + "from": "jch3ooh" + }, + { + "to": "jhonitr", + "__reaction": "HONITR + hv -> NO2 + 0.67*HO2 + 0.33*CH3CHO + 0.33*CH2O + 0.33*CO + 0.33*GLYALD + 0.33*CH3CO3 + 0.17*HYAC + 0.17*CH3COCH3", + "from": "jch2o_a" + }, + { + "to": "jhpald", + "__reaction": "HPALD + hv -> BIGALD3 + OH + HO2", + "from": "jno2", + "scale by": 0.006 + }, + { + "to": "jisopnooh", + "__reaction": "ISOPNOOH + hv -> NO2 + HO2 + ISOPOOH", + "from": "jch3ooh" + }, + { + "to": "jnc4cho", + "__reaction": "NC4CHO + hv -> BIGALD3 + NO2 + HO2", + "from": "jch2o_a" + }, + { + "to": "jnoa", + "__reaction": "NOA + hv -> NO2 + CH2O + CH3CO3", + "from": "jch2o_a" + }, + { + "to": "jnterpooh", + "__reaction": "NTERPOOH + hv -> TERPROD1 + NO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jphenooh", + "__reaction": "PHENOOH + hv -> OH + HO2 + 0.7*GLYOXAL", + "from": "jch3ooh" + }, + { + "to": "jtepomuc", + "__reaction": "TEPOMUC + hv -> 0.5*CH3CO3 + HO2 + 1.5*CO", + "from": "jno2", + "scale by": 0.1 + }, + { + "to": "jterp2ooh", + "__reaction": "TERP2OOH + hv -> OH + 0.375*CH2O + 0.3*CH3COCH3 + 0.25*CO + CO2 + TERPROD2 + HO2 + 0.25*GLYALD", + "from": "jch3ooh" + }, + { + "to": "jterpnit", + "__reaction": "TERPNIT + hv -> TERPROD1 + NO2 + HO2", + "from": "jch3ooh" + }, + { + "to": "jterprd1", + "__reaction": "TERPROD1 + hv -> HO2 + CO + TERPROD2", + "from": "jch3cho" + }, + { + "to": "jterprd2", + "__reaction": "TERPROD2 + hv -> 0.15*RO2 + 0.68*CH2O + 0.8*CO2 + 0.5*CH3COCH3 + 0.65*CH3CO3 + 1.2*HO2 + 1.7*CO", + "from": "jch3cho" + }, + { + "to": "jxylenooh", + "__reaction": "XYLENOOH + hv -> OH + HO2 + 0.34*GLYOXAL + 0.54*CH3COCHO + 0.06*BIGALD1 + 0.2*BIGALD2 + 0.15*BIGALD3 + 0.21*BIGALD4", + "from": "jch3ooh" + }, + { + "to": "jxylolooh", + "__reaction": "XYLOLOOH + hv -> OH + 0.17*GLYOXAL + 0.51*CH3COCHO + HO2", + "from": "jch3ooh" + }, + { + "to": "jch2br2", + "__reaction": "CH2BR2 + hv -> 2*BR", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jcl2o2", + "__reaction": "CL2O2 + hv -> 2*CL", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jh2402", + "__reaction": "H2402 + hv -> 2*BR + 2*COF2", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jhbr", + "__reaction": "HBR + hv -> BR + H", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jhf", + "__reaction": "HF + hv -> H + F", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jsf6", + "__reaction": "SF6 + hv -> sink", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jh2so4", + "__reaction": "H2SO4 + hv -> SO3 + H2O", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jocs", + "__reaction": "OCS + hv -> S + CO", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jso", + "__reaction": "SO + hv -> S + O", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jso2", + "__reaction": "SO2 + hv -> SO + O", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jso3", + "__reaction": "SO3 + hv -> SO2 + O", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jsoa1_a1", + "__reaction": "soa1_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa1_a2", + "__reaction": "soa1_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa2_a1", + "__reaction": "soa2_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa2_a2", + "__reaction": "soa2_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa3_a1", + "__reaction": "soa3_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa3_a2", + "__reaction": "soa3_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa4_a1", + "__reaction": "soa4_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa4_a2", + "__reaction": "soa4_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa5_a1", + "__reaction": "soa5_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa5_a2", + "__reaction": "soa5_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + } + ] + } } diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index ad41d8f0ed..075f69e5f5 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -308,6 +308,8 @@ subroutine tuvx_timestep_init( ) integer :: i_thread + if( .not. tuvx_active ) return + do i_thread = 1, size( tuvx_ptrs ) associate( tuvx => tuvx_ptrs( i_thread ) ) call set_et_flux( tuvx ) From 1f433ff3a3f47b93a233c2eb50881020ae6de4b0 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Tue, 22 Nov 2022 18:14:44 -0700 Subject: [PATCH 38/84] add euv rate calc calls to tuv-x --- cime_config/buildnml | 2 - cime_config/tuvx_BASIC.json | 154 ----- cime_config/tuvx_MOZART_TSMLT.json | 899 ++++++++++++++-------------- src/chemistry/mozart/chemistry.F90 | 7 +- src/chemistry/mozart/mo_chemini.F90 | 13 +- src/chemistry/mozart/mo_jeuv.F90 | 6 + src/chemistry/mozart/mo_tuvx.F90 | 196 +++++- 7 files changed, 654 insertions(+), 623 deletions(-) delete mode 100644 cime_config/tuvx_BASIC.json diff --git a/cime_config/buildnml b/cime_config/buildnml index 9a85ec91bd..9b2c7bcb22 100755 --- a/cime_config/buildnml +++ b/cime_config/buildnml @@ -204,8 +204,6 @@ def buildnml(case, caseroot, compname): if os.path.exists(dest_data): shutil.rmtree(dest_data) shutil.copytree(os.path.join(srcroot, "libraries", "tuv-x", "data"), dest_data) - shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_BASIC.json"), \ - os.path.join(rundir, "tuvx_BASIC.json")) shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_MOZART.json"), \ os.path.join(rundir, "tuvx_MOZART.json")) shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_MOZART_TS1.json"), \ diff --git a/cime_config/tuvx_BASIC.json b/cime_config/tuvx_BASIC.json deleted file mode 100644 index 6a81dd6875..0000000000 --- a/cime_config/tuvx_BASIC.json +++ /dev/null @@ -1,154 +0,0 @@ -{ - "__description": "This example configuration includes photolysis and dose rate calculations", - "O2 absorption" : { - "cross section parameters file": "data/cross_sections/O2_parameters.txt" - }, - "grids": { - }, - "profiles": { - }, - "radiative transfer": { - "solver": { - "type": "delta eddington" - }, - "cross sections": [ - { - "name": "air", - "type": "air" - }, - { - "name": "O3", - "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], - "type": "O3" - }, - { - "name": "O2", - "netcdf files": ["data/cross_sections/O2_1.nc"], - "type": "base", - "lower extrapolation": { "type": "boundary" } - } - ], - "radiators": [ - { - "name": "air", - "type": "base", - "cross section": "air", - "vertical profile": "air", - "vertical profile units": "molecule cm-3" - }, - { - "name": "O2", - "type": "base", - "cross section": "O2", - "vertical profile": "O2", - "vertical profile units": "molecule cm-3" - }, - { - "name": "O3", - "type": "base", - "cross section": "O3", - "vertical profile": "O3", - "vertical profile units": "molecule cm-3" - } - ] - }, - "photolysis": { - "reactions": [ - { - "name": "jo3_a", - "__reaction": "O3+hv->O2+O(1D)", - "cross section": { - "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], - "type": "O3" - }, - "quantum yield": { - "type": "O3+hv->O2+O(1D)" - } - }, - { - "name": "jo3_b", - "__reaction": "O3+hv->O2+O(3P)", - "cross section": { - "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], - "type": "O3" - }, - "quantum yield": { - "type": "O3+hv->O2+O(3P)" - } - }, - { - "name": "jo2_b", - "__reaction": "O2+hv->O+O", - "cross section": { - "netcdf files": ["data/cross_sections/O2_1.nc"], - "type": "base", - "lower extrapolation": { "type": "boundary" } - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jno2", - "__reaction": "NO2+hv->NO+O(3P)", - "cross section": { - "netcdf files": ["data/cross_sections/NO2_1.nc"], - "type": "NO2 tint" - }, - "quantum yield": { - "netcdf files": ["data/quantum_yields/NO2_1.nc"], - "type": "NO2 tint", - "lower extrapolation": { "type": "boundary" } - } - }, - { - "name": "jacet", - "__reaction": "CH3COCH3+hv->CH3CO+CH3", - "cross section": { - "netcdf files": ["data/cross_sections/CH3COCH3_1.nc"], - "type": "CH3COCH3+hv->CH3CO+CH3" - }, - "quantum yield": { - "type": "CH3COCH3+hv->CH3CO+CH3" - } - }, - { - "name": "jcfcl3", - "__reaction": "CCl3F+hv->Products", - "cross section": { - "netcdf files": ["data/cross_sections/CFC-11_1.nc"], - "type": "CCl3F+hv->Products" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jpan1", - "__reaction": "PAN+hv->CH3CO(OO)+NO2", - "cross section": { - "netcdf files": ["data/cross_sections/PAN_1.nc"], - "type": "CH3ONO2+hv->CH3O+NO2" - }, - "quantum yield": { - "type": "base", - "constant value": 0.7 - } - }, - { - "name": "jpan2", - "__reaction": "PAN+hv->CH3CO(O)+NO3", - "cross section": { - "netcdf files": ["data/cross_sections/PAN_1.nc"], - "type": "CH3ONO2+hv->CH3O+NO2" - }, - "quantum yield": { - "type": "base", - "constant value": 0.3 - } - } - ] - } -} diff --git a/cime_config/tuvx_MOZART_TSMLT.json b/cime_config/tuvx_MOZART_TSMLT.json index 2c07e5a1bd..fc873f4696 100644 --- a/cime_config/tuvx_MOZART_TSMLT.json +++ b/cime_config/tuvx_MOZART_TSMLT.json @@ -749,452 +749,455 @@ } ] }, - "__CAM aliasing": [ - { - "for": "jh2o_a", - "reaction": "H2O + hv -> OH + H", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jh2o_b", - "reaction": "H2O + hv -> H2 + O1D", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jh2o_c", - "reaction": "H2O + hv -> 2*H + O", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jo2_a", - "reaction": "O2 + hv -> O + O1D", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jho2no2_a", - "reaction": "HO2NO2 + hv -> OH + NO3", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jho2no2_b", - "reaction": "HO2NO2 + hv -> NO2 + HO2", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jn2o5_b", - "reaction": "N2O5 + hv -> NO + O + NO3", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jno", - "reaction": "NO + hv -> N + O", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jno_i", - "reaction": "NO + hv -> NOp + e", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jalknit", - "reaction": "ALKNIT + hv -> NO2 + 0.4*CH3CHO + 0.1*CH2O + 0.25*CH3COCH3 + HO2 + 0.8*MEK", - "use": "jch3ooh" - }, - { - "for": "jpooh", - "reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", - "use": "jch3ooh" - }, - { - "for": "jch3co3h", - "reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", - "use": "jh2o2", - "scale by": 0.28 - }, - { - "for": "jmpan", - "reaction": "MPAN + hv -> MCO3 + NO2", - "use": "jpan" - }, - { - "for": "jmacr_a", - "reaction": "MACR + hv -> 1.34*HO2 + 0.66*MCO3 + 1.34*CH2O + 1.34*CH3CO3", - "comments": "TODO - find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jmacr_b", - "reaction": "MACR + hv -> 0.66*HO2 + 1.34*CO", - "comments": "TODO - find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jc2h5ooh", - "reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", - "use": "jch3ooh" - }, - { - "for": "jc3h7ooh", - "reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", - "use": "jch3ooh" - }, - { - "for": "jc6h5ooh", - "reaction": "C6H5OOH + hv -> PHENO + OH", - "use": "jch3ooh" - }, - { - "for": "jch4_a", - "reaction": "CH4 + hv -> H + CH3O2", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jch4_b", - "reaction": "CH4 + hv -> 1.44*H2 + 0.18*CH2O + 0.18*O + 0.33*OH + 0.33*H + 0.44*CO2 + 0.38*CO + 0.05*H2O", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jco2", - "reaction": "CO2 + hv -> CO + O", - "comments": "TODO find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jeooh", - "reaction": "EOOH + hv -> EO + OH", - "use": "jch3ooh" - }, - { - "for": "jrooh", - "reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", - "use": "jch3ooh" - }, - { - "for": "jxooh", - "reaction": "XOOH + hv -> OH", - "use": "jch3ooh" - }, - { - "for": "jonitr", - "reaction": "ONITR + hv -> NO2", - "use": "jch3cho" - }, - { - "for": "jisopooh", - "reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", - "use": "jch3ooh" - }, - { - "for": "jhyac", - "reaction": "HYAC (CH3COCH2OH) + hv -> CH3CO3 + HO2 + CH2O", - "comments": "TODO - find this rate", - "use": "jo2_b", - "scale by": 0.0 - }, - { - "for": "jmek", - "reaction": "MEK + hv -> CH3CO3 + C2H5O2", - "use": "jacet" - }, - { - "for": "jalkooh", - "reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", - "use": "jch3ooh" - }, - { - "for": "jbenzooh", - "reaction": "BENZOOH + hv -> OH + GLYOXAL + 0.5*BIGALD1 + HO2", - "use": "jch3ooh" - }, - { - "for": "jbepomuc", - "reaction": "BEPOMUC + hv -> BIGALD1 + 1.5*HO2 + 1.5*CO", - "use": "jno2", - "scale by": 0.1 - }, - { - "for": "jbigald", - "reaction": "BIGALD + hv -> 0.45*CO + 0.13*GLYOXAL + 0.56*HO2 + 0.13*CH3CO3 + 0.18*CH3COCHO", - "use": "jno2", - "scale by": 0.2 - }, - { - "for": "jbigald1", - "reaction": "BIGALD1 + hv -> 0.6*MALO2 + HO2", - "use": "jno2", - "scale by": 0.14 - }, - { - "for": "jbigald2", - "reaction": "BIGALD2 + hv -> 0.6*HO2 + 0.6*DICARBO2", - "use": "jno2", - "scale by": 0.2 - }, - { - "for": "jbigald3", - "reaction": "BIGALD3 + hv -> 0.6*HO2 + 0.6*CO + 0.6*MDIALO2", - "use": "jno2", - "scale by": 0.2 - }, - { - "for": "jbigald4", - "reaction": "BIGALD4 + hv -> HO2 + CO + CH3COCHO + CH3CO3", - "use": "jno2", - "scale by": 0.006 - }, - { - "for": "jbzooh", - "reaction": "BZOOH + hv -> BZALD + OH + HO2", - "use": "jch3ooh" - }, - { - "for": "jmekooh", - "reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", - "use": "jch3ooh" - }, - { - "for": "jtolooh", - "reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", - "use": "jch3ooh" - }, - { - "for": "jterpooh", - "reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", - "use": "jch3ooh" - }, - { - "for": "jhonitr", - "reaction": "HONITR + hv -> NO2 + 0.67*HO2 + 0.33*CH3CHO + 0.33*CH2O + 0.33*CO + 0.33*GLYALD + 0.33*CH3CO3 + 0.17*HYAC + 0.17*CH3COCH3", - "use": "jch2o_a" - }, - { - "for": "jhpald", - "reaction": "HPALD + hv -> BIGALD3 + OH + HO2", - "use": "jno2", - "scale by": 0.006 - }, - { - "for": "jisopnooh", - "reaction": "ISOPNOOH + hv -> NO2 + HO2 + ISOPOOH", - "use": "jch3ooh" - }, - { - "for": "jnc4cho", - "reaction": "NC4CHO + hv -> BIGALD3 + NO2 + HO2", - "use": "jch2o_a" - }, - { - "for": "jnoa", - "reaction": "NOA + hv -> NO2 + CH2O + CH3CO3", - "use": "jch2o_a" - }, - { - "for": "jnterpooh", - "reaction": "NTERPOOH + hv -> TERPROD1 + NO2 + OH", - "use": "jch3ooh" - }, - { - "for": "jphenooh", - "reaction": "PHENOOH + hv -> OH + HO2 + 0.7*GLYOXAL", - "use": "jch3ooh" - }, - { - "for": "jtepomuc", - "reaction": "TEPOMUC + hv -> 0.5*CH3CO3 + HO2 + 1.5*CO", - "use": "jno2", - "scale by": 0.1 - }, - { - "for": "jterp2ooh", - "reaction": "TERP2OOH + hv -> OH + 0.375*CH2O + 0.3*CH3COCH3 + 0.25*CO + CO2 + TERPROD2 + HO2 + 0.25*GLYALD", - "use": "jch3ooh" - }, - { - "for": "jterpnit", - "reaction": "TERPNIT + hv -> TERPROD1 + NO2 + HO2", - "use": "jch3ooh" - }, - { - "for": "jterprd1", - "reaction": "TERPROD1 + hv -> HO2 + CO + TERPROD2", - "use": "jch3cho" - }, - { - "for": "jterprd2", - "reaction": "TERPROD2 + hv -> 0.15*RO2 + 0.68*CH2O + 0.8*CO2 + 0.5*CH3COCH3 + 0.65*CH3CO3 + 1.2*HO2 + 1.7*CO", - "use": "jch3cho" - }, - { - "for": "jxylenooh", - "reaction": "XYLENOOH + hv -> OH + HO2 + 0.34*GLYOXAL + 0.54*CH3COCHO + 0.06*BIGALD1 + 0.2*BIGALD2 + 0.15*BIGALD3 + 0.21*BIGALD4", - "use": "jch3ooh" - }, - { - "for": "jxylolooh", - "reaction": "XYLOLOOH + hv -> OH + 0.17*GLYOXAL + 0.51*CH3COCHO + HO2", - "use": "jch3ooh" - }, - { - "for": "jch2br2", - "reaction": "CH2BR2 + hv -> 2*BR", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jcl2o2", - "reaction": "CL2O2 + hv -> 2*CL", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jh2402", - "reaction": "H2402 + hv -> 2*BR + 2*COF2", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jhbr", - "reaction": "HBR + hv -> BR + H", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jhf", - "reaction": "HF + hv -> H + F", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jsf6", - "reaction": "SF6 + hv -> sink", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jh2so4", - "reaction": "H2SO4 + hv -> SO3 + H2O", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jocs", - "reaction": "OCS + hv -> S + CO", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jso", - "reaction": "SO + hv -> S + O", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jso2", - "reaction": "SO2 + hv -> SO + O", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jso3", - "reaction": "SO3 + hv -> SO2 + O", - "comments": "TODO - find this rate", - "use": "jo2_a", - "scale by": 0.0 - }, - { - "for": "jsoa1_a1", - "reaction": "soa1_a1 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa1_a2", - "reaction": "soa1_a2 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa2_a1", - "reaction": "soa2_a1 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa2_a2", - "reaction": "soa2_a2 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa3_a1", - "reaction": "soa3_a1 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa3_a2", - "reaction": "soa3_a2 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa4_a1", - "reaction": "soa4_a1 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa4_a2", - "reaction": "soa4_a2 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa5_a1", - "reaction": "soa5_a1 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - }, - { - "for": "jsoa5_a2", - "reaction": "soa5_a2 + hv -> Products", - "use": "jno2", - "scale by": 0.0004 - } - ] + "__CAM aliasing": { + "default matching": "backup", + "pairs": [ + { + "to": "jh2o_a", + "__reaction": "H2O + hv -> OH + H", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jh2o_b", + "__reaction": "H2O + hv -> H2 + O1D", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jh2o_c", + "__reaction": "H2O + hv -> 2*H + O", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jo2_a", + "__reaction": "O2 + hv -> O + O1D", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jho2no2_a", + "__reaction": "HO2NO2 + hv -> OH + NO3", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jho2no2_b", + "__reaction": "HO2NO2 + hv -> NO2 + HO2", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jn2o5_b", + "__reaction": "N2O5 + hv -> NO + O + NO3", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jno", + "__reaction": "NO + hv -> N + O", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jno_i", + "__reaction": "NO + hv -> NOp + e", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jalknit", + "__reaction": "ALKNIT + hv -> NO2 + 0.4*CH3CHO + 0.1*CH2O + 0.25*CH3COCH3 + HO2 + 0.8*MEK", + "from": "jch3ooh" + }, + { + "to": "jpooh", + "__reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jch3co3h", + "__reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", + "from": "jh2o2", + "scale by": 0.28 + }, + { + "to": "jmpan", + "__reaction": "MPAN + hv -> MCO3 + NO2", + "from": "jpan" + }, + { + "to": "jmacr_a", + "__reaction": "MACR + hv -> 1.34*HO2 + 0.66*MCO3 + 1.34*CH2O + 1.34*CH3CO3", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jmacr_b", + "__reaction": "MACR + hv -> 0.66*HO2 + 1.34*CO", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jc2h5ooh", + "__reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jc3h7ooh", + "__reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", + "from": "jch3ooh" + }, + { + "to": "jc6h5ooh", + "__reaction": "C6H5OOH + hv -> PHENO + OH", + "from": "jch3ooh" + }, + { + "to": "jch4_a", + "__reaction": "CH4 + hv -> H + CH3O2", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jch4_b", + "__reaction": "CH4 + hv -> 1.44*H2 + 0.18*CH2O + 0.18*O + 0.33*OH + 0.33*H + 0.44*CO2 + 0.38*CO + 0.05*H2O", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jco2", + "__reaction": "CO2 + hv -> CO + O", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jeooh", + "__reaction": "EOOH + hv -> EO + OH", + "from": "jch3ooh" + }, + { + "to": "jrooh", + "__reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", + "from": "jch3ooh" + }, + { + "to": "jxooh", + "__reaction": "XOOH + hv -> OH", + "from": "jch3ooh" + }, + { + "to": "jonitr", + "__reaction": "ONITR + hv -> NO2", + "from": "jch3cho" + }, + { + "to": "jisopooh", + "__reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", + "from": "jch3ooh" + }, + { + "to": "jhyac", + "__reaction": "HYAC (CH3COCH2OH) + hv -> CH3CO3 + HO2 + CH2O", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jmek", + "__reaction": "MEK + hv -> CH3CO3 + C2H5O2", + "from": "jacet" + }, + { + "to": "jalkooh", + "__reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", + "from": "jch3ooh" + }, + { + "to": "jbenzooh", + "__reaction": "BENZOOH + hv -> OH + GLYOXAL + 0.5*BIGALD1 + HO2", + "from": "jch3ooh" + }, + { + "to": "jbepomuc", + "__reaction": "BEPOMUC + hv -> BIGALD1 + 1.5*HO2 + 1.5*CO", + "from": "jno2", + "scale by": 0.1 + }, + { + "to": "jbigald", + "__reaction": "BIGALD + hv -> 0.45*CO + 0.13*GLYOXAL + 0.56*HO2 + 0.13*CH3CO3 + 0.18*CH3COCHO", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald1", + "__reaction": "BIGALD1 + hv -> 0.6*MALO2 + HO2", + "from": "jno2", + "scale by": 0.14 + }, + { + "to": "jbigald2", + "__reaction": "BIGALD2 + hv -> 0.6*HO2 + 0.6*DICARBO2", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald3", + "__reaction": "BIGALD3 + hv -> 0.6*HO2 + 0.6*CO + 0.6*MDIALO2", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald4", + "__reaction": "BIGALD4 + hv -> HO2 + CO + CH3COCHO + CH3CO3", + "from": "jno2", + "scale by": 0.006 + }, + { + "to": "jbzooh", + "__reaction": "BZOOH + hv -> BZALD + OH + HO2", + "from": "jch3ooh" + }, + { + "to": "jmekooh", + "__reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", + "from": "jch3ooh" + }, + { + "to": "jtolooh", + "__reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", + "from": "jch3ooh" + }, + { + "to": "jterpooh", + "__reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", + "from": "jch3ooh" + }, + { + "to": "jhonitr", + "__reaction": "HONITR + hv -> NO2 + 0.67*HO2 + 0.33*CH3CHO + 0.33*CH2O + 0.33*CO + 0.33*GLYALD + 0.33*CH3CO3 + 0.17*HYAC + 0.17*CH3COCH3", + "from": "jch2o_a" + }, + { + "to": "jhpald", + "__reaction": "HPALD + hv -> BIGALD3 + OH + HO2", + "from": "jno2", + "scale by": 0.006 + }, + { + "to": "jisopnooh", + "__reaction": "ISOPNOOH + hv -> NO2 + HO2 + ISOPOOH", + "from": "jch3ooh" + }, + { + "to": "jnc4cho", + "__reaction": "NC4CHO + hv -> BIGALD3 + NO2 + HO2", + "from": "jch2o_a" + }, + { + "to": "jnoa", + "__reaction": "NOA + hv -> NO2 + CH2O + CH3CO3", + "from": "jch2o_a" + }, + { + "to": "jnterpooh", + "__reaction": "NTERPOOH + hv -> TERPROD1 + NO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jphenooh", + "__reaction": "PHENOOH + hv -> OH + HO2 + 0.7*GLYOXAL", + "from": "jch3ooh" + }, + { + "to": "jtepomuc", + "__reaction": "TEPOMUC + hv -> 0.5*CH3CO3 + HO2 + 1.5*CO", + "from": "jno2", + "scale by": 0.1 + }, + { + "to": "jterp2ooh", + "__reaction": "TERP2OOH + hv -> OH + 0.375*CH2O + 0.3*CH3COCH3 + 0.25*CO + CO2 + TERPROD2 + HO2 + 0.25*GLYALD", + "from": "jch3ooh" + }, + { + "to": "jterpnit", + "__reaction": "TERPNIT + hv -> TERPROD1 + NO2 + HO2", + "from": "jch3ooh" + }, + { + "to": "jterprd1", + "__reaction": "TERPROD1 + hv -> HO2 + CO + TERPROD2", + "from": "jch3cho" + }, + { + "to": "jterprd2", + "__reaction": "TERPROD2 + hv -> 0.15*RO2 + 0.68*CH2O + 0.8*CO2 + 0.5*CH3COCH3 + 0.65*CH3CO3 + 1.2*HO2 + 1.7*CO", + "from": "jch3cho" + }, + { + "to": "jxylenooh", + "__reaction": "XYLENOOH + hv -> OH + HO2 + 0.34*GLYOXAL + 0.54*CH3COCHO + 0.06*BIGALD1 + 0.2*BIGALD2 + 0.15*BIGALD3 + 0.21*BIGALD4", + "from": "jch3ooh" + }, + { + "to": "jxylolooh", + "__reaction": "XYLOLOOH + hv -> OH + 0.17*GLYOXAL + 0.51*CH3COCHO + HO2", + "from": "jch3ooh" + }, + { + "to": "jch2br2", + "__reaction": "CH2BR2 + hv -> 2*BR", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jcl2o2", + "__reaction": "CL2O2 + hv -> 2*CL", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jh2402", + "__reaction": "H2402 + hv -> 2*BR + 2*COF2", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jhbr", + "__reaction": "HBR + hv -> BR + H", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jhf", + "__reaction": "HF + hv -> H + F", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jsf6", + "__reaction": "SF6 + hv -> sink", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jh2so4", + "__reaction": "H2SO4 + hv -> SO3 + H2O", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jocs", + "__reaction": "OCS + hv -> S + CO", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jso", + "__reaction": "SO + hv -> S + O", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jso2", + "__reaction": "SO2 + hv -> SO + O", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jso3", + "__reaction": "SO3 + hv -> SO2 + O", + "__comments": "TODO - find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jsoa1_a1", + "__reaction": "soa1_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa1_a2", + "__reaction": "soa1_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa2_a1", + "__reaction": "soa2_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa2_a2", + "__reaction": "soa2_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa3_a1", + "__reaction": "soa3_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa3_a2", + "__reaction": "soa3_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa4_a1", + "__reaction": "soa4_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa4_a2", + "__reaction": "soa4_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa5_a1", + "__reaction": "soa5_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa5_a2", + "__reaction": "soa5_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + } + ] + } } diff --git a/src/chemistry/mozart/chemistry.F90 b/src/chemistry/mozart/chemistry.F90 index 046b669ddb..2eb8a3ac13 100644 --- a/src/chemistry/mozart/chemistry.F90 +++ b/src/chemistry/mozart/chemistry.F90 @@ -162,6 +162,7 @@ subroutine chem_register use short_lived_species, only : slvd_index, short_lived_map=>map, register_short_lived_species use cfc11star, only : register_cfc11star use mo_photo, only : photo_register + use mo_tuvx, only : tuvx_register, tuvx_active use mo_aurora, only : aurora_register use aero_model, only : aero_model_register use physics_buffer, only : pbuf_add_field, dtype_r8 @@ -311,7 +312,11 @@ subroutine chem_register call register_cfc11star() if ( waccmx_is('ionosphere') ) then - call photo_register() + if( tuvx_active ) then + call tuvx_register( ) + else + call photo_register( ) + end if call aurora_register() endif diff --git a/src/chemistry/mozart/mo_chemini.F90 b/src/chemistry/mozart/mo_chemini.F90 index 46ba96c933..e9f9c7e3e2 100644 --- a/src/chemistry/mozart/mo_chemini.F90 +++ b/src/chemistry/mozart/mo_chemini.F90 @@ -48,7 +48,7 @@ subroutine chemini & use mo_srf_emissions, only : srf_emissions_inti use mo_sulf, only : sulf_inti use mo_photo, only : photo_inti - use mo_tuvx, only : tuvx_init + use mo_tuvx, only : tuvx_init, tuvx_active use mo_lightning, only : lightning_inti use mo_drydep, only : drydep_inti use mo_imp_sol, only : imp_slv_inti @@ -196,17 +196,16 @@ subroutine chemini & call euvac_init (euvac_file) call photo_inti( xs_coef_file, xs_short_file, xs_long_file, rsf_file, & - photon_file, electron_file, & - exo_coldens_file, photo_max_zen ) - + photon_file, electron_file, exo_coldens_file, photo_max_zen ) if (masterproc) write(iulog,*) 'chemini: after photo_inti on node ',iam !----------------------------------------------------------------------- ! ... initialize the TUV-x photolysis rate constant calculator !----------------------------------------------------------------------- - - call tuvx_init( ) - if (masterproc) write(iulog,*) 'chemini: after tuvx_init on node ',iam + if( tuvx_active ) then + call tuvx_init( photon_file, electron_file ) + if (masterproc) write(iulog,*) 'chemini: after tuvx_init on node ',iam + end if !----------------------------------------------------------------------- ! ... initialize ion production diff --git a/src/chemistry/mozart/mo_jeuv.F90 b/src/chemistry/mozart/mo_jeuv.F90 index c13255250e..b893ad9f20 100644 --- a/src/chemistry/mozart/mo_jeuv.F90 +++ b/src/chemistry/mozart/mo_jeuv.F90 @@ -80,6 +80,7 @@ subroutine jeuv_init (photon_file, electron_file, indexer) character(len=2) :: mstring character(len=7) :: jstring logical :: do_jeuv + logical, save :: is_initialized = .false. do_jeuv=.false. do_heating=.false. @@ -97,6 +98,11 @@ subroutine jeuv_init (photon_file, electron_file, indexer) endif enddo + ! If the module has already been initialized, return after + ! computing index map + if( is_initialized ) return + is_initialized = .true. + if (.not.do_jeuv) return if (solar_euv_data_active) then diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 075f69e5f5..3e6987823a 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -17,6 +17,7 @@ module mo_tuvx private public :: tuvx_readnl + public :: tuvx_register public :: tuvx_init public :: tuvx_timestep_init public :: tuvx_get_photo_rates @@ -44,14 +45,24 @@ module mo_tuvx integer, parameter :: RADIATOR_INDEX_AEROSOL = 1 ! Aerosol radiator index ! Information needed to access CAM species state data + logical :: is_fixed_N2 = .false. ! indicates whether N2 concentrations are fixed + logical :: is_fixed_O = .false. ! indicates whether O concentrations are fixed logical :: is_fixed_O2 = .false. ! indicates whether O2 concentrations are fixed logical :: is_fixed_O3 = .false. ! indicates whether O3 concentrations are fixed + integer :: index_N2 = 0 ! index for N2 in concentration array + integer :: index_O = 0 ! index for O in concentration array integer :: index_O2 = 0 ! index for O2 in concentration array integer :: index_O3 = 0 ! index for O3 in concentration array ! Information needed to access aerosol optical properties logical :: aerosol_exists = .false. ! indicates whether aerosol optical properties are available + ! Information needed to set extended-UV photo rates + logical :: do_euv = .false. ! Indicates whether to calculate + ! extended-UV photo rates + integer :: ion_rates_pbuf_index = 0 ! Index in physics buffer for + ! ionization rates + ! TODO how should these paths be set and communicated to this wrapper? character(len=*), parameter :: wavelength_config_path = & "data/grids/wavelength/combined.grid" @@ -60,7 +71,8 @@ module mo_tuvx ! TUV-x calculator for each OMP thread type :: tuvx_ptr type(core_t), pointer :: core_ => null( ) ! TUV-x calculator - integer :: n_photo_rates_ ! number of photo reactions in TUV-x + integer :: n_photo_rates_ = 0 ! number of photo reactions in TUV-x + integer :: n_euv_rates_ = 0 ! number of extreme-UV rates real(r8), allocatable :: photo_rates_(:,:,:) ! photolysis rate constants ! (column, vertical level, reaction) [s-1] type(map_t) :: photo_rate_map_ ! map between TUV-x and CAM @@ -94,6 +106,23 @@ module mo_tuvx !================================================================================================ contains +!================================================================================================ + + !----------------------------------------------------------------------- + ! registers fields in the physics buffer + !----------------------------------------------------------------------- + subroutine tuvx_register( ) + + use mo_jeuv, only : nIonRates + use physics_buffer, only : pbuf_add_field, dtype_r8 + use ppgrid, only : pcols ! maximum number of columns + + ! add photo-ionization rates to physics buffer for WACCMX Ionosphere module + call pbuf_add_field( 'IonRates', 'physpkg', dtype_r8, (/ pcols, pver, nIonRates /), & + ion_rates_pbuf_index ) ! Ionization rates for O+, O2+, N+, N2+, NO+ + + end subroutine tuvx_register + !================================================================================================ !----------------------------------------------------------------------- @@ -159,13 +188,14 @@ end subroutine tuvx_readnl !----------------------------------------------------------------------- ! Initializes TUV-x for photolysis calculations !----------------------------------------------------------------------- - subroutine tuvx_init( ) + subroutine tuvx_init( photon_file, electron_file ) #ifdef HAVE_MPI use mpi #endif use cam_logfile, only : iulog ! log file output unit use mo_chem_utls, only : get_spc_ndx, get_inv_ndx + use mo_jeuv, only : neuv ! number of extreme-UV rates use musica_assert, only : assert_msg use musica_config, only : config_t use musica_mpi, only : musica_mpi_rank @@ -180,6 +210,9 @@ subroutine tuvx_init( ) use tuvx_profile_warehouse, only : profile_warehouse_t use tuvx_radiator_warehouse, only : radiator_warehouse_t + 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 + character(len=*), parameter :: my_name = "TUV-x wrapper initialization" class(core_t), pointer :: core character, allocatable :: buffer(:) @@ -191,8 +224,11 @@ subroutine tuvx_init( ) class(profile_warehouse_t), pointer :: cam_profiles class(radiator_warehouse_t), pointer :: cam_radiators integer :: pack_size, pos, i_core, i_err + logical, save :: is_initialized = .false. - if (.not.tuvx_active) return + if( .not. tuvx_active ) return + if( is_initialized ) return + is_initialized = .true. config_path = trim(tuvx_config_path) @@ -203,6 +239,11 @@ subroutine tuvx_init( ) //"MPI support enabled for TUV-x" ) #endif + ! ================================= + ! initialize the extended-UV module + ! ================================= + call initialize_euv( photon_file, electron_file, do_euv ) + ! ========================================================================== ! Create the set of TUV-x grids and profiles that CAM will update at runtime ! ========================================================================== @@ -218,7 +259,7 @@ subroutine tuvx_init( ) call tuvx_config%from_file( config_path%to_char( ) ) call tuvx_config%get( "__CAM aliasing", map_config, my_name ) core => core_t( config_path, cam_grids, cam_profiles, cam_radiators ) - call set_photo_rate_map( core, map_config, map ) + call set_photo_rate_map( core, map_config, do_euv, map ) pack_size = core%pack_size( mpicom ) + & map%pack_size( mpicom ) allocate( buffer( pack_size ) ) @@ -265,8 +306,10 @@ subroutine tuvx_init( ) ! Create a working array for calculated photolysis rate constants ! =============================================================== tuvx%n_photo_rates_ = tuvx%core_%number_of_photolysis_reactions( ) + if( do_euv ) tuvx%n_euv_rates_ = neuv height => tuvx%core_%get_grid( "height", "km" ) - allocate( tuvx%photo_rates_( pcols, height%ncells_ + 1, tuvx%n_photo_rates_ ) ) + allocate( tuvx%photo_rates_( pcols, height%ncells_ + 1, & + tuvx%n_photo_rates_ + tuvx%n_euv_rates_ ) ) deallocate( height ) end associate @@ -279,6 +322,12 @@ subroutine tuvx_init( ) ! ============================================= ! Get index info for CAM species concentrations ! ============================================= + index_N2 = get_inv_ndx( 'N2' ) + is_fixed_N2 = index_N2 > 0 + if( .not. is_fixed_N2 ) index_N2 = get_spc_ndx( 'N2' ) + index_O = get_inv_ndx( 'O' ) + is_fixed_O = index_O > 0 + if( .not. is_fixed_O ) index_O = get_spc_ndx( 'O' ) index_O2 = get_inv_ndx( 'O2' ) is_fixed_O2 = index_O2 > 0 if( .not. is_fixed_O2 ) index_O2 = get_spc_ndx( 'O2' ) @@ -364,7 +413,7 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & integer :: i_col ! column index integer :: i_level ! vertical level index - if (.not.tuvx_active) return + if( .not. tuvx_active ) return associate( tuvx => tuvx_ptrs( thread_id( ) ) ) @@ -395,7 +444,22 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & solar_zenith_angle(i_col) * 180.0_r8 / pi, & earth_sun_distance = earth_sun_distance, & photolysis_rate_constants = & - tuvx%photo_rates_(i_col,:,:) ) + tuvx%photo_rates_(i_col,:,1:tuvx%n_photo_rates_) ) + + ! ============================== + ! Calculate the extreme-UV rates + ! ============================== + if( do_euv ) then + associate( euv_begin => tuvx%n_photo_rates_ + 1, & + euv_end => tuvx%n_photo_rates_ + tuvx%n_euv_rates_ ) + call calculate_euv_rates( solar_zenith_angle(i_col) * 180.0_r8 / pi, & + fixed_species_conc(i_col,:,:), & + species_vmr(i_col,:,:), & + height_mid(i_col,:), & + height_int(i_col,:), & + tuvx%photo_rates_(i_col,:,euv_begin:euv_end) ) + end associate + end if ! ============================================ ! Return the photolysis rates on the CAM grids @@ -511,6 +575,32 @@ subroutine log_initialization( ) end subroutine log_initialization +!================================================================================================ + + !----------------------------------------------------------------------- + ! initializes the external extreme-UV module + !----------------------------------------------------------------------- + subroutine initialize_euv( photon_file, electron_file, do_euv ) + + use chem_mods, only : phtcnt ! number of CAM-Chem photolysis reactions + use mo_jeuv, only : jeuv_init ! extreme-UV initialization + + 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 + logical, intent(out) :: do_euv ! indicates whether extreme-UV + ! calculations are needed + + integer, allocatable :: euv_index_map(:) + + allocate( euv_index_map( phtcnt ) ) + euv_index_map(:) = 0 + call jeuv_init( photon_file, electron_file, euv_index_map ) + do_euv = any( euv_index_map(:) > 0 ) + + end subroutine initialize_euv + !================================================================================================ !----------------------------------------------------------------------- @@ -550,32 +640,46 @@ end subroutine initialize_diagnostics !----------------------------------------------------------------------- ! Sets up a map between the TUV-x and CAM photolysis rate arrays !----------------------------------------------------------------------- - subroutine set_photo_rate_map( core, config, map ) + subroutine set_photo_rate_map( core, config, do_euv, map ) use cam_logfile, only : iulog ! log info output unit use chem_mods, only : phtcnt, & ! number of photolysis reactions rxt_tag_lst ! labels for all chemical reactions ! NOTE photolysis reactions are ! expected to appear first + use mo_jeuv, only : neuv ! number of extreme-UV rates use musica_config, only : config_t use musica_string, only : string_t type(core_t), intent(in) :: core ! TUV-x core type(config_t), intent(inout) :: config ! CAM<->TUV-x map configuration + logical, intent(in) :: do_euv ! indicates whether to include + ! extreme-UV rates in the mapping type(map_t), intent(out) :: map integer :: i_label - type(string_t), allocatable :: tuvx_labels(:), cam_labels(:) + type(string_t) :: str_label + type(string_t), allocatable :: tuvx_labels(:), all_labels(:), cam_labels(:) allocate( cam_labels( phtcnt ) ) do i_label = 1, phtcnt cam_labels( i_label ) = trim( rxt_tag_lst( i_label ) ) end do tuvx_labels = core%photolysis_reaction_labels( ) - map = map_t( config, tuvx_labels, cam_labels ) + if( do_euv ) then + allocate( all_labels( size( tuvx_labels ) + neuv ) ) + all_labels( 1 : size( tuvx_labels ) ) = tuvx_labels(:) + do i_label = 1, neuv + str_label = i_label + all_labels( size( tuvx_labels ) + i_label ) = "jeuv_"//str_label + end do + else + all_labels = tuvx_labels + end if + map = map_t( config, all_labels, cam_labels ) write(iulog,*) write(iulog,*) "TUV-x <-> CAM-Chem photolysis rate constant map" - call map%print( tuvx_labels, cam_labels, iulog ) + call map%print( all_labels, cam_labels, iulog ) end subroutine set_photo_rate_map @@ -1237,6 +1341,76 @@ subroutine reorder_optics_array( optics_array ) end subroutine reorder_optics_array +!================================================================================================ + + !----------------------------------------------------------------------- + ! Calculates extreme-UV ionization rates + !----------------------------------------------------------------------- + subroutine calculate_euv_rates( solar_zenith_angle, fixed_species_conc, & + species_vmr, height_mid, height_int, euv_rates ) + + use chem_mods, only : gas_pcnst, & ! number of non-fixed species + nfs, & ! number of fixed species + indexm ! index for air density in fixed species array + use mo_jeuv, only : jeuv, neuv ! number of extreme-UV rates + use ref_pres, only : ptop_ref ! pressure at the top of the column (Pa) + + real(r8), intent(in) :: solar_zenith_angle ! degrees + real(r8), intent(in) :: fixed_species_conc(pver,max(1,nfs)) ! fixed species densities + ! (molecule cm-3) + real(r8), intent(in) :: species_vmr(pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) + real(r8), intent(in) :: height_mid(pver) ! height at mid-points (km) + real(r8), intent(in) :: height_int(pver+1) ! height at interfaces (km) + real(r8), intent(out) :: euv_rates(pver,neuv) ! calculated extreme-UV rates + + real(r8) :: o_dens(pver+1), o2_dens(pver+1), n2_dens(pver+1), height_arg(pver+1) + + ! ========== + ! N2 density + ! ========== + if( is_fixed_N2 ) then + n2_dens(2:) = fixed_species_conc(:pver,index_N2) + else + n2_dens(2:) = species_vmr(:pver,index_N2) * fixed_species_conc(:pver,indexm) + end if + n2_dens(1) = 0.0_r8 + if( ptop_ref > 10.0_r8 ) n2_dens(1) = n2_dens(2) * 0.9_r8 + + ! ========= + ! O density + ! ========= + if( is_fixed_O ) then + o_dens(2:) = fixed_species_conc(:pver,index_O) + else + o_dens(2:) = species_vmr(:pver,index_O) * fixed_species_conc(:pver,indexm) + end if + o_dens(1) = 0.0_r8 + + ! ========== + ! O2 density + ! ========== + if( is_fixed_O2 ) then + o2_dens(2:) = fixed_species_conc(:pver,index_O2) + else + o2_dens(2:) = species_vmr(:pver,index_O2) * fixed_species_conc(:pver,indexm) + end if + o2_dens(1) = 0.0_r8 + if( ptop_ref > 10.0_r8 ) then + o2_dens(1) = o2_dens(2) * 7.0_r8 / ( height_int(1) - height_int(2) ) + end if + + ! ======================= + ! special height argument + ! ======================= + height_arg(2:) = height_mid(:) + height_arg(1) = height_arg(2) + ( height_int(1) - height_int(2) ) + + call jeuv( pver, solar_zenith_angle, o_dens, o2_dens, n2_dens, height_arg, & + euv_rates ) + + end subroutine calculate_euv_rates + !================================================================================================ end module mo_tuvx From fd715e185a8193c357b10e66b99401375533b871 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Tue, 29 Nov 2022 12:14:56 -0700 Subject: [PATCH 39/84] fix tuvx_active flag use; fix height values near surface --- src/chemistry/mozart/chemistry.F90 | 4 ++-- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 6 +++--- src/chemistry/mozart/mo_tuvx.F90 | 18 ++++++++++-------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/chemistry/mozart/chemistry.F90 b/src/chemistry/mozart/chemistry.F90 index 2eb8a3ac13..ff9f17db68 100644 --- a/src/chemistry/mozart/chemistry.F90 +++ b/src/chemistry/mozart/chemistry.F90 @@ -999,7 +999,7 @@ subroutine chem_timestep_init(phys_state,pbuf2d) use mo_aurora, only : aurora_timestep_init use mo_photo, only : photo_timestep_init - use mo_tuvx, only : tuvx_timestep_init + use mo_tuvx, only : tuvx_active, tuvx_timestep_init use cfc11star, only : update_cfc11star use physics_buffer, only : physics_buffer_desc @@ -1074,7 +1074,7 @@ subroutine chem_timestep_init(phys_state,pbuf2d) !----------------------------------------------------------------------------- ! ... setup the TUV-x profiles for this timestep !----------------------------------------------------------------------------- - call tuvx_timestep_init( ) + if( tuvx_active ) call tuvx_timestep_init( ) call update_cfc11star( pbuf2d, phys_state ) diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index c11aed989c..3675d0d1cd 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -785,7 +785,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & call shr_orb_decl( calday, eccen, mvelpp, lambm0, obliqr , & delta, esfact ) - if (tuvx_active) then + if (.not.tuvx_active) then !----------------------------------------------------------------- ! ... lookup the photolysis rates from table !----------------------------------------------------------------- @@ -797,10 +797,10 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & !----------------------------------------------------------------- ! ... get calculated photolysis rates from TUV-x !----------------------------------------------------------------- - call tuvx_get_photo_rates( state, pbuf, ncol, lchnk, zm, zi, & + call tuvx_get_photo_rates( state, pbuf, ncol, lchnk, zmid, zint, & tfld, ts, invariants, vmr, col_delta, & asdir, zen_angle, esfact, & - reaction_rates ) + reaction_rates(:,:,1:phtcnt) ) endif do i = 1,phtcnt diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 3e6987823a..f01c6342b9 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -394,16 +394,16 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & type(physics_buffer_desc), pointer, intent(inout) :: pbuf(:) integer, intent(in) :: ncol ! number of active columns on this thread integer, intent(in) :: lchnk ! identifier for this thread - real(r8), intent(in) :: height_mid(pcols,pver) ! height at mid-points (km) - real(r8), intent(in) :: height_int(pcols,pver+1) ! height at interfaces (km) + real(r8), intent(in) :: height_mid(ncol,pver) ! height at mid-points (km) + real(r8), intent(in) :: height_int(ncol,pver+1) ! height at interfaces (km) real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities ! (molecule cm-3) real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing ! ratios (mol mol-1) - real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! above column densities - ! (molecule cm-3) + real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! layer column densities + ! (molecule cm-2) real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) real(r8), intent(in) :: solar_zenith_angle(ncol) ! solar zenith angle (radians) real(r8), intent(in) :: earth_sun_distance ! Earth-Sun distance (AU) @@ -1025,16 +1025,17 @@ subroutine set_heights( this, i_col, ncol, height_mid, height_int ) class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator integer, intent(in) :: i_col ! column to set conditions for integer, intent(in) :: ncol ! number of colums to calculated photolysis for - real(r8), intent(in) :: height_mid(pcols,pver) ! height above the surface at mid-points (km) - real(r8), intent(in) :: height_int(pcols,pver+1) ! height above the surface at interfaces (km) + real(r8), intent(in) :: height_mid(ncol,pver) ! height above the surface at mid-points (km) + real(r8), intent(in) :: height_int(ncol,pver+1) ! height above the surface at interfaces (km) integer :: i_level real(r8) :: edges(pver+1) real(r8) :: mid_points(pver) - edges(1) = 0.0_r8 + edges(1) = height_int(i_col,pver+1) edges(2:pver+1) = height_mid(i_col,pver:1:-1) - mid_points(1) = height_mid(i_col,pver) * 0.5_r8 + mid_points(1) = ( height_mid(i_col,pver) - height_int(i_col,pver+1) ) * 0.5_r8 & + + height_int(i_col,pver+1) mid_points(2:pver) = height_int(i_col,pver:2:-1) call this%grids_( GRID_INDEX_HEIGHT )%update( edges = edges, mid_points = mid_points ) this%height_delta_(1:pver) = edges(2:pver+1) - edges(1:pver) @@ -1107,6 +1108,7 @@ subroutine set_et_flux( this ) ! (photon cm-2 nm-1 s-1) class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + real(r8) :: et_flux_orig(nbins) integer :: n_tuvx_bins From 9c3d234093f9f5c313191087fb611889922bc248 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Tue, 29 Nov 2022 15:56:19 -0700 Subject: [PATCH 40/84] exclude dark columns from photo calculations --- src/chemistry/mozart/mo_chemini.F90 | 2 +- src/chemistry/mozart/mo_tuvx.F90 | 46 ++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/chemistry/mozart/mo_chemini.F90 b/src/chemistry/mozart/mo_chemini.F90 index e9f9c7e3e2..b63a498864 100644 --- a/src/chemistry/mozart/mo_chemini.F90 +++ b/src/chemistry/mozart/mo_chemini.F90 @@ -203,7 +203,7 @@ subroutine chemini & ! ... initialize the TUV-x photolysis rate constant calculator !----------------------------------------------------------------------- if( tuvx_active ) then - call tuvx_init( photon_file, electron_file ) + call tuvx_init( photon_file, electron_file, photo_max_zen ) if (masterproc) write(iulog,*) 'chemini: after tuvx_init on node ',iam end if diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index f01c6342b9..a9c74d99fe 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -63,6 +63,9 @@ module mo_tuvx integer :: ion_rates_pbuf_index = 0 ! Index in physics buffer for ! ionization rates + ! Cutoff solar zenith angle for doing photolysis rate calculations [degrees] + integer :: max_sza = 0.0_r8 + ! TODO how should these paths be set and communicated to this wrapper? character(len=*), parameter :: wavelength_config_path = & "data/grids/wavelength/combined.grid" @@ -188,11 +191,12 @@ end subroutine tuvx_readnl !----------------------------------------------------------------------- ! Initializes TUV-x for photolysis calculations !----------------------------------------------------------------------- - subroutine tuvx_init( photon_file, electron_file ) + subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) #ifdef HAVE_MPI use mpi #endif + use cam_abortutils, only : endrun use cam_logfile, only : iulog ! log file output unit use mo_chem_utls, only : get_spc_ndx, get_inv_ndx use mo_jeuv, only : neuv ! number of extreme-UV rates @@ -201,6 +205,7 @@ subroutine tuvx_init( photon_file, electron_file ) use musica_mpi, only : musica_mpi_rank use musica_string, only : string_t, to_char use ppgrid, only : pcols ! maximum number of columns + use shr_const_mod, only : pi => shr_const_pi use solar_irrad_data, only : has_spectrum use spmd_utils, only : main_task => masterprocid, & is_main_task => masterproc, & @@ -212,6 +217,8 @@ subroutine tuvx_init( photon_file, electron_file ) 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] character(len=*), parameter :: my_name = "TUV-x wrapper initialization" class(core_t), pointer :: core @@ -239,13 +246,22 @@ subroutine tuvx_init( photon_file, electron_file ) //"MPI support enabled for TUV-x" ) #endif + ! =============================================================== + ! set the maximum solar zenith angle to calculate photo rates for + ! =============================================================== + max_sza = max_solar_zenith_angle + if( is_main_task ) write(iulog,*) "TUV-x max solar zenith angle [degrees]:", max_sza + if( max_sza <= 0.0_r8 .or. max_sza > 180.0_r8 ) then + call endrun( "TUV-x: max solar zenith angle must be between 0 and 180 degress" ) + end if + ! ================================= ! initialize the extended-UV module ! ================================= call initialize_euv( photon_file, electron_file, do_euv ) ! ========================================================================== - ! Create the set of TUV-x grids and profiles that CAM will update at runtime + ! create the set of TUV-x grids and profiles that CAM will update at runtime ! ========================================================================== cam_grids => get_cam_grids( wavelength_config_path ) cam_profiles => get_cam_profiles( cam_grids ) @@ -410,20 +426,29 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & real(r8), intent(inout) :: photolysis_rates(ncol,pver,phtcnt) ! photolysis rate ! constants (1/s) - integer :: i_col ! column index - integer :: i_level ! vertical level index + integer :: i_col ! column index + integer :: i_level ! vertical level index + real(r8) :: sza ! solar zenith angle [degrees] if( .not. tuvx_active ) return associate( tuvx => tuvx_ptrs( thread_id( ) ) ) + tuvx%photo_rates_(:,:,:) = 0.0_r8 + ! ============================================== - ! get aerosol optical properties for all columns + ! set aerosol optical properties for all columns ! ============================================== call get_aerosol_optical_properties( tuvx, state, pbuf ) do i_col = 1, ncol + ! =================================== + ! skip columns in near total darkness + ! =================================== + sza = solar_zenith_angle(i_col) * 180.0_r8 / pi + if( sza < 0.0_r8 .or. sza > max_sza ) cycle + ! =================== ! update grid heights ! =================== @@ -440,8 +465,7 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & ! =================================================== ! Calculate photolysis rate constants for this column ! =================================================== - call tuvx%core_%run( solar_zenith_angle = & - solar_zenith_angle(i_col) * 180.0_r8 / pi, & + call tuvx%core_%run( solar_zenith_angle = sza, & earth_sun_distance = earth_sun_distance, & photolysis_rate_constants = & tuvx%photo_rates_(i_col,:,1:tuvx%n_photo_rates_) ) @@ -460,10 +484,12 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & tuvx%photo_rates_(i_col,:,euv_begin:euv_end) ) end associate end if + end do - ! ============================================ - ! Return the photolysis rates on the CAM grids - ! ============================================ + ! ============================================ + ! Return the photolysis rates on the CAM grids + ! ============================================ + do i_col = 1, ncol do i_level = 1, pver call tuvx%photo_rate_map_%apply( tuvx%photo_rates_(i_col,pver-i_level+1,:), & photolysis_rates(i_col,i_level,:) ) From a60e90d135f0ee8a2858232b0df4e8edbafda07a Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Wed, 30 Nov 2022 18:03:21 -0700 Subject: [PATCH 41/84] fix flux and density calcs --- src/chemistry/mozart/mo_tuvx.F90 | 38 +++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index a9c74d99fe..938e00dbe1 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -476,7 +476,7 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & if( do_euv ) then associate( euv_begin => tuvx%n_photo_rates_ + 1, & euv_end => tuvx%n_photo_rates_ + tuvx%n_euv_rates_ ) - call calculate_euv_rates( solar_zenith_angle(i_col) * 180.0_r8 / pi, & + call calculate_euv_rates( sza, & fixed_species_conc(i_col,:,:), & species_vmr(i_col,:,:), & height_mid(i_col,:), & @@ -1138,12 +1138,30 @@ subroutine set_et_flux( this ) real(r8) :: et_flux_orig(nbins) integer :: n_tuvx_bins - et_flux_orig(:) = sol_etf(:) * ( we(2:nbins+1) - we(1:nbins) ) + ! =============================================== + ! regrid normalized flux to TUV-x wavelength grid + !================================================ + et_flux_orig(:) = sol_etf(:) n_tuvx_bins = size(this%wavelength_mid_values_) call rebin( nbins, n_tuvx_bins, we, this%wavelength_edges_, et_flux_orig, & this%wavelength_mid_values_ ) + + ! ======================================================== + ! convert normalized flux to flux on TUV-x wavelength grid + ! ======================================================== + this%wavelength_mid_values_(:) = this%wavelength_mid_values_(:) * & + ( this%wavelength_edges_(2:n_tuvx_bins+1) - & + this%wavelength_edges_(1:n_tuvx_bins) ) + + ! ==================================== + ! estimate unused edge values for flux + ! ==================================== this%wavelength_values_(1) = this%wavelength_mid_values_(1) this%wavelength_values_(2:n_tuvx_bins+1) = this%wavelength_mid_values_(1:n_tuvx_bins) + + ! ============================ + ! update TUV-x ET flux profile + ! ============================ call this%profiles_( PROFILE_INDEX_ET_FLUX )%update( & mid_point_values = this%wavelength_mid_values_, & edge_values = this%wavelength_values_) @@ -1190,7 +1208,7 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species edges(1) = fixed_species_conc(i_col,pver,indexm) edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,indexm) densities(1:pver) = this%height_delta_(1:pver) * km2cm * & - sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) + sqrt(edges(1:pver)) * sqrt(edges(2:pver+1)) call this%profiles_( PROFILE_INDEX_AIR )%update( & edge_values = edges, layer_densities = densities, & scale_height = 8.01_r8 ) ! scale height in [km] @@ -1209,9 +1227,13 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species else edges(:) = 0.0_r8 end if - exo_val = exo_column_conc(i_col,0,1) + if( nabscol >= 2 ) then + exo_val = exo_column_conc(i_col,0,2) + else + exo_val = 0.0_r8 + end if densities(1:pver) = this%height_delta_(1:pver) * km2cm * & - sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) + sqrt(edges(1:pver)) * sqrt(edges(2:pver+1)) call this%profiles_( PROFILE_INDEX_O2 )%update( & edge_values = edges, layer_densities = densities, & exo_density = exo_val ) @@ -1230,13 +1252,13 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species else edges(:) = 0.0_r8 end if - if( nabscol >= 2 ) then - exo_val = exo_column_conc(i_col,0,2) + if( nabscol >= 1 ) then + exo_val = exo_column_conc(i_col,0,1) else exo_val = 0.0_r8 end if densities(1:pver) = this%height_delta_(1:pver) * km2cm * & - sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) + sqrt(edges(1:pver)) * sqrt(edges(2:pver+1)) call this%profiles_( PROFILE_INDEX_O3 )%update( & edge_values = edges, layer_densities = densities, & exo_density = exo_val ) From fe1f142a6707c8deebf92e5503ce63ca06f7e7df Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Thu, 1 Dec 2022 14:43:18 -0800 Subject: [PATCH 42/84] add missing MOZART photo rates --- cime_config/tuvx_MOZART.json | 102 ++++++++++++++++++++++------------- 1 file changed, 66 insertions(+), 36 deletions(-) diff --git a/cime_config/tuvx_MOZART.json b/cime_config/tuvx_MOZART.json index 08041a0f6e..cf23650b93 100644 --- a/cime_config/tuvx_MOZART.json +++ b/cime_config/tuvx_MOZART.json @@ -328,7 +328,7 @@ { "name": "jglyoxal", "__reaction": "GLYOXAL + hv -> 2*CO + 2*HO2", - "__comments": "NOTE the products of this reaction don't exactly match", + "__comments": "TODO the products of this reaction don't exactly match", "cross section": { "netcdf files": [ "data/cross_sections/CHOCHO_1.nc" @@ -344,26 +344,77 @@ "type": "boundary" } } + }, + { + "name": "jho2no2_a", + "__reaction": "HNO4 + hv -> OH + NO3", + "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", + "cross section": { + "netcdf files": ["data/cross_sections/HNO4_1.nc"], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.2 + } + }, + { + "name": "jho2no2_b", + "__reaction": "HNO4 + hv -> HO2 + NO2", + "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", + "cross section": { + "netcdf files": ["data/cross_sections/HNO4_1.nc"], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.8 + } + }, + { + "name": "jmacr_a", + "__reaction": "CH2=C(CH3)CHO->1.34HO2+0.66MCO3+1.34CH2O+CH3CO3", + "__comments": "Methacrolein photolysis channel 1", + "cross section": { + "netcdf files": ["data/cross_sections/MACR_JPL2006.nc"], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.005 + } + }, + { + "name": "jmacr_b", + "__reaction": "CH2=C(CH3)CHO->0.66OH+1.34CO", + "__comments": "Methacrolein photolysis channel 2", + "cross section": { + "netcdf files": ["data/cross_sections/MACR_JPL2006.nc"], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.005 + } + }, + { + "name": "jhyac", + "__reaction": "CH2(OH)COCH3->CH3CO3+HO2+CH2O", + "__comments": "hydroxy acetone", + "cross section": { + "netcdf files": ["data/cross_sections/HYAC.nc"], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.65 + } } ] }, "__CAM aliasing": { "default matching": "backup", "pairs": [ - { - "to": "jho2no2_a", - "__reaction": "HO2NO2 + hv -> OH + NO3", - "__comments": "TODO find this rate", - "from": "jo2", - "scale by": 0.0 - }, - { - "to": "jho2no2_b", - "__reaction": "HO2NO2 + hv -> NO2 + HO2", - "__comments": "TODO find this rate", - "from": "jo2", - "scale by": 0.0 - }, { "to": "jpooh", "__reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", @@ -380,20 +431,6 @@ "__reaction": "MPAN + hv -> MCO3 + NO2", "from": "jpan" }, - { - "to": "jmacr_a", - "__reaction": "MACR + hv -> 1.34*HO2 + 0.66*MCO3 + 1.34*CH2O + 1.34*CH3CO3", - "__comments": "TODO - find this rate", - "from": "jo2", - "scale by": 0.0 - }, - { - "to": "jmacr_b", - "__reaction": "MACR + hv -> 0.66*HO2 + 1.34*CO", - "__comments": "TODO - find this rate", - "from": "jo2", - "scale by": 0.0 - }, { "to": "jc2h5ooh", "__reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", @@ -424,13 +461,6 @@ "__reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", "from": "jch3ooh" }, - { - "to": "jhyac", - "__reaction": "HYAC (CH3COCH2OH) + hv -> CH3CO3 + HO2 + CH2O", - "__comments": "TODO - find this rate", - "from": "jo2", - "scale by": 0.0 - }, { "to": "jmek", "__reaction": "MEK + hv -> CH3CO3 + C2H5O2", From d5c4d84fe149efdf359a5e01ed7bf7418c4333a5 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Wed, 14 Dec 2022 15:25:27 -0700 Subject: [PATCH 43/84] update for new TUV-x build scripts --- cime_config/buildlib | 168 +++++++++++++++++++++++++------ cime_config/config_component.xml | 2 +- 2 files changed, 137 insertions(+), 33 deletions(-) diff --git a/cime_config/buildlib b/cime_config/buildlib index 39f4d6906a..4677c3de65 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -7,6 +7,7 @@ create the cam library #pylint: disable=unused-wildcard-import, bad-whitespace, too-many-locals #pylint: disable=invalid-name import sys, os, filecmp, shutil, imp +from glob import glob _CIMEROOT = os.environ.get("CIMEROOT") if _CIMEROOT is None: @@ -165,22 +166,59 @@ def _build_json_fortran(caseroot, libroot, bldroot): cmake_args += "-DCMAKE_CXX_COMPILER_WORKS=1 " cmake_args += "-DCMAKE_BUILD_TYPE=Release " cmake_args += "-DSKIP_DOC_GEN:BOOL=TRUE " + cmake_args += "-DCMAKE_INSTALL_PREFIX='{}' ".format(libroot) cmake_args += srcpath _run_cmd("cmake {}".format(cmake_args), bldpath) _run_cmd(case.get_value("GMAKE"), bldpath) + _run_cmd("{} install".format(case.get_value("GMAKE")), bldpath) # add json-fortran to include paths incldir = os.environ.get('USER_INCLDIR') if incldir is None: incldir = '' os.environ['USER_INCLDIR'] = incldir + \ - " -I{} ".format(os.path.join(bldroot, "json-fortran", "include")) + " -I{} ".format(_json_fortran_include_dir(libroot)) - # The built library is included in the CAM_LINKED_LIBS entry in - # config_component.xml and is staged here to the lib folder - os.rename(os.path.join(bldroot, "json-fortran", "lib", "libjsonfortran.a"), \ - os.path.join(libroot, "libjsonfortran.a")) + # create simlink to library in folder CIME expects libraries to be in + dst = os.path.join(libroot, "libjsonfortran.a") + if os.path.isfile(dst): + os.remove(dst) + os.symlink(_json_fortran_lib_path(libroot), dst) + +############################################################################### +def _json_fortran_include_dir(libroot): +############################################################################### +# Returns the path to the json-fortran include directory + + jsoninc = os.path.join(_json_fortran_install_dir(libroot), "lib", "") + expect(os.path.exists(jsoninc), \ + "JSON-Fortran include directory not found at {}".format(jsoninc)) + return jsoninc + +############################################################################### +def _json_fortran_lib_path(libroot): +############################################################################### +# Returns the path to the json-fortran library + + jsonlib = os.path.join(_json_fortran_install_dir(libroot), "lib", "libjsonfortran.a") + expect(os.path.exists(jsonlib), \ + "JSON-Fortran library not found at {}".format(jsonlib)) + return jsonlib + +############################################################################### +def _json_fortran_install_dir(libroot): +############################################################################### +# Returns the path to the json-fortran install directory + + jsonpaths = glob(os.path.join(libroot, "jsonfortran*")) + expect(len(jsonpaths)>0, \ + "JSON-Fortran not found at {}".format(libroot)) + expect(len(jsonpaths)<2, \ + "Multiple JSON-Fortran versions found at {}".format(libroot)) + expect(os.path.exists(jsonpaths[0]), \ + "JSON-Fortran install directory not found at {}".format(jsonpaths[0])) + return jsonpaths[0] ############################################################################### def _build_musica_core(caseroot, libroot, bldroot): @@ -193,12 +231,8 @@ def _build_musica_core(caseroot, libroot, bldroot): bldpath = os.path.join(bldroot, "musica-core") if not os.path.exists(bldpath): os.makedirs(bldpath) - jsoninc = os.path.join(bldroot, "json-fortran", "include", "") - expect(os.path.exists(jsoninc), \ - "JSON-Fortran include folder for musica-core build not found at {}".format(jsoninc)) - jsonlib = os.path.join(libroot, "libjsonfortran.a") - expect(os.path.exists(jsonlib), \ - "JSON-Fortran library for musica-core build not found at {}".format(jsonlib)) + jsoninc = _json_fortran_include_dir(libroot) + jsonlib = _json_fortran_lib_path(libroot) srcpath = os.path.abspath(os.path.join(case.get_value("COMP_ROOT_DIR_ATM"), \ "libraries", "musica-core", "")) logger.info("Building musica-core in {} from source in {}\n".format(bldpath, srcpath)) @@ -220,22 +254,61 @@ def _build_musica_core(caseroot, libroot, bldroot): cmake_args += "-DJSON_INCLUDE_DIR={} ".format(jsoninc) cmake_args += "-DJSON_LIB={} ".format(jsonlib) cmake_args += "-DCMAKE_Fortran_FLAGS='{}' ".format(arg_dict["FFLAGS"]) + cmake_args += "-DCMAKE_INSTALL_PREFIX='{}' ".format(libroot) cmake_args += srcpath _run_cmd("cmake {}".format(cmake_args), bldpath) _run_cmd(case.get_value("GMAKE"), bldpath) + _run_cmd("{} install".format(case.get_value("GMAKE")), bldpath) # add musica-core to include paths incldir = os.environ.get('USER_INCLDIR') if incldir is None: incldir = '' os.environ['USER_INCLDIR'] = incldir + \ - " -I{} ".format(os.path.join(bldroot, "musica-core", "include")) + " -I{} ".format(_musica_core_include_dir(libroot)) - # The musica-core library is included in the CAM_LINKED_LIBS entry in - # config_component.xml and are staged here to the lib folder - os.rename(os.path.join(bldroot, "musica-core", "lib", "libmusica_core.a"), \ - os.path.join(libroot, "libmusica_core.a")) + # create simlink to library in folder CIME expects libraries to be in + dst = os.path.join(libroot, "libmusicacore.a") + if os.path.isfile(dst): + os.remove(dst) + os.symlink(_musica_core_lib_path(libroot), dst) + +############################################################################### +def _musica_core_include_dir(libroot): +############################################################################### +# Returns the path to the musica-core include directory + + coreinc = os.path.join(_musica_core_install_dir(libroot), "include", "") + expect(os.path.exists(coreinc), \ + "musica-core include directory not found at {}".format(coreinc)) + return coreinc + +############################################################################### +def _musica_core_lib_path(libroot): +############################################################################### +# Returns the path to the musica-core library + + corelib = os.path.join(_musica_core_install_dir(libroot), "lib64", "libmusicacore.a") + if not os.path.exists(corelib): + corelib = os.path.join(_musica_core_install_dir(libroot), "lib", "libmusicacore.a") + expect(os.path.exists(corelib), \ + "musica-core library not found at {}".format(corelib)) + return corelib + +############################################################################### +def _musica_core_install_dir(libroot): +############################################################################### +# Returns the path to the musica-core install directory + + corepaths = glob(os.path.join(libroot, "ncar", "musicacore*")) + expect(len(corepaths)>0, \ + "musica-core not found at {}".format(libroot)) + expect(len(corepaths)<2, \ + "Multiple musica-core versions found at {}".format(libroot)) + expect(os.path.exists(corepaths[0]), \ + "musica-core install directory not found at {}".format(corepaths[0])) + return corepaths[0] ############################################################################### def _build_tuvx(caseroot, libroot, bldroot): @@ -248,18 +321,10 @@ def _build_tuvx(caseroot, libroot, bldroot): bldpath = os.path.join(bldroot, "tuv-x") if not os.path.exists(bldpath): os.makedirs(bldpath) - jsoninc = os.path.join(bldroot, "json-fortran", "include", "") - expect(os.path.exists(jsoninc), \ - "JSON-Fortran include folder for TUV-x build not found at {}".format(jsoninc)) - jsonlib = os.path.join(libroot, "libjsonfortran.a") - expect(os.path.exists(jsonlib), \ - "JSON-Fortran library for TUV-x build not found at {}".format(jsonlib)) - coreinc = os.path.join(bldroot, "musica-core", "include", "") - expect(os.path.exists(coreinc), \ - "musica-core include folder for TUV-x build not found at {}".format(coreinc)) - corelib = os.path.join(libroot, "libmusica_core.a") - expect(os.path.exists(corelib), \ - "musica-core library for TUV-x build not found at {}".format(corelib)) + jsoninc = _json_fortran_include_dir(libroot) + jsonlib = _json_fortran_lib_path(libroot) + coreinc = _musica_core_include_dir(libroot) + corelib = _musica_core_lib_path(libroot) srcpath = os.path.abspath(os.path.join(case.get_value("COMP_ROOT_DIR_ATM"), \ "libraries", "tuv-x", "")) logger.info("Building TUV-x in {} from source in {}\n".format(bldpath, srcpath)) @@ -285,10 +350,12 @@ def _build_tuvx(caseroot, libroot, bldroot): cmake_args += "-DMUSICA_CORE_INCLUDE_DIR={} ".format(coreinc) cmake_args += "-DMUSICA_CORE_LIB={} ".format(corelib) cmake_args += "-DCMAKE_Fortran_FLAGS='{}' ".format(arg_dict["FFLAGS"]) + cmake_args += "-DCMAKE_INSTALL_PREFIX='{}' ".format(libroot) cmake_args += srcpath _run_cmd("cmake {}".format(cmake_args), bldpath) _run_cmd(case.get_value("GMAKE"), bldpath) + _run_cmd("{} install".format(case.get_value("GMAKE")), bldpath) # add TUV-x to include paths incldir = os.environ.get('USER_INCLDIR') @@ -297,10 +364,47 @@ def _build_tuvx(caseroot, libroot, bldroot): os.environ['USER_INCLDIR'] = incldir + \ " -I{} ".format(os.path.join(bldroot, "tuv-x", "include")) - # The TUV-x library is included in the CAM_LINKED_LIBS entry in - # config_component.xml and are staged here to the lib folder - os.rename(os.path.join(bldroot, "tuv-x", "lib", "libtuvx.a"), \ - os.path.join(libroot, "libtuvx.a")) + # create simlink 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) + +############################################################################### +def _tuvx_include_dir(libroot): +############################################################################### +# Returns the path to the TUV-x include directory + + tuvxinc = os.path.join(_tuvx_install_dir(libroot), "include", "") + expect(os.path.exists(tuvxinc), \ + "TUV-x include directory not found at {}".format(tuvxinc)) + return tuvxinc + +############################################################################### +def _tuvx_lib_path(libroot): +############################################################################### +# Returns the path to the TUV-x library + + tuvxlib = os.path.join(_tuvx_install_dir(libroot), "lib64", "libtuvx.a") + if not os.path.exists(tuvxlib): + tuvxlib = os.path.join(_tuvx_install_dir(libroot), "lib", "libtuvx.a") + expect(os.path.exists(tuvxlib), \ + "TUV-x library not found at {}".format(tuvxlib)) + return tuvxlib + +############################################################################### +def _tuvx_install_dir(libroot): +############################################################################### +# Returns the path to the TUV-x install directory + + tuvxpaths = glob(os.path.join(libroot, "ncar", "tuvx*")) + expect(len(tuvxpaths)>0, \ + "TUV-x not found at {}".format(libroot)) + expect(len(tuvxpaths)<2, \ + "Multiple TUV-x versions found at {}".format(libroot)) + expect(os.path.exists(tuvxpaths[0]), \ + "TUV-x install directory not found at {}".format(tuvxpaths[0])) + return tuvxpaths[0] ############################################################################### diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index 73f28135cc..8f742460d6 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -193,7 +193,7 @@ char - -ltuvx -lmusica_core -ljsonfortran + -ltuvx -lmusicacore -ljsonfortran build_component_cam env_build.xml From 26375d922b75e9d2b1076bb5c91c1f9ffed068ac Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Wed, 14 Dec 2022 17:45:09 -0700 Subject: [PATCH 44/84] update MOZART TUV-x config --- cime_config/tuvx_MOZART.json | 101 ++++++++++++++++++++++++----------- 1 file changed, 69 insertions(+), 32 deletions(-) diff --git a/cime_config/tuvx_MOZART.json b/cime_config/tuvx_MOZART.json index cf23650b93..6137fabd7d 100644 --- a/cime_config/tuvx_MOZART.json +++ b/cime_config/tuvx_MOZART.json @@ -18,14 +18,23 @@ }, { "name": "O3", - "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], "type": "O3" }, { "name": "O2", - "netcdf files": ["data/cross_sections/O2_1.nc"], - "type": "base", - "lower extrapolation": { "type": "boundary" } + "netcdf files": [ + { + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" } + } + ], + "type": "base" } ], "radiators": [ @@ -58,9 +67,13 @@ "name": "jo2", "__reaction": "O2 + hv -> O + O", "cross section": { - "netcdf files": ["data/cross_sections/O2_1.nc"], - "type": "base", - "lower extrapolation": { "type": "boundary" } + "netcdf files": [ + { + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" } + } + ], + "type": "base" }, "quantum yield": { "type": "base", @@ -71,7 +84,12 @@ "name": "jo1d", "__reaction": "O3 + hv -> O2 + O(1D)", "cross section": { - "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], "type": "O3" }, "quantum yield": { @@ -82,7 +100,12 @@ "name": "jo3p", "__reaction": "O3 + hv -> O2 + O(3P)", "cross section": { - "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], "type": "O3" }, "quantum yield": { @@ -104,7 +127,9 @@ "name": "jno2", "__reaction": "NO2 + hv -> NO + O(3P)", "cross section": { - "netcdf files": ["data/cross_sections/NO2_1.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/NO2_1.nc" } + ], "type": "NO2 tint" }, "quantum yield": { @@ -118,8 +143,8 @@ "__reaction": "N2O5 + hv -> NO2 + NO3", "cross section": { "netcdf files": [ - "data/cross_sections/N2O5_1.nc", - "data/cross_sections/N2O5_2.nc" + { "file path": "data/cross_sections/N2O5_1.nc" }, + { "file path": "data/cross_sections/N2O5_2.nc" } ], "type": "N2O5+hv->NO2+NO3" }, @@ -133,7 +158,7 @@ "__reaction": "HNO3 + hv -> OH + NO2", "cross section": { "netcdf files": [ - "data/cross_sections/HNO3_1.nc" + { "file path": "data/cross_sections/HNO3_1.nc" } ], "type": "HNO3+hv->OH+NO2" }, @@ -147,13 +172,13 @@ "__reaction": "NO3 + hv -> NO2 + O(3P)", "cross section": { "netcdf files": [ - "data/cross_sections/NO3_1.nc" + { "file path": "data/cross_sections/NO3_1.nc" } ], "type": "base" }, "quantum yield": { "netcdf files": [ - "data/quantum_yields/NO3-NO2+O(3P)_1.nc" + "data/quantum_yields/NO3-NO2+O(3P)_1.nc" ], "type": "tint", "lower extrapolation": { @@ -167,7 +192,7 @@ "__reaction": "NO3 + hv -> NO + O2", "cross section": { "netcdf files": [ - "data/cross_sections/NO3_1.nc" + { "file path": "data/cross_sections/NO3_1.nc" } ], "type": "base" }, @@ -183,7 +208,7 @@ "__reaction": "CH3OOH + hv -> CH3O + OH", "cross section": { "netcdf files": [ - "data/cross_sections/CH3OOH_1.nc" + { "file path": "data/cross_sections/CH3OOH_1.nc" } ], "type": "base" }, @@ -197,7 +222,7 @@ "__reaction": "CH2O + hv -> H + HCO", "cross section": { "netcdf files": [ - "data/cross_sections/CH2O_1.nc" + { "file path": "data/cross_sections/CH2O_1.nc" } ], "type": "CH2O" }, @@ -216,7 +241,7 @@ "__reaction": "CH2O + hv -> H2 + CO", "cross section": { "netcdf files": [ - "data/cross_sections/CH2O_1.nc" + { "file path": "data/cross_sections/CH2O_1.nc" } ], "type": "CH2O" }, @@ -235,7 +260,7 @@ "__reaction": "H2O2 + hv -> OH + OH", "cross section": { "netcdf files": [ - "data/cross_sections/H2O2_1.nc" + { "file path": "data/cross_sections/H2O2_1.nc" } ], "type": "H2O2+hv->OH+OH" }, @@ -249,7 +274,7 @@ "__reaction": "CH3CHO + hv -> CH3 + HCO", "cross section": { "netcdf files": [ - "data/cross_sections/CH3CHO_1.nc" + { "file path": "data/cross_sections/CH3CHO_1.nc" } ], "type": "base" }, @@ -265,7 +290,7 @@ "__reaction": "PAN + hv -> 0.6*CH3CO3 + 0.6*NO2 + 0.4*CH3O2 + 0.4*NO3 + 0.4*CO2", "cross section": { "netcdf files": [ - "data/cross_sections/PAN_1.nc" + { "file path": "data/cross_sections/PAN_1.nc" } ], "type": "CH3ONO2+hv->CH3O+NO2" }, @@ -279,7 +304,7 @@ "__reaction": "MVK + hv -> 0.7*C3H6 + 0.7*CO + 0.3*CH3O2 + 0.3*CH3CO3", "cross section": { "netcdf files": [ - "data/cross_sections/MVK_1.nc" + { "file path": "data/cross_sections/MVK_1.nc" } ], "type": "base" }, @@ -291,7 +316,9 @@ "name": "jacet", "__reaction": "CH3COCH3 + hv -> CH3CO + CH3", "cross section": { - "netcdf files": ["data/cross_sections/CH3COCH3_1.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/CH3COCH3_1.nc" } + ], "type": "CH3COCH3+hv->CH3CO+CH3" }, "quantum yield": { @@ -303,7 +330,7 @@ "__reaction": "CH3COCHO + hv -> CH3CO3 + CO + HO2", "cross section": { "netcdf files": [ - "data/cross_sections/CH3COCHO_1.nc" + { "file path": "data/cross_sections/CH3COCHO_1.nc" } ], "type": "base" }, @@ -316,7 +343,7 @@ "__reaction": "GLYALD + hv -> 2*HO2 + CO + CH2O", "cross section": { "netcdf files": [ - "data/cross_sections/HOCH2CHO_1.nc" + { "file path": "data/cross_sections/HOCH2CHO_1.nc" } ], "type": "base" }, @@ -331,7 +358,7 @@ "__comments": "TODO the products of this reaction don't exactly match", "cross section": { "netcdf files": [ - "data/cross_sections/CHOCHO_1.nc" + { "file path": "data/cross_sections/CHOCHO_1.nc" } ], "type": "base" }, @@ -350,7 +377,9 @@ "__reaction": "HNO4 + hv -> OH + NO3", "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", "cross section": { - "netcdf files": ["data/cross_sections/HNO4_1.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/HNO4_1.nc" } + ], "type": "base" }, "quantum yield": { @@ -363,7 +392,9 @@ "__reaction": "HNO4 + hv -> HO2 + NO2", "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", "cross section": { - "netcdf files": ["data/cross_sections/HNO4_1.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/HNO4_1.nc" } + ], "type": "base" }, "quantum yield": { @@ -376,7 +407,9 @@ "__reaction": "CH2=C(CH3)CHO->1.34HO2+0.66MCO3+1.34CH2O+CH3CO3", "__comments": "Methacrolein photolysis channel 1", "cross section": { - "netcdf files": ["data/cross_sections/MACR_JPL2006.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/Methacrolein_1.nc" } + ], "type": "base" }, "quantum yield": { @@ -389,7 +422,9 @@ "__reaction": "CH2=C(CH3)CHO->0.66OH+1.34CO", "__comments": "Methacrolein photolysis channel 2", "cross section": { - "netcdf files": ["data/cross_sections/MACR_JPL2006.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/Methacrolein_1.nc" } + ], "type": "base" }, "quantum yield": { @@ -402,7 +437,9 @@ "__reaction": "CH2(OH)COCH3->CH3CO3+HO2+CH2O", "__comments": "hydroxy acetone", "cross section": { - "netcdf files": ["data/cross_sections/HYAC.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/Hydroxyacetone_1.nc" } + ], "type": "base" }, "quantum yield": { From 3d8852cbe76745e0e8fb19b14e55fe82fa1398d0 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Thu, 15 Dec 2022 13:16:27 -0700 Subject: [PATCH 45/84] update TS1 TUV-x config format --- cime_config/tuvx_MOZART_TS1.json | 255 ++++++++++++++++++++----------- 1 file changed, 164 insertions(+), 91 deletions(-) diff --git a/cime_config/tuvx_MOZART_TS1.json b/cime_config/tuvx_MOZART_TS1.json index 20bb437026..d46461d5b8 100644 --- a/cime_config/tuvx_MOZART_TS1.json +++ b/cime_config/tuvx_MOZART_TS1.json @@ -18,14 +18,23 @@ }, { "name": "O3", - "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], "type": "O3" }, { "name": "O2", - "netcdf files": ["data/cross_sections/O2_1.nc"], - "type": "base", - "lower extrapolation": { "type": "boundary" } + "netcdf files": [ + { + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" } + } + ], + "type": "base" } ], "radiators": [ @@ -58,9 +67,13 @@ "name": "jo2_b", "__reaction": "O2 + hv -> O + O", "cross section": { - "netcdf files": ["data/cross_sections/O2_1.nc"], - "type": "base", - "lower extrapolation": { "type": "boundary" } + "netcdf files": [ + { + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" } + } + ], + "type": "base" }, "quantum yield": { "type": "base", @@ -71,7 +84,12 @@ "name": "jo3_a", "__reaction": "O3 + hv -> O2 + O(1D)", "cross section": { - "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], "type": "O3" }, "quantum yield": { @@ -82,7 +100,12 @@ "name": "jo3_b", "__reaction": "O3 + hv -> O2 + O(3P)", "cross section": { - "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], "type": "O3" }, "quantum yield": { @@ -104,7 +127,9 @@ "name": "jno2", "__reaction": "NO2 + hv -> NO + O(3P)", "cross section": { - "netcdf files": ["data/cross_sections/NO2_1.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/NO2_1.nc" } + ], "type": "NO2 tint" }, "quantum yield": { @@ -118,8 +143,8 @@ "__reaction": "N2O5 + hv -> NO2 + NO3", "cross section": { "netcdf files": [ - "data/cross_sections/N2O5_1.nc", - "data/cross_sections/N2O5_2.nc" + { "file path": "data/cross_sections/N2O5_1.nc" }, + { "file path": "data/cross_sections/N2O5_2.nc" } ], "type": "N2O5+hv->NO2+NO3" }, @@ -133,7 +158,7 @@ "__reaction": "HNO3 + hv -> OH + NO2", "cross section": { "netcdf files": [ - "data/cross_sections/HNO3_1.nc" + { "file path": "data/cross_sections/HNO3_1.nc" } ], "type": "HNO3+hv->OH+NO2" }, @@ -147,7 +172,7 @@ "__reaction": "NO3 + hv -> NO2 + O(3P)", "cross section": { "netcdf files": [ - "data/cross_sections/NO3_1.nc" + { "file path": "data/cross_sections/NO3_1.nc" } ], "type": "base" }, @@ -167,7 +192,7 @@ "__reaction": "NO3 + hv -> NO + O2", "cross section": { "netcdf files": [ - "data/cross_sections/NO3_1.nc" + { "file path": "data/cross_sections/NO3_1.nc" } ], "type": "base" }, @@ -183,7 +208,7 @@ "__reaction": "CH3OOH + hv -> CH3O + OH", "cross section": { "netcdf files": [ - "data/cross_sections/CH3OOH_1.nc" + { "file path": "data/cross_sections/CH3OOH_1.nc" } ], "type": "base" }, @@ -197,7 +222,7 @@ "__reaction": "CH2O + hv -> H + HCO", "cross section": { "netcdf files": [ - "data/cross_sections/CH2O_1.nc" + { "file path": "data/cross_sections/CH2O_1.nc" } ], "type": "CH2O" }, @@ -216,7 +241,7 @@ "__reaction": "CH2O + hv -> H2 + CO", "cross section": { "netcdf files": [ - "data/cross_sections/CH2O_1.nc" + { "file path": "data/cross_sections/CH2O_1.nc" } ], "type": "CH2O" }, @@ -235,7 +260,7 @@ "__reaction": "H2O2 + hv -> OH + OH", "cross section": { "netcdf files": [ - "data/cross_sections/H2O2_1.nc" + { "file path": "data/cross_sections/H2O2_1.nc" } ], "type": "H2O2+hv->OH+OH" }, @@ -249,7 +274,7 @@ "__reaction": "CH3CHO + hv -> CH3 + HCO", "cross section": { "netcdf files": [ - "data/cross_sections/CH3CHO_1.nc" + { "file path": "data/cross_sections/CH3CHO_1.nc" } ], "type": "base" }, @@ -265,7 +290,7 @@ "__reaction": "PAN + hv -> 0.6*CH3CO3 + 0.6*NO2 + 0.4*CH3O2 + 0.4*NO3 + 0.4*CO2", "cross section": { "netcdf files": [ - "data/cross_sections/PAN_1.nc" + { "file path": "data/cross_sections/PAN_1.nc" } ], "type": "CH3ONO2+hv->CH3O+NO2" }, @@ -279,7 +304,7 @@ "__reaction": "MVK + hv -> 0.7*C3H6 + 0.7*CO + 0.3*CH3O2 + 0.3*CH3CO3", "cross section": { "netcdf files": [ - "data/cross_sections/MVK_1.nc" + { "file path": "data/cross_sections/MVK_1.nc" } ], "type": "base" }, @@ -291,7 +316,9 @@ "name": "jacet", "__reaction": "CH3COCH3 + hv -> CH3CO + CH3", "cross section": { - "netcdf files": ["data/cross_sections/CH3COCH3_1.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/CH3COCH3_1.nc" } + ], "type": "CH3COCH3+hv->CH3CO+CH3" }, "quantum yield": { @@ -303,7 +330,7 @@ "__reaction": "CH3COCHO + hv -> CH3CO3 + CO + HO2", "cross section": { "netcdf files": [ - "data/cross_sections/CH3COCHO_1.nc" + { "file path": "data/cross_sections/CH3COCHO_1.nc" } ], "type": "base" }, @@ -316,7 +343,7 @@ "__reaction": "GLYALD + hv -> 2*HO2 + CO + CH2O", "cross section": { "netcdf files": [ - "data/cross_sections/HOCH2CHO_1.nc" + { "file path": "data/cross_sections/HOCH2CHO_1.nc" } ], "type": "base" }, @@ -331,7 +358,7 @@ "__comments": "NOTE the products of this reaction don't exactly match", "cross section": { "netcdf files": [ - "data/cross_sections/CHOCHO_1.nc" + { "file path": "data/cross_sections/CHOCHO_1.nc" } ], "type": "base" }, @@ -350,7 +377,7 @@ "__reaction": "BrCl + hv -> Br + Cl", "cross section": { "netcdf files": [ - "data/cross_sections/BrCl_1.nc" + { "file path": "data/cross_sections/BrCl_1.nc" } ], "type": "base" }, @@ -364,9 +391,15 @@ "__reaction": "BrO + hv -> Br + O", "cross section": { "netcdf files": [ - "data/cross_sections/BrO_1.nc" + { + "file path": "data/cross_sections/BrO_1.nc", + "interpolator": { + "type": "fractional target", + "fold in": true + } + } ], - "type": "BrO+hv->Br+O" + "type": "base" }, "quantum yield": { "type": "base", @@ -378,7 +411,7 @@ "__reaction": "BrONO2 + hv -> Br + NO3", "cross section": { "netcdf files": [ - "data/cross_sections/BrONO2_1.nc" + { "file path": "data/cross_sections/BrONO2_1.nc" } ], "type": "base" }, @@ -392,7 +425,7 @@ "__reaction": "BrONO2 + hv -> BrO + NO2", "cross section": { "netcdf files": [ - "data/cross_sections/BrONO2_1.nc" + { "file path": "data/cross_sections/BrONO2_1.nc" } ], "type": "base" }, @@ -406,7 +439,7 @@ "__reaction": "CCl4 + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CCl4_1.nc" + { "file path": "data/cross_sections/CCl4_1.nc" } ], "type": "CCl4+hv->Products" }, @@ -420,7 +453,7 @@ "__reaction": "CF2BrCl + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CF2BrCl_1.nc" + { "file path": "data/cross_sections/CF2BrCl_1.nc" } ], "type": "base" }, @@ -434,7 +467,7 @@ "__reaction": "CF3Br + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CF3Br_1.nc" + { "file path": "data/cross_sections/CF3Br_1.nc" } ], "type": "base" }, @@ -448,7 +481,7 @@ "__reaction": "CCl3F + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CFC-11_1.nc" + { "file path": "data/cross_sections/CFC-11_1.nc" } ], "type": "CCl3F+hv->Products" }, @@ -462,7 +495,7 @@ "__reaction": "CFC-113 + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CFC-113_1.nc" + { "file path": "data/cross_sections/CFC-113_1.nc" } ], "type": "tint" }, @@ -476,7 +509,7 @@ "__reaction": "CFC-114 + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CFC-114_1.nc" + { "file path": "data/cross_sections/CFC-114_1.nc" } ], "type": "tint" }, @@ -490,7 +523,7 @@ "__reaction": "CFC-115 + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CFC-115_1.nc" + { "file path": "data/cross_sections/CFC-115_1.nc" } ], "type": "base" }, @@ -504,7 +537,7 @@ "__reaction": "CCl2F2 + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CFC-12_1.nc" + { "file path": "data/cross_sections/CFC-12_1.nc" } ], "type": "CCl3F+hv->Products" }, @@ -518,7 +551,7 @@ "__reaction": "CH3Br + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CH3Br_1.nc" + { "file path": "data/cross_sections/CH3Br_1.nc" } ], "type": "base" }, @@ -532,7 +565,7 @@ "__reaction": "CH3CCl3+hv->Products", "cross section": { "netcdf files": [ - "data/cross_sections/CH3CCl3_1.nc" + { "file path": "data/cross_sections/CH3CCl3_1.nc" } ], "type": "tint" }, @@ -546,7 +579,7 @@ "__reaction": "CH3Cl + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CH3Cl_1.nc" + { "file path": "data/cross_sections/CH3Cl_1.nc" } ], "type": "tint" }, @@ -560,7 +593,7 @@ "__reaction": "CHBr3 + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CHBr3_1.nc" + { "file path": "data/cross_sections/CHBr3_1.nc" } ], "type": "CHBr3+hv->Products", "lower extrapolation": { @@ -588,7 +621,7 @@ "__reaction": "ClO + hv -> Cl + O(1D)", "cross section": { "netcdf files": [ - "data/cross_sections/ClO_1.nc" + { "file path": "data/cross_sections/ClO_1.nc" } ], "type": "tint" }, @@ -601,7 +634,7 @@ "__reaction": "ClONO2 + hv -> Cl + NO3", "cross section": { "netcdf files": [ - "data/cross_sections/ClONO2_1.nc" + { "file path": "data/cross_sections/ClONO2_1.nc" } ], "type": "ClONO2" }, @@ -614,7 +647,7 @@ "__reaction": "ClONO2 + hv -> ClO + NO2", "cross section": { "netcdf files": [ - "data/cross_sections/ClONO2_1.nc" + { "file path": "data/cross_sections/ClONO2_1.nc" } ], "type": "ClONO2" }, @@ -627,7 +660,7 @@ "__reaction": "CF2O + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CF2O_1.nc" + { "file path": "data/cross_sections/CF2O_1.nc" } ], "type": "base" }, @@ -641,7 +674,7 @@ "__reaction": "CClFO + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CClFO_1.nc" + { "file path": "data/cross_sections/CClFO_1.nc" } ], "type": "base" }, @@ -655,7 +688,7 @@ "__reaction": "HCFC-141b + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CH3CFCl2_1.nc" + { "file path": "data/cross_sections/CH3CFCl2_1.nc" } ], "type": "base" }, @@ -669,7 +702,7 @@ "__reaction": "HCFC-142b + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CH3CF2Cl_1.nc" + { "file path": "data/cross_sections/CH3CF2Cl_1.nc" } ], "type": "HCFC+hv->Products" }, @@ -683,7 +716,7 @@ "__reaction": "HCFC-22 + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CHClF2_1.nc" + { "file path": "data/cross_sections/CHClF2_1.nc" } ], "type": "tint" }, @@ -697,7 +730,7 @@ "__reaction": "HCl + hv -> H + Cl", "cross section": { "netcdf files": [ - "data/cross_sections/HCl_1.nc" + { "file path": "data/cross_sections/HCl_1.nc" } ], "type": "base" }, @@ -722,7 +755,7 @@ "__reaction": "HOCl + hv -> HO + Cl", "cross section": { "netcdf files": [ - "data/cross_sections/HOCl_1.nc" + { "file path": "data/cross_sections/HOCl_1.nc" } ], "type": "base" }, @@ -736,9 +769,9 @@ "__reaction": "OClO + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/OClO_1.nc", - "data/cross_sections/OClO_2.nc", - "data/cross_sections/OClO_3.nc" + { "file path": "data/cross_sections/OClO_1.nc" }, + { "file path": "data/cross_sections/OClO_2.nc" }, + { "file path": "data/cross_sections/OClO_3.nc" } ], "type": "OClO+hv->Products" }, @@ -746,6 +779,81 @@ "type": "base", "constant value": 1.0 } + }, + { + "name": "jho2no2_a", + "__reaction": "HNO4 + hv -> OH + NO3", + "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HNO4_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.2 + } + }, + { + "name": "jho2no2_b", + "__reaction": "HNO4 + hv -> HO2 + NO2", + "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HNO4_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.8 + } + }, + { + "name": "jmacr_a", + "__reaction": "CH2=C(CH3)CHO->1.34HO2+0.66MCO3+1.34CH2O+CH3CO3", + "__comments": "Methacrolein photolysis channel 1", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/Methacrolein_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.005 + } + }, + { + "name": "jmacr_b", + "__reaction": "CH2=C(CH3)CHO->0.66OH+1.34CO", + "__comments": "Methacrolein photolysis channel 2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/Methacrolein_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.005 + } + }, + { + "name": "jhyac", + "__reaction": "CH2(OH)COCH3->CH3CO3+HO2+CH2O", + "__comments": "hydroxy acetone NOTE: the products of this reaction differ from standalone TUV-x", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/Hydroxyacetone_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.65 + } } ] }, @@ -780,20 +888,6 @@ "from": "jo2_b", "scale by": 0.0 }, - { - "to": "jho2no2_a", - "__reaction": "HO2NO2 + hv -> OH + NO3", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jho2no2_b", - "__reaction": "HO2NO2 + hv -> NO2 + HO2", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, { "to": "jn2o5_b", "__reaction": "N2O5 + hv -> NO + O + NO3", @@ -829,20 +923,6 @@ "__reaction": "MPAN + hv -> MCO3 + NO2", "from": "jpan" }, - { - "to": "jmacr_a", - "__reaction": "MACR + hv -> 1.34*HO2 + 0.66*MCO3 + 1.34*CH2O + 1.34*CH3CO3", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jmacr_b", - "__reaction": "MACR + hv -> 0.66*HO2 + 1.34*CO", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, { "to": "jc2h5ooh", "__reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", @@ -904,13 +984,6 @@ "__reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", "from": "jch3ooh" }, - { - "to": "jhyac", - "__reaction": "HYAC (CH3COCH2OH) + hv -> CH3CO3 + HO2 + CH2O", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, { "to": "jmek", "__reaction": "MEK + hv -> CH3CO3 + C2H5O2", From 364d9054b8f1db955444fea1b7eb2435b2ad4a03 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Thu, 15 Dec 2022 13:56:45 -0700 Subject: [PATCH 46/84] add h2o photo rates to TS1 TUV-x config --- cime_config/tuvx_MOZART_TS1.json | 98 +++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 19 deletions(-) diff --git a/cime_config/tuvx_MOZART_TS1.json b/cime_config/tuvx_MOZART_TS1.json index d46461d5b8..7afdfa180c 100644 --- a/cime_config/tuvx_MOZART_TS1.json +++ b/cime_config/tuvx_MOZART_TS1.json @@ -854,33 +854,93 @@ "type": "base", "constant value": 0.65 } - } - ] - }, - "__CAM aliasing": { - "default matching": "backup", - "pairs": [ + }, { - "to": "jh2o_a", + "name": "jh2o_a", "__reaction": "H2O + hv -> OH + H", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 + "cross section": { + "type": "base", + "merge data": true, + "netcdf files": [ + { + "file path": "data/cross_sections/H2O_1.nc", + "zero above": 183.0 + }, + { + "file path": "data/cross_sections/H2O_2.nc", + "zero below": 183.00000000001, + "zero above": 190.0 + }, + { + "file path": "data/cross_sections/H2O_3.nc", + "zero below": 190.00000000001 + } + ] + }, + "quantum yield" : { + "type": "base", + "netcdf files": [ "data/quantum_yields/H2O_H_OH.nc" ] + } }, { - "to": "jh2o_b", + "name": "jh2o_b", "__reaction": "H2O + hv -> H2 + O1D", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 + "cross section": { + "type": "base", + "merge data": true, + "netcdf files": [ + { + "file path": "data/cross_sections/H2O_1.nc", + "zero above": 183.0 + }, + { + "file path": "data/cross_sections/H2O_2.nc", + "zero below": 183.00000000001, + "zero above": 190.0 + }, + { + "file path": "data/cross_sections/H2O_3.nc", + "zero below": 190.00000000001 + } + ] + }, + "quantum yield" : { + "type": "base", + "netcdf files": [ "data/quantum_yields/H2O_H2_O1D.nc" ] + } }, { - "to": "jh2o_c", + "name": "jh2o_c", "__reaction": "H2O + hv -> 2*H + O", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, + "cross section": { + "type": "base", + "merge data": true, + "netcdf files": [ + { + "file path": "data/cross_sections/H2O_1.nc", + "zero above": 183.0 + }, + { + "file path": "data/cross_sections/H2O_2.nc", + "zero below": 183.00000000001, + "zero above": 190.0 + }, + { + "file path": "data/cross_sections/H2O_3.nc", + "zero below": 190.00000000001 + } + ] + }, + "quantum yield" : { + "type": "base", + "netcdf files": [ "data/quantum_yields/H2O_2H_O3P.nc" ] + } + } + ] + }, + "__CAM aliasing": { + "default matching": "backup", + "pairs": [ { "to": "jo2_a", "__reaction": "O2 + hv -> O + O1D", From d29ea425cd43b7e144d531a357e37b39b5ef72e8 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Thu, 15 Dec 2022 14:50:33 -0700 Subject: [PATCH 47/84] add new datasets to TS1 TUV-x config --- cime_config/tuvx_MOZART_TS1.json | 235 ++++++++++++++++++++----------- 1 file changed, 156 insertions(+), 79 deletions(-) diff --git a/cime_config/tuvx_MOZART_TS1.json b/cime_config/tuvx_MOZART_TS1.json index 7afdfa180c..16a13d57e8 100644 --- a/cime_config/tuvx_MOZART_TS1.json +++ b/cime_config/tuvx_MOZART_TS1.json @@ -355,7 +355,7 @@ { "name": "jglyoxal", "__reaction": "GLYOXAL + hv -> 2*CO + 2*HO2", - "__comments": "NOTE the products of this reaction don't exactly match", + "__comments": "TODO the products of this reaction don't exactly match", "cross section": { "netcdf files": [ { "file path": "data/cross_sections/CHOCHO_1.nc" } @@ -843,7 +843,7 @@ { "name": "jhyac", "__reaction": "CH2(OH)COCH3->CH3CO3+HO2+CH2O", - "__comments": "hydroxy acetone NOTE: the products of this reaction differ from standalone TUV-x", + "__comments": "hydroxy acetone TODO: the products of this reaction differ from standalone TUV-x", "cross section": { "netcdf files": [ { "file path": "data/cross_sections/Hydroxyacetone_1.nc" } @@ -935,6 +935,160 @@ "type": "base", "netcdf files": [ "data/quantum_yields/H2O_2H_O3P.nc" ] } + }, + { + "name": "jch4_a", + "__reaction": "CH4 + hv -> H + CH3O2", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/CH4_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 0.55 + } + }, + { + "name": "jch4_b", + "__reaction": "CH4 + hv -> 1.44*H2 + 0.18*CH2O + 0.18*O + 0.33*OH + 0.33*H + 0.44*CO2 + 0.38*CO + 0.05*H2O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/CH4_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 0.45 + } + }, + { + "name": "jco2", + "__reaction": "CO2 + hv -> CO + O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/CO2_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhbr", + "__reaction": "HBR + hv -> BR + H", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/HBr_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhf", + "__reaction": "HF + hv -> H + F", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/HF_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jsf6", + "__reaction": "SF6 + hv -> sink", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/SF6_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jh2so4", + "__reaction": "H2SO4 + hv -> SO3 + H2O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/H2SO4_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jocs", + "__reaction": "OCS + hv -> S + CO", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/OCS_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jso", + "__reaction": "SO + hv -> S + O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/SO_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jso2", + "__reaction": "SO2 + hv -> SO + O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/SO2_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jso3", + "__reaction": "SO3 + hv -> SO2 + O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/SO3_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } } ] }, @@ -998,27 +1152,6 @@ "__reaction": "C6H5OOH + hv -> PHENO + OH", "from": "jch3ooh" }, - { - "to": "jch4_a", - "__reaction": "CH4 + hv -> H + CH3O2", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jch4_b", - "__reaction": "CH4 + hv -> 1.44*H2 + 0.18*CH2O + 0.18*O + 0.33*OH + 0.33*H + 0.44*CO2 + 0.38*CO + 0.05*H2O", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jco2", - "__reaction": "CO2 + hv -> CO + O", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, { "to": "jeooh", "__reaction": "EOOH + hv -> EO + OH", @@ -1208,62 +1341,6 @@ "from": "jo2_b", "scale by": 0.0 }, - { - "to": "jhbr", - "__reaction": "HBR + hv -> BR + H", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jhf", - "__reaction": "HF + hv -> H + F", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jsf6", - "__reaction": "SF6 + hv -> sink", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jh2so4", - "__reaction": "H2SO4 + hv -> SO3 + H2O", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jocs", - "__reaction": "OCS + hv -> S + CO", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jso", - "__reaction": "SO + hv -> S + O", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jso2", - "__reaction": "SO2 + hv -> SO + O", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jso3", - "__reaction": "SO3 + hv -> SO2 + O", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, { "to": "jsoa1_a1", "__reaction": "soa1_a1 + hv -> Products", From 3c6def82c576b7ccd67690cbf0c921b519c2a439 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Thu, 15 Dec 2022 15:37:02 -0700 Subject: [PATCH 48/84] update TUV-x config format --- cime_config/tuvx_MOZART.json | 8 ++++---- cime_config/tuvx_MOZART_TS1.json | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cime_config/tuvx_MOZART.json b/cime_config/tuvx_MOZART.json index 6137fabd7d..e53fa391b2 100644 --- a/cime_config/tuvx_MOZART.json +++ b/cime_config/tuvx_MOZART.json @@ -3,10 +3,10 @@ "O2 absorption" : { "cross section parameters file": "data/cross_sections/O2_parameters.txt" }, - "grids": { - }, - "profiles": { - }, + "grids": [ + ], + "profiles": [ + ], "radiative transfer": { "solver": { "type": "delta eddington" diff --git a/cime_config/tuvx_MOZART_TS1.json b/cime_config/tuvx_MOZART_TS1.json index 16a13d57e8..a18f0ff9b9 100644 --- a/cime_config/tuvx_MOZART_TS1.json +++ b/cime_config/tuvx_MOZART_TS1.json @@ -3,10 +3,10 @@ "O2 absorption" : { "cross section parameters file": "data/cross_sections/O2_parameters.txt" }, - "grids": { - }, - "profiles": { - }, + "grids": [ + ], + "profiles": [ + ], "radiative transfer": { "solver": { "type": "delta eddington" From a7e1030b2a1acf422ea6ec864828f274450ccc62 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Wed, 21 Dec 2022 11:58:39 -0700 Subject: [PATCH 49/84] add remaining TS1 config excluding jno --- cime_config/tuvx_MOZART_TS1.json | 154 +++++++++++++++++++++++-------- 1 file changed, 117 insertions(+), 37 deletions(-) diff --git a/cime_config/tuvx_MOZART_TS1.json b/cime_config/tuvx_MOZART_TS1.json index a18f0ff9b9..2dc88fea7f 100644 --- a/cime_config/tuvx_MOZART_TS1.json +++ b/cime_config/tuvx_MOZART_TS1.json @@ -63,6 +63,33 @@ }, "photolysis": { "reactions": [ + { + "name": "jo2_a", + "__reaction": "O2 + hv -> O + O1D", + "cross section": { + "netcdf files": [ + { + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" } + } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0, + "override bands": [ + { + "band": "lyman-alpha", + "value": 0.47 + }, + { + "band": "schumann-runge", + "value": 1.0 + } + ] + } + }, { "name": "jo2_b", "__reaction": "O2 + hv -> O + O", @@ -77,7 +104,17 @@ }, "quantum yield": { "type": "base", - "constant value": 1.0 + "constant value": 1.0, + "override bands": [ + { + "band": "lyman-alpha", + "value": 0.53 + }, + { + "band": "schumann-runge continuum", + "value": 1.0 + } + ] } }, { @@ -150,7 +187,22 @@ }, "quantum yield": { "type": "base", - "constant value": 1.0 + "netcdf files": [ "data/quantum_yields/N2O5_NO3_NO2.nc" ] + } + }, + { + "name": "jn2o5_b", + "__reaction": "N2O5 + hv -> NO + O + NO3", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/N2O5_1.nc" }, + { "file path": "data/cross_sections/N2O5_2.nc" } + ], + "type": "N2O5+hv->NO2+NO3" + }, + "quantum yield": { + "type": "base", + "netcdf files": [ "data/quantum_yields/N2O5_NO3_NO_O.nc" ] } }, { @@ -546,6 +598,39 @@ "constant value": 1.0 } }, + { + "name": "jch2br2", + "__reaction": "CH2BR2 + hv -> 2*BR", + "cross section": { + "type": "temperature based", + "netcdf file": "data/cross_sections/CH2BR2_1.nc", + "parameterization": { + "AA": [ -70.211776, 1.940326e-1, 2.726152e-3, -1.695472e-5, 2.500066e-8 ], + "BB": [ 2.899280, -4.327724e-2, 2.391599e-4, -5.807506e-7, 5.244883e-10 ], + "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], + "minimum wavelength": 210.0, + "maximum wavelength": 290.0, + "temperature ranges": [ + { + "maximum": 209.999999999999, + "fixed value": 210.0 + }, + { + "minimum": 210, + "maximum": 300 + }, + { + "minimum": 300.00000000001, + "fixed value": 300.0 + } + ] + } + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, { "name": "jch3br", "__reaction": "CH3Br + hv -> Products", @@ -616,6 +701,21 @@ "constant value": 1.0 } }, + { + "name": "jcl2o2", + "__reaction": "ClOOCl + hv -> Cl + ClOO", + "__comments": "TODO - this doesn't exactly match the products in TS1", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/ClOOCl_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, { "name": "jclo", "__reaction": "ClO + hv -> Cl + O(1D)", @@ -683,6 +783,21 @@ "constant value": 1.0 } }, + { + "name": "jh2402", + "__reaction": "H2402 + hv -> 2*BR + 2*COF2", + "__comments": "TUV data set name CF2BrCF2Br", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CF2BrCF2Br_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, { "name": "jhcfc141b", "__reaction": "HCFC-141b + hv -> Products", @@ -1095,20 +1210,6 @@ "__CAM aliasing": { "default matching": "backup", "pairs": [ - { - "to": "jo2_a", - "__reaction": "O2 + hv -> O + O1D", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jn2o5_b", - "__reaction": "N2O5 + hv -> NO + O + NO3", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, { "to": "jno", "__reaction": "NO + hv -> N + O", @@ -1320,27 +1421,6 @@ "__reaction": "XYLOLOOH + hv -> OH + 0.17*GLYOXAL + 0.51*CH3COCHO + HO2", "from": "jch3ooh" }, - { - "to": "jch2br2", - "__reaction": "CH2BR2 + hv -> 2*BR", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jcl2o2", - "__reaction": "CL2O2 + hv -> 2*CL", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jh2402", - "__reaction": "H2402 + hv -> 2*BR + 2*COF2", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, { "to": "jsoa1_a1", "__reaction": "soa1_a1 + hv -> Products", From be7e7f12e399db566bc8b0ff647ac95d19b9146f Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Wed, 21 Dec 2022 17:23:25 -0700 Subject: [PATCH 50/84] add jno stub function --- cime_config/tuvx_MOZART_TS1.json | 7 -- src/chemistry/mozart/mo_tuvx.F90 | 178 +++++++++++++++++++++++++++---- 2 files changed, 155 insertions(+), 30 deletions(-) diff --git a/cime_config/tuvx_MOZART_TS1.json b/cime_config/tuvx_MOZART_TS1.json index 2dc88fea7f..052137bfa0 100644 --- a/cime_config/tuvx_MOZART_TS1.json +++ b/cime_config/tuvx_MOZART_TS1.json @@ -1210,13 +1210,6 @@ "__CAM aliasing": { "default matching": "backup", "pairs": [ - { - "to": "jno", - "__reaction": "NO + hv -> N + O", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, { "to": "jalknit", "__reaction": "ALKNIT + hv -> NO2 + 0.4*CH3CHO + 0.1*CH2O + 0.25*CH3COCH3 + HO2 + 0.8*MEK", diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 938e00dbe1..c01856a1df 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -11,6 +11,7 @@ module mo_tuvx use tuvx_grid_from_host, only : grid_updater_t use tuvx_profile_from_host, only : profile_updater_t use tuvx_radiator_from_host, only : radiator_updater_t + use tuvx_spherical_geometry, only : spherical_geometry_t implicit none @@ -49,10 +50,12 @@ module mo_tuvx logical :: is_fixed_O = .false. ! indicates whether O concentrations are fixed logical :: is_fixed_O2 = .false. ! indicates whether O2 concentrations are fixed logical :: is_fixed_O3 = .false. ! indicates whether O3 concentrations are fixed + logical :: is_fixed_NO = .false. ! indicates whether NO concentrations are fixed integer :: index_N2 = 0 ! index for N2 in concentration array integer :: index_O = 0 ! index for O in concentration array integer :: index_O2 = 0 ! index for O2 in concentration array integer :: index_O3 = 0 ! index for O3 in concentration array + integer :: index_NO = 0 ! index for NO in concentration array ! Information needed to access aerosol optical properties logical :: aerosol_exists = .false. ! indicates whether aerosol optical properties are available @@ -63,6 +66,10 @@ module mo_tuvx integer :: ion_rates_pbuf_index = 0 ! Index in physics buffer for ! ionization rates + ! Information needed to do special NO photolysis rate calculation + logical :: do_jno = .false. ! Indicates whether to calculate jno + integer :: jno_index = 0 ! Index in tuvx_ptr::photo_rates_ array for jno + ! Cutoff solar zenith angle for doing photolysis rate calculations [degrees] integer :: max_sza = 0.0_r8 @@ -76,6 +83,7 @@ module mo_tuvx type(core_t), pointer :: core_ => null( ) ! TUV-x calculator integer :: n_photo_rates_ = 0 ! number of photo reactions in TUV-x integer :: n_euv_rates_ = 0 ! number of extreme-UV rates + integer :: n_special_rates_ = 0 ! number of special photo rates real(r8), allocatable :: photo_rates_(:,:,:) ! photolysis rate constants ! (column, vertical level, reaction) [s-1] type(map_t) :: photo_rate_map_ ! map between TUV-x and CAM @@ -93,6 +101,7 @@ module mo_tuvx real(r8), allocatable :: optical_depth_(:,:,:) ! (column, vertical level, wavelength) [unitless] real(r8), allocatable :: single_scattering_albedo_(:,:,:) ! (column, vertical level, wavelength) [unitless] real(r8), allocatable :: asymmetry_factor_(:,:,:) ! (column, vertical level, wavelength) [unitless] + type(spherical_geometry_t), pointer :: spherical_geometry_ => null( ) ! calculator of slant paths end type tuvx_ptr type(tuvx_ptr), allocatable :: tuvx_ptrs(:) @@ -202,7 +211,10 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) use mo_jeuv, only : neuv ! number of extreme-UV rates use musica_assert, only : assert_msg use musica_config, only : config_t - use musica_mpi, only : musica_mpi_rank + use musica_mpi, only : musica_mpi_rank, & + musica_mpi_pack_size, & + musica_mpi_pack, & + musica_mpi_unpack use musica_string, only : string_t, to_char use ppgrid, only : pcols ! maximum number of columns use shr_const_mod, only : pi => shr_const_pi @@ -275,13 +287,17 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) call tuvx_config%from_file( config_path%to_char( ) ) call tuvx_config%get( "__CAM aliasing", map_config, my_name ) core => core_t( config_path, cam_grids, cam_profiles, cam_radiators ) - call set_photo_rate_map( core, map_config, do_euv, map ) + call set_photo_rate_map( core, map_config, do_euv, do_jno, jno_index, map ) pack_size = core%pack_size( mpicom ) + & - map%pack_size( mpicom ) + map%pack_size( mpicom ) + & + musica_mpi_pack_size( do_jno, mpicom ) + & + musica_mpi_pack_size( jno_index, mpicom ) allocate( buffer( pack_size ) ) pos = 0 call core%mpi_pack( buffer, pos, mpicom ) call map%mpi_pack( buffer, pos, mpicom ) + call musica_mpi_pack( buffer, pos, do_jno, mpicom ) + call musica_mpi_pack( buffer, pos, jno_index, mpicom ) deallocate( core ) end if @@ -312,6 +328,8 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) pos = 0 call tuvx%core_%mpi_unpack( buffer, pos, mpicom ) call tuvx%photo_rate_map_%mpi_unpack( buffer, pos, mpicom ) + call musica_mpi_unpack( buffer, pos, do_jno, mpicom ) + call musica_mpi_unpack( buffer, pos, jno_index, mpicom ) ! =================================================================== ! Set up connections between CAM and TUV-x input data for each thread @@ -323,11 +341,18 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) ! =============================================================== tuvx%n_photo_rates_ = tuvx%core_%number_of_photolysis_reactions( ) if( do_euv ) tuvx%n_euv_rates_ = neuv + if( do_jno ) tuvx%n_special_rates_ = tuvx%n_special_rates_ + 1 height => tuvx%core_%get_grid( "height", "km" ) allocate( tuvx%photo_rates_( pcols, height%ncells_ + 1, & - tuvx%n_photo_rates_ + tuvx%n_euv_rates_ ) ) + tuvx%n_photo_rates_ + tuvx%n_euv_rates_ + & + tuvx%n_special_rates_ ) ) deallocate( height ) + ! ======================================================= + ! Initialize a calculator for slant paths for each thread + ! ======================================================= + tuvx%spherical_geometry_ => spherical_geometry_t( cam_grids ) + end associate end do @@ -350,6 +375,9 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) index_O3 = get_inv_ndx( 'O3' ) is_fixed_O3 = index_O3 > 0 if( .not. is_fixed_O3 ) index_O3 = get_spc_ndx( 'O3' ) + index_NO = get_inv_ndx( 'NO' ) + is_fixed_NO = index_NO > 0 + if( .not. is_fixed_NO ) index_NO = get_spc_ndx( 'NO' ) ! ==================================================== ! make sure extraterrestrial flux values are available @@ -484,6 +512,16 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & tuvx%photo_rates_(i_col,:,euv_begin:euv_end) ) end associate end if + + ! ============================= + ! Calculate special photo rates + ! ============================= + if( do_jno ) then + call calculate_jno( tuvx%spherical_geometry_, & + fixed_species_conc(i_col,:,:), & + species_vmr(i_col,:,:), & + tuvx%photo_rates_(i_col,:,jno_index) ) + end if end do ! ============================================ @@ -515,6 +553,7 @@ subroutine tuvx_finalize( ) do i_core = 1, size( tuvx_ptrs ) associate( tuvx => tuvx_ptrs( i_core ) ) if( associated( tuvx%core_ ) ) deallocate( tuvx%core_ ) + if( associated( tuvx%spherical_geometry_ ) ) deallocate( tuvx%spherical_geometry_ ) end associate end do end if @@ -635,11 +674,12 @@ end subroutine initialize_euv subroutine initialize_diagnostics( this ) use cam_history, only : addfld + use musica_assert, only : assert use musica_string, only : string_t type(tuvx_ptr), intent(in) :: this - type(string_t), allocatable :: labels(:) + type(string_t), allocatable :: labels(:), all_labels(:) integer :: i_label if( .not. enable_diagnostics ) then @@ -651,9 +691,17 @@ subroutine initialize_diagnostics( this ) ! add output for specific photolysis reaction rate constants ! ========================================================== labels = this%core_%photolysis_reaction_labels( ) - allocate( diagnostics( size( labels ) ) ) - do i_label = 1, size( labels ) - diagnostics( i_label )%name_ = trim( labels( i_label )%to_char( ) ) + allocate( all_labels( size( labels ) + this%n_special_rates_ ) ) + all_labels( 1 : size( labels ) ) = labels(:) + i_label = size( labels ) + 1 + if( do_jno ) then + all_labels( i_label ) = "jno" + i_label = i_label + 1 + end if + call assert( 522515214, i_label == size( all_labels ) + 1 ) + allocate( diagnostics( size( all_labels ) ) ) + do i_label = 1, size( all_labels ) + diagnostics( i_label )%name_ = trim( all_labels( i_label )%to_char( ) ) diagnostics( i_label )%index_ = i_label call addfld( "tuvx_"//diagnostics( i_label )%name_, (/ 'lev' /), 'A', 'sec-1', & 'photolysis rate constant' ) @@ -666,7 +714,7 @@ end subroutine initialize_diagnostics !----------------------------------------------------------------------- ! Sets up a map between the TUV-x and CAM photolysis rate arrays !----------------------------------------------------------------------- - subroutine set_photo_rate_map( core, config, do_euv, map ) + subroutine set_photo_rate_map( core, config, do_euv, do_jno, jno_index, map ) use cam_logfile, only : iulog ! log info output unit use chem_mods, only : phtcnt, & ! number of photolysis reactions @@ -677,34 +725,94 @@ subroutine set_photo_rate_map( core, config, do_euv, map ) use musica_config, only : config_t use musica_string, only : string_t - type(core_t), intent(in) :: core ! TUV-x core - type(config_t), intent(inout) :: config ! CAM<->TUV-x map configuration - logical, intent(in) :: do_euv ! indicates whether to include - ! extreme-UV rates in the mapping + type(core_t), intent(in) :: core ! TUV-x core + type(config_t), intent(inout) :: config ! CAM<->TUV-x map configuration + logical, intent(in) :: do_euv ! indicates whether to include + ! extreme-UV rates in the mapping + logical, intent(out) :: do_jno ! indicates whether jno should be + ! calculated + integer, intent(out) :: jno_index ! index for jno in source photo + ! rate array type(map_t), intent(out) :: map - integer :: i_label + integer :: i_label, i_start, i_end type(string_t) :: str_label - type(string_t), allocatable :: tuvx_labels(:), all_labels(:), cam_labels(:) + type(string_t), allocatable :: tuvx_labels(:), euv_labels(:), special_labels(:), & + all_labels(:), cam_labels(:) + ! ================== + ! MOZART photo rates + ! ================== allocate( cam_labels( phtcnt ) ) do i_label = 1, phtcnt cam_labels( i_label ) = trim( rxt_tag_lst( i_label ) ) end do + + ! ================= + ! TUV-x photo rates + ! ================= tuvx_labels = core%photolysis_reaction_labels( ) + + ! ====================== + ! Extreme-UV photo rates + ! ====================== if( do_euv ) then - allocate( all_labels( size( tuvx_labels ) + neuv ) ) - all_labels( 1 : size( tuvx_labels ) ) = tuvx_labels(:) + allocate( euv_labels( neuv ) ) do i_label = 1, neuv str_label = i_label - all_labels( size( tuvx_labels ) + i_label ) = "jeuv_"//str_label + euv_labels( i_label ) = "jeuv_"//str_label end do else - all_labels = tuvx_labels + allocate( euv_labels(0) ) + end if + + ! =============================== + ! Special photo rate calculations + ! =============================== + do_jno = .false. + jno_index = 0 + do i_label = 1, size( cam_labels ) + if( cam_labels( i_label ) == "jno" ) then + do_jno = .true. + exit + end if + end do + if( do_jno ) then + allocate( special_labels(1) ) + special_labels(1) = "jno" + else + allocate( special_labels(0) ) + end if + + ! ========================== + ! Combine photo rate sources + ! ========================== + allocate( all_labels( size( tuvx_labels ) + size( euv_labels ) + & + size( special_labels ) ) ) + i_end = 0 + if( size( tuvx_labels ) > 0 ) then + i_start = i_end + 1 + i_end = i_start + size( tuvx_labels ) - 1 + all_labels( i_start : i_end ) = tuvx_labels(:) end if + if( size( euv_labels ) > 0 ) then + i_start = i_end + 1 + i_end = i_start + size( euv_labels ) - 1 + all_labels( i_start : i_end ) = euv_labels(:) + end if + if( size( special_labels ) > 0 ) then + i_start = i_end + 1 + i_end = i_start + size( special_labels ) - 1 + all_labels( i_start : i_end ) = special_labels(:) + jno_index = i_start + end if + + ! ========== + ! Create map + ! ========== map = map_t( config, all_labels, cam_labels ) write(iulog,*) - write(iulog,*) "TUV-x <-> CAM-Chem photolysis rate constant map" + write(iulog,*) "TUV-x --> CAM-Chem photolysis rate constant map" call map%print( all_labels, cam_labels, iulog ) end subroutine set_photo_rate_map @@ -1406,10 +1514,10 @@ subroutine calculate_euv_rates( solar_zenith_angle, fixed_species_conc, & use ref_pres, only : ptop_ref ! pressure at the top of the column (Pa) real(r8), intent(in) :: solar_zenith_angle ! degrees - real(r8), intent(in) :: fixed_species_conc(pver,max(1,nfs)) ! fixed species densities + real(r8), intent(in) :: fixed_species_conc(pver,max(1,nfs)) ! fixed species densities ! (molecule cm-3) - real(r8), intent(in) :: species_vmr(pver,max(1,gas_pcnst)) ! species volume mixing - ! ratios (mol mol-1) + real(r8), intent(in) :: species_vmr(pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) real(r8), intent(in) :: height_mid(pver) ! height at mid-points (km) real(r8), intent(in) :: height_int(pver+1) ! height at interfaces (km) real(r8), intent(out) :: euv_rates(pver,neuv) ! calculated extreme-UV rates @@ -1461,6 +1569,30 @@ subroutine calculate_euv_rates( solar_zenith_angle, fixed_species_conc, & end subroutine calculate_euv_rates +!================================================================================================ + + !----------------------------------------------------------------------- + ! Calculates NO photolysis rates + !----------------------------------------------------------------------- + subroutine calculate_jno( spherical_geometry, fixed_species_conc, & + species_vmr, jno ) + + use chem_mods, only : gas_pcnst, & ! number of non-fixed species + nfs, & ! number of fixed species + indexm ! index for air density in fixed species array + use tuvx_spherical_geometry, only : spherical_geometry_t + + type(spherical_geometry_t), intent(in) :: spherical_geometry ! slant column calculator + real(r8), intent(in) :: fixed_species_conc(pver,max(1,nfs)) ! fixed species densities + ! (molecule cm-3) + real(r8), intent(in) :: species_vmr(pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) + real(r8), intent(out) :: jno(pver) ! calculated NO rate + + jno(:) = 0.0_r8 + + end subroutine calculate_jno + !================================================================================================ end module mo_tuvx From 37326c43df61ee9922e61ccd409ead5d85e5069d Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Thu, 22 Dec 2022 13:41:26 -0700 Subject: [PATCH 51/84] finish jno calculation --- src/chemistry/mozart/mo_jshort.F90 | 1 + src/chemistry/mozart/mo_tuvx.F90 | 109 +++++++++++++++++++++++++---- 2 files changed, 96 insertions(+), 14 deletions(-) diff --git a/src/chemistry/mozart/mo_jshort.F90 b/src/chemistry/mozart/mo_jshort.F90 index aa47dffb31..65efa66de4 100644 --- a/src/chemistry/mozart/mo_jshort.F90 +++ b/src/chemistry/mozart/mo_jshort.F90 @@ -27,6 +27,7 @@ module mo_jshort public :: jshort public :: sphers public :: slant_col + public :: calc_jno public :: nj save diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index c01856a1df..6230b7daa8 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -11,7 +11,6 @@ module mo_tuvx use tuvx_grid_from_host, only : grid_updater_t use tuvx_profile_from_host, only : profile_updater_t use tuvx_radiator_from_host, only : radiator_updater_t - use tuvx_spherical_geometry, only : spherical_geometry_t implicit none @@ -45,6 +44,11 @@ module mo_tuvx integer, parameter :: NUM_RADIATORS = 1 ! number of radiators that CAM will update at runtime integer, parameter :: RADIATOR_INDEX_AEROSOL = 1 ! Aerosol radiator index + ! Definition of the MS93 wavelength grid TODO add description of this + integer, parameter :: NUM_BINS_MS93 = 4 + real(kind=r8), parameter :: WAVELENGTH_EDGES_MS93(NUM_BINS_MS93+1) = & + (/ 181.6_r8, 183.1_r8, 184.6_r8, 190.2_r8, 192.5_r8 /) + ! Information needed to access CAM species state data logical :: is_fixed_N2 = .false. ! indicates whether N2 concentrations are fixed logical :: is_fixed_O = .false. ! indicates whether O concentrations are fixed @@ -101,7 +105,8 @@ module mo_tuvx real(r8), allocatable :: optical_depth_(:,:,:) ! (column, vertical level, wavelength) [unitless] real(r8), allocatable :: single_scattering_albedo_(:,:,:) ! (column, vertical level, wavelength) [unitless] real(r8), allocatable :: asymmetry_factor_(:,:,:) ! (column, vertical level, wavelength) [unitless] - type(spherical_geometry_t), pointer :: spherical_geometry_ => null( ) ! calculator of slant paths + real(r8) :: et_flux_ms93_(NUM_BINS_MS93) ! extraterrestrial flux on the MS93 grid + ! [photon cm-2 nm-1 s-1] end type tuvx_ptr type(tuvx_ptr), allocatable :: tuvx_ptrs(:) @@ -348,11 +353,6 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) tuvx%n_special_rates_ ) ) deallocate( height ) - ! ======================================================= - ! Initialize a calculator for slant paths for each thread - ! ======================================================= - tuvx%spherical_geometry_ => spherical_geometry_t( cam_grids ) - end associate end do @@ -517,9 +517,11 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & ! Calculate special photo rates ! ============================= if( do_jno ) then - call calculate_jno( tuvx%spherical_geometry_, & + call calculate_jno( sza, & + tuvx%et_flux_ms93_, & fixed_species_conc(i_col,:,:), & species_vmr(i_col,:,:), & + height_int(i_col,:), & tuvx%photo_rates_(i_col,:,jno_index) ) end if end do @@ -553,7 +555,6 @@ subroutine tuvx_finalize( ) do i_core = 1, size( tuvx_ptrs ) associate( tuvx => tuvx_ptrs( i_core ) ) if( associated( tuvx%core_ ) ) deallocate( tuvx%core_ ) - if( associated( tuvx%spherical_geometry_ ) ) deallocate( tuvx%spherical_geometry_ ) end associate end do end if @@ -1274,6 +1275,12 @@ subroutine set_et_flux( this ) mid_point_values = this%wavelength_mid_values_, & edge_values = this%wavelength_values_) + ! ====================================================================== + ! rebin extraterrestrial flux to MS93 grid for use with jno calculations + ! ====================================================================== + call rebin( nbins, NUM_BINS_MS93, we, WAVELENGTH_EDGES_MS93, et_flux_orig, & + this%et_flux_ms93_ ) + end subroutine set_et_flux !================================================================================================ @@ -1573,23 +1580,97 @@ end subroutine calculate_euv_rates !----------------------------------------------------------------------- ! Calculates NO photolysis rates + ! + ! NOTE: Always includes an above-column layer !----------------------------------------------------------------------- - subroutine calculate_jno( spherical_geometry, fixed_species_conc, & - species_vmr, jno ) + subroutine calculate_jno( solar_zenith_angle, et_flux, fixed_species_conc, species_vmr, & + height_int, jno ) use chem_mods, only : gas_pcnst, & ! number of non-fixed species nfs, & ! number of fixed species indexm ! index for air density in fixed species array - use tuvx_spherical_geometry, only : spherical_geometry_t + use mo_jshort, only : sphers, slant_col, calc_jno + use ref_pres, only : ptop_ref ! pressure at the top of the column (Pa) - type(spherical_geometry_t), intent(in) :: spherical_geometry ! slant column calculator + real(r8), intent(in) :: solar_zenith_angle ! degrees + real(r8), intent(in) :: et_flux(NUM_BINS_MS93) ! extraterrestrial flux MS93 grid + ! (photon cm-2 nm-1 s-1) real(r8), intent(in) :: fixed_species_conc(pver,max(1,nfs)) ! fixed species densities ! (molecule cm-3) real(r8), intent(in) :: species_vmr(pver,max(1,gas_pcnst)) ! species volume mixing ! ratios (mol mol-1) + real(r8), intent(in) :: height_int(pver+1) ! height at interfaces (km) real(r8), intent(out) :: jno(pver) ! calculated NO rate - jno(:) = 0.0_r8 + ! species column densities (molecule cm-3) + real(kind=r8) :: n2_dens(pver+1), o2_dens(pver+1), o3_dens(pver+1), no_dens(pver+1) + ! species slant column densities (molecule cm-2) + real(kind=r8) :: o2_slant(pver+1), o3_slant(pver+1), no_slant(pver+1) + ! working photo rate array + real(kind=r8) :: work_jno(pver+1) + ! parameters needed to calculate slant column densities + ! (see sphers routine description for details) + integer :: nid(pver+1) + real(kind=r8) :: dsdh(0:pver+1,pver+1) + ! layer thickness (cm) + real(kind=r8) :: delz(pver+1) + ! conversion from km to cm + real(kind=r8), parameter :: km2cm = 1.0e5_r8 + + ! ========== + ! N2 density + ! ========== + if( is_fixed_N2 ) then + n2_dens(2:) = fixed_species_conc(:pver,index_N2) + else + n2_dens(2:) = species_vmr(:pver,index_N2) * fixed_species_conc(:pver,indexm) + end if + n2_dens(1) = n2_dens(2) * 0.9_r8 + + ! ========== + ! O2 density + ! ========== + if( is_fixed_O2 ) then + o2_dens(2:) = fixed_species_conc(:pver,index_O2) + else + o2_dens(2:) = species_vmr(:pver,index_O2) * fixed_species_conc(:pver,indexm) + end if + o2_dens(1) = o2_dens(2) * 7.0_r8 / ( height_int(1) - height_int(2) ) + + ! ========== + ! O3 density + ! ========== + if( is_fixed_O3 ) then + o3_dens(2:) = fixed_species_conc(:pver,index_O3) + else + o3_dens(2:) = species_vmr(:pver,index_O3) * fixed_species_conc(:pver,indexm) + end if + o3_dens(1) = o3_dens(2) * 7.0_r8 / ( height_int(1) - height_int(2) ) + + ! ========== + ! NO density + ! ========== + if( is_fixed_NO ) then + no_dens(2:) = fixed_species_conc(:pver,index_NO) + else + no_dens(2:) = species_vmr(:pver,index_NO) * fixed_species_conc(:pver,indexm) + end if + no_dens(1) = no_dens(2) * 0.9_r8 + + ! ================================ + ! calculate slant column densities + ! ================================ + call sphers( pver+1, height_int, solar_zenith_angle, dsdh, nid ) + delz(1:pver) = km2cm * ( height_int(1:pver) - height_int(2:pver+1) ) + call slant_col( pver+1, delz, dsdh, nid, o2_dens, o2_slant ) + call slant_col( pver+1, delz, dsdh, nid, o3_dens, o3_slant ) + call slant_col( pver+1, delz, dsdh, nid, no_dens, no_slant ) + + ! ========================================= + ! calculate the NO photolysis rate constant + ! ========================================= + call calc_jno( pver+1, et_flux, n2_dens, o2_slant, o3_slant, no_slant, work_jno ) + jno(:) = work_jno(:pver) end subroutine calculate_jno From 0608ae35a75506ff81b9f035b18faf5afde16890 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Thu, 22 Dec 2022 16:02:34 -0700 Subject: [PATCH 52/84] update TSMLT config file --- cime_config/tuvx_MOZART_TSMLT.json | 725 ++++++++++++++++++++--------- src/chemistry/mozart/mo_tuvx.F90 | 29 +- 2 files changed, 515 insertions(+), 239 deletions(-) diff --git a/cime_config/tuvx_MOZART_TSMLT.json b/cime_config/tuvx_MOZART_TSMLT.json index fc873f4696..98c1e3335e 100644 --- a/cime_config/tuvx_MOZART_TSMLT.json +++ b/cime_config/tuvx_MOZART_TSMLT.json @@ -18,14 +18,23 @@ }, { "name": "O3", - "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], "type": "O3" }, { "name": "O2", - "netcdf files": ["data/cross_sections/O2_1.nc"], - "type": "base", - "lower extrapolation": { "type": "boundary" } + "netcdf files": [ + { + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" } + } + ], + "type": "base" } ], "radiators": [ @@ -54,24 +63,70 @@ }, "photolysis": { "reactions": [ + { + "name": "jo2_a", + "__reaction": "O2 + hv -> O + O1D", + "cross section": { + "netcdf files": [ + { + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" } + } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0, + "override bands": [ + { + "band": "lyman-alpha", + "value": 0.47 + }, + { + "band": "schumann-runge", + "value": 1.0 + } + ] + } + }, { "name": "jo2_b", "__reaction": "O2 + hv -> O + O", "cross section": { - "netcdf files": ["data/cross_sections/O2_1.nc"], - "type": "base", - "lower extrapolation": { "type": "boundary" } + "netcdf files": [ + { + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" } + } + ], + "type": "base" }, "quantum yield": { - "type": "base", - "constant value": 1.0 + "type": "base", + "constant value": 1.0, + "override bands": [ + { + "band": "lyman-alpha", + "value": 0.53 + }, + { + "band": "schumann-runge continuum", + "value": 1.0 + } + ] } }, { "name": "jo3_a", "__reaction": "O3 + hv -> O2 + O(1D)", "cross section": { - "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], "type": "O3" }, "quantum yield": { @@ -82,7 +137,12 @@ "name": "jo3_b", "__reaction": "O3 + hv -> O2 + O(3P)", "cross section": { - "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], "type": "O3" }, "quantum yield": { @@ -104,7 +164,9 @@ "name": "jno2", "__reaction": "NO2 + hv -> NO + O(3P)", "cross section": { - "netcdf files": ["data/cross_sections/NO2_1.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/NO2_1.nc" } + ], "type": "NO2 tint" }, "quantum yield": { @@ -118,14 +180,29 @@ "__reaction": "N2O5 + hv -> NO2 + NO3", "cross section": { "netcdf files": [ - "data/cross_sections/N2O5_1.nc", - "data/cross_sections/N2O5_2.nc" + { "file path": "data/cross_sections/N2O5_1.nc" }, + { "file path": "data/cross_sections/N2O5_2.nc" } ], "type": "N2O5+hv->NO2+NO3" }, "quantum yield": { "type": "base", - "constant value": 1.0 + "netcdf files": [ "data/quantum_yields/N2O5_NO3_NO2.nc" ] + } + }, + { + "name": "jn2o5_b", + "__reaction": "N2O5 + hv -> NO + O + NO3", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/N2O5_1.nc" }, + { "file path": "data/cross_sections/N2O5_2.nc" } + ], + "type": "N2O5+hv->NO2+NO3" + }, + "quantum yield": { + "type": "base", + "netcdf files": [ "data/quantum_yields/N2O5_NO3_NO_O.nc" ] } }, { @@ -133,7 +210,7 @@ "__reaction": "HNO3 + hv -> OH + NO2", "cross section": { "netcdf files": [ - "data/cross_sections/HNO3_1.nc" + { "file path": "data/cross_sections/HNO3_1.nc" } ], "type": "HNO3+hv->OH+NO2" }, @@ -147,7 +224,7 @@ "__reaction": "NO3 + hv -> NO2 + O(3P)", "cross section": { "netcdf files": [ - "data/cross_sections/NO3_1.nc" + { "file path": "data/cross_sections/NO3_1.nc" } ], "type": "base" }, @@ -167,7 +244,7 @@ "__reaction": "NO3 + hv -> NO + O2", "cross section": { "netcdf files": [ - "data/cross_sections/NO3_1.nc" + { "file path": "data/cross_sections/NO3_1.nc" } ], "type": "base" }, @@ -183,7 +260,7 @@ "__reaction": "CH3OOH + hv -> CH3O + OH", "cross section": { "netcdf files": [ - "data/cross_sections/CH3OOH_1.nc" + { "file path": "data/cross_sections/CH3OOH_1.nc" } ], "type": "base" }, @@ -197,7 +274,7 @@ "__reaction": "CH2O + hv -> H + HCO", "cross section": { "netcdf files": [ - "data/cross_sections/CH2O_1.nc" + { "file path": "data/cross_sections/CH2O_1.nc" } ], "type": "CH2O" }, @@ -216,7 +293,7 @@ "__reaction": "CH2O + hv -> H2 + CO", "cross section": { "netcdf files": [ - "data/cross_sections/CH2O_1.nc" + { "file path": "data/cross_sections/CH2O_1.nc" } ], "type": "CH2O" }, @@ -235,7 +312,7 @@ "__reaction": "H2O2 + hv -> OH + OH", "cross section": { "netcdf files": [ - "data/cross_sections/H2O2_1.nc" + { "file path": "data/cross_sections/H2O2_1.nc" } ], "type": "H2O2+hv->OH+OH" }, @@ -249,7 +326,7 @@ "__reaction": "CH3CHO + hv -> CH3 + HCO", "cross section": { "netcdf files": [ - "data/cross_sections/CH3CHO_1.nc" + { "file path": "data/cross_sections/CH3CHO_1.nc" } ], "type": "base" }, @@ -265,7 +342,7 @@ "__reaction": "PAN + hv -> 0.6*CH3CO3 + 0.6*NO2 + 0.4*CH3O2 + 0.4*NO3 + 0.4*CO2", "cross section": { "netcdf files": [ - "data/cross_sections/PAN_1.nc" + { "file path": "data/cross_sections/PAN_1.nc" } ], "type": "CH3ONO2+hv->CH3O+NO2" }, @@ -279,7 +356,7 @@ "__reaction": "MVK + hv -> 0.7*C3H6 + 0.7*CO + 0.3*CH3O2 + 0.3*CH3CO3", "cross section": { "netcdf files": [ - "data/cross_sections/MVK_1.nc" + { "file path": "data/cross_sections/MVK_1.nc" } ], "type": "base" }, @@ -291,7 +368,9 @@ "name": "jacet", "__reaction": "CH3COCH3 + hv -> CH3CO + CH3", "cross section": { - "netcdf files": ["data/cross_sections/CH3COCH3_1.nc"], + "netcdf files": [ + { "file path": "data/cross_sections/CH3COCH3_1.nc" } + ], "type": "CH3COCH3+hv->CH3CO+CH3" }, "quantum yield": { @@ -303,7 +382,7 @@ "__reaction": "CH3COCHO + hv -> CH3CO3 + CO + HO2", "cross section": { "netcdf files": [ - "data/cross_sections/CH3COCHO_1.nc" + { "file path": "data/cross_sections/CH3COCHO_1.nc" } ], "type": "base" }, @@ -316,7 +395,7 @@ "__reaction": "GLYALD + hv -> 2*HO2 + CO + CH2O", "cross section": { "netcdf files": [ - "data/cross_sections/HOCH2CHO_1.nc" + { "file path": "data/cross_sections/HOCH2CHO_1.nc" } ], "type": "base" }, @@ -331,7 +410,7 @@ "__comments": "NOTE the products of this reaction don't exactly match", "cross section": { "netcdf files": [ - "data/cross_sections/CHOCHO_1.nc" + { "file path": "data/cross_sections/CHOCHO_1.nc" } ], "type": "base" }, @@ -350,7 +429,7 @@ "__reaction": "BrCl + hv -> Br + Cl", "cross section": { "netcdf files": [ - "data/cross_sections/BrCl_1.nc" + { "file path": "data/cross_sections/BrCl_1.nc" } ], "type": "base" }, @@ -364,9 +443,15 @@ "__reaction": "BrO + hv -> Br + O", "cross section": { "netcdf files": [ - "data/cross_sections/BrO_1.nc" + { + "file path": "data/cross_sections/BrO_1.nc", + "interpolator": { + "type": "fractional target", + "fold in": true + } + } ], - "type": "BrO+hv->Br+O" + "type": "base" }, "quantum yield": { "type": "base", @@ -378,7 +463,7 @@ "__reaction": "BrONO2 + hv -> Br + NO3", "cross section": { "netcdf files": [ - "data/cross_sections/BrONO2_1.nc" + { "file path": "data/cross_sections/BrONO2_1.nc" } ], "type": "base" }, @@ -392,7 +477,7 @@ "__reaction": "BrONO2 + hv -> BrO + NO2", "cross section": { "netcdf files": [ - "data/cross_sections/BrONO2_1.nc" + { "file path": "data/cross_sections/BrONO2_1.nc" } ], "type": "base" }, @@ -406,7 +491,7 @@ "__reaction": "CCl4 + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CCl4_1.nc" + { "file path": "data/cross_sections/CCl4_1.nc" } ], "type": "CCl4+hv->Products" }, @@ -420,7 +505,7 @@ "__reaction": "CF2BrCl + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CF2BrCl_1.nc" + { "file path": "data/cross_sections/CF2BrCl_1.nc" } ], "type": "base" }, @@ -434,7 +519,7 @@ "__reaction": "CF3Br + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CF3Br_1.nc" + { "file path": "data/cross_sections/CF3Br_1.nc" } ], "type": "base" }, @@ -448,7 +533,7 @@ "__reaction": "CCl3F + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CFC-11_1.nc" + { "file path": "data/cross_sections/CFC-11_1.nc" } ], "type": "CCl3F+hv->Products" }, @@ -462,7 +547,7 @@ "__reaction": "CFC-113 + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CFC-113_1.nc" + { "file path": "data/cross_sections/CFC-113_1.nc" } ], "type": "tint" }, @@ -476,7 +561,7 @@ "__reaction": "CFC-114 + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CFC-114_1.nc" + { "file path": "data/cross_sections/CFC-114_1.nc" } ], "type": "tint" }, @@ -490,7 +575,7 @@ "__reaction": "CFC-115 + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CFC-115_1.nc" + { "file path": "data/cross_sections/CFC-115_1.nc" } ], "type": "base" }, @@ -504,7 +589,7 @@ "__reaction": "CCl2F2 + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CFC-12_1.nc" + { "file path": "data/cross_sections/CFC-12_1.nc" } ], "type": "CCl3F+hv->Products" }, @@ -513,12 +598,45 @@ "constant value": 1.0 } }, + { + "name": "jch2br2", + "__reaction": "CH2BR2 + hv -> 2*BR", + "cross section": { + "type": "temperature based", + "netcdf file": "data/cross_sections/CH2BR2_1.nc", + "parameterization": { + "AA": [ -70.211776, 1.940326e-1, 2.726152e-3, -1.695472e-5, 2.500066e-8 ], + "BB": [ 2.899280, -4.327724e-2, 2.391599e-4, -5.807506e-7, 5.244883e-10 ], + "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], + "minimum wavelength": 210.0, + "maximum wavelength": 290.0, + "temperature ranges": [ + { + "maximum": 209.999999999999, + "fixed value": 210.0 + }, + { + "minimum": 210, + "maximum": 300 + }, + { + "minimum": 300.00000000001, + "fixed value": 300.0 + } + ] + } + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, { "name": "jch3br", "__reaction": "CH3Br + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CH3Br_1.nc" + { "file path": "data/cross_sections/CH3Br_1.nc" } ], "type": "base" }, @@ -532,7 +650,7 @@ "__reaction": "CH3CCl3+hv->Products", "cross section": { "netcdf files": [ - "data/cross_sections/CH3CCl3_1.nc" + { "file path": "data/cross_sections/CH3CCl3_1.nc" } ], "type": "tint" }, @@ -546,7 +664,7 @@ "__reaction": "CH3Cl + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CH3Cl_1.nc" + { "file path": "data/cross_sections/CH3Cl_1.nc" } ], "type": "tint" }, @@ -560,7 +678,7 @@ "__reaction": "CHBr3 + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CHBr3_1.nc" + { "file path": "data/cross_sections/CHBr3_1.nc" } ], "type": "CHBr3+hv->Products", "lower extrapolation": { @@ -583,12 +701,27 @@ "constant value": 1.0 } }, + { + "name": "jcl2o2", + "__reaction": "ClOOCl + hv -> Cl + ClOO", + "__comments": "TODO - this doesn't exactly match the products in TS1", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/ClOOCl_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, { "name": "jclo", "__reaction": "ClO + hv -> Cl + O(1D)", "cross section": { "netcdf files": [ - "data/cross_sections/ClO_1.nc" + { "file path": "data/cross_sections/ClO_1.nc" } ], "type": "tint" }, @@ -601,7 +734,7 @@ "__reaction": "ClONO2 + hv -> Cl + NO3", "cross section": { "netcdf files": [ - "data/cross_sections/ClONO2_1.nc" + { "file path": "data/cross_sections/ClONO2_1.nc" } ], "type": "ClONO2" }, @@ -614,7 +747,7 @@ "__reaction": "ClONO2 + hv -> ClO + NO2", "cross section": { "netcdf files": [ - "data/cross_sections/ClONO2_1.nc" + { "file path": "data/cross_sections/ClONO2_1.nc" } ], "type": "ClONO2" }, @@ -627,7 +760,7 @@ "__reaction": "CF2O + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CF2O_1.nc" + { "file path": "data/cross_sections/CF2O_1.nc" } ], "type": "base" }, @@ -641,7 +774,7 @@ "__reaction": "CClFO + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CClFO_1.nc" + { "file path": "data/cross_sections/CClFO_1.nc" } ], "type": "base" }, @@ -650,12 +783,27 @@ "constant value": 1.0 } }, + { + "name": "jh2402", + "__reaction": "H2402 + hv -> 2*BR + 2*COF2", + "__comments": "TUV data set name CF2BrCF2Br", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CF2BrCF2Br_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, { "name": "jhcfc141b", "__reaction": "HCFC-141b + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CH3CFCl2_1.nc" + { "file path": "data/cross_sections/CH3CFCl2_1.nc" } ], "type": "base" }, @@ -669,7 +817,7 @@ "__reaction": "HCFC-142b + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CH3CF2Cl_1.nc" + { "file path": "data/cross_sections/CH3CF2Cl_1.nc" } ], "type": "HCFC+hv->Products" }, @@ -683,7 +831,7 @@ "__reaction": "HCFC-22 + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/CHClF2_1.nc" + { "file path": "data/cross_sections/CHClF2_1.nc" } ], "type": "tint" }, @@ -697,7 +845,7 @@ "__reaction": "HCl + hv -> H + Cl", "cross section": { "netcdf files": [ - "data/cross_sections/HCl_1.nc" + { "file path": "data/cross_sections/HCl_1.nc" } ], "type": "base" }, @@ -722,7 +870,7 @@ "__reaction": "HOCl + hv -> HO + Cl", "cross section": { "netcdf files": [ - "data/cross_sections/HOCl_1.nc" + { "file path": "data/cross_sections/HOCl_1.nc" } ], "type": "base" }, @@ -736,9 +884,9 @@ "__reaction": "OClO + hv -> Products", "cross section": { "netcdf files": [ - "data/cross_sections/OClO_1.nc", - "data/cross_sections/OClO_2.nc", - "data/cross_sections/OClO_3.nc" + { "file path": "data/cross_sections/OClO_1.nc" }, + { "file path": "data/cross_sections/OClO_2.nc" }, + { "file path": "data/cross_sections/OClO_3.nc" } ], "type": "OClO+hv->Products" }, @@ -746,68 +894,322 @@ "type": "base", "constant value": 1.0 } - } - ] - }, - "__CAM aliasing": { - "default matching": "backup", - "pairs": [ + }, + { + "name": "jho2no2_a", + "__reaction": "HNO4 + hv -> OH + NO3", + "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HNO4_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.2 + } + }, + { + "name": "jho2no2_b", + "__reaction": "HNO4 + hv -> HO2 + NO2", + "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HNO4_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.8 + } + }, + { + "name": "jmacr_a", + "__reaction": "CH2=C(CH3)CHO->1.34HO2+0.66MCO3+1.34CH2O+CH3CO3", + "__comments": "Methacrolein photolysis channel 1", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/Methacrolein_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.005 + } + }, + { + "name": "jmacr_b", + "__reaction": "CH2=C(CH3)CHO->0.66OH+1.34CO", + "__comments": "Methacrolein photolysis channel 2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/Methacrolein_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.005 + } + }, + { + "name": "jhyac", + "__reaction": "CH2(OH)COCH3->CH3CO3+HO2+CH2O", + "__comments": "hydroxy acetone TODO: the products of this reaction differ from standalone TUV-x", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/Hydroxyacetone_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.65 + } + }, { - "to": "jh2o_a", + "name": "jh2o_a", "__reaction": "H2O + hv -> OH + H", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 + "cross section": { + "type": "base", + "merge data": true, + "netcdf files": [ + { + "file path": "data/cross_sections/H2O_1.nc", + "zero above": 183.0 + }, + { + "file path": "data/cross_sections/H2O_2.nc", + "zero below": 183.00000000001, + "zero above": 190.0 + }, + { + "file path": "data/cross_sections/H2O_3.nc", + "zero below": 190.00000000001 + } + ] + }, + "quantum yield" : { + "type": "base", + "netcdf files": [ "data/quantum_yields/H2O_H_OH.nc" ] + } }, { - "to": "jh2o_b", + "name": "jh2o_b", "__reaction": "H2O + hv -> H2 + O1D", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 + "cross section": { + "type": "base", + "merge data": true, + "netcdf files": [ + { + "file path": "data/cross_sections/H2O_1.nc", + "zero above": 183.0 + }, + { + "file path": "data/cross_sections/H2O_2.nc", + "zero below": 183.00000000001, + "zero above": 190.0 + }, + { + "file path": "data/cross_sections/H2O_3.nc", + "zero below": 190.00000000001 + } + ] + }, + "quantum yield" : { + "type": "base", + "netcdf files": [ "data/quantum_yields/H2O_H2_O1D.nc" ] + } }, { - "to": "jh2o_c", + "name": "jh2o_c", "__reaction": "H2O + hv -> 2*H + O", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 + "cross section": { + "type": "base", + "merge data": true, + "netcdf files": [ + { + "file path": "data/cross_sections/H2O_1.nc", + "zero above": 183.0 + }, + { + "file path": "data/cross_sections/H2O_2.nc", + "zero below": 183.00000000001, + "zero above": 190.0 + }, + { + "file path": "data/cross_sections/H2O_3.nc", + "zero below": 190.00000000001 + } + ] + }, + "quantum yield" : { + "type": "base", + "netcdf files": [ "data/quantum_yields/H2O_2H_O3P.nc" ] + } }, { - "to": "jo2_a", - "__reaction": "O2 + hv -> O + O1D", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 + "name": "jch4_a", + "__reaction": "CH4 + hv -> H + CH3O2", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/CH4_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 0.55 + } }, { - "to": "jho2no2_a", - "__reaction": "HO2NO2 + hv -> OH + NO3", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 + "name": "jch4_b", + "__reaction": "CH4 + hv -> 1.44*H2 + 0.18*CH2O + 0.18*O + 0.33*OH + 0.33*H + 0.44*CO2 + 0.38*CO + 0.05*H2O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/CH4_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 0.45 + } }, { - "to": "jho2no2_b", - "__reaction": "HO2NO2 + hv -> NO2 + HO2", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 + "name": "jco2", + "__reaction": "CO2 + hv -> CO + O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/CO2_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } }, { - "to": "jn2o5_b", - "__reaction": "N2O5 + hv -> NO + O + NO3", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 + "name": "jhbr", + "__reaction": "HBR + hv -> BR + H", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/HBr_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } }, { - "to": "jno", - "__reaction": "NO + hv -> N + O", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 + "name": "jhf", + "__reaction": "HF + hv -> H + F", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/HF_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jsf6", + "__reaction": "SF6 + hv -> sink", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/SF6_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jh2so4", + "__reaction": "H2SO4 + hv -> SO3 + H2O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/H2SO4_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jocs", + "__reaction": "OCS + hv -> S + CO", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/OCS_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jso", + "__reaction": "SO + hv -> S + O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/SO_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jso2", + "__reaction": "SO2 + hv -> SO + O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/SO2_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } }, + { + "name": "jso3", + "__reaction": "SO3 + hv -> SO2 + O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/SO3_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + } + ] + }, + "__CAM aliasing": { + "default matching": "backup", + "pairs": [ { "to": "jno_i", "__reaction": "NO + hv -> NOp + e", @@ -836,20 +1238,6 @@ "__reaction": "MPAN + hv -> MCO3 + NO2", "from": "jpan" }, - { - "to": "jmacr_a", - "__reaction": "MACR + hv -> 1.34*HO2 + 0.66*MCO3 + 1.34*CH2O + 1.34*CH3CO3", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jmacr_b", - "__reaction": "MACR + hv -> 0.66*HO2 + 1.34*CO", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, { "to": "jc2h5ooh", "__reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", @@ -865,27 +1253,6 @@ "__reaction": "C6H5OOH + hv -> PHENO + OH", "from": "jch3ooh" }, - { - "to": "jch4_a", - "__reaction": "CH4 + hv -> H + CH3O2", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jch4_b", - "__reaction": "CH4 + hv -> 1.44*H2 + 0.18*CH2O + 0.18*O + 0.33*OH + 0.33*H + 0.44*CO2 + 0.38*CO + 0.05*H2O", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jco2", - "__reaction": "CO2 + hv -> CO + O", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, { "to": "jeooh", "__reaction": "EOOH + hv -> EO + OH", @@ -911,13 +1278,6 @@ "__reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", "from": "jch3ooh" }, - { - "to": "jhyac", - "__reaction": "HYAC (CH3COCH2OH) + hv -> CH3CO3 + HO2 + CH2O", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, { "to": "jmek", "__reaction": "MEK + hv -> CH3CO3 + C2H5O2", @@ -1061,83 +1421,6 @@ "__reaction": "XYLOLOOH + hv -> OH + 0.17*GLYOXAL + 0.51*CH3COCHO + HO2", "from": "jch3ooh" }, - { - "to": "jch2br2", - "__reaction": "CH2BR2 + hv -> 2*BR", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jcl2o2", - "__reaction": "CL2O2 + hv -> 2*CL", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jh2402", - "__reaction": "H2402 + hv -> 2*BR + 2*COF2", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jhbr", - "__reaction": "HBR + hv -> BR + H", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jhf", - "__reaction": "HF + hv -> H + F", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jsf6", - "__reaction": "SF6 + hv -> sink", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jh2so4", - "__reaction": "H2SO4 + hv -> SO3 + H2O", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jocs", - "__reaction": "OCS + hv -> S + CO", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jso", - "__reaction": "SO + hv -> S + O", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jso2", - "__reaction": "SO2 + hv -> SO + O", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jso3", - "__reaction": "SO3 + hv -> SO2 + O", - "__comments": "TODO - find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, { "to": "jsoa1_a1", "__reaction": "soa1_a1 + hv -> Products", diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 6230b7daa8..713315a3a4 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -1510,6 +1510,8 @@ end subroutine reorder_optics_array !----------------------------------------------------------------------- ! Calculates extreme-UV ionization rates + ! + ! NOTE This never includes an above-column layer !----------------------------------------------------------------------- subroutine calculate_euv_rates( solar_zenith_angle, fixed_species_conc, & species_vmr, height_mid, height_int, euv_rates ) @@ -1529,50 +1531,41 @@ subroutine calculate_euv_rates( solar_zenith_angle, fixed_species_conc, & real(r8), intent(in) :: height_int(pver+1) ! height at interfaces (km) real(r8), intent(out) :: euv_rates(pver,neuv) ! calculated extreme-UV rates - real(r8) :: o_dens(pver+1), o2_dens(pver+1), n2_dens(pver+1), height_arg(pver+1) + real(r8) :: o_dens(pver), o2_dens(pver), n2_dens(pver), height_arg(pver) ! ========== ! N2 density ! ========== if( is_fixed_N2 ) then - n2_dens(2:) = fixed_species_conc(:pver,index_N2) + n2_dens(:) = fixed_species_conc(:pver,index_N2) else - n2_dens(2:) = species_vmr(:pver,index_N2) * fixed_species_conc(:pver,indexm) + n2_dens(:) = species_vmr(:pver,index_N2) * fixed_species_conc(:pver,indexm) end if - n2_dens(1) = 0.0_r8 - if( ptop_ref > 10.0_r8 ) n2_dens(1) = n2_dens(2) * 0.9_r8 ! ========= ! O density ! ========= if( is_fixed_O ) then - o_dens(2:) = fixed_species_conc(:pver,index_O) + o_dens(:) = fixed_species_conc(:pver,index_O) else - o_dens(2:) = species_vmr(:pver,index_O) * fixed_species_conc(:pver,indexm) + o_dens(:) = species_vmr(:pver,index_O) * fixed_species_conc(:pver,indexm) end if - o_dens(1) = 0.0_r8 ! ========== ! O2 density ! ========== if( is_fixed_O2 ) then - o2_dens(2:) = fixed_species_conc(:pver,index_O2) + o2_dens(:) = fixed_species_conc(:pver,index_O2) else - o2_dens(2:) = species_vmr(:pver,index_O2) * fixed_species_conc(:pver,indexm) - end if - o2_dens(1) = 0.0_r8 - if( ptop_ref > 10.0_r8 ) then - o2_dens(1) = o2_dens(2) * 7.0_r8 / ( height_int(1) - height_int(2) ) + o2_dens(:) = species_vmr(:pver,index_O2) * fixed_species_conc(:pver,indexm) end if ! ======================= ! special height argument ! ======================= - height_arg(2:) = height_mid(:) - height_arg(1) = height_arg(2) + ( height_int(1) - height_int(2) ) + height_arg(:) = height_mid(:) - call jeuv( pver, solar_zenith_angle, o_dens, o2_dens, n2_dens, height_arg, & - euv_rates ) + call jeuv( pver, solar_zenith_angle, o_dens, o2_dens, n2_dens, height_arg, euv_rates ) end subroutine calculate_euv_rates From 1b4f238036bde1a33ea5286249ecdfbd656cddb6 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Sat, 24 Dec 2022 10:35:15 -0700 Subject: [PATCH 53/84] update tuv-x build flags --- cime_config/buildlib | 1 + 1 file changed, 1 insertion(+) diff --git a/cime_config/buildlib b/cime_config/buildlib index 4677c3de65..46cebec430 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -347,6 +347,7 @@ def _build_tuvx(caseroot, libroot, bldroot): cmake_args += "-DENABLE_TESTS=OFF " cmake_args += "-DJSON_INCLUDE_DIR={} ".format(jsoninc) cmake_args += "-DJSON_LIB={} ".format(jsonlib) + cmake_args += "-DENABLE_LOCAL_MUSICA_CORE=ON " cmake_args += "-DMUSICA_CORE_INCLUDE_DIR={} ".format(coreinc) cmake_args += "-DMUSICA_CORE_LIB={} ".format(corelib) cmake_args += "-DCMAKE_Fortran_FLAGS='{}' ".format(arg_dict["FFLAGS"]) From 790a0e1da3fdb84316c1938112a6b5e2b1295e9c Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Sat, 24 Dec 2022 13:13:30 -0800 Subject: [PATCH 54/84] Update tuvx_MOZART_TS1.json Fix O2 rate configs --- cime_config/tuvx_MOZART_TS1.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cime_config/tuvx_MOZART_TS1.json b/cime_config/tuvx_MOZART_TS1.json index 052137bfa0..dfb0aeab1d 100644 --- a/cime_config/tuvx_MOZART_TS1.json +++ b/cime_config/tuvx_MOZART_TS1.json @@ -81,10 +81,10 @@ "override bands": [ { "band": "lyman-alpha", - "value": 0.47 + "value": 0.53 }, { - "band": "schumann-runge", + "band": "schumann-runge continuum", "value": 1.0 } ] @@ -108,11 +108,11 @@ "override bands": [ { "band": "lyman-alpha", - "value": 0.53 + "value": 0.47 }, { "band": "schumann-runge continuum", - "value": 1.0 + "value": 0.0 } ] } From 4b8ca93e9acff83a2e5d2e6db8bfd503c8e3cd13 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Tue, 10 Jan 2023 17:23:54 -0700 Subject: [PATCH 55/84] output photo rate diagnostics on CAM vertical grid --- src/chemistry/mozart/mo_tuvx.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 713315a3a4..e8c18d3d4c 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -837,7 +837,7 @@ subroutine output_diagnostics( this, ncol, lchnk ) do i_diag = 1, size( diagnostics ) associate( diag => diagnostics( i_diag ) ) - call outfld( "tuvx_"//diag%name_, this%photo_rates_(:ncol,:,diag%index_), & + call outfld( "tuvx_"//diag%name_, this%photo_rates_(:ncol,pver:1:-1,diag%index_), & ncol, lchnk ) end associate end do From 22811e6e301f530f8c69fa1267a2a9eb5c4ead56 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Fri, 13 Jan 2023 09:38:17 -0700 Subject: [PATCH 56/84] fix TUV-x config filees for air radiator; add disable aerosol option for TUV-x --- cime_config/tuvx_MOZART.json | 165 ++++----- cime_config/tuvx_MOZART_TS1.json | 539 ++++++++++++++-------------- cime_config/tuvx_MOZART_TSMLT.json | 553 +++++++++++++++-------------- src/chemistry/mozart/mo_tuvx.F90 | 56 ++- 4 files changed, 675 insertions(+), 638 deletions(-) diff --git a/cime_config/tuvx_MOZART.json b/cime_config/tuvx_MOZART.json index e53fa391b2..ea3fd57c0b 100644 --- a/cime_config/tuvx_MOZART.json +++ b/cime_config/tuvx_MOZART.json @@ -41,6 +41,7 @@ { "name": "air", "type": "base", + "treat as air": true, "cross section": "air", "vertical profile": "air", "vertical profile units": "molecule cm-3" @@ -449,86 +450,88 @@ } ] }, - "__CAM aliasing": { - "default matching": "backup", - "pairs": [ - { - "to": "jpooh", - "__reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", - "from": "jch3ooh" - }, - { - "to": "jch3co3h", - "__reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", - "from": "jh2o2", - "scale by": 0.28 - }, - { - "to": "jmpan", - "__reaction": "MPAN + hv -> MCO3 + NO2", - "from": "jpan" - }, - { - "to": "jc2h5ooh", - "__reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", - "from": "jch3ooh" - }, - { - "to": "jc3h7ooh", - "__reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", - "from": "jch3ooh" - }, - { - "to": "jrooh", - "__reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", - "from": "jch3ooh" - }, - { - "to": "jxooh", - "__reaction": "XOOH + hv -> OH", - "from": "jch3ooh" - }, - { - "to": "jonitr", - "__reaction": "ONITR + hv -> NO2", - "from": "jch3cho" - }, - { - "to": "jisopooh", - "__reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", - "from": "jch3ooh" - }, - { - "to": "jmek", - "__reaction": "MEK + hv -> CH3CO3 + C2H5O2", - "from": "jacet" - }, - { - "to": "jbigald", - "__reaction": "BIGALD + hv -> .45*CO + .13*GLYOXAL + .56*HO2 + .13*CH3CO3 + .18*CH3COCHO", - "from": "jno2", - "scale by": 0.2 - }, - { - "to": "jalkooh", - "__reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", - "from": "jch3ooh" - }, - { - "to": "jmekooh", - "__reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", - "from": "jch3ooh" - }, - { - "to": "jtolooh", - "__reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", - "from": "jch3ooh" - }, - { - "to": "jterpooh", - "__reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", - "from": "jch3ooh" - } - ] + "__CAM options": { + "aliasing": { + "default matching": "backup", + "pairs": [ + { + "to": "jpooh", + "__reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jch3co3h", + "__reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", + "from": "jh2o2", + "scale by": 0.28 + }, + { + "to": "jmpan", + "__reaction": "MPAN + hv -> MCO3 + NO2", + "from": "jpan" + }, + { + "to": "jc2h5ooh", + "__reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jc3h7ooh", + "__reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", + "from": "jch3ooh" + }, + { + "to": "jrooh", + "__reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", + "from": "jch3ooh" + }, + { + "to": "jxooh", + "__reaction": "XOOH + hv -> OH", + "from": "jch3ooh" + }, + { + "to": "jonitr", + "__reaction": "ONITR + hv -> NO2", + "from": "jch3cho" + }, + { + "to": "jisopooh", + "__reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", + "from": "jch3ooh" + }, + { + "to": "jmek", + "__reaction": "MEK + hv -> CH3CO3 + C2H5O2", + "from": "jacet" + }, + { + "to": "jbigald", + "__reaction": "BIGALD + hv -> .45*CO + .13*GLYOXAL + .56*HO2 + .13*CH3CO3 + .18*CH3COCHO", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jalkooh", + "__reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", + "from": "jch3ooh" + }, + { + "to": "jmekooh", + "__reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", + "from": "jch3ooh" + }, + { + "to": "jtolooh", + "__reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", + "from": "jch3ooh" + }, + { + "to": "jterpooh", + "__reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", + "from": "jch3ooh" + } + ] + } } } diff --git a/cime_config/tuvx_MOZART_TS1.json b/cime_config/tuvx_MOZART_TS1.json index dfb0aeab1d..ef08cdb3c3 100644 --- a/cime_config/tuvx_MOZART_TS1.json +++ b/cime_config/tuvx_MOZART_TS1.json @@ -41,6 +41,7 @@ { "name": "air", "type": "base", + "treat as air": true, "cross section": "air", "vertical profile": "air", "vertical profile units": "molecule cm-3" @@ -1207,273 +1208,275 @@ } ] }, - "__CAM aliasing": { - "default matching": "backup", - "pairs": [ - { - "to": "jalknit", - "__reaction": "ALKNIT + hv -> NO2 + 0.4*CH3CHO + 0.1*CH2O + 0.25*CH3COCH3 + HO2 + 0.8*MEK", - "from": "jch3ooh" - }, - { - "to": "jpooh", - "__reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", - "from": "jch3ooh" - }, - { - "to": "jch3co3h", - "__reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", - "from": "jh2o2", - "scale by": 0.28 - }, - { - "to": "jmpan", - "__reaction": "MPAN + hv -> MCO3 + NO2", - "from": "jpan" - }, - { - "to": "jc2h5ooh", - "__reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", - "from": "jch3ooh" - }, - { - "to": "jc3h7ooh", - "__reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", - "from": "jch3ooh" - }, - { - "to": "jc6h5ooh", - "__reaction": "C6H5OOH + hv -> PHENO + OH", - "from": "jch3ooh" - }, - { - "to": "jeooh", - "__reaction": "EOOH + hv -> EO + OH", - "from": "jch3ooh" - }, - { - "to": "jrooh", - "__reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", - "from": "jch3ooh" - }, - { - "to": "jxooh", - "__reaction": "XOOH + hv -> OH", - "from": "jch3ooh" - }, - { - "to": "jonitr", - "__reaction": "ONITR + hv -> NO2", - "from": "jch3cho" - }, - { - "to": "jisopooh", - "__reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", - "from": "jch3ooh" - }, - { - "to": "jmek", - "__reaction": "MEK + hv -> CH3CO3 + C2H5O2", - "from": "jacet" - }, - { - "to": "jalkooh", - "__reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", - "from": "jch3ooh" - }, - { - "to": "jbenzooh", - "__reaction": "BENZOOH + hv -> OH + GLYOXAL + 0.5*BIGALD1 + HO2", - "from": "jch3ooh" - }, - { - "to": "jbepomuc", - "__reaction": "BEPOMUC + hv -> BIGALD1 + 1.5*HO2 + 1.5*CO", - "from": "jno2", - "scale by": 0.1 - }, - { - "to": "jbigald", - "__reaction": "BIGALD + hv -> 0.45*CO + 0.13*GLYOXAL + 0.56*HO2 + 0.13*CH3CO3 + 0.18*CH3COCHO", - "from": "jno2", - "scale by": 0.2 - }, - { - "to": "jbigald1", - "__reaction": "BIGALD1 + hv -> 0.6*MALO2 + HO2", - "from": "jno2", - "scale by": 0.14 - }, - { - "to": "jbigald2", - "__reaction": "BIGALD2 + hv -> 0.6*HO2 + 0.6*DICARBO2", - "from": "jno2", - "scale by": 0.2 - }, - { - "to": "jbigald3", - "__reaction": "BIGALD3 + hv -> 0.6*HO2 + 0.6*CO + 0.6*MDIALO2", - "from": "jno2", - "scale by": 0.2 - }, - { - "to": "jbigald4", - "__reaction": "BIGALD4 + hv -> HO2 + CO + CH3COCHO + CH3CO3", - "from": "jno2", - "scale by": 0.006 - }, - { - "to": "jbzooh", - "__reaction": "BZOOH + hv -> BZALD + OH + HO2", - "from": "jch3ooh" - }, - { - "to": "jmekooh", - "__reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", - "from": "jch3ooh" - }, - { - "to": "jtolooh", - "__reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", - "from": "jch3ooh" - }, - { - "to": "jterpooh", - "__reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", - "from": "jch3ooh" - }, - { - "to": "jhonitr", - "__reaction": "HONITR + hv -> NO2 + 0.67*HO2 + 0.33*CH3CHO + 0.33*CH2O + 0.33*CO + 0.33*GLYALD + 0.33*CH3CO3 + 0.17*HYAC + 0.17*CH3COCH3", - "from": "jch2o_a" - }, - { - "to": "jhpald", - "__reaction": "HPALD + hv -> BIGALD3 + OH + HO2", - "from": "jno2", - "scale by": 0.006 - }, - { - "to": "jisopnooh", - "__reaction": "ISOPNOOH + hv -> NO2 + HO2 + ISOPOOH", - "from": "jch3ooh" - }, - { - "to": "jnc4cho", - "__reaction": "NC4CHO + hv -> BIGALD3 + NO2 + HO2", - "from": "jch2o_a" - }, - { - "to": "jnoa", - "__reaction": "NOA + hv -> NO2 + CH2O + CH3CO3", - "from": "jch2o_a" - }, - { - "to": "jnterpooh", - "__reaction": "NTERPOOH + hv -> TERPROD1 + NO2 + OH", - "from": "jch3ooh" - }, - { - "to": "jphenooh", - "__reaction": "PHENOOH + hv -> OH + HO2 + 0.7*GLYOXAL", - "from": "jch3ooh" - }, - { - "to": "jtepomuc", - "__reaction": "TEPOMUC + hv -> 0.5*CH3CO3 + HO2 + 1.5*CO", - "from": "jno2", - "scale by": 0.1 - }, - { - "to": "jterp2ooh", - "__reaction": "TERP2OOH + hv -> OH + 0.375*CH2O + 0.3*CH3COCH3 + 0.25*CO + CO2 + TERPROD2 + HO2 + 0.25*GLYALD", - "from": "jch3ooh" - }, - { - "to": "jterpnit", - "__reaction": "TERPNIT + hv -> TERPROD1 + NO2 + HO2", - "from": "jch3ooh" - }, - { - "to": "jterprd1", - "__reaction": "TERPROD1 + hv -> HO2 + CO + TERPROD2", - "from": "jch3cho" - }, - { - "to": "jterprd2", - "__reaction": "TERPROD2 + hv -> 0.15*RO2 + 0.68*CH2O + 0.8*CO2 + 0.5*CH3COCH3 + 0.65*CH3CO3 + 1.2*HO2 + 1.7*CO", - "from": "jch3cho" - }, - { - "to": "jxylenooh", - "__reaction": "XYLENOOH + hv -> OH + HO2 + 0.34*GLYOXAL + 0.54*CH3COCHO + 0.06*BIGALD1 + 0.2*BIGALD2 + 0.15*BIGALD3 + 0.21*BIGALD4", - "from": "jch3ooh" - }, - { - "to": "jxylolooh", - "__reaction": "XYLOLOOH + hv -> OH + 0.17*GLYOXAL + 0.51*CH3COCHO + HO2", - "from": "jch3ooh" - }, - { - "to": "jsoa1_a1", - "__reaction": "soa1_a1 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa1_a2", - "__reaction": "soa1_a2 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa2_a1", - "__reaction": "soa2_a1 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa2_a2", - "__reaction": "soa2_a2 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa3_a1", - "__reaction": "soa3_a1 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa3_a2", - "__reaction": "soa3_a2 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa4_a1", - "__reaction": "soa4_a1 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa4_a2", - "__reaction": "soa4_a2 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa5_a1", - "__reaction": "soa5_a1 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa5_a2", - "__reaction": "soa5_a2 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - } - ] + "__CAM options": { + "aliasing": { + "default matching": "backup", + "pairs": [ + { + "to": "jalknit", + "__reaction": "ALKNIT + hv -> NO2 + 0.4*CH3CHO + 0.1*CH2O + 0.25*CH3COCH3 + HO2 + 0.8*MEK", + "from": "jch3ooh" + }, + { + "to": "jpooh", + "__reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jch3co3h", + "__reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", + "from": "jh2o2", + "scale by": 0.28 + }, + { + "to": "jmpan", + "__reaction": "MPAN + hv -> MCO3 + NO2", + "from": "jpan" + }, + { + "to": "jc2h5ooh", + "__reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jc3h7ooh", + "__reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", + "from": "jch3ooh" + }, + { + "to": "jc6h5ooh", + "__reaction": "C6H5OOH + hv -> PHENO + OH", + "from": "jch3ooh" + }, + { + "to": "jeooh", + "__reaction": "EOOH + hv -> EO + OH", + "from": "jch3ooh" + }, + { + "to": "jrooh", + "__reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", + "from": "jch3ooh" + }, + { + "to": "jxooh", + "__reaction": "XOOH + hv -> OH", + "from": "jch3ooh" + }, + { + "to": "jonitr", + "__reaction": "ONITR + hv -> NO2", + "from": "jch3cho" + }, + { + "to": "jisopooh", + "__reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", + "from": "jch3ooh" + }, + { + "to": "jmek", + "__reaction": "MEK + hv -> CH3CO3 + C2H5O2", + "from": "jacet" + }, + { + "to": "jalkooh", + "__reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", + "from": "jch3ooh" + }, + { + "to": "jbenzooh", + "__reaction": "BENZOOH + hv -> OH + GLYOXAL + 0.5*BIGALD1 + HO2", + "from": "jch3ooh" + }, + { + "to": "jbepomuc", + "__reaction": "BEPOMUC + hv -> BIGALD1 + 1.5*HO2 + 1.5*CO", + "from": "jno2", + "scale by": 0.1 + }, + { + "to": "jbigald", + "__reaction": "BIGALD + hv -> 0.45*CO + 0.13*GLYOXAL + 0.56*HO2 + 0.13*CH3CO3 + 0.18*CH3COCHO", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald1", + "__reaction": "BIGALD1 + hv -> 0.6*MALO2 + HO2", + "from": "jno2", + "scale by": 0.14 + }, + { + "to": "jbigald2", + "__reaction": "BIGALD2 + hv -> 0.6*HO2 + 0.6*DICARBO2", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald3", + "__reaction": "BIGALD3 + hv -> 0.6*HO2 + 0.6*CO + 0.6*MDIALO2", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald4", + "__reaction": "BIGALD4 + hv -> HO2 + CO + CH3COCHO + CH3CO3", + "from": "jno2", + "scale by": 0.006 + }, + { + "to": "jbzooh", + "__reaction": "BZOOH + hv -> BZALD + OH + HO2", + "from": "jch3ooh" + }, + { + "to": "jmekooh", + "__reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", + "from": "jch3ooh" + }, + { + "to": "jtolooh", + "__reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", + "from": "jch3ooh" + }, + { + "to": "jterpooh", + "__reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", + "from": "jch3ooh" + }, + { + "to": "jhonitr", + "__reaction": "HONITR + hv -> NO2 + 0.67*HO2 + 0.33*CH3CHO + 0.33*CH2O + 0.33*CO + 0.33*GLYALD + 0.33*CH3CO3 + 0.17*HYAC + 0.17*CH3COCH3", + "from": "jch2o_a" + }, + { + "to": "jhpald", + "__reaction": "HPALD + hv -> BIGALD3 + OH + HO2", + "from": "jno2", + "scale by": 0.006 + }, + { + "to": "jisopnooh", + "__reaction": "ISOPNOOH + hv -> NO2 + HO2 + ISOPOOH", + "from": "jch3ooh" + }, + { + "to": "jnc4cho", + "__reaction": "NC4CHO + hv -> BIGALD3 + NO2 + HO2", + "from": "jch2o_a" + }, + { + "to": "jnoa", + "__reaction": "NOA + hv -> NO2 + CH2O + CH3CO3", + "from": "jch2o_a" + }, + { + "to": "jnterpooh", + "__reaction": "NTERPOOH + hv -> TERPROD1 + NO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jphenooh", + "__reaction": "PHENOOH + hv -> OH + HO2 + 0.7*GLYOXAL", + "from": "jch3ooh" + }, + { + "to": "jtepomuc", + "__reaction": "TEPOMUC + hv -> 0.5*CH3CO3 + HO2 + 1.5*CO", + "from": "jno2", + "scale by": 0.1 + }, + { + "to": "jterp2ooh", + "__reaction": "TERP2OOH + hv -> OH + 0.375*CH2O + 0.3*CH3COCH3 + 0.25*CO + CO2 + TERPROD2 + HO2 + 0.25*GLYALD", + "from": "jch3ooh" + }, + { + "to": "jterpnit", + "__reaction": "TERPNIT + hv -> TERPROD1 + NO2 + HO2", + "from": "jch3ooh" + }, + { + "to": "jterprd1", + "__reaction": "TERPROD1 + hv -> HO2 + CO + TERPROD2", + "from": "jch3cho" + }, + { + "to": "jterprd2", + "__reaction": "TERPROD2 + hv -> 0.15*RO2 + 0.68*CH2O + 0.8*CO2 + 0.5*CH3COCH3 + 0.65*CH3CO3 + 1.2*HO2 + 1.7*CO", + "from": "jch3cho" + }, + { + "to": "jxylenooh", + "__reaction": "XYLENOOH + hv -> OH + HO2 + 0.34*GLYOXAL + 0.54*CH3COCHO + 0.06*BIGALD1 + 0.2*BIGALD2 + 0.15*BIGALD3 + 0.21*BIGALD4", + "from": "jch3ooh" + }, + { + "to": "jxylolooh", + "__reaction": "XYLOLOOH + hv -> OH + 0.17*GLYOXAL + 0.51*CH3COCHO + HO2", + "from": "jch3ooh" + }, + { + "to": "jsoa1_a1", + "__reaction": "soa1_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa1_a2", + "__reaction": "soa1_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa2_a1", + "__reaction": "soa2_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa2_a2", + "__reaction": "soa2_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa3_a1", + "__reaction": "soa3_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa3_a2", + "__reaction": "soa3_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa4_a1", + "__reaction": "soa4_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa4_a2", + "__reaction": "soa4_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa5_a1", + "__reaction": "soa5_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa5_a2", + "__reaction": "soa5_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + } + ] + } } } diff --git a/cime_config/tuvx_MOZART_TSMLT.json b/cime_config/tuvx_MOZART_TSMLT.json index 98c1e3335e..94fc63ac2c 100644 --- a/cime_config/tuvx_MOZART_TSMLT.json +++ b/cime_config/tuvx_MOZART_TSMLT.json @@ -41,6 +41,7 @@ { "name": "air", "type": "base", + "treat as air": true, "cross section": "air", "vertical profile": "air", "vertical profile units": "molecule cm-3" @@ -1207,280 +1208,282 @@ } ] }, - "__CAM aliasing": { - "default matching": "backup", - "pairs": [ - { - "to": "jno_i", - "__reaction": "NO + hv -> NOp + e", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jalknit", - "__reaction": "ALKNIT + hv -> NO2 + 0.4*CH3CHO + 0.1*CH2O + 0.25*CH3COCH3 + HO2 + 0.8*MEK", - "from": "jch3ooh" - }, - { - "to": "jpooh", - "__reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", - "from": "jch3ooh" - }, - { - "to": "jch3co3h", - "__reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", - "from": "jh2o2", - "scale by": 0.28 - }, - { - "to": "jmpan", - "__reaction": "MPAN + hv -> MCO3 + NO2", - "from": "jpan" - }, - { - "to": "jc2h5ooh", - "__reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", - "from": "jch3ooh" - }, - { - "to": "jc3h7ooh", - "__reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", - "from": "jch3ooh" - }, - { - "to": "jc6h5ooh", - "__reaction": "C6H5OOH + hv -> PHENO + OH", - "from": "jch3ooh" - }, - { - "to": "jeooh", - "__reaction": "EOOH + hv -> EO + OH", - "from": "jch3ooh" - }, - { - "to": "jrooh", - "__reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", - "from": "jch3ooh" - }, - { - "to": "jxooh", - "__reaction": "XOOH + hv -> OH", - "from": "jch3ooh" - }, - { - "to": "jonitr", - "__reaction": "ONITR + hv -> NO2", - "from": "jch3cho" - }, - { - "to": "jisopooh", - "__reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", - "from": "jch3ooh" - }, - { - "to": "jmek", - "__reaction": "MEK + hv -> CH3CO3 + C2H5O2", - "from": "jacet" - }, - { - "to": "jalkooh", - "__reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", - "from": "jch3ooh" - }, - { - "to": "jbenzooh", - "__reaction": "BENZOOH + hv -> OH + GLYOXAL + 0.5*BIGALD1 + HO2", - "from": "jch3ooh" - }, - { - "to": "jbepomuc", - "__reaction": "BEPOMUC + hv -> BIGALD1 + 1.5*HO2 + 1.5*CO", - "from": "jno2", - "scale by": 0.1 - }, - { - "to": "jbigald", - "__reaction": "BIGALD + hv -> 0.45*CO + 0.13*GLYOXAL + 0.56*HO2 + 0.13*CH3CO3 + 0.18*CH3COCHO", - "from": "jno2", - "scale by": 0.2 - }, - { - "to": "jbigald1", - "__reaction": "BIGALD1 + hv -> 0.6*MALO2 + HO2", - "from": "jno2", - "scale by": 0.14 - }, - { - "to": "jbigald2", - "__reaction": "BIGALD2 + hv -> 0.6*HO2 + 0.6*DICARBO2", - "from": "jno2", - "scale by": 0.2 - }, - { - "to": "jbigald3", - "__reaction": "BIGALD3 + hv -> 0.6*HO2 + 0.6*CO + 0.6*MDIALO2", - "from": "jno2", - "scale by": 0.2 - }, - { - "to": "jbigald4", - "__reaction": "BIGALD4 + hv -> HO2 + CO + CH3COCHO + CH3CO3", - "from": "jno2", - "scale by": 0.006 - }, - { - "to": "jbzooh", - "__reaction": "BZOOH + hv -> BZALD + OH + HO2", - "from": "jch3ooh" - }, - { - "to": "jmekooh", - "__reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", - "from": "jch3ooh" - }, - { - "to": "jtolooh", - "__reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", - "from": "jch3ooh" - }, - { - "to": "jterpooh", - "__reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", - "from": "jch3ooh" - }, - { - "to": "jhonitr", - "__reaction": "HONITR + hv -> NO2 + 0.67*HO2 + 0.33*CH3CHO + 0.33*CH2O + 0.33*CO + 0.33*GLYALD + 0.33*CH3CO3 + 0.17*HYAC + 0.17*CH3COCH3", - "from": "jch2o_a" - }, - { - "to": "jhpald", - "__reaction": "HPALD + hv -> BIGALD3 + OH + HO2", - "from": "jno2", - "scale by": 0.006 - }, - { - "to": "jisopnooh", - "__reaction": "ISOPNOOH + hv -> NO2 + HO2 + ISOPOOH", - "from": "jch3ooh" - }, - { - "to": "jnc4cho", - "__reaction": "NC4CHO + hv -> BIGALD3 + NO2 + HO2", - "from": "jch2o_a" - }, - { - "to": "jnoa", - "__reaction": "NOA + hv -> NO2 + CH2O + CH3CO3", - "from": "jch2o_a" - }, - { - "to": "jnterpooh", - "__reaction": "NTERPOOH + hv -> TERPROD1 + NO2 + OH", - "from": "jch3ooh" - }, - { - "to": "jphenooh", - "__reaction": "PHENOOH + hv -> OH + HO2 + 0.7*GLYOXAL", - "from": "jch3ooh" - }, - { - "to": "jtepomuc", - "__reaction": "TEPOMUC + hv -> 0.5*CH3CO3 + HO2 + 1.5*CO", - "from": "jno2", - "scale by": 0.1 - }, - { - "to": "jterp2ooh", - "__reaction": "TERP2OOH + hv -> OH + 0.375*CH2O + 0.3*CH3COCH3 + 0.25*CO + CO2 + TERPROD2 + HO2 + 0.25*GLYALD", - "from": "jch3ooh" - }, - { - "to": "jterpnit", - "__reaction": "TERPNIT + hv -> TERPROD1 + NO2 + HO2", - "from": "jch3ooh" - }, - { - "to": "jterprd1", - "__reaction": "TERPROD1 + hv -> HO2 + CO + TERPROD2", - "from": "jch3cho" - }, - { - "to": "jterprd2", - "__reaction": "TERPROD2 + hv -> 0.15*RO2 + 0.68*CH2O + 0.8*CO2 + 0.5*CH3COCH3 + 0.65*CH3CO3 + 1.2*HO2 + 1.7*CO", - "from": "jch3cho" - }, - { - "to": "jxylenooh", - "__reaction": "XYLENOOH + hv -> OH + HO2 + 0.34*GLYOXAL + 0.54*CH3COCHO + 0.06*BIGALD1 + 0.2*BIGALD2 + 0.15*BIGALD3 + 0.21*BIGALD4", - "from": "jch3ooh" - }, - { - "to": "jxylolooh", - "__reaction": "XYLOLOOH + hv -> OH + 0.17*GLYOXAL + 0.51*CH3COCHO + HO2", - "from": "jch3ooh" - }, - { - "to": "jsoa1_a1", - "__reaction": "soa1_a1 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa1_a2", - "__reaction": "soa1_a2 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa2_a1", - "__reaction": "soa2_a1 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa2_a2", - "__reaction": "soa2_a2 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa3_a1", - "__reaction": "soa3_a1 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa3_a2", - "__reaction": "soa3_a2 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa4_a1", - "__reaction": "soa4_a1 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa4_a2", - "__reaction": "soa4_a2 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa5_a1", - "__reaction": "soa5_a1 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa5_a2", - "__reaction": "soa5_a2 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - } - ] + "__CAM options": { + "aliasing": { + "default matching": "backup", + "pairs": [ + { + "to": "jno_i", + "__reaction": "NO + hv -> NOp + e", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jalknit", + "__reaction": "ALKNIT + hv -> NO2 + 0.4*CH3CHO + 0.1*CH2O + 0.25*CH3COCH3 + HO2 + 0.8*MEK", + "from": "jch3ooh" + }, + { + "to": "jpooh", + "__reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jch3co3h", + "__reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", + "from": "jh2o2", + "scale by": 0.28 + }, + { + "to": "jmpan", + "__reaction": "MPAN + hv -> MCO3 + NO2", + "from": "jpan" + }, + { + "to": "jc2h5ooh", + "__reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jc3h7ooh", + "__reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", + "from": "jch3ooh" + }, + { + "to": "jc6h5ooh", + "__reaction": "C6H5OOH + hv -> PHENO + OH", + "from": "jch3ooh" + }, + { + "to": "jeooh", + "__reaction": "EOOH + hv -> EO + OH", + "from": "jch3ooh" + }, + { + "to": "jrooh", + "__reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", + "from": "jch3ooh" + }, + { + "to": "jxooh", + "__reaction": "XOOH + hv -> OH", + "from": "jch3ooh" + }, + { + "to": "jonitr", + "__reaction": "ONITR + hv -> NO2", + "from": "jch3cho" + }, + { + "to": "jisopooh", + "__reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", + "from": "jch3ooh" + }, + { + "to": "jmek", + "__reaction": "MEK + hv -> CH3CO3 + C2H5O2", + "from": "jacet" + }, + { + "to": "jalkooh", + "__reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", + "from": "jch3ooh" + }, + { + "to": "jbenzooh", + "__reaction": "BENZOOH + hv -> OH + GLYOXAL + 0.5*BIGALD1 + HO2", + "from": "jch3ooh" + }, + { + "to": "jbepomuc", + "__reaction": "BEPOMUC + hv -> BIGALD1 + 1.5*HO2 + 1.5*CO", + "from": "jno2", + "scale by": 0.1 + }, + { + "to": "jbigald", + "__reaction": "BIGALD + hv -> 0.45*CO + 0.13*GLYOXAL + 0.56*HO2 + 0.13*CH3CO3 + 0.18*CH3COCHO", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald1", + "__reaction": "BIGALD1 + hv -> 0.6*MALO2 + HO2", + "from": "jno2", + "scale by": 0.14 + }, + { + "to": "jbigald2", + "__reaction": "BIGALD2 + hv -> 0.6*HO2 + 0.6*DICARBO2", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald3", + "__reaction": "BIGALD3 + hv -> 0.6*HO2 + 0.6*CO + 0.6*MDIALO2", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald4", + "__reaction": "BIGALD4 + hv -> HO2 + CO + CH3COCHO + CH3CO3", + "from": "jno2", + "scale by": 0.006 + }, + { + "to": "jbzooh", + "__reaction": "BZOOH + hv -> BZALD + OH + HO2", + "from": "jch3ooh" + }, + { + "to": "jmekooh", + "__reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", + "from": "jch3ooh" + }, + { + "to": "jtolooh", + "__reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", + "from": "jch3ooh" + }, + { + "to": "jterpooh", + "__reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", + "from": "jch3ooh" + }, + { + "to": "jhonitr", + "__reaction": "HONITR + hv -> NO2 + 0.67*HO2 + 0.33*CH3CHO + 0.33*CH2O + 0.33*CO + 0.33*GLYALD + 0.33*CH3CO3 + 0.17*HYAC + 0.17*CH3COCH3", + "from": "jch2o_a" + }, + { + "to": "jhpald", + "__reaction": "HPALD + hv -> BIGALD3 + OH + HO2", + "from": "jno2", + "scale by": 0.006 + }, + { + "to": "jisopnooh", + "__reaction": "ISOPNOOH + hv -> NO2 + HO2 + ISOPOOH", + "from": "jch3ooh" + }, + { + "to": "jnc4cho", + "__reaction": "NC4CHO + hv -> BIGALD3 + NO2 + HO2", + "from": "jch2o_a" + }, + { + "to": "jnoa", + "__reaction": "NOA + hv -> NO2 + CH2O + CH3CO3", + "from": "jch2o_a" + }, + { + "to": "jnterpooh", + "__reaction": "NTERPOOH + hv -> TERPROD1 + NO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jphenooh", + "__reaction": "PHENOOH + hv -> OH + HO2 + 0.7*GLYOXAL", + "from": "jch3ooh" + }, + { + "to": "jtepomuc", + "__reaction": "TEPOMUC + hv -> 0.5*CH3CO3 + HO2 + 1.5*CO", + "from": "jno2", + "scale by": 0.1 + }, + { + "to": "jterp2ooh", + "__reaction": "TERP2OOH + hv -> OH + 0.375*CH2O + 0.3*CH3COCH3 + 0.25*CO + CO2 + TERPROD2 + HO2 + 0.25*GLYALD", + "from": "jch3ooh" + }, + { + "to": "jterpnit", + "__reaction": "TERPNIT + hv -> TERPROD1 + NO2 + HO2", + "from": "jch3ooh" + }, + { + "to": "jterprd1", + "__reaction": "TERPROD1 + hv -> HO2 + CO + TERPROD2", + "from": "jch3cho" + }, + { + "to": "jterprd2", + "__reaction": "TERPROD2 + hv -> 0.15*RO2 + 0.68*CH2O + 0.8*CO2 + 0.5*CH3COCH3 + 0.65*CH3CO3 + 1.2*HO2 + 1.7*CO", + "from": "jch3cho" + }, + { + "to": "jxylenooh", + "__reaction": "XYLENOOH + hv -> OH + HO2 + 0.34*GLYOXAL + 0.54*CH3COCHO + 0.06*BIGALD1 + 0.2*BIGALD2 + 0.15*BIGALD3 + 0.21*BIGALD4", + "from": "jch3ooh" + }, + { + "to": "jxylolooh", + "__reaction": "XYLOLOOH + hv -> OH + 0.17*GLYOXAL + 0.51*CH3COCHO + HO2", + "from": "jch3ooh" + }, + { + "to": "jsoa1_a1", + "__reaction": "soa1_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa1_a2", + "__reaction": "soa1_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa2_a1", + "__reaction": "soa2_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa2_a2", + "__reaction": "soa2_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa3_a1", + "__reaction": "soa3_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa3_a2", + "__reaction": "soa3_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa4_a1", + "__reaction": "soa4_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa4_a2", + "__reaction": "soa4_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa5_a1", + "__reaction": "soa5_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa5_a2", + "__reaction": "soa5_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + } + ] + } } } diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index e8c18d3d4c..031591f4e0 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -241,22 +241,30 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) class(core_t), pointer :: core character, allocatable :: buffer(:) type(string_t) :: config_path - type(config_t) :: tuvx_config, map_config + type(config_t) :: tuvx_config, cam_config, map_config type(map_t) :: map class(grid_t), pointer :: height class(grid_warehouse_t), pointer :: cam_grids class(profile_warehouse_t), pointer :: cam_profiles class(radiator_warehouse_t), pointer :: cam_radiators integer :: pack_size, pos, i_core, i_err + logical :: disable_aerosols + type(string_t) :: required_keys(1), optional_keys(1) logical, save :: is_initialized = .false. if( .not. tuvx_active ) return if( is_initialized ) return is_initialized = .true. + if( is_main_task ) write(iulog,*) "Beginning TUV-x Initialization" + config_path = trim(tuvx_config_path) - if( is_main_task ) call log_initialization( ) + ! =============================== + ! CAM TUV-x configuration options + ! =============================== + required_keys(1) = "aliasing" + optional_keys(1) = "disable aerosols" #ifndef HAVE_MPI call assert_msg( 113937299, is_main_task, "Multiple tasks present without " & @@ -267,7 +275,6 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) ! set the maximum solar zenith angle to calculate photo rates for ! =============================================================== max_sza = max_solar_zenith_angle - if( is_main_task ) write(iulog,*) "TUV-x max solar zenith angle [degrees]:", max_sza if( max_sza <= 0.0_r8 .or. max_sza > 180.0_r8 ) then call endrun( "TUV-x: max solar zenith angle must be between 0 and 180 degress" ) end if @@ -290,19 +297,27 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) ! ================================================================== if( is_main_task ) then call tuvx_config%from_file( config_path%to_char( ) ) - call tuvx_config%get( "__CAM aliasing", map_config, my_name ) + call tuvx_config%get( "__CAM options", cam_config, my_name ) + call assert_msg( 973680295, & + cam_config%validate( required_keys, optional_keys ), & + "Bad configuration for CAM TUV-x options." ) + call cam_config%get( "disable aerosols", disable_aerosols, my_name, & + default = .false. ) + call cam_config%get( "aliasing", map_config, my_name ) core => core_t( config_path, cam_grids, cam_profiles, cam_radiators ) call set_photo_rate_map( core, map_config, do_euv, do_jno, jno_index, map ) pack_size = core%pack_size( mpicom ) + & map%pack_size( mpicom ) + & musica_mpi_pack_size( do_jno, mpicom ) + & - musica_mpi_pack_size( jno_index, mpicom ) + musica_mpi_pack_size( jno_index, mpicom ) + & + musica_mpi_pack_size( disable_aerosols, mpicom ) allocate( buffer( pack_size ) ) pos = 0 call core%mpi_pack( buffer, pos, mpicom ) call map%mpi_pack( buffer, pos, mpicom ) - call musica_mpi_pack( buffer, pos, do_jno, mpicom ) - call musica_mpi_pack( buffer, pos, jno_index, mpicom ) + call musica_mpi_pack( buffer, pos, do_jno, mpicom ) + call musica_mpi_pack( buffer, pos, jno_index, mpicom ) + call musica_mpi_pack( buffer, pos, disable_aerosols, mpicom ) deallocate( core ) end if @@ -333,13 +348,15 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) pos = 0 call tuvx%core_%mpi_unpack( buffer, pos, mpicom ) call tuvx%photo_rate_map_%mpi_unpack( buffer, pos, mpicom ) - call musica_mpi_unpack( buffer, pos, do_jno, mpicom ) - call musica_mpi_unpack( buffer, pos, jno_index, mpicom ) + call musica_mpi_unpack( buffer, pos, do_jno, mpicom ) + call musica_mpi_unpack( buffer, pos, jno_index, mpicom ) + call musica_mpi_unpack( buffer, pos, disable_aerosols, mpicom ) ! =================================================================== ! Set up connections between CAM and TUV-x input data for each thread ! =================================================================== - call create_updaters( tuvx, cam_grids, cam_profiles, cam_radiators ) + call create_updaters( tuvx, cam_grids, cam_profiles, cam_radiators, & + disable_aerosols ) ! =============================================================== ! Create a working array for calculated photolysis rate constants @@ -390,6 +407,8 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) ! ============================================ call initialize_diagnostics( tuvx_ptrs( 1 ) ) + if( is_main_task ) call log_initialization( ) + end subroutine tuvx_init !================================================================================================ @@ -618,7 +637,7 @@ subroutine log_initialization( ) is_main_task => masterproc if( is_main_task ) then - write(iulog,*) "Initializing TUV-x" + write(iulog,*) "Initialized TUV-x" #ifdef HAVE_MPI write(iulog,*) " - with MPI support on task "//trim( to_char( main_task ) ) #else @@ -637,6 +656,14 @@ subroutine log_initialization( ) else write(iulog,*) " - without on-line aerosols" end if + if( index_N2 > 0 ) write(iulog,*) " - including N2" + if( index_O > 0 ) write(iulog,*) " - including O" + if( index_O2 > 0 ) write(iulog,*) " - including O2" + if( index_O3 > 0 ) write(iulog,*) " - including O3" + if( index_NO > 0 ) write(iulog,*) " - including NO" + if( do_euv ) write(iulog,*) " - doing Extreme-UV calculations" + if( do_jno ) write(iulog,*) " - including special jno rate calculation" + write(iulog,*) " - max solar zenith angle [degrees]:", max_sza end if end subroutine log_initialization @@ -963,7 +990,7 @@ end function get_cam_profiles !================================================================================================ !----------------------------------------------------------------------- - ! Creates and loads a radiator warehouse with radiators that CAM + ! Creates and loads a radiator warehouse with radiators that CAM will ! update at runtime !----------------------------------------------------------------------- function get_cam_radiators( grids ) result( radiators ) @@ -1008,7 +1035,7 @@ end function get_cam_radiators ! for runtime access of CAM data ! !----------------------------------------------------------------------- - subroutine create_updaters( this, grids, profiles, radiators ) + subroutine create_updaters( this, grids, profiles, radiators, disable_aerosols ) use modal_aer_opt, only : modal_aer_opt_init use ppgrid, only : pcols ! maximum number of columns @@ -1028,6 +1055,7 @@ subroutine create_updaters( this, grids, profiles, radiators ) class(grid_warehouse_t), intent(in) :: grids class(profile_warehouse_t), intent(in) :: profiles class(radiator_warehouse_t), intent(in) :: radiators + logical, intent(in) :: disable_aerosols class(grid_t), pointer :: height, wavelength class(profile_t), pointer :: host_profile @@ -1105,7 +1133,7 @@ subroutine create_updaters( this, grids, profiles, radiators ) ! intialize the aerosol optics module ! ==================================================================== call rad_cnst_get_info( 0, nmodes = n_modes ) - if( n_modes > 0 .and. .not. aerosol_exists ) then + if( n_modes > 0 .and. .not. aerosol_exists .and. .not. disable_aerosols ) then aerosol_exists = .true. call modal_aer_opt_init( ) else From 770f6ccd578b8fa8998092b0b1141010e9b2d5bd Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Thu, 19 Jan 2023 19:25:22 -0700 Subject: [PATCH 57/84] improve et flux edge calcs; use scale height for 02, 03 --- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 19 ++++++------- src/chemistry/mozart/mo_tuvx.F90 | 30 ++++++++++---------- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index 3675d0d1cd..5896149ec6 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -785,15 +785,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & call shr_orb_decl( calday, eccen, mvelpp, lambm0, obliqr , & delta, esfact ) - if (.not.tuvx_active) then - !----------------------------------------------------------------- - ! ... lookup the photolysis rates from table - !----------------------------------------------------------------- - call table_photo( reaction_rates, pmid, pdel, tfld, zmid, zint, & - col_dens, zen_angle, asdir, cwat, cldfr, & - esfact, vmr, invariants, ncol, lchnk, pbuf ) - - else +! if (tuvx_active) then !----------------------------------------------------------------- ! ... get calculated photolysis rates from TUV-x !----------------------------------------------------------------- @@ -801,7 +793,14 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & tfld, ts, invariants, vmr, col_delta, & asdir, zen_angle, esfact, & reaction_rates(:,:,1:phtcnt) ) - endif +! else + !----------------------------------------------------------------- + ! ... lookup the photolysis rates from table + !----------------------------------------------------------------- + call table_photo( reaction_rates, pmid, pdel, tfld, zmid, zint, & + col_dens, zen_angle, asdir, cwat, cldfr, & + esfact, vmr, invariants, ncol, lchnk, pbuf ) +! endif do i = 1,phtcnt call outfld( tag_names(i), reaction_rates(:ncol,:,rxt_tag_map(i)), ncol, lchnk ) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 031591f4e0..889913ed93 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -1273,7 +1273,7 @@ subroutine set_et_flux( this ) class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator real(r8) :: et_flux_orig(nbins) - integer :: n_tuvx_bins + integer :: n_tuvx_bins, i_bin ! =============================================== ! regrid normalized flux to TUV-x wavelength grid @@ -1293,8 +1293,18 @@ subroutine set_et_flux( this ) ! ==================================== ! estimate unused edge values for flux ! ==================================== - this%wavelength_values_(1) = this%wavelength_mid_values_(1) - this%wavelength_values_(2:n_tuvx_bins+1) = this%wavelength_mid_values_(1:n_tuvx_bins) + this%wavelength_values_(1) = this%wavelength_mid_values_(1) - & + ( this%wavelength_mid_values_(2) - & + this%wavelength_mid_values_(1) ) * 0.5_r8 + do i_bin = 2, n_tuvx_bins + this%wavelength_values_(i_bin) = this%wavelength_mid_values_(i_bin-1) + & + ( this%wavelength_mid_values_(i_bin) - & + this%wavelength_mid_values_(i_bin-1) ) * 0.5_r8 + end do + this%wavelength_values_(n_tuvx_bins+1) = & + this%wavelength_mid_values_(n_tuvx_bins) + & + ( this%wavelength_mid_values_(n_tuvx_bins) - & + this%wavelength_mid_values_(n_tuvx_bins-1) ) * 0.5_r8 ! ============================ ! update TUV-x ET flux profile @@ -1370,16 +1380,11 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species else edges(:) = 0.0_r8 end if - if( nabscol >= 2 ) then - exo_val = exo_column_conc(i_col,0,2) - else - exo_val = 0.0_r8 - end if densities(1:pver) = this%height_delta_(1:pver) * km2cm * & sqrt(edges(1:pver)) * sqrt(edges(2:pver+1)) call this%profiles_( PROFILE_INDEX_O2 )%update( & edge_values = edges, layer_densities = densities, & - exo_density = exo_val ) + scale_height = 7.0_r8 ) ! ========== ! O3 profile @@ -1395,16 +1400,11 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species else edges(:) = 0.0_r8 end if - if( nabscol >= 1 ) then - exo_val = exo_column_conc(i_col,0,1) - else - exo_val = 0.0_r8 - end if densities(1:pver) = this%height_delta_(1:pver) * km2cm * & sqrt(edges(1:pver)) * sqrt(edges(2:pver+1)) call this%profiles_( PROFILE_INDEX_O3 )%update( & edge_values = edges, layer_densities = densities, & - exo_density = exo_val ) + scale_height = 7.0_r8 ) ! =============== ! aerosol profile From 620b8d4296c9b1a1df276a4b08e5957d37402e81 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Wed, 25 Jan 2023 17:55:21 -0700 Subject: [PATCH 58/84] fix index offset in photo rate profiles --- cime_config/tuvx_MOZART_TS1.json | 5 +++-- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 5 +++-- src/chemistry/mozart/mo_tuvx.F90 | 17 +++++++++++------ 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/cime_config/tuvx_MOZART_TS1.json b/cime_config/tuvx_MOZART_TS1.json index ef08cdb3c3..edf0ca12af 100644 --- a/cime_config/tuvx_MOZART_TS1.json +++ b/cime_config/tuvx_MOZART_TS1.json @@ -9,7 +9,8 @@ ], "radiative transfer": { "solver": { - "type": "delta eddington" + "type": "discrete ordinate", + "number of streams": 4 }, "cross sections": [ { @@ -402,7 +403,7 @@ }, "quantum yield": { "type": "base", - "constant value": 1.0 + "constant value": 0.5 } }, { diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index 5896149ec6..82542a5187 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -250,7 +250,8 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & use chem_mods, only : nabscol, nfs, indexm, clscnt4 use physconst, only : rga use mo_photo, only : set_ub_col, setcol, table_photo - use mo_tuvx, only : tuvx_get_photo_rates, tuvx_active + use mo_tuvx, only : tuvx_get_photo_rates, tuvx_active, & + tuvx_is_first_time_step use mo_exp_sol, only : exp_sol use mo_imp_sol, only : imp_sol use mo_setrxt, only : setrxt @@ -785,7 +786,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & call shr_orb_decl( calday, eccen, mvelpp, lambm0, obliqr , & delta, esfact ) -! if (tuvx_active) then +! if (tuvx_active .and. .not. tuvx_is_first_time_step) then !----------------------------------------------------------------- ! ... get calculated photolysis rates from TUV-x !----------------------------------------------------------------- diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 889913ed93..9811d6a56c 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -23,6 +23,7 @@ module mo_tuvx public :: tuvx_get_photo_rates public :: tuvx_finalize public :: tuvx_active + public :: tuvx_is_first_time_step ! Inidices for grid updaters integer, parameter :: NUM_GRIDS = 2 ! number of grids that CAM will update at runtime @@ -79,7 +80,7 @@ module mo_tuvx ! TODO how should these paths be set and communicated to this wrapper? character(len=*), parameter :: wavelength_config_path = & - "data/grids/wavelength/combined.grid" + "data/grids/wavelength/cam.csv" logical, parameter :: enable_diagnostics = .true. ! TUV-x calculator for each OMP thread @@ -120,6 +121,7 @@ module mo_tuvx ! namelist options character(len=cl) :: tuvx_config_path = 'NONE' ! absolute path to TUVX configuration file logical, protected :: tuvx_active = .false. + logical, protected :: tuvx_is_first_time_step = .true. !================================================================================================ contains @@ -372,7 +374,6 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) end associate end do - deallocate( cam_grids ) deallocate( cam_profiles ) deallocate( cam_radiators ) @@ -419,6 +420,10 @@ end subroutine tuvx_init subroutine tuvx_timestep_init( ) integer :: i_thread + integer, save :: n_time_step = 0 + + n_time_step = n_time_step + 1 + if( n_time_step > 1 ) tuvx_is_first_time_step = .false. if( .not. tuvx_active ) return @@ -528,7 +533,7 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & species_vmr(i_col,:,:), & height_mid(i_col,:), & height_int(i_col,:), & - tuvx%photo_rates_(i_col,:,euv_begin:euv_end) ) + tuvx%photo_rates_(i_col,2:pver+1,euv_begin:euv_end) ) end associate end if @@ -541,7 +546,7 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & fixed_species_conc(i_col,:,:), & species_vmr(i_col,:,:), & height_int(i_col,:), & - tuvx%photo_rates_(i_col,:,jno_index) ) + tuvx%photo_rates_(i_col,2:pver+1,jno_index) ) end if end do @@ -550,7 +555,7 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & ! ============================================ do i_col = 1, ncol do i_level = 1, pver - call tuvx%photo_rate_map_%apply( tuvx%photo_rates_(i_col,pver-i_level+1,:), & + call tuvx%photo_rate_map_%apply( tuvx%photo_rates_(i_col,pver-i_level+2,:), & photolysis_rates(i_col,i_level,:) ) end do end do @@ -864,7 +869,7 @@ subroutine output_diagnostics( this, ncol, lchnk ) do i_diag = 1, size( diagnostics ) associate( diag => diagnostics( i_diag ) ) - call outfld( "tuvx_"//diag%name_, this%photo_rates_(:ncol,pver:1:-1,diag%index_), & + call outfld( "tuvx_"//diag%name_, this%photo_rates_(:ncol,pver+1:2:-1,diag%index_), & ncol, lchnk ) end associate end do From d69a00acb2e2d473b79955443b1b9831ca8e9078 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Fri, 27 Jan 2023 16:21:12 -0700 Subject: [PATCH 59/84] minor profile calculation changes --- cime_config/tuvx_MOZART_TS1.json | 10 +++++++--- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 6 +++--- src/chemistry/mozart/mo_photo.F90 | 4 +++- src/chemistry/mozart/mo_tuvx.F90 | 8 ++++---- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/cime_config/tuvx_MOZART_TS1.json b/cime_config/tuvx_MOZART_TS1.json index edf0ca12af..d50e4c63a0 100644 --- a/cime_config/tuvx_MOZART_TS1.json +++ b/cime_config/tuvx_MOZART_TS1.json @@ -32,7 +32,8 @@ "netcdf files": [ { "file path": "data/cross_sections/O2_1.nc", - "lower extrapolation": { "type": "boundary" } + "lower extrapolation": { "type": "boundary" }, + "interpolator": { "type": "fractional target" } } ], "type": "base" @@ -72,7 +73,8 @@ "netcdf files": [ { "file path": "data/cross_sections/O2_1.nc", - "lower extrapolation": { "type": "boundary" } + "lower extrapolation": { "type": "boundary" }, + "interpolator": { "type": "fractional target" } } ], "type": "base" @@ -99,7 +101,8 @@ "netcdf files": [ { "file path": "data/cross_sections/O2_1.nc", - "lower extrapolation": { "type": "boundary" } + "lower extrapolation": { "type": "boundary" }, + "interpolator": { "type": "fractional target" } } ], "type": "base" @@ -1210,6 +1213,7 @@ ] }, "__CAM options": { + "disable aerosols": true, "aliasing": { "default matching": "backup", "pairs": [ diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index 82542a5187..f2da8017cb 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -786,7 +786,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & call shr_orb_decl( calday, eccen, mvelpp, lambm0, obliqr , & delta, esfact ) -! if (tuvx_active .and. .not. tuvx_is_first_time_step) then + if (tuvx_active .and. .not. tuvx_is_first_time_step) then !----------------------------------------------------------------- ! ... get calculated photolysis rates from TUV-x !----------------------------------------------------------------- @@ -794,14 +794,14 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & tfld, ts, invariants, vmr, col_delta, & asdir, zen_angle, esfact, & reaction_rates(:,:,1:phtcnt) ) -! else + else !----------------------------------------------------------------- ! ... lookup the photolysis rates from table !----------------------------------------------------------------- call table_photo( reaction_rates, pmid, pdel, tfld, zmid, zint, & col_dens, zen_angle, asdir, cwat, cldfr, & esfact, vmr, invariants, ncol, lchnk, pbuf ) -! endif + endif do i = 1,phtcnt call outfld( tag_names(i), reaction_rates(:ncol,:,rxt_tag_map(i)), ncol, lchnk ) diff --git a/src/chemistry/mozart/mo_photo.F90 b/src/chemistry/mozart/mo_photo.F90 index 5ef22df875..c7224c2c06 100644 --- a/src/chemistry/mozart/mo_photo.F90 +++ b/src/chemistry/mozart/mo_photo.F90 @@ -825,7 +825,9 @@ subroutine table_photo( photos, pmid, pdel, temper, zmid, zint, & !----------------------------------------------------------------- call cloud_mod( zen_angle(i), cld_line, lwc_line, fac1, srf_alb(i), & eff_alb, cld_mult ) - cld_mult(:) = esfact * cld_mult(:) + cld_mult(:) = esfact ! * cld_mult(:) + eff_alb(:) = 0.0_r8 + eff_alb(pver) = srf_alb(i) !----------------------------------------------------------------- ! ... long wave length component diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 9811d6a56c..a374d3b538 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -1315,7 +1315,7 @@ subroutine set_et_flux( this ) ! update TUV-x ET flux profile ! ============================ call this%profiles_( PROFILE_INDEX_ET_FLUX )%update( & - mid_point_values = this%wavelength_mid_values_, & + !mid_point_values = this%wavelength_mid_values_, & edge_values = this%wavelength_values_) ! ====================================================================== @@ -1366,7 +1366,7 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species edges(1) = fixed_species_conc(i_col,pver,indexm) edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,indexm) densities(1:pver) = this%height_delta_(1:pver) * km2cm * & - sqrt(edges(1:pver)) * sqrt(edges(2:pver+1)) + ( edges(1:pver) + edges(2:pver+1) ) * 0.5_r8 call this%profiles_( PROFILE_INDEX_AIR )%update( & edge_values = edges, layer_densities = densities, & scale_height = 8.01_r8 ) ! scale height in [km] @@ -1386,7 +1386,7 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species edges(:) = 0.0_r8 end if densities(1:pver) = this%height_delta_(1:pver) * km2cm * & - sqrt(edges(1:pver)) * sqrt(edges(2:pver+1)) + ( edges(1:pver) + edges(2:pver+1) ) * 0.5_r8 call this%profiles_( PROFILE_INDEX_O2 )%update( & edge_values = edges, layer_densities = densities, & scale_height = 7.0_r8 ) @@ -1406,7 +1406,7 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species edges(:) = 0.0_r8 end if densities(1:pver) = this%height_delta_(1:pver) * km2cm * & - sqrt(edges(1:pver)) * sqrt(edges(2:pver+1)) + ( edges(1:pver) + edges(2:pver+1) ) * 0.5_r8 call this%profiles_( PROFILE_INDEX_O3 )%update( & edge_values = edges, layer_densities = densities, & scale_height = 7.0_r8 ) From 1e8d9fa45471f4f697b0d0d83c6a8d2e7a2f38a2 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Mon, 30 Jan 2023 12:11:12 -0700 Subject: [PATCH 60/84] add thin top layer to TUV-x grid --- src/chemistry/mozart/mo_tuvx.F90 | 76 +++++++++++++------ .../pp_trop_strat_mam4_vbs/mo_imp_sol.F90 | 5 +- 2 files changed, 57 insertions(+), 24 deletions(-) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index a374d3b538..2ab9895e5b 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -96,7 +96,7 @@ module mo_tuvx type(grid_updater_t) :: grids_(NUM_GRIDS) ! grid updaters type(profile_updater_t) :: profiles_(NUM_PROFILES) ! profile updaters type(radiator_updater_t) :: radiators_(NUM_RADIATORS) ! radiator updaters - real(r8) :: height_delta_(pver) ! change in height in each + real(r8) :: height_delta_(pver+1) ! change in height in each ! vertical layer (km) real(r8), allocatable :: wavelength_edges_(:) ! TUV-x wavelength bin edges (nm) real(r8), allocatable :: wavelength_values_(:) ! Working array for interface values @@ -903,7 +903,7 @@ function get_cam_grids( wavelength_path ) result( grids ) ! ========================= ! heights above the surface ! ========================= - host_grid => grid_from_host_t( "height", "km", pver ) + host_grid => grid_from_host_t( "height", "km", pver+1 ) call grids%add( host_grid ) deallocate( host_grid ) @@ -1168,13 +1168,17 @@ end subroutine create_updaters ! TUV-x heights are "bottom-up" and require atmospheric constituent ! concentrations at interfaces. Therefore, CAM mid-points are used as ! TUV-x grid interfaces, with an additional layer introduced between - ! the surface and the lowest CAM mid-point. + ! the surface and the lowest CAM mid-point, and a thin layer at the + ! top of the TUV-x grid to hold above-column species densities. ! ! ---- (interface) ===== (mid-point) ! ! CAM TUV-x - ! ------(top)------ i_int = 1 (exo values) - ! ================= i_mid = 1 -------(top)------ i_int = pver + 1 + ! ************************ (exo values) ***************************** + ! ------(top)------ i_int = 1 + ! -------(top)------ i_int = pver + 2 + ! ================== i_mid = pver + 1 + ! ================= i_mid = 1 ------------------ i_int = pver + 1 ! ----------------- i_int = 2 ================== i_mid = pver ! ------------------ i_int = pver ! || @@ -1197,16 +1201,18 @@ subroutine set_heights( this, i_col, ncol, height_mid, height_int ) real(r8), intent(in) :: height_int(ncol,pver+1) ! height above the surface at interfaces (km) integer :: i_level - real(r8) :: edges(pver+1) - real(r8) :: mid_points(pver) + real(r8) :: edges(pver+2) + real(r8) :: mid_points(pver+1) edges(1) = height_int(i_col,pver+1) edges(2:pver+1) = height_mid(i_col,pver:1:-1) + edges(pver+2) = edges(pver+1) + 1.0e-10_r8 ! thin layer at the top to hold exo values mid_points(1) = ( height_mid(i_col,pver) - height_int(i_col,pver+1) ) * 0.5_r8 & + height_int(i_col,pver+1) mid_points(2:pver) = height_int(i_col,pver:2:-1) + mid_points(pver+1) = edges(pver+1) + 0.5e-10_r8 call this%grids_( GRID_INDEX_HEIGHT )%update( edges = edges, mid_points = mid_points ) - this%height_delta_(1:pver) = edges(2:pver+1) - edges(1:pver) + this%height_delta_(1:pver+1) = edges(2:pver+2) - edges(1:pver+1) end subroutine set_heights @@ -1227,10 +1233,11 @@ subroutine set_temperatures( this, i_col, temperature_mid, surface_temperature ) real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) - real(r8) :: edges(pver+1) + real(r8) :: edges(pver+2) edges(1) = surface_temperature(i_col) edges(2:pver+1) = temperature_mid(i_col,pver:1:-1) + edges(pver+2) = temperature_mid(i_col,1) ! Use upper mid-point temperature for top edge call this%profiles_( PROFILE_INDEX_TEMPERATURE )%update( edge_values = edges ) end subroutine set_temperatures @@ -1356,7 +1363,7 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! above column densities ! (molecule cm-3) - real(r8) :: edges(pver+1), densities(pver) + real(r8) :: edges(pver+2), densities(pver+1) real(r8) :: exo_val real(r8), parameter :: km2cm = 1.0e5 ! conversion from km to cm @@ -1365,8 +1372,9 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species ! =========== edges(1) = fixed_species_conc(i_col,pver,indexm) edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,indexm) - densities(1:pver) = this%height_delta_(1:pver) * km2cm * & - ( edges(1:pver) + edges(2:pver+1) ) * 0.5_r8 + edges(pver+2) = fixed_species_conc(i_col,1,indexm) ! use upper mid-point value for top edge + densities(1:pver+1) = this%height_delta_(1:pver+1) * km2cm * & + sqrt(edges(1:pver+1)) * sqrt(edges(2:pver+2)) call this%profiles_( PROFILE_INDEX_AIR )%update( & edge_values = edges, layer_densities = densities, & scale_height = 8.01_r8 ) ! scale height in [km] @@ -1377,19 +1385,28 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species if( is_fixed_O2 ) then edges(1) = fixed_species_conc(i_col,pver,index_O2) edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,index_O2) + edges(pver+2) = fixed_species_conc(i_col,1,index_O2) else if( index_O2 > 0 ) then edges(1) = species_vmr(i_col,pver,index_O2) * & fixed_species_conc(i_col,pver,indexm) edges(2:pver+1) = species_vmr(i_col,pver:1:-1,index_O2) * & fixed_species_conc(i_col,pver:1:-1,indexm) + edges(pver+2) = species_vmr(i_col,1,index_O2) * & + fixed_species_conc(i_col,1,indexm) else edges(:) = 0.0_r8 end if - densities(1:pver) = this%height_delta_(1:pver) * km2cm * & - ( edges(1:pver) + edges(2:pver+1) ) * 0.5_r8 - call this%profiles_( PROFILE_INDEX_O2 )%update( & - edge_values = edges, layer_densities = densities, & - scale_height = 7.0_r8 ) + densities(1:pver+1) = this%height_delta_(1:pver+1) * km2cm * & + sqrt(edges(1:pver+1)) * sqrt(edges(2:pver+2)) + if( nabscol >= 2 ) then + call this%profiles_( PROFILE_INDEX_O2 )%update( & + edge_values = edges, layer_densities = densities, & + exo_density = exo_column_conc(i_col,0,2) ) + else + call this%profiles_( PROFILE_INDEX_O2 )%update( & + edge_values = edges, layer_densities = densities, & + scale_height = 7.0_r8 ) + end if ! ========== ! O3 profile @@ -1397,19 +1414,28 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species if( is_fixed_O3 ) then edges(1) = fixed_species_conc(i_col,pver,index_O3) edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,index_O3) + edges(pver+2) = fixed_species_conc(i_col,1,index_O3) else if( index_O3 > 0 ) then edges(1) = species_vmr(i_col,pver,index_O3) * & fixed_species_conc(i_col,pver,indexm) edges(2:pver+1) = species_vmr(i_col,pver:1:-1,index_O3) * & fixed_species_conc(i_col,pver:1:-1,indexm) + edges(pver+2) = species_vmr(i_col,1,index_O3) * & + fixed_species_conc(i_col,1,indexm) else edges(:) = 0.0_r8 end if - densities(1:pver) = this%height_delta_(1:pver) * km2cm * & - ( edges(1:pver) + edges(2:pver+1) ) * 0.5_r8 - call this%profiles_( PROFILE_INDEX_O3 )%update( & - edge_values = edges, layer_densities = densities, & - scale_height = 7.0_r8 ) + densities(1:pver+1) = this%height_delta_(1:pver+1) * km2cm * & + ( edges(1:pver+1) + edges(2:pver+2) ) * 0.5_r8 + if( nabscol >= 1 ) then + call this%profiles_( PROFILE_INDEX_O3 )%update( & + edge_values = edges, layer_densities = densities, & + exo_density = exo_column_conc(i_col,0,1) ) + else + call this%profiles_( PROFILE_INDEX_O3 )%update( & + edge_values = edges, layer_densities = densities, & + scale_height = 7.0_r8 ) + end if ! =============== ! aerosol profile @@ -1496,6 +1522,12 @@ subroutine get_aerosol_optical_properties( this, state, pbuf ) call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & aer_tau_w_g(i_col,pver-i_level,:), this%asymmetry_factor_(i_col,i_level,:)) end do + this%optical_depth_(i_col,pver+1,:) = & + this%optical_depth_(i_col,pver,:) + this%single_scattering_albedo_(i_col,pver+1,:) = & + this%single_scattering_albedo_(i_col,pver,:) + this%asymmetry_factor_(i_col,pver+1,:) = & + this%asymmetry_factor_(i_col,pver,:) end do ! ================================================================ diff --git a/src/chemistry/pp_trop_strat_mam4_vbs/mo_imp_sol.F90 b/src/chemistry/pp_trop_strat_mam4_vbs/mo_imp_sol.F90 index 98cadb9050..bfcbed423c 100644 --- a/src/chemistry/pp_trop_strat_mam4_vbs/mo_imp_sol.F90 +++ b/src/chemistry/pp_trop_strat_mam4_vbs/mo_imp_sol.F90 @@ -382,8 +382,9 @@ subroutine imp_sol( base_sol, reaction_rates, het_rates, extfrc, delt, & end if base_sol_blk(i,:) = sbase_sol_blk(i,:) else - write(iulog,'('' imp_sol: step failed to converge @ (lchnk,vctrpos,nstep,dt,time) = '',3i8,1p,2g15.7)') & - lchnk,ofl+i-1,nstep,dt(i),interval_done+dt(i) + write(iulog,'('' imp_sol: step failed to converge '')') + !write(iulog,'('' imp_sol: step failed to converge @ (lchnk,vctrpos,nstep,dt,time) = '',3i8,1p,2g15.7)') & + ! lchnk,ofl+i-1,nstep,dt(i),interval_done+dt(i) do m = 1,clscnt4 if( .not. spc_conv_blk(i,m) ) then write(iulog,'(1x,a16,1x,1pe10.3)') solsym(clsmap(m,4)), max_delta(m) From c47a4c342185253153b74687634ff634ba9eb708 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Tue, 31 Jan 2023 11:06:43 -0700 Subject: [PATCH 61/84] adjust top layer density calculations --- src/chemistry/mozart/mo_tuvx.F90 | 35 ++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 2ab9895e5b..45d26304b3 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -1168,15 +1168,15 @@ end subroutine create_updaters ! TUV-x heights are "bottom-up" and require atmospheric constituent ! concentrations at interfaces. Therefore, CAM mid-points are used as ! TUV-x grid interfaces, with an additional layer introduced between - ! the surface and the lowest CAM mid-point, and a thin layer at the - ! top of the TUV-x grid to hold above-column species densities. + ! the surface and the lowest CAM mid-point, and a layer at the + ! top of the TUV-x grid to hold species densities above the top CAM + ! mid-point. ! ! ---- (interface) ===== (mid-point) ! ! CAM TUV-x + ! ------(top)------ i_int = 1 -------(top)------ i_int = pver + 2 ! ************************ (exo values) ***************************** - ! ------(top)------ i_int = 1 - ! -------(top)------ i_int = pver + 2 ! ================== i_mid = pver + 1 ! ================= i_mid = 1 ------------------ i_int = pver + 1 ! ----------------- i_int = 2 ================== i_mid = pver @@ -1206,11 +1206,11 @@ subroutine set_heights( this, i_col, ncol, height_mid, height_int ) edges(1) = height_int(i_col,pver+1) edges(2:pver+1) = height_mid(i_col,pver:1:-1) - edges(pver+2) = edges(pver+1) + 1.0e-10_r8 ! thin layer at the top to hold exo values + edges(pver+2) = height_int(i_col,1) mid_points(1) = ( height_mid(i_col,pver) - height_int(i_col,pver+1) ) * 0.5_r8 & + height_int(i_col,pver+1) mid_points(2:pver) = height_int(i_col,pver:2:-1) - mid_points(pver+1) = edges(pver+1) + 0.5e-10_r8 + mid_points(pver+1) = 0.5_r8 * ( edges(pver+1) + edges(pver+2) ) call this%grids_( GRID_INDEX_HEIGHT )%update( edges = edges, mid_points = mid_points ) this%height_delta_(1:pver+1) = edges(2:pver+2) - edges(1:pver+1) @@ -1361,7 +1361,7 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing ! ratios (mol mol-1) real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! above column densities - ! (molecule cm-3) + ! (molecule cm-2) real(r8) :: edges(pver+2), densities(pver+1) real(r8) :: exo_val @@ -1396,13 +1396,17 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species else edges(:) = 0.0_r8 end if - densities(1:pver+1) = this%height_delta_(1:pver+1) * km2cm * & - sqrt(edges(1:pver+1)) * sqrt(edges(2:pver+2)) if( nabscol >= 2 ) then + densities(1) = 0.5_r8 * exo_column_conc(i_col,pver,2) + densities(2:pver) = 0.5_r8 * ( exo_column_conc(i_col,pver-1:1:-1,2) & + + exo_column_conc(i_col,pver:2:-1,2) ) + densities(pver+1) = exo_column_conc(i_col,0,2) & + + 0.5_r8 * exo_column_conc(i_col,1,2) call this%profiles_( PROFILE_INDEX_O2 )%update( & - edge_values = edges, layer_densities = densities, & - exo_density = exo_column_conc(i_col,0,2) ) + edge_values = edges, layer_densities = densities ) else + densities(1:pver+1) = this%height_delta_(1:pver+1) * km2cm * & + sqrt(edges(1:pver+1)) * sqrt(edges(2:pver+2)) call this%profiles_( PROFILE_INDEX_O2 )%update( & edge_values = edges, layer_densities = densities, & scale_height = 7.0_r8 ) @@ -1425,13 +1429,18 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species else edges(:) = 0.0_r8 end if - densities(1:pver+1) = this%height_delta_(1:pver+1) * km2cm * & - ( edges(1:pver+1) + edges(2:pver+2) ) * 0.5_r8 if( nabscol >= 1 ) then + densities(1) = 0.5_r8 * exo_column_conc(i_col,pver,1) + densities(2:pver) = 0.5_r8 * ( exo_column_conc(i_col,pver-1:1:-1,1) & + + exo_column_conc(i_col,pver:2:-1,1) ) + densities(pver+1) = exo_column_conc(i_col,0,1) & + + 0.5_r8 * exo_column_conc(i_col,1,1) call this%profiles_( PROFILE_INDEX_O3 )%update( & edge_values = edges, layer_densities = densities, & exo_density = exo_column_conc(i_col,0,1) ) else + densities(1:pver+1) = this%height_delta_(1:pver+1) * km2cm * & + ( edges(1:pver+1) + edges(2:pver+2) ) * 0.5_r8 call this%profiles_( PROFILE_INDEX_O3 )%update( & edge_values = edges, layer_densities = densities, & scale_height = 7.0_r8 ) From 60e0f5448e85c8dbab1b9f2ce39947753c0fb9dd Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Tue, 31 Jan 2023 12:21:19 -0700 Subject: [PATCH 62/84] add in ET mid values --- src/chemistry/mozart/mo_tuvx.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 45d26304b3..38c436891a 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -1322,7 +1322,7 @@ subroutine set_et_flux( this ) ! update TUV-x ET flux profile ! ============================ call this%profiles_( PROFILE_INDEX_ET_FLUX )%update( & - !mid_point_values = this%wavelength_mid_values_, & + mid_point_values = this%wavelength_mid_values_, & edge_values = this%wavelength_values_) ! ====================================================================== From eb69196769fa9febfdcc6275fc95804546436aa9 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Thu, 2 Feb 2023 17:29:51 -0700 Subject: [PATCH 63/84] update build; adjust O2 profile --- cime_config/buildlib | 25 ++++++++++++++++++------- src/chemistry/mozart/mo_photo.F90 | 4 +--- src/chemistry/mozart/mo_tuvx.F90 | 25 ++++++++++--------------- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/cime_config/buildlib b/cime_config/buildlib index 46cebec430..c6ccbd9a1c 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -301,7 +301,7 @@ def _musica_core_install_dir(libroot): ############################################################################### # Returns the path to the musica-core install directory - corepaths = glob(os.path.join(libroot, "ncar", "musicacore*")) + corepaths = glob(os.path.join(libroot, "musicacore*")) expect(len(corepaths)>0, \ "musica-core not found at {}".format(libroot)) expect(len(corepaths)<2, \ @@ -310,6 +310,20 @@ def _musica_core_install_dir(libroot): "musica-core install directory not found at {}".format(corepaths[0])) return corepaths[0] +############################################################################### +def _musica_core_package_dir(libroot): +############################################################################### +# Returns the path to the musica-core CMake package + + corepaths = glob(os.path.join(libroot, "musicacore*", "cmake", "musicacore*" )) + expect(len(corepaths)>0, \ + "musica-core package not found at {}".format(libroot)) + expect(len(corepaths)<2, \ + "Multiple musica-core versions found at {}".format(libroot)) + expect(os.path.exists(corepaths[0]), \ + "musica-core package directory not found at {}".format(corepaths[0])) + return corepaths[0] + ############################################################################### def _build_tuvx(caseroot, libroot, bldroot): ############################################################################### @@ -323,8 +337,7 @@ def _build_tuvx(caseroot, libroot, bldroot): os.makedirs(bldpath) jsoninc = _json_fortran_include_dir(libroot) jsonlib = _json_fortran_lib_path(libroot) - coreinc = _musica_core_include_dir(libroot) - corelib = _musica_core_lib_path(libroot) + corepackage = _musica_core_package_dir(libroot) srcpath = os.path.abspath(os.path.join(case.get_value("COMP_ROOT_DIR_ATM"), \ "libraries", "tuv-x", "")) logger.info("Building TUV-x in {} from source in {}\n".format(bldpath, srcpath)) @@ -347,9 +360,7 @@ def _build_tuvx(caseroot, libroot, bldroot): cmake_args += "-DENABLE_TESTS=OFF " cmake_args += "-DJSON_INCLUDE_DIR={} ".format(jsoninc) cmake_args += "-DJSON_LIB={} ".format(jsonlib) - cmake_args += "-DENABLE_LOCAL_MUSICA_CORE=ON " - cmake_args += "-DMUSICA_CORE_INCLUDE_DIR={} ".format(coreinc) - cmake_args += "-DMUSICA_CORE_LIB={} ".format(corelib) + cmake_args += "-DCMAKE_PREFIX_PATH={} ".format(corepackage) cmake_args += "-DCMAKE_Fortran_FLAGS='{}' ".format(arg_dict["FFLAGS"]) cmake_args += "-DCMAKE_INSTALL_PREFIX='{}' ".format(libroot) cmake_args += srcpath @@ -398,7 +409,7 @@ def _tuvx_install_dir(libroot): ############################################################################### # Returns the path to the TUV-x install directory - tuvxpaths = glob(os.path.join(libroot, "ncar", "tuvx*")) + tuvxpaths = glob(os.path.join(libroot, "tuvx*")) expect(len(tuvxpaths)>0, \ "TUV-x not found at {}".format(libroot)) expect(len(tuvxpaths)<2, \ diff --git a/src/chemistry/mozart/mo_photo.F90 b/src/chemistry/mozart/mo_photo.F90 index c7224c2c06..5ef22df875 100644 --- a/src/chemistry/mozart/mo_photo.F90 +++ b/src/chemistry/mozart/mo_photo.F90 @@ -825,9 +825,7 @@ subroutine table_photo( photos, pmid, pdel, temper, zmid, zint, & !----------------------------------------------------------------- call cloud_mod( zen_angle(i), cld_line, lwc_line, fac1, srf_alb(i), & eff_alb, cld_mult ) - cld_mult(:) = esfact ! * cld_mult(:) - eff_alb(:) = 0.0_r8 - eff_alb(pver) = srf_alb(i) + cld_mult(:) = esfact * cld_mult(:) !----------------------------------------------------------------- ! ... long wave length component diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 38c436891a..18f966f4d9 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -550,6 +550,11 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & end if end do + ! ============================================== + ! Filter negative rates TODO fix inputs to TUV-x + ! ============================================== + tuvx%photo_rates_(:,:,:) = max( 0.0_r8, tuvx%photo_rates_(:,:,:) ) + ! ============================================ ! Return the photolysis rates on the CAM grids ! ============================================ @@ -1396,21 +1401,11 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species else edges(:) = 0.0_r8 end if - if( nabscol >= 2 ) then - densities(1) = 0.5_r8 * exo_column_conc(i_col,pver,2) - densities(2:pver) = 0.5_r8 * ( exo_column_conc(i_col,pver-1:1:-1,2) & - + exo_column_conc(i_col,pver:2:-1,2) ) - densities(pver+1) = exo_column_conc(i_col,0,2) & - + 0.5_r8 * exo_column_conc(i_col,1,2) - call this%profiles_( PROFILE_INDEX_O2 )%update( & - edge_values = edges, layer_densities = densities ) - else - densities(1:pver+1) = this%height_delta_(1:pver+1) * km2cm * & - sqrt(edges(1:pver+1)) * sqrt(edges(2:pver+2)) - call this%profiles_( PROFILE_INDEX_O2 )%update( & - edge_values = edges, layer_densities = densities, & - scale_height = 7.0_r8 ) - end if + densities(1:pver+1) = this%height_delta_(1:pver+1) * km2cm * & + sqrt(edges(1:pver+1)) * sqrt(edges(2:pver+2)) + call this%profiles_( PROFILE_INDEX_O2 )%update( & + edge_values = edges, layer_densities = densities, & + scale_height = 7.0_r8 ) ! ========== ! O3 profile From 1a2204ed0104e3e977f35b5b27494c6b69c903e1 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Thu, 23 Feb 2023 13:55:31 -0700 Subject: [PATCH 64/84] update tuvx config files --- cime_config/tuvx_MOZART.json | 9 ++-- cime_config/tuvx_MOZART_TS1.json | 1 - cime_config/tuvx_MOZART_TSMLT.json | 70 ++++++++++++++++-------------- 3 files changed, 43 insertions(+), 37 deletions(-) diff --git a/cime_config/tuvx_MOZART.json b/cime_config/tuvx_MOZART.json index ea3fd57c0b..8129266467 100644 --- a/cime_config/tuvx_MOZART.json +++ b/cime_config/tuvx_MOZART.json @@ -9,7 +9,8 @@ ], "radiative transfer": { "solver": { - "type": "delta eddington" + "type": "discrete ordinate", + "number of streams": 4 }, "cross sections": [ { @@ -31,7 +32,8 @@ "netcdf files": [ { "file path": "data/cross_sections/O2_1.nc", - "lower extrapolation": { "type": "boundary" } + "lower extrapolation": { "type": "boundary" }, + "interpolator": { "type": "fractional target" } } ], "type": "base" @@ -71,7 +73,8 @@ "netcdf files": [ { "file path": "data/cross_sections/O2_1.nc", - "lower extrapolation": { "type": "boundary" } + "lower extrapolation": { "type": "boundary" }, + "interpolator": { "type": "fractional target" } } ], "type": "base" diff --git a/cime_config/tuvx_MOZART_TS1.json b/cime_config/tuvx_MOZART_TS1.json index d50e4c63a0..d9423a5e55 100644 --- a/cime_config/tuvx_MOZART_TS1.json +++ b/cime_config/tuvx_MOZART_TS1.json @@ -1213,7 +1213,6 @@ ] }, "__CAM options": { - "disable aerosols": true, "aliasing": { "default matching": "backup", "pairs": [ diff --git a/cime_config/tuvx_MOZART_TSMLT.json b/cime_config/tuvx_MOZART_TSMLT.json index 94fc63ac2c..2b340e5f1e 100644 --- a/cime_config/tuvx_MOZART_TSMLT.json +++ b/cime_config/tuvx_MOZART_TSMLT.json @@ -3,13 +3,14 @@ "O2 absorption" : { "cross section parameters file": "data/cross_sections/O2_parameters.txt" }, - "grids": { - }, - "profiles": { - }, + "grids": [ + ], + "profiles": [ + ], "radiative transfer": { "solver": { - "type": "delta eddington" + "type": "discrete ordinate", + "number of streams": 4 }, "cross sections": [ { @@ -31,7 +32,8 @@ "netcdf files": [ { "file path": "data/cross_sections/O2_1.nc", - "lower extrapolation": { "type": "boundary" } + "lower extrapolation": { "type": "boundary" }, + "interpolator": { "type": "fractional target" } } ], "type": "base" @@ -70,11 +72,12 @@ "cross section": { "netcdf files": [ { - "file path": "data/cross_sections/O2_1.nc", - "lower extrapolation": { "type": "boundary" } - } - ], - "type": "base" + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" }, + "interpolator": { "type": "fractional target" } + } + ], + "type": "base" }, "quantum yield": { "type": "base", @@ -82,10 +85,10 @@ "override bands": [ { "band": "lyman-alpha", - "value": 0.47 + "value": 0.53 }, { - "band": "schumann-runge", + "band": "schumann-runge continuum", "value": 1.0 } ] @@ -95,27 +98,28 @@ "name": "jo2_b", "__reaction": "O2 + hv -> O + O", "cross section": { - "netcdf files": [ - { - "file path": "data/cross_sections/O2_1.nc", - "lower extrapolation": { "type": "boundary" } - } - ], - "type": "base" + "netcdf files": [ + { + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" }, + "interpolator": { "type": "fractional target" } + } + ], + "type": "base" }, "quantum yield": { - "type": "base", - "constant value": 1.0, - "override bands": [ - { - "band": "lyman-alpha", - "value": 0.53 - }, - { - "band": "schumann-runge continuum", - "value": 1.0 - } - ] + "type": "base", + "constant value": 1.0, + "override bands": [ + { + "band": "lyman-alpha", + "value": 0.47 + }, + { + "band": "schumann-runge continuum", + "value": 0.0 + } + ] } }, { @@ -402,7 +406,7 @@ }, "quantum yield": { "type": "base", - "constant value": 1.0 + "constant value": 0.5 } }, { From 1a85f0a364415fd9ba29704ec063dc0f322cfd6f Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Thu, 23 Feb 2023 14:42:32 -0700 Subject: [PATCH 65/84] use TUV-x from first time step --- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 5 ++--- src/chemistry/mozart/mo_tuvx.F90 | 6 ------ 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index f2da8017cb..b0313618b2 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -250,8 +250,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & use chem_mods, only : nabscol, nfs, indexm, clscnt4 use physconst, only : rga use mo_photo, only : set_ub_col, setcol, table_photo - use mo_tuvx, only : tuvx_get_photo_rates, tuvx_active, & - tuvx_is_first_time_step + use mo_tuvx, only : tuvx_get_photo_rates, tuvx_active use mo_exp_sol, only : exp_sol use mo_imp_sol, only : imp_sol use mo_setrxt, only : setrxt @@ -786,7 +785,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & call shr_orb_decl( calday, eccen, mvelpp, lambm0, obliqr , & delta, esfact ) - if (tuvx_active .and. .not. tuvx_is_first_time_step) then + if (tuvx_active) then !----------------------------------------------------------------- ! ... get calculated photolysis rates from TUV-x !----------------------------------------------------------------- diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 18f966f4d9..a1adf776e4 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -23,7 +23,6 @@ module mo_tuvx public :: tuvx_get_photo_rates public :: tuvx_finalize public :: tuvx_active - public :: tuvx_is_first_time_step ! Inidices for grid updaters integer, parameter :: NUM_GRIDS = 2 ! number of grids that CAM will update at runtime @@ -121,7 +120,6 @@ module mo_tuvx ! namelist options character(len=cl) :: tuvx_config_path = 'NONE' ! absolute path to TUVX configuration file logical, protected :: tuvx_active = .false. - logical, protected :: tuvx_is_first_time_step = .true. !================================================================================================ contains @@ -420,10 +418,6 @@ end subroutine tuvx_init subroutine tuvx_timestep_init( ) integer :: i_thread - integer, save :: n_time_step = 0 - - n_time_step = n_time_step + 1 - if( n_time_step > 1 ) tuvx_is_first_time_step = .false. if( .not. tuvx_active ) return From f8882cd8eb885e0cacfd7b4efea2cda847b3303c Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Fri, 24 Feb 2023 13:45:29 -0700 Subject: [PATCH 66/84] use LUT for first time step --- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 5 +++-- src/chemistry/mozart/mo_tuvx.F90 | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index b0313618b2..f2da8017cb 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -250,7 +250,8 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & use chem_mods, only : nabscol, nfs, indexm, clscnt4 use physconst, only : rga use mo_photo, only : set_ub_col, setcol, table_photo - use mo_tuvx, only : tuvx_get_photo_rates, tuvx_active + use mo_tuvx, only : tuvx_get_photo_rates, tuvx_active, & + tuvx_is_first_time_step use mo_exp_sol, only : exp_sol use mo_imp_sol, only : imp_sol use mo_setrxt, only : setrxt @@ -785,7 +786,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & call shr_orb_decl( calday, eccen, mvelpp, lambm0, obliqr , & delta, esfact ) - if (tuvx_active) then + if (tuvx_active .and. .not. tuvx_is_first_time_step) then !----------------------------------------------------------------- ! ... get calculated photolysis rates from TUV-x !----------------------------------------------------------------- diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index a1adf776e4..b653657c8a 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -23,6 +23,7 @@ module mo_tuvx public :: tuvx_get_photo_rates public :: tuvx_finalize public :: tuvx_active + public :: tuvx_is_first_time_step ! Inidices for grid updaters integer, parameter :: NUM_GRIDS = 2 ! number of grids that CAM will update at runtime @@ -120,6 +121,7 @@ module mo_tuvx ! namelist options character(len=cl) :: tuvx_config_path = 'NONE' ! absolute path to TUVX configuration file logical, protected :: tuvx_active = .false. + logical, protected :: tuvx_is_first_time_step = .true. !================================================================================================ contains @@ -418,9 +420,13 @@ end subroutine tuvx_init subroutine tuvx_timestep_init( ) integer :: i_thread + integer, save :: n_time_step = 0 if( .not. tuvx_active ) return + if( n_time_step > 0 ) tuvx_is_first_time_step = .false. + n_time_step = n_time_step + 1 + do i_thread = 1, size( tuvx_ptrs ) associate( tuvx => tuvx_ptrs( i_thread ) ) call set_et_flux( tuvx ) From 07fae13922a62e544978584fdb0ca50bdee79a48 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Fri, 24 Feb 2023 14:13:37 -0700 Subject: [PATCH 67/84] combine TS1 and TSMLT tuv-x configurations --- cime_config/tuvx_MOZART_TS1.json | 2 +- cime_config/tuvx_MOZART_TSMLT.json | 1493 ---------------------------- 2 files changed, 1 insertion(+), 1494 deletions(-) delete mode 100644 cime_config/tuvx_MOZART_TSMLT.json diff --git a/cime_config/tuvx_MOZART_TS1.json b/cime_config/tuvx_MOZART_TS1.json index d9423a5e55..666cee23b7 100644 --- a/cime_config/tuvx_MOZART_TS1.json +++ b/cime_config/tuvx_MOZART_TS1.json @@ -1,5 +1,5 @@ { - "__description": "TUV-x configuration for the MOZART-TS1 chemical mechanism", + "__description": "TUV-x configuration for the MOZART-TS1 and MOZART-TSMLT chemical mechanisms", "O2 absorption" : { "cross section parameters file": "data/cross_sections/O2_parameters.txt" }, diff --git a/cime_config/tuvx_MOZART_TSMLT.json b/cime_config/tuvx_MOZART_TSMLT.json deleted file mode 100644 index 2b340e5f1e..0000000000 --- a/cime_config/tuvx_MOZART_TSMLT.json +++ /dev/null @@ -1,1493 +0,0 @@ -{ - "__description": "TUV-x configuration for the MOZART-TSMLT chemical mechanism", - "O2 absorption" : { - "cross section parameters file": "data/cross_sections/O2_parameters.txt" - }, - "grids": [ - ], - "profiles": [ - ], - "radiative transfer": { - "solver": { - "type": "discrete ordinate", - "number of streams": 4 - }, - "cross sections": [ - { - "name": "air", - "type": "air" - }, - { - "name": "O3", - "netcdf files": [ - { "file path": "data/cross_sections/O3_1.nc" }, - { "file path": "data/cross_sections/O3_2.nc" }, - { "file path": "data/cross_sections/O3_3.nc" }, - { "file path": "data/cross_sections/O3_4.nc" } - ], - "type": "O3" - }, - { - "name": "O2", - "netcdf files": [ - { - "file path": "data/cross_sections/O2_1.nc", - "lower extrapolation": { "type": "boundary" }, - "interpolator": { "type": "fractional target" } - } - ], - "type": "base" - } - ], - "radiators": [ - { - "name": "air", - "type": "base", - "treat as air": true, - "cross section": "air", - "vertical profile": "air", - "vertical profile units": "molecule cm-3" - }, - { - "name": "O2", - "type": "base", - "cross section": "O2", - "vertical profile": "O2", - "vertical profile units": "molecule cm-3" - }, - { - "name": "O3", - "type": "base", - "cross section": "O3", - "vertical profile": "O3", - "vertical profile units": "molecule cm-3" - } - ] - }, - "photolysis": { - "reactions": [ - { - "name": "jo2_a", - "__reaction": "O2 + hv -> O + O1D", - "cross section": { - "netcdf files": [ - { - "file path": "data/cross_sections/O2_1.nc", - "lower extrapolation": { "type": "boundary" }, - "interpolator": { "type": "fractional target" } - } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 0, - "override bands": [ - { - "band": "lyman-alpha", - "value": 0.53 - }, - { - "band": "schumann-runge continuum", - "value": 1.0 - } - ] - } - }, - { - "name": "jo2_b", - "__reaction": "O2 + hv -> O + O", - "cross section": { - "netcdf files": [ - { - "file path": "data/cross_sections/O2_1.nc", - "lower extrapolation": { "type": "boundary" }, - "interpolator": { "type": "fractional target" } - } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0, - "override bands": [ - { - "band": "lyman-alpha", - "value": 0.47 - }, - { - "band": "schumann-runge continuum", - "value": 0.0 - } - ] - } - }, - { - "name": "jo3_a", - "__reaction": "O3 + hv -> O2 + O(1D)", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/O3_1.nc" }, - { "file path": "data/cross_sections/O3_2.nc" }, - { "file path": "data/cross_sections/O3_3.nc" }, - { "file path": "data/cross_sections/O3_4.nc" } - ], - "type": "O3" - }, - "quantum yield": { - "type": "O3+hv->O2+O(1D)" - } - }, - { - "name": "jo3_b", - "__reaction": "O3 + hv -> O2 + O(3P)", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/O3_1.nc" }, - { "file path": "data/cross_sections/O3_2.nc" }, - { "file path": "data/cross_sections/O3_3.nc" }, - { "file path": "data/cross_sections/O3_4.nc" } - ], - "type": "O3" - }, - "quantum yield": { - "type": "O3+hv->O2+O(3P)" - } - }, - { - "name": "jn2o", - "__reaction": "N2O + hv -> N2 + O(1D)", - "cross section": { - "type": "N2O+hv->N2+O(1D)" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jno2", - "__reaction": "NO2 + hv -> NO + O(3P)", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/NO2_1.nc" } - ], - "type": "NO2 tint" - }, - "quantum yield": { - "netcdf files": ["data/quantum_yields/NO2_1.nc"], - "type": "NO2 tint", - "lower extrapolation": { "type": "boundary" } - } - }, - { - "name": "jn2o5_a", - "__reaction": "N2O5 + hv -> NO2 + NO3", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/N2O5_1.nc" }, - { "file path": "data/cross_sections/N2O5_2.nc" } - ], - "type": "N2O5+hv->NO2+NO3" - }, - "quantum yield": { - "type": "base", - "netcdf files": [ "data/quantum_yields/N2O5_NO3_NO2.nc" ] - } - }, - { - "name": "jn2o5_b", - "__reaction": "N2O5 + hv -> NO + O + NO3", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/N2O5_1.nc" }, - { "file path": "data/cross_sections/N2O5_2.nc" } - ], - "type": "N2O5+hv->NO2+NO3" - }, - "quantum yield": { - "type": "base", - "netcdf files": [ "data/quantum_yields/N2O5_NO3_NO_O.nc" ] - } - }, - { - "name": "jhno3", - "__reaction": "HNO3 + hv -> OH + NO2", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/HNO3_1.nc" } - ], - "type": "HNO3+hv->OH+NO2" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jno3_a", - "__reaction": "NO3 + hv -> NO2 + O(3P)", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/NO3_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "netcdf files": [ - "data/quantum_yields/NO3-NO2+O(3P)_1.nc" - ], - "type": "tint", - "lower extrapolation": { - "type": "constant", - "value": 1.0 - } - } - }, - { - "name": "jno3_b", - "__reaction": "NO3 + hv -> NO + O2", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/NO3_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "netcdf files": [ - "data/quantum_yields/NO3-NO+O2_1.nc" - ], - "type": "tint" - } - }, - { - "name": "jch3ooh", - "__reaction": "CH3OOH + hv -> CH3O + OH", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CH3OOH_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jch2o_a", - "__reaction": "CH2O + hv -> H + HCO", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CH2O_1.nc" } - ], - "type": "CH2O" - }, - "quantum yield": { - "netcdf files": [ - "data/quantum_yields/CH2O_1.nc" - ], - "type": "base", - "lower extrapolation": { - "type": "boundary" - } - } - }, - { - "name": "jch2o_b", - "__reaction": "CH2O + hv -> H2 + CO", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CH2O_1.nc" } - ], - "type": "CH2O" - }, - "quantum yield": { - "netcdf files": [ - "data/quantum_yields/CH2O_1.nc" - ], - "type": "CH2O", - "lower extrapolation": { - "type": "boundary" - } - } - }, - { - "name": "jh2o2", - "__reaction": "H2O2 + hv -> OH + OH", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/H2O2_1.nc" } - ], - "type": "H2O2+hv->OH+OH" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jch3cho", - "__reaction": "CH3CHO + hv -> CH3 + HCO", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CH3CHO_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "netcdf files": [ - "data/quantum_yields/CH3CHO_1.nc" - ], - "type": "CH3CHO+hv->CH3+HCO" - } - }, - { - "name": "jpan", - "__reaction": "PAN + hv -> 0.6*CH3CO3 + 0.6*NO2 + 0.4*CH3O2 + 0.4*NO3 + 0.4*CO2", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/PAN_1.nc" } - ], - "type": "CH3ONO2+hv->CH3O+NO2" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jmvk", - "__reaction": "MVK + hv -> 0.7*C3H6 + 0.7*CO + 0.3*CH3O2 + 0.3*CH3CO3", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/MVK_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "MVK+hv->Products" - } - }, - { - "name": "jacet", - "__reaction": "CH3COCH3 + hv -> CH3CO + CH3", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CH3COCH3_1.nc" } - ], - "type": "CH3COCH3+hv->CH3CO+CH3" - }, - "quantum yield": { - "type": "CH3COCH3+hv->CH3CO+CH3" - } - }, - { - "name": "jmgly", - "__reaction": "CH3COCHO + hv -> CH3CO3 + CO + HO2", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CH3COCHO_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "CH3COCHO+hv->CH3CO+HCO" - } - }, - { - "name": "jglyald", - "__reaction": "GLYALD + hv -> 2*HO2 + CO + CH2O", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/HOCH2CHO_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 0.5 - } - }, - { - "name": "jglyoxal", - "__reaction": "GLYOXAL + hv -> 2*CO + 2*HO2", - "__comments": "NOTE the products of this reaction don't exactly match", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CHOCHO_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "netcdf files": [ - "data/quantum_yields/CHOCHO-H2_CO_CO_1.nc" - ], - "type": "base", - "lower extrapolation": { - "type": "boundary" - } - } - }, - { - "name": "jbrcl", - "__reaction": "BrCl + hv -> Br + Cl", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/BrCl_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jbro", - "__reaction": "BrO + hv -> Br + O", - "cross section": { - "netcdf files": [ - { - "file path": "data/cross_sections/BrO_1.nc", - "interpolator": { - "type": "fractional target", - "fold in": true - } - } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jbrono2_a", - "__reaction": "BrONO2 + hv -> Br + NO3", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/BrONO2_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 0.85 - } - }, - { - "name": "jbrono2_b", - "__reaction": "BrONO2 + hv -> BrO + NO2", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/BrONO2_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 0.15 - } - }, - { - "name": "jccl4", - "__reaction": "CCl4 + hv -> Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CCl4_1.nc" } - ], - "type": "CCl4+hv->Products" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jcf2clbr", - "__reaction": "CF2BrCl + hv -> Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CF2BrCl_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jcf3br", - "__reaction": "CF3Br + hv -> Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CF3Br_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jcfcl3", - "__reaction": "CCl3F + hv -> Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CFC-11_1.nc" } - ], - "type": "CCl3F+hv->Products" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jcfc113", - "__reaction": "CFC-113 + hv -> Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CFC-113_1.nc" } - ], - "type": "tint" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jcfc114", - "__reaction": "CFC-114 + hv -> Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CFC-114_1.nc" } - ], - "type": "tint" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jcfc115", - "__reaction": "CFC-115 + hv -> Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CFC-115_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jcf2cl2", - "__reaction": "CCl2F2 + hv -> Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CFC-12_1.nc" } - ], - "type": "CCl3F+hv->Products" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jch2br2", - "__reaction": "CH2BR2 + hv -> 2*BR", - "cross section": { - "type": "temperature based", - "netcdf file": "data/cross_sections/CH2BR2_1.nc", - "parameterization": { - "AA": [ -70.211776, 1.940326e-1, 2.726152e-3, -1.695472e-5, 2.500066e-8 ], - "BB": [ 2.899280, -4.327724e-2, 2.391599e-4, -5.807506e-7, 5.244883e-10 ], - "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], - "minimum wavelength": 210.0, - "maximum wavelength": 290.0, - "temperature ranges": [ - { - "maximum": 209.999999999999, - "fixed value": 210.0 - }, - { - "minimum": 210, - "maximum": 300 - }, - { - "minimum": 300.00000000001, - "fixed value": 300.0 - } - ] - } - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jch3br", - "__reaction": "CH3Br + hv -> Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CH3Br_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jch3ccl3", - "__reaction": "CH3CCl3+hv->Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CH3CCl3_1.nc" } - ], - "type": "tint" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jch3cl", - "__reaction": "CH3Cl + hv -> Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CH3Cl_1.nc" } - ], - "type": "tint" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jchbr3", - "__reaction": "CHBr3 + hv -> Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CHBr3_1.nc" } - ], - "type": "CHBr3+hv->Products", - "lower extrapolation": { - "type": "boundary" - } - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jcl2", - "__reaction": "Cl2 + hv -> Cl + Cl", - "cross section": { - "type": "Cl2+hv->Cl+Cl" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jcl2o2", - "__reaction": "ClOOCl + hv -> Cl + ClOO", - "__comments": "TODO - this doesn't exactly match the products in TS1", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/ClOOCl_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jclo", - "__reaction": "ClO + hv -> Cl + O(1D)", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/ClO_1.nc" } - ], - "type": "tint" - }, - "quantum yield": { - "type": "ClO+hv->Cl+O(1D)" - } - }, - { - "name": "jclono2_a", - "__reaction": "ClONO2 + hv -> Cl + NO3", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/ClONO2_1.nc" } - ], - "type": "ClONO2" - }, - "quantum yield": { - "type": "ClONO2+hv->Cl+NO3" - } - }, - { - "name": "jclono2_b", - "__reaction": "ClONO2 + hv -> ClO + NO2", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/ClONO2_1.nc" } - ], - "type": "ClONO2" - }, - "quantum yield": { - "type": "ClONO2+hv->ClO+NO2" - } - }, - { - "name": "jcof2", - "__reaction": "CF2O + hv -> Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CF2O_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jcofcl", - "__reaction": "CClFO + hv -> Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CClFO_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jh2402", - "__reaction": "H2402 + hv -> 2*BR + 2*COF2", - "__comments": "TUV data set name CF2BrCF2Br", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CF2BrCF2Br_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jhcfc141b", - "__reaction": "HCFC-141b + hv -> Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CH3CFCl2_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jhcfc142b", - "__reaction": "HCFC-142b + hv -> Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CH3CF2Cl_1.nc" } - ], - "type": "HCFC+hv->Products" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jhcfc22", - "__reaction": "HCFC-22 + hv -> Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CHClF2_1.nc" } - ], - "type": "tint" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jhcl", - "__reaction": "HCl + hv -> H + Cl", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/HCl_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jhobr", - "__reaction": "HOBr + hv -> OH + Br", - "cross section": { - "type": "HOBr+hv->OH+Br" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jhocl", - "__reaction": "HOCl + hv -> HO + Cl", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/HOCl_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "joclo", - "__reaction": "OClO + hv -> Products", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/OClO_1.nc" }, - { "file path": "data/cross_sections/OClO_2.nc" }, - { "file path": "data/cross_sections/OClO_3.nc" } - ], - "type": "OClO+hv->Products" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jho2no2_a", - "__reaction": "HNO4 + hv -> OH + NO3", - "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/HNO4_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 0.2 - } - }, - { - "name": "jho2no2_b", - "__reaction": "HNO4 + hv -> HO2 + NO2", - "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/HNO4_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 0.8 - } - }, - { - "name": "jmacr_a", - "__reaction": "CH2=C(CH3)CHO->1.34HO2+0.66MCO3+1.34CH2O+CH3CO3", - "__comments": "Methacrolein photolysis channel 1", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/Methacrolein_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 0.005 - } - }, - { - "name": "jmacr_b", - "__reaction": "CH2=C(CH3)CHO->0.66OH+1.34CO", - "__comments": "Methacrolein photolysis channel 2", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/Methacrolein_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 0.005 - } - }, - { - "name": "jhyac", - "__reaction": "CH2(OH)COCH3->CH3CO3+HO2+CH2O", - "__comments": "hydroxy acetone TODO: the products of this reaction differ from standalone TUV-x", - "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/Hydroxyacetone_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 0.65 - } - }, - { - "name": "jh2o_a", - "__reaction": "H2O + hv -> OH + H", - "cross section": { - "type": "base", - "merge data": true, - "netcdf files": [ - { - "file path": "data/cross_sections/H2O_1.nc", - "zero above": 183.0 - }, - { - "file path": "data/cross_sections/H2O_2.nc", - "zero below": 183.00000000001, - "zero above": 190.0 - }, - { - "file path": "data/cross_sections/H2O_3.nc", - "zero below": 190.00000000001 - } - ] - }, - "quantum yield" : { - "type": "base", - "netcdf files": [ "data/quantum_yields/H2O_H_OH.nc" ] - } - }, - { - "name": "jh2o_b", - "__reaction": "H2O + hv -> H2 + O1D", - "cross section": { - "type": "base", - "merge data": true, - "netcdf files": [ - { - "file path": "data/cross_sections/H2O_1.nc", - "zero above": 183.0 - }, - { - "file path": "data/cross_sections/H2O_2.nc", - "zero below": 183.00000000001, - "zero above": 190.0 - }, - { - "file path": "data/cross_sections/H2O_3.nc", - "zero below": 190.00000000001 - } - ] - }, - "quantum yield" : { - "type": "base", - "netcdf files": [ "data/quantum_yields/H2O_H2_O1D.nc" ] - } - }, - { - "name": "jh2o_c", - "__reaction": "H2O + hv -> 2*H + O", - "cross section": { - "type": "base", - "merge data": true, - "netcdf files": [ - { - "file path": "data/cross_sections/H2O_1.nc", - "zero above": 183.0 - }, - { - "file path": "data/cross_sections/H2O_2.nc", - "zero below": 183.00000000001, - "zero above": 190.0 - }, - { - "file path": "data/cross_sections/H2O_3.nc", - "zero below": 190.00000000001 - } - ] - }, - "quantum yield" : { - "type": "base", - "netcdf files": [ "data/quantum_yields/H2O_2H_O3P.nc" ] - } - }, - { - "name": "jch4_a", - "__reaction": "CH4 + hv -> H + CH3O2", - "cross section": { - "type": "base", - "netcdf files": [ - { "file path": "data/cross_sections/CH4_1.nc" } - ] - }, - "quantum yield": { - "type": "base", - "constant value": 0.55 - } - }, - { - "name": "jch4_b", - "__reaction": "CH4 + hv -> 1.44*H2 + 0.18*CH2O + 0.18*O + 0.33*OH + 0.33*H + 0.44*CO2 + 0.38*CO + 0.05*H2O", - "cross section": { - "type": "base", - "netcdf files": [ - { "file path": "data/cross_sections/CH4_1.nc" } - ] - }, - "quantum yield": { - "type": "base", - "constant value": 0.45 - } - }, - { - "name": "jco2", - "__reaction": "CO2 + hv -> CO + O", - "cross section": { - "type": "base", - "netcdf files": [ - { "file path": "data/cross_sections/CO2_1.nc" } - ] - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jhbr", - "__reaction": "HBR + hv -> BR + H", - "cross section": { - "type": "base", - "netcdf files": [ - { "file path": "data/cross_sections/HBr_1.nc" } - ] - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jhf", - "__reaction": "HF + hv -> H + F", - "cross section": { - "type": "base", - "netcdf files": [ - { "file path": "data/cross_sections/HF_1.nc" } - ] - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jsf6", - "__reaction": "SF6 + hv -> sink", - "cross section": { - "type": "base", - "netcdf files": [ - { "file path": "data/cross_sections/SF6_1.nc" } - ] - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jh2so4", - "__reaction": "H2SO4 + hv -> SO3 + H2O", - "cross section": { - "type": "base", - "netcdf files": [ - { "file path": "data/cross_sections/H2SO4_1.nc" } - ] - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jocs", - "__reaction": "OCS + hv -> S + CO", - "cross section": { - "type": "base", - "netcdf files": [ - { "file path": "data/cross_sections/OCS_1.nc" } - ] - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jso", - "__reaction": "SO + hv -> S + O", - "cross section": { - "type": "base", - "netcdf files": [ - { "file path": "data/cross_sections/SO_1.nc" } - ] - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jso2", - "__reaction": "SO2 + hv -> SO + O", - "cross section": { - "type": "base", - "netcdf files": [ - { "file path": "data/cross_sections/SO2_1.nc" } - ] - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jso3", - "__reaction": "SO3 + hv -> SO2 + O", - "cross section": { - "type": "base", - "netcdf files": [ - { "file path": "data/cross_sections/SO3_1.nc" } - ] - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - } - ] - }, - "__CAM options": { - "aliasing": { - "default matching": "backup", - "pairs": [ - { - "to": "jno_i", - "__reaction": "NO + hv -> NOp + e", - "__comments": "TODO find this rate", - "from": "jo2_b", - "scale by": 0.0 - }, - { - "to": "jalknit", - "__reaction": "ALKNIT + hv -> NO2 + 0.4*CH3CHO + 0.1*CH2O + 0.25*CH3COCH3 + HO2 + 0.8*MEK", - "from": "jch3ooh" - }, - { - "to": "jpooh", - "__reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", - "from": "jch3ooh" - }, - { - "to": "jch3co3h", - "__reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", - "from": "jh2o2", - "scale by": 0.28 - }, - { - "to": "jmpan", - "__reaction": "MPAN + hv -> MCO3 + NO2", - "from": "jpan" - }, - { - "to": "jc2h5ooh", - "__reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", - "from": "jch3ooh" - }, - { - "to": "jc3h7ooh", - "__reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", - "from": "jch3ooh" - }, - { - "to": "jc6h5ooh", - "__reaction": "C6H5OOH + hv -> PHENO + OH", - "from": "jch3ooh" - }, - { - "to": "jeooh", - "__reaction": "EOOH + hv -> EO + OH", - "from": "jch3ooh" - }, - { - "to": "jrooh", - "__reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", - "from": "jch3ooh" - }, - { - "to": "jxooh", - "__reaction": "XOOH + hv -> OH", - "from": "jch3ooh" - }, - { - "to": "jonitr", - "__reaction": "ONITR + hv -> NO2", - "from": "jch3cho" - }, - { - "to": "jisopooh", - "__reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", - "from": "jch3ooh" - }, - { - "to": "jmek", - "__reaction": "MEK + hv -> CH3CO3 + C2H5O2", - "from": "jacet" - }, - { - "to": "jalkooh", - "__reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", - "from": "jch3ooh" - }, - { - "to": "jbenzooh", - "__reaction": "BENZOOH + hv -> OH + GLYOXAL + 0.5*BIGALD1 + HO2", - "from": "jch3ooh" - }, - { - "to": "jbepomuc", - "__reaction": "BEPOMUC + hv -> BIGALD1 + 1.5*HO2 + 1.5*CO", - "from": "jno2", - "scale by": 0.1 - }, - { - "to": "jbigald", - "__reaction": "BIGALD + hv -> 0.45*CO + 0.13*GLYOXAL + 0.56*HO2 + 0.13*CH3CO3 + 0.18*CH3COCHO", - "from": "jno2", - "scale by": 0.2 - }, - { - "to": "jbigald1", - "__reaction": "BIGALD1 + hv -> 0.6*MALO2 + HO2", - "from": "jno2", - "scale by": 0.14 - }, - { - "to": "jbigald2", - "__reaction": "BIGALD2 + hv -> 0.6*HO2 + 0.6*DICARBO2", - "from": "jno2", - "scale by": 0.2 - }, - { - "to": "jbigald3", - "__reaction": "BIGALD3 + hv -> 0.6*HO2 + 0.6*CO + 0.6*MDIALO2", - "from": "jno2", - "scale by": 0.2 - }, - { - "to": "jbigald4", - "__reaction": "BIGALD4 + hv -> HO2 + CO + CH3COCHO + CH3CO3", - "from": "jno2", - "scale by": 0.006 - }, - { - "to": "jbzooh", - "__reaction": "BZOOH + hv -> BZALD + OH + HO2", - "from": "jch3ooh" - }, - { - "to": "jmekooh", - "__reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", - "from": "jch3ooh" - }, - { - "to": "jtolooh", - "__reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", - "from": "jch3ooh" - }, - { - "to": "jterpooh", - "__reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", - "from": "jch3ooh" - }, - { - "to": "jhonitr", - "__reaction": "HONITR + hv -> NO2 + 0.67*HO2 + 0.33*CH3CHO + 0.33*CH2O + 0.33*CO + 0.33*GLYALD + 0.33*CH3CO3 + 0.17*HYAC + 0.17*CH3COCH3", - "from": "jch2o_a" - }, - { - "to": "jhpald", - "__reaction": "HPALD + hv -> BIGALD3 + OH + HO2", - "from": "jno2", - "scale by": 0.006 - }, - { - "to": "jisopnooh", - "__reaction": "ISOPNOOH + hv -> NO2 + HO2 + ISOPOOH", - "from": "jch3ooh" - }, - { - "to": "jnc4cho", - "__reaction": "NC4CHO + hv -> BIGALD3 + NO2 + HO2", - "from": "jch2o_a" - }, - { - "to": "jnoa", - "__reaction": "NOA + hv -> NO2 + CH2O + CH3CO3", - "from": "jch2o_a" - }, - { - "to": "jnterpooh", - "__reaction": "NTERPOOH + hv -> TERPROD1 + NO2 + OH", - "from": "jch3ooh" - }, - { - "to": "jphenooh", - "__reaction": "PHENOOH + hv -> OH + HO2 + 0.7*GLYOXAL", - "from": "jch3ooh" - }, - { - "to": "jtepomuc", - "__reaction": "TEPOMUC + hv -> 0.5*CH3CO3 + HO2 + 1.5*CO", - "from": "jno2", - "scale by": 0.1 - }, - { - "to": "jterp2ooh", - "__reaction": "TERP2OOH + hv -> OH + 0.375*CH2O + 0.3*CH3COCH3 + 0.25*CO + CO2 + TERPROD2 + HO2 + 0.25*GLYALD", - "from": "jch3ooh" - }, - { - "to": "jterpnit", - "__reaction": "TERPNIT + hv -> TERPROD1 + NO2 + HO2", - "from": "jch3ooh" - }, - { - "to": "jterprd1", - "__reaction": "TERPROD1 + hv -> HO2 + CO + TERPROD2", - "from": "jch3cho" - }, - { - "to": "jterprd2", - "__reaction": "TERPROD2 + hv -> 0.15*RO2 + 0.68*CH2O + 0.8*CO2 + 0.5*CH3COCH3 + 0.65*CH3CO3 + 1.2*HO2 + 1.7*CO", - "from": "jch3cho" - }, - { - "to": "jxylenooh", - "__reaction": "XYLENOOH + hv -> OH + HO2 + 0.34*GLYOXAL + 0.54*CH3COCHO + 0.06*BIGALD1 + 0.2*BIGALD2 + 0.15*BIGALD3 + 0.21*BIGALD4", - "from": "jch3ooh" - }, - { - "to": "jxylolooh", - "__reaction": "XYLOLOOH + hv -> OH + 0.17*GLYOXAL + 0.51*CH3COCHO + HO2", - "from": "jch3ooh" - }, - { - "to": "jsoa1_a1", - "__reaction": "soa1_a1 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa1_a2", - "__reaction": "soa1_a2 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa2_a1", - "__reaction": "soa2_a1 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa2_a2", - "__reaction": "soa2_a2 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa3_a1", - "__reaction": "soa3_a1 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa3_a2", - "__reaction": "soa3_a2 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa4_a1", - "__reaction": "soa4_a1 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa4_a2", - "__reaction": "soa4_a2 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa5_a1", - "__reaction": "soa5_a1 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - }, - { - "to": "jsoa5_a2", - "__reaction": "soa5_a2 + hv -> Products", - "from": "jno2", - "scale by": 0.0004 - } - ] - } - } -} From 3e1f445d7c3a23b798ba6f8e1206f71aed05c0da Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Fri, 24 Feb 2023 16:51:13 -0700 Subject: [PATCH 68/84] add clouds to TUV-x radiators --- cime_config/buildnml | 2 - src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 4 +- src/chemistry/mozart/mo_tuvx.F90 | 117 +++++++++++++++---- 3 files changed, 98 insertions(+), 25 deletions(-) diff --git a/cime_config/buildnml b/cime_config/buildnml index 9b2c7bcb22..f83ad7de2b 100755 --- a/cime_config/buildnml +++ b/cime_config/buildnml @@ -208,8 +208,6 @@ def buildnml(case, caseroot, compname): os.path.join(rundir, "tuvx_MOZART.json")) shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_MOZART_TS1.json"), \ os.path.join(rundir, "tuvx_MOZART_TS1.json")) - shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_MOZART_TSMLT.json"), \ - os.path.join(rundir, "tuvx_MOZART_TSMLT.json")) ############################################################################### def _main_func(): diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index f2da8017cb..991ee8ff90 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -792,8 +792,8 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & !----------------------------------------------------------------- call tuvx_get_photo_rates( state, pbuf, ncol, lchnk, zmid, zint, & tfld, ts, invariants, vmr, col_delta, & - asdir, zen_angle, esfact, & - reaction_rates(:,:,1:phtcnt) ) + asdir, zen_angle, esfact, pdel, cldfr,& + cwat, reaction_rates(:,:,1:phtcnt) ) else !----------------------------------------------------------------- ! ... lookup the photolysis rates from table diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index b653657c8a..039827a93a 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -42,8 +42,9 @@ module mo_tuvx integer, parameter :: PROFILE_INDEX_NO2 = 8 ! Nitrogen dioxide profile index ! Indices for radiator updaters - integer, parameter :: NUM_RADIATORS = 1 ! number of radiators that CAM will update at runtime + integer, parameter :: NUM_RADIATORS = 2 ! number of radiators that CAM will update at runtime integer, parameter :: RADIATOR_INDEX_AEROSOL = 1 ! Aerosol radiator index + integer, parameter :: RADIATOR_INDEX_CLOUDS = 2 ! Cloud radiator index ! Definition of the MS93 wavelength grid TODO add description of this integer, parameter :: NUM_BINS_MS93 = 4 @@ -62,8 +63,13 @@ module mo_tuvx integer :: index_O3 = 0 ! index for O3 in concentration array integer :: index_NO = 0 ! index for NO in concentration array - ! Information needed to access aerosol optical properties - logical :: aerosol_exists = .false. ! indicates whether aerosol optical properties are available + ! Information needed to access aerosol and cloud optical properties + logical :: do_aerosol = .false. ! indicates whether aerosol optical properties + ! are available and should be used in radiative + ! transfer calculations + logical :: do_clouds = .false. ! indicates whether cloud optical properties + ! should be calculated and used in radiative + ! transfer calculations ! Information needed to set extended-UV photo rates logical :: do_euv = .false. ! Indicates whether to calculate @@ -250,8 +256,8 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) class(profile_warehouse_t), pointer :: cam_profiles class(radiator_warehouse_t), pointer :: cam_radiators integer :: pack_size, pos, i_core, i_err - logical :: disable_aerosols - type(string_t) :: required_keys(1), optional_keys(1) + logical :: disable_aerosols, disable_clouds + type(string_t) :: required_keys(1), optional_keys(2) logical, save :: is_initialized = .false. if( .not. tuvx_active ) return @@ -267,6 +273,7 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) ! =============================== required_keys(1) = "aliasing" optional_keys(1) = "disable aerosols" + optional_keys(2) = "disable clouds" #ifndef HAVE_MPI call assert_msg( 113937299, is_main_task, "Multiple tasks present without " & @@ -305,6 +312,8 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) "Bad configuration for CAM TUV-x options." ) call cam_config%get( "disable aerosols", disable_aerosols, my_name, & default = .false. ) + call cam_config%get( "disable clouds", disable_clouds, my_name, & + default = .false. ) call cam_config%get( "aliasing", map_config, my_name ) core => core_t( config_path, cam_grids, cam_profiles, cam_radiators ) call set_photo_rate_map( core, map_config, do_euv, do_jno, jno_index, map ) @@ -312,7 +321,8 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) map%pack_size( mpicom ) + & musica_mpi_pack_size( do_jno, mpicom ) + & musica_mpi_pack_size( jno_index, mpicom ) + & - musica_mpi_pack_size( disable_aerosols, mpicom ) + musica_mpi_pack_size( disable_aerosols, mpicom ) + & + musica_mpi_pack_size( disable_clouds, mpicom ) allocate( buffer( pack_size ) ) pos = 0 call core%mpi_pack( buffer, pos, mpicom ) @@ -320,6 +330,7 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) call musica_mpi_pack( buffer, pos, do_jno, mpicom ) call musica_mpi_pack( buffer, pos, jno_index, mpicom ) call musica_mpi_pack( buffer, pos, disable_aerosols, mpicom ) + call musica_mpi_pack( buffer, pos, disable_clouds, mpicom ) deallocate( core ) end if @@ -353,12 +364,13 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) call musica_mpi_unpack( buffer, pos, do_jno, mpicom ) call musica_mpi_unpack( buffer, pos, jno_index, mpicom ) call musica_mpi_unpack( buffer, pos, disable_aerosols, mpicom ) + call musica_mpi_unpack( buffer, pos, disable_clouds, mpicom ) ! =================================================================== ! Set up connections between CAM and TUV-x input data for each thread ! =================================================================== call create_updaters( tuvx, cam_grids, cam_profiles, cam_radiators, & - disable_aerosols ) + disable_aerosols, disable_clouds ) ! =============================================================== ! Create a working array for calculated photolysis rate constants @@ -443,7 +455,8 @@ end subroutine tuvx_timestep_init subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & height_int, temperature_mid, surface_temperature, fixed_species_conc, & species_vmr, exo_column_conc, surface_albedo, solar_zenith_angle, & - earth_sun_distance, photolysis_rates ) + earth_sun_distance, pressure_delta, cloud_fraction, liquid_water_content, & + photolysis_rates ) use cam_logfile, only : iulog ! log info output unit use chem_mods, only : phtcnt, & ! number of photolysis reactions @@ -475,6 +488,9 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) real(r8), intent(in) :: solar_zenith_angle(ncol) ! solar zenith angle (radians) real(r8), intent(in) :: earth_sun_distance ! Earth-Sun distance (AU) + real(r8), intent(in) :: pressure_delta(pcols,pver) ! pressure delta about midpoints (Pa) + real(r8), intent(in) :: cloud_fraction(ncol,pver) ! cloud fraction (unitless) + 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) @@ -512,7 +528,9 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & call set_temperatures( tuvx, i_col, temperature_mid, surface_temperature ) call set_surface_albedo( tuvx, i_col, surface_albedo ) call set_radiator_profiles( tuvx, i_col, ncol, fixed_species_conc, & - species_vmr, exo_column_conc ) + species_vmr, exo_column_conc, & + pressure_delta(1:ncol,:), cloud_fraction, & + liquid_water_content ) ! =================================================== ! Calculate photolysis rate constants for this column @@ -520,7 +538,7 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & call tuvx%core_%run( solar_zenith_angle = sza, & earth_sun_distance = earth_sun_distance, & photolysis_rate_constants = & - tuvx%photo_rates_(i_col,:,1:tuvx%n_photo_rates_) ) + tuvx%photo_rates_(i_col,:,1:tuvx%n_photo_rates_) ) ! ============================== ! Calculate the extreme-UV rates @@ -661,11 +679,16 @@ subroutine log_initialization( ) write(iulog,*) " - without OpenMP support" #endif write(iulog,*) " - with configuration file: '"//trim( tuvx_config_path )//"'" - if( aerosol_exists ) then + if( do_aerosol ) then write(iulog,*) " - with on-line aerosols" else write(iulog,*) " - without on-line aerosols" end if + if( do_clouds ) then + write(iulog,*) " - with on-line clouds" + else + write(iulog,*) " - without on-line clouds" + end if if( index_N2 > 0 ) write(iulog,*) " - including N2" if( index_O > 0 ) write(iulog,*) " - including O" if( index_O2 > 0 ) write(iulog,*) " - including O2" @@ -1027,6 +1050,13 @@ function get_cam_radiators( grids ) result( radiators ) call radiators%add( host_radiator ) deallocate( host_radiator ) + ! ============== + ! Cloud radiator + ! ============== + host_radiator => radiator_from_host_t( "clouds", height, wavelength ) + call radiators%add( host_radiator ) + deallocate( host_radiator ) + deallocate( height ) deallocate( wavelength ) @@ -1045,7 +1075,8 @@ end function get_cam_radiators ! for runtime access of CAM data ! !----------------------------------------------------------------------- - subroutine create_updaters( this, grids, profiles, radiators, disable_aerosols ) + subroutine create_updaters( this, grids, profiles, radiators, disable_aerosols, & + disable_clouds ) use modal_aer_opt, only : modal_aer_opt_init use ppgrid, only : pcols ! maximum number of columns @@ -1066,6 +1097,7 @@ subroutine create_updaters( this, grids, profiles, radiators, disable_aerosols ) class(profile_warehouse_t), intent(in) :: profiles class(radiator_warehouse_t), intent(in) :: radiators logical, intent(in) :: disable_aerosols + logical, intent(in) :: disable_clouds class(grid_t), pointer :: height, wavelength class(profile_t), pointer :: host_profile @@ -1143,8 +1175,8 @@ subroutine create_updaters( this, grids, profiles, radiators, disable_aerosols ) ! intialize the aerosol optics module ! ==================================================================== call rad_cnst_get_info( 0, nmodes = n_modes ) - if( n_modes > 0 .and. .not. aerosol_exists .and. .not. disable_aerosols ) then - aerosol_exists = .true. + if( n_modes > 0 .and. .not. do_aerosol .and. .not. disable_aerosols ) then + do_aerosol = .true. call modal_aer_opt_init( ) else ! TODO are there default aerosol optical properties that should be used @@ -1159,6 +1191,16 @@ subroutine create_updaters( this, grids, profiles, radiators, disable_aerosols ) call assert( 675200430, found ) nullify( host_radiator ) + ! ===================================== + ! get an updater for the cloud radiator + ! ===================================== + do_clouds = .not. disable_clouds + host_radiator => radiators%get_radiator( "clouds" ) + this%radiators_( RADIATOR_INDEX_CLOUDS ) = & + this%core_%get_updater( host_radiator, found ) + call assert( 993715720, found ) + nullify( host_radiator ) + end subroutine create_updaters !================================================================================================ @@ -1351,7 +1393,8 @@ end subroutine set_et_flux ! and pre-calculated values for O2 and O3 !----------------------------------------------------------------------- subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species_vmr, & - exo_column_conc ) + exo_column_conc, delta_pressure, cloud_fraction, & + liquid_water_content ) use chem_mods, only : gas_pcnst, & ! number of non-fixed species nfs, & ! number of fixed species @@ -1367,10 +1410,17 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species ! ratios (mol mol-1) real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! above column densities ! (molecule cm-2) + real(r8), intent(in) :: delta_pressure(ncol,pver) ! pressure delta about midpoints (Pa) + real(r8), intent(in) :: cloud_fraction(ncol,pver) ! cloud fraction (unitless) + real(r8), intent(in) :: liquid_water_content(ncol,pver) ! liquid water content (kg/kg) + integer :: i_level + real(r8) :: tmp(pver) + real(r8) :: tau(pver+1, size(this%wavelength_mid_values_)) real(r8) :: edges(pver+2), densities(pver+1) real(r8) :: exo_val - real(r8), parameter :: km2cm = 1.0e5 ! conversion from km to cm + real(r8), parameter :: rgrav = 1.0_r8 / 9.80616_r8 ! reciprocal of acceleration by gravity (s/m) + real(r8), parameter :: km2cm = 1.0e5_r8 ! conversion from km to cm ! =========== ! air profile @@ -1444,10 +1494,35 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species ! =============== ! aerosol profile ! =============== - call this%radiators_( RADIATOR_INDEX_AEROSOL )%update( & - optical_depths = this%optical_depth_(i_col,:,:), & - single_scattering_albedos = this%single_scattering_albedo_(i_col,:,:), & - asymmetry_factors = this%asymmetry_factor_(i_col,:,:) ) + if( do_aerosol ) then + call this%radiators_( RADIATOR_INDEX_AEROSOL )%update( & + optical_depths = this%optical_depth_(i_col,:,:), & + single_scattering_albedos = this%single_scattering_albedo_(i_col,:,:), & + asymmetry_factors = this%asymmetry_factor_(i_col,:,:) ) + end if + + ! ============= + ! cloud profile + ! ============= + if( do_clouds ) then + ! =================================================== + ! estimate cloud optical depth as: + ! liquid_water_path * 0.155 * cloud_fraction^(1.5) + ! =================================================== + associate( clouds => cloud_fraction(i_col,:) ) + where( clouds(:) /= 0.0_r8 ) + tmp(:) = ( rgrav * liquid_water_content(i_col,:) * delta_pressure(i_col,:) & + * 1.0e3_r8 / clouds(:) ) * 0.155_r8 * clouds(:)**1.5_r8 + elsewhere + tmp(:) = 0.0_r8 + end where + end associate + do i_level = 1, pver + tau(i_level,:) = tmp(pver-i_level+1) + end do + tau(pver+1,:) = 0.0_r8 + call this%radiators_( RADIATOR_INDEX_CLOUDS )%update( optical_depths = tau ) + end if end subroutine set_radiator_profiles @@ -1486,7 +1561,7 @@ subroutine get_aerosol_optical_properties( this, state, pbuf ) ! ============================================ ! do nothing if no aerosol module is available ! ============================================ - if( .not. aerosol_exists ) return + if( .not. do_aerosol ) return ! TODO just assume all daylight columns for now ! can adjust later if necessary From 6d528a2368c6cff209713955860d996d7a056d09 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Tue, 14 Mar 2023 08:28:58 -0700 Subject: [PATCH 69/84] Add TUV-x config files and use rates to drive chemistry (#7) * add height and temperature updaters to tuv-x wrapper * set up remaining profile updaters * send wavelength grid to TUV-x * add radiator updater for aerosols * send CAM height and temperature data to TUV-x * fix height variables sent to tuvx * send surface albedos to TUV-x * send air density to TUV-x * send O2 and O3 concentrations to TUV-x * sent solar zenith angle to TUV-x * send ET flux to tuv-x * use native TUV-x grid for photo calcs * send aerosol optical properties to tuv-x * output whether using online aerosols; include air scale height for exo values * send above-column O2 and O3 concentrations to TUV-x * add diagnostic TUV-x output; set wavelength edges; fix et flux values * pass earth sun distance to TUV-x * minor clean up * update externals file * update for changes in TUV-x * draft MOZART TUV-x configuration file * draft MOZART-TS1 TUV-x config file * draft MOZART-TSMLT TUV-x config file * draft mapping for tuv-x photo rates * mix tuv map message passing * update TUV-x TS1 config * add euv rate calc calls to tuv-x * fix tuvx_active flag use; fix height values near surface * exclude dark columns from photo calculations * fix flux and density calcs * add missing MOZART photo rates * update for new TUV-x build scripts * update MOZART TUV-x config * update TS1 TUV-x config format * add h2o photo rates to TS1 TUV-x config * add new datasets to TS1 TUV-x config * update TUV-x config format * add remaining TS1 config excluding jno * add jno stub function * finish jno calculation * update TSMLT config file * update tuv-x build flags * Update tuvx_MOZART_TS1.json Fix O2 rate configs * output photo rate diagnostics on CAM vertical grid * fix TUV-x config filees for air radiator; add disable aerosol option for TUV-x * improve et flux edge calcs; use scale height for 02, 03 * fix index offset in photo rate profiles * minor profile calculation changes * add thin top layer to TUV-x grid * adjust top layer density calculations * add in ET mid values * update build; adjust O2 profile * update tuvx config files * use TUV-x from first time step --- cime_config/buildlib | 184 +- cime_config/buildnml | 8 +- cime_config/config_component.xml | 2 +- cime_config/temp_tuvx_config.json | 154 -- cime_config/tuvx_MOZART.json | 540 ++++++ cime_config/tuvx_MOZART_TS1.json | 1486 ++++++++++++++++ cime_config/tuvx_MOZART_TSMLT.json | 1493 +++++++++++++++++ src/chemistry/mozart/chemistry.F90 | 11 +- src/chemistry/mozart/mo_chemini.F90 | 13 +- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 20 +- src/chemistry/mozart/mo_jeuv.F90 | 6 + src/chemistry/mozart/mo_jshort.F90 | 1 + src/chemistry/mozart/mo_tuvx.F90 | 742 +++++++- .../pp_trop_strat_mam4_vbs/mo_imp_sol.F90 | 5 +- 14 files changed, 4359 insertions(+), 306 deletions(-) delete mode 100644 cime_config/temp_tuvx_config.json create mode 100644 cime_config/tuvx_MOZART.json create mode 100644 cime_config/tuvx_MOZART_TS1.json create mode 100644 cime_config/tuvx_MOZART_TSMLT.json diff --git a/cime_config/buildlib b/cime_config/buildlib index 39f4d6906a..c6ccbd9a1c 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -7,6 +7,7 @@ create the cam library #pylint: disable=unused-wildcard-import, bad-whitespace, too-many-locals #pylint: disable=invalid-name import sys, os, filecmp, shutil, imp +from glob import glob _CIMEROOT = os.environ.get("CIMEROOT") if _CIMEROOT is None: @@ -165,22 +166,59 @@ def _build_json_fortran(caseroot, libroot, bldroot): cmake_args += "-DCMAKE_CXX_COMPILER_WORKS=1 " cmake_args += "-DCMAKE_BUILD_TYPE=Release " cmake_args += "-DSKIP_DOC_GEN:BOOL=TRUE " + cmake_args += "-DCMAKE_INSTALL_PREFIX='{}' ".format(libroot) cmake_args += srcpath _run_cmd("cmake {}".format(cmake_args), bldpath) _run_cmd(case.get_value("GMAKE"), bldpath) + _run_cmd("{} install".format(case.get_value("GMAKE")), bldpath) # add json-fortran to include paths incldir = os.environ.get('USER_INCLDIR') if incldir is None: incldir = '' os.environ['USER_INCLDIR'] = incldir + \ - " -I{} ".format(os.path.join(bldroot, "json-fortran", "include")) + " -I{} ".format(_json_fortran_include_dir(libroot)) - # The built library is included in the CAM_LINKED_LIBS entry in - # config_component.xml and is staged here to the lib folder - os.rename(os.path.join(bldroot, "json-fortran", "lib", "libjsonfortran.a"), \ - os.path.join(libroot, "libjsonfortran.a")) + # create simlink to library in folder CIME expects libraries to be in + dst = os.path.join(libroot, "libjsonfortran.a") + if os.path.isfile(dst): + os.remove(dst) + os.symlink(_json_fortran_lib_path(libroot), dst) + +############################################################################### +def _json_fortran_include_dir(libroot): +############################################################################### +# Returns the path to the json-fortran include directory + + jsoninc = os.path.join(_json_fortran_install_dir(libroot), "lib", "") + expect(os.path.exists(jsoninc), \ + "JSON-Fortran include directory not found at {}".format(jsoninc)) + return jsoninc + +############################################################################### +def _json_fortran_lib_path(libroot): +############################################################################### +# Returns the path to the json-fortran library + + jsonlib = os.path.join(_json_fortran_install_dir(libroot), "lib", "libjsonfortran.a") + expect(os.path.exists(jsonlib), \ + "JSON-Fortran library not found at {}".format(jsonlib)) + return jsonlib + +############################################################################### +def _json_fortran_install_dir(libroot): +############################################################################### +# Returns the path to the json-fortran install directory + + jsonpaths = glob(os.path.join(libroot, "jsonfortran*")) + expect(len(jsonpaths)>0, \ + "JSON-Fortran not found at {}".format(libroot)) + expect(len(jsonpaths)<2, \ + "Multiple JSON-Fortran versions found at {}".format(libroot)) + expect(os.path.exists(jsonpaths[0]), \ + "JSON-Fortran install directory not found at {}".format(jsonpaths[0])) + return jsonpaths[0] ############################################################################### def _build_musica_core(caseroot, libroot, bldroot): @@ -193,12 +231,8 @@ def _build_musica_core(caseroot, libroot, bldroot): bldpath = os.path.join(bldroot, "musica-core") if not os.path.exists(bldpath): os.makedirs(bldpath) - jsoninc = os.path.join(bldroot, "json-fortran", "include", "") - expect(os.path.exists(jsoninc), \ - "JSON-Fortran include folder for musica-core build not found at {}".format(jsoninc)) - jsonlib = os.path.join(libroot, "libjsonfortran.a") - expect(os.path.exists(jsonlib), \ - "JSON-Fortran library for musica-core build not found at {}".format(jsonlib)) + jsoninc = _json_fortran_include_dir(libroot) + jsonlib = _json_fortran_lib_path(libroot) srcpath = os.path.abspath(os.path.join(case.get_value("COMP_ROOT_DIR_ATM"), \ "libraries", "musica-core", "")) logger.info("Building musica-core in {} from source in {}\n".format(bldpath, srcpath)) @@ -220,22 +254,75 @@ def _build_musica_core(caseroot, libroot, bldroot): cmake_args += "-DJSON_INCLUDE_DIR={} ".format(jsoninc) cmake_args += "-DJSON_LIB={} ".format(jsonlib) cmake_args += "-DCMAKE_Fortran_FLAGS='{}' ".format(arg_dict["FFLAGS"]) + cmake_args += "-DCMAKE_INSTALL_PREFIX='{}' ".format(libroot) cmake_args += srcpath _run_cmd("cmake {}".format(cmake_args), bldpath) _run_cmd(case.get_value("GMAKE"), bldpath) + _run_cmd("{} install".format(case.get_value("GMAKE")), bldpath) # add musica-core to include paths incldir = os.environ.get('USER_INCLDIR') if incldir is None: incldir = '' os.environ['USER_INCLDIR'] = incldir + \ - " -I{} ".format(os.path.join(bldroot, "musica-core", "include")) + " -I{} ".format(_musica_core_include_dir(libroot)) + + # create simlink to library in folder CIME expects libraries to be in + dst = os.path.join(libroot, "libmusicacore.a") + if os.path.isfile(dst): + os.remove(dst) + os.symlink(_musica_core_lib_path(libroot), dst) + +############################################################################### +def _musica_core_include_dir(libroot): +############################################################################### +# Returns the path to the musica-core include directory + + coreinc = os.path.join(_musica_core_install_dir(libroot), "include", "") + expect(os.path.exists(coreinc), \ + "musica-core include directory not found at {}".format(coreinc)) + return coreinc + +############################################################################### +def _musica_core_lib_path(libroot): +############################################################################### +# Returns the path to the musica-core library - # The musica-core library is included in the CAM_LINKED_LIBS entry in - # config_component.xml and are staged here to the lib folder - os.rename(os.path.join(bldroot, "musica-core", "lib", "libmusica_core.a"), \ - os.path.join(libroot, "libmusica_core.a")) + corelib = os.path.join(_musica_core_install_dir(libroot), "lib64", "libmusicacore.a") + if not os.path.exists(corelib): + corelib = os.path.join(_musica_core_install_dir(libroot), "lib", "libmusicacore.a") + expect(os.path.exists(corelib), \ + "musica-core library not found at {}".format(corelib)) + return corelib + +############################################################################### +def _musica_core_install_dir(libroot): +############################################################################### +# Returns the path to the musica-core install directory + + corepaths = glob(os.path.join(libroot, "musicacore*")) + expect(len(corepaths)>0, \ + "musica-core not found at {}".format(libroot)) + expect(len(corepaths)<2, \ + "Multiple musica-core versions found at {}".format(libroot)) + expect(os.path.exists(corepaths[0]), \ + "musica-core install directory not found at {}".format(corepaths[0])) + return corepaths[0] + +############################################################################### +def _musica_core_package_dir(libroot): +############################################################################### +# Returns the path to the musica-core CMake package + + corepaths = glob(os.path.join(libroot, "musicacore*", "cmake", "musicacore*" )) + expect(len(corepaths)>0, \ + "musica-core package not found at {}".format(libroot)) + expect(len(corepaths)<2, \ + "Multiple musica-core versions found at {}".format(libroot)) + expect(os.path.exists(corepaths[0]), \ + "musica-core package directory not found at {}".format(corepaths[0])) + return corepaths[0] ############################################################################### def _build_tuvx(caseroot, libroot, bldroot): @@ -248,18 +335,9 @@ def _build_tuvx(caseroot, libroot, bldroot): bldpath = os.path.join(bldroot, "tuv-x") if not os.path.exists(bldpath): os.makedirs(bldpath) - jsoninc = os.path.join(bldroot, "json-fortran", "include", "") - expect(os.path.exists(jsoninc), \ - "JSON-Fortran include folder for TUV-x build not found at {}".format(jsoninc)) - jsonlib = os.path.join(libroot, "libjsonfortran.a") - expect(os.path.exists(jsonlib), \ - "JSON-Fortran library for TUV-x build not found at {}".format(jsonlib)) - coreinc = os.path.join(bldroot, "musica-core", "include", "") - expect(os.path.exists(coreinc), \ - "musica-core include folder for TUV-x build not found at {}".format(coreinc)) - corelib = os.path.join(libroot, "libmusica_core.a") - expect(os.path.exists(corelib), \ - "musica-core library for TUV-x build not found at {}".format(corelib)) + jsoninc = _json_fortran_include_dir(libroot) + jsonlib = _json_fortran_lib_path(libroot) + corepackage = _musica_core_package_dir(libroot) srcpath = os.path.abspath(os.path.join(case.get_value("COMP_ROOT_DIR_ATM"), \ "libraries", "tuv-x", "")) logger.info("Building TUV-x in {} from source in {}\n".format(bldpath, srcpath)) @@ -282,13 +360,14 @@ def _build_tuvx(caseroot, libroot, bldroot): cmake_args += "-DENABLE_TESTS=OFF " cmake_args += "-DJSON_INCLUDE_DIR={} ".format(jsoninc) cmake_args += "-DJSON_LIB={} ".format(jsonlib) - cmake_args += "-DMUSICA_CORE_INCLUDE_DIR={} ".format(coreinc) - cmake_args += "-DMUSICA_CORE_LIB={} ".format(corelib) + cmake_args += "-DCMAKE_PREFIX_PATH={} ".format(corepackage) cmake_args += "-DCMAKE_Fortran_FLAGS='{}' ".format(arg_dict["FFLAGS"]) + cmake_args += "-DCMAKE_INSTALL_PREFIX='{}' ".format(libroot) cmake_args += srcpath _run_cmd("cmake {}".format(cmake_args), bldpath) _run_cmd(case.get_value("GMAKE"), bldpath) + _run_cmd("{} install".format(case.get_value("GMAKE")), bldpath) # add TUV-x to include paths incldir = os.environ.get('USER_INCLDIR') @@ -297,10 +376,47 @@ def _build_tuvx(caseroot, libroot, bldroot): os.environ['USER_INCLDIR'] = incldir + \ " -I{} ".format(os.path.join(bldroot, "tuv-x", "include")) - # The TUV-x library is included in the CAM_LINKED_LIBS entry in - # config_component.xml and are staged here to the lib folder - os.rename(os.path.join(bldroot, "tuv-x", "lib", "libtuvx.a"), \ - os.path.join(libroot, "libtuvx.a")) + # create simlink 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) + +############################################################################### +def _tuvx_include_dir(libroot): +############################################################################### +# Returns the path to the TUV-x include directory + + tuvxinc = os.path.join(_tuvx_install_dir(libroot), "include", "") + expect(os.path.exists(tuvxinc), \ + "TUV-x include directory not found at {}".format(tuvxinc)) + return tuvxinc + +############################################################################### +def _tuvx_lib_path(libroot): +############################################################################### +# Returns the path to the TUV-x library + + tuvxlib = os.path.join(_tuvx_install_dir(libroot), "lib64", "libtuvx.a") + if not os.path.exists(tuvxlib): + tuvxlib = os.path.join(_tuvx_install_dir(libroot), "lib", "libtuvx.a") + expect(os.path.exists(tuvxlib), \ + "TUV-x library not found at {}".format(tuvxlib)) + return tuvxlib + +############################################################################### +def _tuvx_install_dir(libroot): +############################################################################### +# Returns the path to the TUV-x install directory + + tuvxpaths = glob(os.path.join(libroot, "tuvx*")) + expect(len(tuvxpaths)>0, \ + "TUV-x not found at {}".format(libroot)) + expect(len(tuvxpaths)<2, \ + "Multiple TUV-x versions found at {}".format(libroot)) + expect(os.path.exists(tuvxpaths[0]), \ + "TUV-x install directory not found at {}".format(tuvxpaths[0])) + return tuvxpaths[0] ############################################################################### diff --git a/cime_config/buildnml b/cime_config/buildnml index e256f2d070..9b2c7bcb22 100755 --- a/cime_config/buildnml +++ b/cime_config/buildnml @@ -204,8 +204,12 @@ def buildnml(case, caseroot, compname): if os.path.exists(dest_data): shutil.rmtree(dest_data) shutil.copytree(os.path.join(srcroot, "libraries", "tuv-x", "data"), dest_data) - shutil.copy2(os.path.join(srcroot, "cime_config", "temp_tuvx_config.json"), \ - os.path.join(rundir, "tuvx_config.json")) + shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_MOZART.json"), \ + os.path.join(rundir, "tuvx_MOZART.json")) + shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_MOZART_TS1.json"), \ + os.path.join(rundir, "tuvx_MOZART_TS1.json")) + shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_MOZART_TSMLT.json"), \ + os.path.join(rundir, "tuvx_MOZART_TSMLT.json")) ############################################################################### def _main_func(): diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index 73f28135cc..8f742460d6 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -193,7 +193,7 @@ char - -ltuvx -lmusica_core -ljsonfortran + -ltuvx -lmusicacore -ljsonfortran build_component_cam env_build.xml diff --git a/cime_config/temp_tuvx_config.json b/cime_config/temp_tuvx_config.json deleted file mode 100644 index 6a81dd6875..0000000000 --- a/cime_config/temp_tuvx_config.json +++ /dev/null @@ -1,154 +0,0 @@ -{ - "__description": "This example configuration includes photolysis and dose rate calculations", - "O2 absorption" : { - "cross section parameters file": "data/cross_sections/O2_parameters.txt" - }, - "grids": { - }, - "profiles": { - }, - "radiative transfer": { - "solver": { - "type": "delta eddington" - }, - "cross sections": [ - { - "name": "air", - "type": "air" - }, - { - "name": "O3", - "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], - "type": "O3" - }, - { - "name": "O2", - "netcdf files": ["data/cross_sections/O2_1.nc"], - "type": "base", - "lower extrapolation": { "type": "boundary" } - } - ], - "radiators": [ - { - "name": "air", - "type": "base", - "cross section": "air", - "vertical profile": "air", - "vertical profile units": "molecule cm-3" - }, - { - "name": "O2", - "type": "base", - "cross section": "O2", - "vertical profile": "O2", - "vertical profile units": "molecule cm-3" - }, - { - "name": "O3", - "type": "base", - "cross section": "O3", - "vertical profile": "O3", - "vertical profile units": "molecule cm-3" - } - ] - }, - "photolysis": { - "reactions": [ - { - "name": "jo3_a", - "__reaction": "O3+hv->O2+O(1D)", - "cross section": { - "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], - "type": "O3" - }, - "quantum yield": { - "type": "O3+hv->O2+O(1D)" - } - }, - { - "name": "jo3_b", - "__reaction": "O3+hv->O2+O(3P)", - "cross section": { - "netcdf files": ["data/cross_sections/O3_1.nc","data/cross_sections/O3_2.nc","data/cross_sections/O3_3.nc","data/cross_sections/O3_4.nc"], - "type": "O3" - }, - "quantum yield": { - "type": "O3+hv->O2+O(3P)" - } - }, - { - "name": "jo2_b", - "__reaction": "O2+hv->O+O", - "cross section": { - "netcdf files": ["data/cross_sections/O2_1.nc"], - "type": "base", - "lower extrapolation": { "type": "boundary" } - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jno2", - "__reaction": "NO2+hv->NO+O(3P)", - "cross section": { - "netcdf files": ["data/cross_sections/NO2_1.nc"], - "type": "NO2 tint" - }, - "quantum yield": { - "netcdf files": ["data/quantum_yields/NO2_1.nc"], - "type": "NO2 tint", - "lower extrapolation": { "type": "boundary" } - } - }, - { - "name": "jacet", - "__reaction": "CH3COCH3+hv->CH3CO+CH3", - "cross section": { - "netcdf files": ["data/cross_sections/CH3COCH3_1.nc"], - "type": "CH3COCH3+hv->CH3CO+CH3" - }, - "quantum yield": { - "type": "CH3COCH3+hv->CH3CO+CH3" - } - }, - { - "name": "jcfcl3", - "__reaction": "CCl3F+hv->Products", - "cross section": { - "netcdf files": ["data/cross_sections/CFC-11_1.nc"], - "type": "CCl3F+hv->Products" - }, - "quantum yield": { - "type": "base", - "constant value": 1.0 - } - }, - { - "name": "jpan1", - "__reaction": "PAN+hv->CH3CO(OO)+NO2", - "cross section": { - "netcdf files": ["data/cross_sections/PAN_1.nc"], - "type": "CH3ONO2+hv->CH3O+NO2" - }, - "quantum yield": { - "type": "base", - "constant value": 0.7 - } - }, - { - "name": "jpan2", - "__reaction": "PAN+hv->CH3CO(O)+NO3", - "cross section": { - "netcdf files": ["data/cross_sections/PAN_1.nc"], - "type": "CH3ONO2+hv->CH3O+NO2" - }, - "quantum yield": { - "type": "base", - "constant value": 0.3 - } - } - ] - } -} diff --git a/cime_config/tuvx_MOZART.json b/cime_config/tuvx_MOZART.json new file mode 100644 index 0000000000..8129266467 --- /dev/null +++ b/cime_config/tuvx_MOZART.json @@ -0,0 +1,540 @@ +{ + "__description": "TUV-x configuration for the MOZART chemical mechanism", + "O2 absorption" : { + "cross section parameters file": "data/cross_sections/O2_parameters.txt" + }, + "grids": [ + ], + "profiles": [ + ], + "radiative transfer": { + "solver": { + "type": "discrete ordinate", + "number of streams": 4 + }, + "cross sections": [ + { + "name": "air", + "type": "air" + }, + { + "name": "O3", + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], + "type": "O3" + }, + { + "name": "O2", + "netcdf files": [ + { + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" }, + "interpolator": { "type": "fractional target" } + } + ], + "type": "base" + } + ], + "radiators": [ + { + "name": "air", + "type": "base", + "treat as air": true, + "cross section": "air", + "vertical profile": "air", + "vertical profile units": "molecule cm-3" + }, + { + "name": "O2", + "type": "base", + "cross section": "O2", + "vertical profile": "O2", + "vertical profile units": "molecule cm-3" + }, + { + "name": "O3", + "type": "base", + "cross section": "O3", + "vertical profile": "O3", + "vertical profile units": "molecule cm-3" + } + ] + }, + "photolysis": { + "reactions": [ + { + "name": "jo2", + "__reaction": "O2 + hv -> O + O", + "cross section": { + "netcdf files": [ + { + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" }, + "interpolator": { "type": "fractional target" } + } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jo1d", + "__reaction": "O3 + hv -> O2 + O(1D)", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(1D)" + } + }, + { + "name": "jo3p", + "__reaction": "O3 + hv -> O2 + O(3P)", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(3P)" + } + }, + { + "name": "jn2o", + "__reaction": "N2O + hv -> N2 + O(1D)", + "cross section": { + "type": "N2O+hv->N2+O(1D)" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jno2", + "__reaction": "NO2 + hv -> NO + O(3P)", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/NO2_1.nc" } + ], + "type": "NO2 tint" + }, + "quantum yield": { + "netcdf files": ["data/quantum_yields/NO2_1.nc"], + "type": "NO2 tint", + "lower extrapolation": { "type": "boundary" } + } + }, + { + "name": "jn2o5", + "__reaction": "N2O5 + hv -> NO2 + NO3", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/N2O5_1.nc" }, + { "file path": "data/cross_sections/N2O5_2.nc" } + ], + "type": "N2O5+hv->NO2+NO3" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhno3", + "__reaction": "HNO3 + hv -> OH + NO2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HNO3_1.nc" } + ], + "type": "HNO3+hv->OH+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jno3_a", + "__reaction": "NO3 + hv -> NO2 + O(3P)", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/NO3_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/NO3-NO2+O(3P)_1.nc" + ], + "type": "tint", + "lower extrapolation": { + "type": "constant", + "value": 1.0 + } + } + }, + { + "name": "jno3_b", + "__reaction": "NO3 + hv -> NO + O2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/NO3_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/NO3-NO+O2_1.nc" + ], + "type": "tint" + } + }, + { + "name": "jch3ooh", + "__reaction": "CH3OOH + hv -> CH3O + OH", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3OOH_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch2o_a", + "__reaction": "CH2O + hv -> H + HCO", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH2O_1.nc" } + ], + "type": "CH2O" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH2O_1.nc" + ], + "type": "base", + "lower extrapolation": { + "type": "boundary" + } + } + }, + { + "name": "jch2o_b", + "__reaction": "CH2O + hv -> H2 + CO", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH2O_1.nc" } + ], + "type": "CH2O" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH2O_1.nc" + ], + "type": "CH2O", + "lower extrapolation": { + "type": "boundary" + } + } + }, + { + "name": "jh2o2", + "__reaction": "H2O2 + hv -> OH + OH", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/H2O2_1.nc" } + ], + "type": "H2O2+hv->OH+OH" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3cho", + "__reaction": "CH3CHO + hv -> CH3 + HCO", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3CHO_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH3CHO_1.nc" + ], + "type": "CH3CHO+hv->CH3+HCO" + } + }, + { + "name": "jpan", + "__reaction": "PAN + hv -> 0.6*CH3CO3 + 0.6*NO2 + 0.4*CH3O2 + 0.4*NO3 + 0.4*CO2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/PAN_1.nc" } + ], + "type": "CH3ONO2+hv->CH3O+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jmvk", + "__reaction": "MVK + hv -> 0.7*C3H6 + 0.7*CO + 0.3*CH3O2 + 0.3*CH3CO3", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/MVK_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "MVK+hv->Products" + } + }, + { + "name": "jacet", + "__reaction": "CH3COCH3 + hv -> CH3CO + CH3", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3COCH3_1.nc" } + ], + "type": "CH3COCH3+hv->CH3CO+CH3" + }, + "quantum yield": { + "type": "CH3COCH3+hv->CH3CO+CH3" + } + }, + { + "name": "jmgly", + "__reaction": "CH3COCHO + hv -> CH3CO3 + CO + HO2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3COCHO_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "CH3COCHO+hv->CH3CO+HCO" + } + }, + { + "name": "jglyald", + "__reaction": "GLYALD + hv -> 2*HO2 + CO + CH2O", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HOCH2CHO_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jglyoxal", + "__reaction": "GLYOXAL + hv -> 2*CO + 2*HO2", + "__comments": "TODO the products of this reaction don't exactly match", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CHOCHO_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CHOCHO-H2_CO_CO_1.nc" + ], + "type": "base", + "lower extrapolation": { + "type": "boundary" + } + } + }, + { + "name": "jho2no2_a", + "__reaction": "HNO4 + hv -> OH + NO3", + "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HNO4_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.2 + } + }, + { + "name": "jho2no2_b", + "__reaction": "HNO4 + hv -> HO2 + NO2", + "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HNO4_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.8 + } + }, + { + "name": "jmacr_a", + "__reaction": "CH2=C(CH3)CHO->1.34HO2+0.66MCO3+1.34CH2O+CH3CO3", + "__comments": "Methacrolein photolysis channel 1", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/Methacrolein_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.005 + } + }, + { + "name": "jmacr_b", + "__reaction": "CH2=C(CH3)CHO->0.66OH+1.34CO", + "__comments": "Methacrolein photolysis channel 2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/Methacrolein_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.005 + } + }, + { + "name": "jhyac", + "__reaction": "CH2(OH)COCH3->CH3CO3+HO2+CH2O", + "__comments": "hydroxy acetone", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/Hydroxyacetone_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.65 + } + } + ] + }, + "__CAM options": { + "aliasing": { + "default matching": "backup", + "pairs": [ + { + "to": "jpooh", + "__reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jch3co3h", + "__reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", + "from": "jh2o2", + "scale by": 0.28 + }, + { + "to": "jmpan", + "__reaction": "MPAN + hv -> MCO3 + NO2", + "from": "jpan" + }, + { + "to": "jc2h5ooh", + "__reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jc3h7ooh", + "__reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", + "from": "jch3ooh" + }, + { + "to": "jrooh", + "__reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", + "from": "jch3ooh" + }, + { + "to": "jxooh", + "__reaction": "XOOH + hv -> OH", + "from": "jch3ooh" + }, + { + "to": "jonitr", + "__reaction": "ONITR + hv -> NO2", + "from": "jch3cho" + }, + { + "to": "jisopooh", + "__reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", + "from": "jch3ooh" + }, + { + "to": "jmek", + "__reaction": "MEK + hv -> CH3CO3 + C2H5O2", + "from": "jacet" + }, + { + "to": "jbigald", + "__reaction": "BIGALD + hv -> .45*CO + .13*GLYOXAL + .56*HO2 + .13*CH3CO3 + .18*CH3COCHO", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jalkooh", + "__reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", + "from": "jch3ooh" + }, + { + "to": "jmekooh", + "__reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", + "from": "jch3ooh" + }, + { + "to": "jtolooh", + "__reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", + "from": "jch3ooh" + }, + { + "to": "jterpooh", + "__reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", + "from": "jch3ooh" + } + ] + } + } +} diff --git a/cime_config/tuvx_MOZART_TS1.json b/cime_config/tuvx_MOZART_TS1.json new file mode 100644 index 0000000000..d9423a5e55 --- /dev/null +++ b/cime_config/tuvx_MOZART_TS1.json @@ -0,0 +1,1486 @@ +{ + "__description": "TUV-x configuration for the MOZART-TS1 chemical mechanism", + "O2 absorption" : { + "cross section parameters file": "data/cross_sections/O2_parameters.txt" + }, + "grids": [ + ], + "profiles": [ + ], + "radiative transfer": { + "solver": { + "type": "discrete ordinate", + "number of streams": 4 + }, + "cross sections": [ + { + "name": "air", + "type": "air" + }, + { + "name": "O3", + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], + "type": "O3" + }, + { + "name": "O2", + "netcdf files": [ + { + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" }, + "interpolator": { "type": "fractional target" } + } + ], + "type": "base" + } + ], + "radiators": [ + { + "name": "air", + "type": "base", + "treat as air": true, + "cross section": "air", + "vertical profile": "air", + "vertical profile units": "molecule cm-3" + }, + { + "name": "O2", + "type": "base", + "cross section": "O2", + "vertical profile": "O2", + "vertical profile units": "molecule cm-3" + }, + { + "name": "O3", + "type": "base", + "cross section": "O3", + "vertical profile": "O3", + "vertical profile units": "molecule cm-3" + } + ] + }, + "photolysis": { + "reactions": [ + { + "name": "jo2_a", + "__reaction": "O2 + hv -> O + O1D", + "cross section": { + "netcdf files": [ + { + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" }, + "interpolator": { "type": "fractional target" } + } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0, + "override bands": [ + { + "band": "lyman-alpha", + "value": 0.53 + }, + { + "band": "schumann-runge continuum", + "value": 1.0 + } + ] + } + }, + { + "name": "jo2_b", + "__reaction": "O2 + hv -> O + O", + "cross section": { + "netcdf files": [ + { + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" }, + "interpolator": { "type": "fractional target" } + } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0, + "override bands": [ + { + "band": "lyman-alpha", + "value": 0.47 + }, + { + "band": "schumann-runge continuum", + "value": 0.0 + } + ] + } + }, + { + "name": "jo3_a", + "__reaction": "O3 + hv -> O2 + O(1D)", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(1D)" + } + }, + { + "name": "jo3_b", + "__reaction": "O3 + hv -> O2 + O(3P)", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(3P)" + } + }, + { + "name": "jn2o", + "__reaction": "N2O + hv -> N2 + O(1D)", + "cross section": { + "type": "N2O+hv->N2+O(1D)" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jno2", + "__reaction": "NO2 + hv -> NO + O(3P)", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/NO2_1.nc" } + ], + "type": "NO2 tint" + }, + "quantum yield": { + "netcdf files": ["data/quantum_yields/NO2_1.nc"], + "type": "NO2 tint", + "lower extrapolation": { "type": "boundary" } + } + }, + { + "name": "jn2o5_a", + "__reaction": "N2O5 + hv -> NO2 + NO3", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/N2O5_1.nc" }, + { "file path": "data/cross_sections/N2O5_2.nc" } + ], + "type": "N2O5+hv->NO2+NO3" + }, + "quantum yield": { + "type": "base", + "netcdf files": [ "data/quantum_yields/N2O5_NO3_NO2.nc" ] + } + }, + { + "name": "jn2o5_b", + "__reaction": "N2O5 + hv -> NO + O + NO3", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/N2O5_1.nc" }, + { "file path": "data/cross_sections/N2O5_2.nc" } + ], + "type": "N2O5+hv->NO2+NO3" + }, + "quantum yield": { + "type": "base", + "netcdf files": [ "data/quantum_yields/N2O5_NO3_NO_O.nc" ] + } + }, + { + "name": "jhno3", + "__reaction": "HNO3 + hv -> OH + NO2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HNO3_1.nc" } + ], + "type": "HNO3+hv->OH+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jno3_a", + "__reaction": "NO3 + hv -> NO2 + O(3P)", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/NO3_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/NO3-NO2+O(3P)_1.nc" + ], + "type": "tint", + "lower extrapolation": { + "type": "constant", + "value": 1.0 + } + } + }, + { + "name": "jno3_b", + "__reaction": "NO3 + hv -> NO + O2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/NO3_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/NO3-NO+O2_1.nc" + ], + "type": "tint" + } + }, + { + "name": "jch3ooh", + "__reaction": "CH3OOH + hv -> CH3O + OH", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3OOH_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch2o_a", + "__reaction": "CH2O + hv -> H + HCO", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH2O_1.nc" } + ], + "type": "CH2O" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH2O_1.nc" + ], + "type": "base", + "lower extrapolation": { + "type": "boundary" + } + } + }, + { + "name": "jch2o_b", + "__reaction": "CH2O + hv -> H2 + CO", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH2O_1.nc" } + ], + "type": "CH2O" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH2O_1.nc" + ], + "type": "CH2O", + "lower extrapolation": { + "type": "boundary" + } + } + }, + { + "name": "jh2o2", + "__reaction": "H2O2 + hv -> OH + OH", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/H2O2_1.nc" } + ], + "type": "H2O2+hv->OH+OH" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3cho", + "__reaction": "CH3CHO + hv -> CH3 + HCO", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3CHO_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH3CHO_1.nc" + ], + "type": "CH3CHO+hv->CH3+HCO" + } + }, + { + "name": "jpan", + "__reaction": "PAN + hv -> 0.6*CH3CO3 + 0.6*NO2 + 0.4*CH3O2 + 0.4*NO3 + 0.4*CO2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/PAN_1.nc" } + ], + "type": "CH3ONO2+hv->CH3O+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jmvk", + "__reaction": "MVK + hv -> 0.7*C3H6 + 0.7*CO + 0.3*CH3O2 + 0.3*CH3CO3", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/MVK_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "MVK+hv->Products" + } + }, + { + "name": "jacet", + "__reaction": "CH3COCH3 + hv -> CH3CO + CH3", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3COCH3_1.nc" } + ], + "type": "CH3COCH3+hv->CH3CO+CH3" + }, + "quantum yield": { + "type": "CH3COCH3+hv->CH3CO+CH3" + } + }, + { + "name": "jmgly", + "__reaction": "CH3COCHO + hv -> CH3CO3 + CO + HO2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3COCHO_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "CH3COCHO+hv->CH3CO+HCO" + } + }, + { + "name": "jglyald", + "__reaction": "GLYALD + hv -> 2*HO2 + CO + CH2O", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HOCH2CHO_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.5 + } + }, + { + "name": "jglyoxal", + "__reaction": "GLYOXAL + hv -> 2*CO + 2*HO2", + "__comments": "TODO the products of this reaction don't exactly match", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CHOCHO_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CHOCHO-H2_CO_CO_1.nc" + ], + "type": "base", + "lower extrapolation": { + "type": "boundary" + } + } + }, + { + "name": "jbrcl", + "__reaction": "BrCl + hv -> Br + Cl", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/BrCl_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jbro", + "__reaction": "BrO + hv -> Br + O", + "cross section": { + "netcdf files": [ + { + "file path": "data/cross_sections/BrO_1.nc", + "interpolator": { + "type": "fractional target", + "fold in": true + } + } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jbrono2_a", + "__reaction": "BrONO2 + hv -> Br + NO3", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/BrONO2_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.85 + } + }, + { + "name": "jbrono2_b", + "__reaction": "BrONO2 + hv -> BrO + NO2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/BrONO2_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.15 + } + }, + { + "name": "jccl4", + "__reaction": "CCl4 + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CCl4_1.nc" } + ], + "type": "CCl4+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcf2clbr", + "__reaction": "CF2BrCl + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CF2BrCl_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcf3br", + "__reaction": "CF3Br + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CF3Br_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcfcl3", + "__reaction": "CCl3F + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CFC-11_1.nc" } + ], + "type": "CCl3F+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcfc113", + "__reaction": "CFC-113 + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CFC-113_1.nc" } + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcfc114", + "__reaction": "CFC-114 + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CFC-114_1.nc" } + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcfc115", + "__reaction": "CFC-115 + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CFC-115_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcf2cl2", + "__reaction": "CCl2F2 + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CFC-12_1.nc" } + ], + "type": "CCl3F+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch2br2", + "__reaction": "CH2BR2 + hv -> 2*BR", + "cross section": { + "type": "temperature based", + "netcdf file": "data/cross_sections/CH2BR2_1.nc", + "parameterization": { + "AA": [ -70.211776, 1.940326e-1, 2.726152e-3, -1.695472e-5, 2.500066e-8 ], + "BB": [ 2.899280, -4.327724e-2, 2.391599e-4, -5.807506e-7, 5.244883e-10 ], + "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], + "minimum wavelength": 210.0, + "maximum wavelength": 290.0, + "temperature ranges": [ + { + "maximum": 209.999999999999, + "fixed value": 210.0 + }, + { + "minimum": 210, + "maximum": 300 + }, + { + "minimum": 300.00000000001, + "fixed value": 300.0 + } + ] + } + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3br", + "__reaction": "CH3Br + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3Br_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3ccl3", + "__reaction": "CH3CCl3+hv->Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3CCl3_1.nc" } + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3cl", + "__reaction": "CH3Cl + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3Cl_1.nc" } + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jchbr3", + "__reaction": "CHBr3 + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CHBr3_1.nc" } + ], + "type": "CHBr3+hv->Products", + "lower extrapolation": { + "type": "boundary" + } + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcl2", + "__reaction": "Cl2 + hv -> Cl + Cl", + "cross section": { + "type": "Cl2+hv->Cl+Cl" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcl2o2", + "__reaction": "ClOOCl + hv -> Cl + ClOO", + "__comments": "TODO - this doesn't exactly match the products in TS1", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/ClOOCl_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jclo", + "__reaction": "ClO + hv -> Cl + O(1D)", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/ClO_1.nc" } + ], + "type": "tint" + }, + "quantum yield": { + "type": "ClO+hv->Cl+O(1D)" + } + }, + { + "name": "jclono2_a", + "__reaction": "ClONO2 + hv -> Cl + NO3", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/ClONO2_1.nc" } + ], + "type": "ClONO2" + }, + "quantum yield": { + "type": "ClONO2+hv->Cl+NO3" + } + }, + { + "name": "jclono2_b", + "__reaction": "ClONO2 + hv -> ClO + NO2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/ClONO2_1.nc" } + ], + "type": "ClONO2" + }, + "quantum yield": { + "type": "ClONO2+hv->ClO+NO2" + } + }, + { + "name": "jcof2", + "__reaction": "CF2O + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CF2O_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcofcl", + "__reaction": "CClFO + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CClFO_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jh2402", + "__reaction": "H2402 + hv -> 2*BR + 2*COF2", + "__comments": "TUV data set name CF2BrCF2Br", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CF2BrCF2Br_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhcfc141b", + "__reaction": "HCFC-141b + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3CFCl2_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhcfc142b", + "__reaction": "HCFC-142b + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3CF2Cl_1.nc" } + ], + "type": "HCFC+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhcfc22", + "__reaction": "HCFC-22 + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CHClF2_1.nc" } + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhcl", + "__reaction": "HCl + hv -> H + Cl", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HCl_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhobr", + "__reaction": "HOBr + hv -> OH + Br", + "cross section": { + "type": "HOBr+hv->OH+Br" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhocl", + "__reaction": "HOCl + hv -> HO + Cl", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HOCl_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "joclo", + "__reaction": "OClO + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/OClO_1.nc" }, + { "file path": "data/cross_sections/OClO_2.nc" }, + { "file path": "data/cross_sections/OClO_3.nc" } + ], + "type": "OClO+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jho2no2_a", + "__reaction": "HNO4 + hv -> OH + NO3", + "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HNO4_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.2 + } + }, + { + "name": "jho2no2_b", + "__reaction": "HNO4 + hv -> HO2 + NO2", + "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HNO4_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.8 + } + }, + { + "name": "jmacr_a", + "__reaction": "CH2=C(CH3)CHO->1.34HO2+0.66MCO3+1.34CH2O+CH3CO3", + "__comments": "Methacrolein photolysis channel 1", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/Methacrolein_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.005 + } + }, + { + "name": "jmacr_b", + "__reaction": "CH2=C(CH3)CHO->0.66OH+1.34CO", + "__comments": "Methacrolein photolysis channel 2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/Methacrolein_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.005 + } + }, + { + "name": "jhyac", + "__reaction": "CH2(OH)COCH3->CH3CO3+HO2+CH2O", + "__comments": "hydroxy acetone TODO: the products of this reaction differ from standalone TUV-x", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/Hydroxyacetone_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.65 + } + }, + { + "name": "jh2o_a", + "__reaction": "H2O + hv -> OH + H", + "cross section": { + "type": "base", + "merge data": true, + "netcdf files": [ + { + "file path": "data/cross_sections/H2O_1.nc", + "zero above": 183.0 + }, + { + "file path": "data/cross_sections/H2O_2.nc", + "zero below": 183.00000000001, + "zero above": 190.0 + }, + { + "file path": "data/cross_sections/H2O_3.nc", + "zero below": 190.00000000001 + } + ] + }, + "quantum yield" : { + "type": "base", + "netcdf files": [ "data/quantum_yields/H2O_H_OH.nc" ] + } + }, + { + "name": "jh2o_b", + "__reaction": "H2O + hv -> H2 + O1D", + "cross section": { + "type": "base", + "merge data": true, + "netcdf files": [ + { + "file path": "data/cross_sections/H2O_1.nc", + "zero above": 183.0 + }, + { + "file path": "data/cross_sections/H2O_2.nc", + "zero below": 183.00000000001, + "zero above": 190.0 + }, + { + "file path": "data/cross_sections/H2O_3.nc", + "zero below": 190.00000000001 + } + ] + }, + "quantum yield" : { + "type": "base", + "netcdf files": [ "data/quantum_yields/H2O_H2_O1D.nc" ] + } + }, + { + "name": "jh2o_c", + "__reaction": "H2O + hv -> 2*H + O", + "cross section": { + "type": "base", + "merge data": true, + "netcdf files": [ + { + "file path": "data/cross_sections/H2O_1.nc", + "zero above": 183.0 + }, + { + "file path": "data/cross_sections/H2O_2.nc", + "zero below": 183.00000000001, + "zero above": 190.0 + }, + { + "file path": "data/cross_sections/H2O_3.nc", + "zero below": 190.00000000001 + } + ] + }, + "quantum yield" : { + "type": "base", + "netcdf files": [ "data/quantum_yields/H2O_2H_O3P.nc" ] + } + }, + { + "name": "jch4_a", + "__reaction": "CH4 + hv -> H + CH3O2", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/CH4_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 0.55 + } + }, + { + "name": "jch4_b", + "__reaction": "CH4 + hv -> 1.44*H2 + 0.18*CH2O + 0.18*O + 0.33*OH + 0.33*H + 0.44*CO2 + 0.38*CO + 0.05*H2O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/CH4_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 0.45 + } + }, + { + "name": "jco2", + "__reaction": "CO2 + hv -> CO + O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/CO2_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhbr", + "__reaction": "HBR + hv -> BR + H", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/HBr_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhf", + "__reaction": "HF + hv -> H + F", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/HF_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jsf6", + "__reaction": "SF6 + hv -> sink", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/SF6_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jh2so4", + "__reaction": "H2SO4 + hv -> SO3 + H2O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/H2SO4_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jocs", + "__reaction": "OCS + hv -> S + CO", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/OCS_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jso", + "__reaction": "SO + hv -> S + O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/SO_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jso2", + "__reaction": "SO2 + hv -> SO + O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/SO2_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jso3", + "__reaction": "SO3 + hv -> SO2 + O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/SO3_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + } + ] + }, + "__CAM options": { + "aliasing": { + "default matching": "backup", + "pairs": [ + { + "to": "jalknit", + "__reaction": "ALKNIT + hv -> NO2 + 0.4*CH3CHO + 0.1*CH2O + 0.25*CH3COCH3 + HO2 + 0.8*MEK", + "from": "jch3ooh" + }, + { + "to": "jpooh", + "__reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jch3co3h", + "__reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", + "from": "jh2o2", + "scale by": 0.28 + }, + { + "to": "jmpan", + "__reaction": "MPAN + hv -> MCO3 + NO2", + "from": "jpan" + }, + { + "to": "jc2h5ooh", + "__reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jc3h7ooh", + "__reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", + "from": "jch3ooh" + }, + { + "to": "jc6h5ooh", + "__reaction": "C6H5OOH + hv -> PHENO + OH", + "from": "jch3ooh" + }, + { + "to": "jeooh", + "__reaction": "EOOH + hv -> EO + OH", + "from": "jch3ooh" + }, + { + "to": "jrooh", + "__reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", + "from": "jch3ooh" + }, + { + "to": "jxooh", + "__reaction": "XOOH + hv -> OH", + "from": "jch3ooh" + }, + { + "to": "jonitr", + "__reaction": "ONITR + hv -> NO2", + "from": "jch3cho" + }, + { + "to": "jisopooh", + "__reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", + "from": "jch3ooh" + }, + { + "to": "jmek", + "__reaction": "MEK + hv -> CH3CO3 + C2H5O2", + "from": "jacet" + }, + { + "to": "jalkooh", + "__reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", + "from": "jch3ooh" + }, + { + "to": "jbenzooh", + "__reaction": "BENZOOH + hv -> OH + GLYOXAL + 0.5*BIGALD1 + HO2", + "from": "jch3ooh" + }, + { + "to": "jbepomuc", + "__reaction": "BEPOMUC + hv -> BIGALD1 + 1.5*HO2 + 1.5*CO", + "from": "jno2", + "scale by": 0.1 + }, + { + "to": "jbigald", + "__reaction": "BIGALD + hv -> 0.45*CO + 0.13*GLYOXAL + 0.56*HO2 + 0.13*CH3CO3 + 0.18*CH3COCHO", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald1", + "__reaction": "BIGALD1 + hv -> 0.6*MALO2 + HO2", + "from": "jno2", + "scale by": 0.14 + }, + { + "to": "jbigald2", + "__reaction": "BIGALD2 + hv -> 0.6*HO2 + 0.6*DICARBO2", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald3", + "__reaction": "BIGALD3 + hv -> 0.6*HO2 + 0.6*CO + 0.6*MDIALO2", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald4", + "__reaction": "BIGALD4 + hv -> HO2 + CO + CH3COCHO + CH3CO3", + "from": "jno2", + "scale by": 0.006 + }, + { + "to": "jbzooh", + "__reaction": "BZOOH + hv -> BZALD + OH + HO2", + "from": "jch3ooh" + }, + { + "to": "jmekooh", + "__reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", + "from": "jch3ooh" + }, + { + "to": "jtolooh", + "__reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", + "from": "jch3ooh" + }, + { + "to": "jterpooh", + "__reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", + "from": "jch3ooh" + }, + { + "to": "jhonitr", + "__reaction": "HONITR + hv -> NO2 + 0.67*HO2 + 0.33*CH3CHO + 0.33*CH2O + 0.33*CO + 0.33*GLYALD + 0.33*CH3CO3 + 0.17*HYAC + 0.17*CH3COCH3", + "from": "jch2o_a" + }, + { + "to": "jhpald", + "__reaction": "HPALD + hv -> BIGALD3 + OH + HO2", + "from": "jno2", + "scale by": 0.006 + }, + { + "to": "jisopnooh", + "__reaction": "ISOPNOOH + hv -> NO2 + HO2 + ISOPOOH", + "from": "jch3ooh" + }, + { + "to": "jnc4cho", + "__reaction": "NC4CHO + hv -> BIGALD3 + NO2 + HO2", + "from": "jch2o_a" + }, + { + "to": "jnoa", + "__reaction": "NOA + hv -> NO2 + CH2O + CH3CO3", + "from": "jch2o_a" + }, + { + "to": "jnterpooh", + "__reaction": "NTERPOOH + hv -> TERPROD1 + NO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jphenooh", + "__reaction": "PHENOOH + hv -> OH + HO2 + 0.7*GLYOXAL", + "from": "jch3ooh" + }, + { + "to": "jtepomuc", + "__reaction": "TEPOMUC + hv -> 0.5*CH3CO3 + HO2 + 1.5*CO", + "from": "jno2", + "scale by": 0.1 + }, + { + "to": "jterp2ooh", + "__reaction": "TERP2OOH + hv -> OH + 0.375*CH2O + 0.3*CH3COCH3 + 0.25*CO + CO2 + TERPROD2 + HO2 + 0.25*GLYALD", + "from": "jch3ooh" + }, + { + "to": "jterpnit", + "__reaction": "TERPNIT + hv -> TERPROD1 + NO2 + HO2", + "from": "jch3ooh" + }, + { + "to": "jterprd1", + "__reaction": "TERPROD1 + hv -> HO2 + CO + TERPROD2", + "from": "jch3cho" + }, + { + "to": "jterprd2", + "__reaction": "TERPROD2 + hv -> 0.15*RO2 + 0.68*CH2O + 0.8*CO2 + 0.5*CH3COCH3 + 0.65*CH3CO3 + 1.2*HO2 + 1.7*CO", + "from": "jch3cho" + }, + { + "to": "jxylenooh", + "__reaction": "XYLENOOH + hv -> OH + HO2 + 0.34*GLYOXAL + 0.54*CH3COCHO + 0.06*BIGALD1 + 0.2*BIGALD2 + 0.15*BIGALD3 + 0.21*BIGALD4", + "from": "jch3ooh" + }, + { + "to": "jxylolooh", + "__reaction": "XYLOLOOH + hv -> OH + 0.17*GLYOXAL + 0.51*CH3COCHO + HO2", + "from": "jch3ooh" + }, + { + "to": "jsoa1_a1", + "__reaction": "soa1_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa1_a2", + "__reaction": "soa1_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa2_a1", + "__reaction": "soa2_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa2_a2", + "__reaction": "soa2_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa3_a1", + "__reaction": "soa3_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa3_a2", + "__reaction": "soa3_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa4_a1", + "__reaction": "soa4_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa4_a2", + "__reaction": "soa4_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa5_a1", + "__reaction": "soa5_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa5_a2", + "__reaction": "soa5_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + } + ] + } + } +} diff --git a/cime_config/tuvx_MOZART_TSMLT.json b/cime_config/tuvx_MOZART_TSMLT.json new file mode 100644 index 0000000000..2b340e5f1e --- /dev/null +++ b/cime_config/tuvx_MOZART_TSMLT.json @@ -0,0 +1,1493 @@ +{ + "__description": "TUV-x configuration for the MOZART-TSMLT chemical mechanism", + "O2 absorption" : { + "cross section parameters file": "data/cross_sections/O2_parameters.txt" + }, + "grids": [ + ], + "profiles": [ + ], + "radiative transfer": { + "solver": { + "type": "discrete ordinate", + "number of streams": 4 + }, + "cross sections": [ + { + "name": "air", + "type": "air" + }, + { + "name": "O3", + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], + "type": "O3" + }, + { + "name": "O2", + "netcdf files": [ + { + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" }, + "interpolator": { "type": "fractional target" } + } + ], + "type": "base" + } + ], + "radiators": [ + { + "name": "air", + "type": "base", + "treat as air": true, + "cross section": "air", + "vertical profile": "air", + "vertical profile units": "molecule cm-3" + }, + { + "name": "O2", + "type": "base", + "cross section": "O2", + "vertical profile": "O2", + "vertical profile units": "molecule cm-3" + }, + { + "name": "O3", + "type": "base", + "cross section": "O3", + "vertical profile": "O3", + "vertical profile units": "molecule cm-3" + } + ] + }, + "photolysis": { + "reactions": [ + { + "name": "jo2_a", + "__reaction": "O2 + hv -> O + O1D", + "cross section": { + "netcdf files": [ + { + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" }, + "interpolator": { "type": "fractional target" } + } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0, + "override bands": [ + { + "band": "lyman-alpha", + "value": 0.53 + }, + { + "band": "schumann-runge continuum", + "value": 1.0 + } + ] + } + }, + { + "name": "jo2_b", + "__reaction": "O2 + hv -> O + O", + "cross section": { + "netcdf files": [ + { + "file path": "data/cross_sections/O2_1.nc", + "lower extrapolation": { "type": "boundary" }, + "interpolator": { "type": "fractional target" } + } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0, + "override bands": [ + { + "band": "lyman-alpha", + "value": 0.47 + }, + { + "band": "schumann-runge continuum", + "value": 0.0 + } + ] + } + }, + { + "name": "jo3_a", + "__reaction": "O3 + hv -> O2 + O(1D)", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(1D)" + } + }, + { + "name": "jo3_b", + "__reaction": "O3 + hv -> O2 + O(3P)", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/O3_1.nc" }, + { "file path": "data/cross_sections/O3_2.nc" }, + { "file path": "data/cross_sections/O3_3.nc" }, + { "file path": "data/cross_sections/O3_4.nc" } + ], + "type": "O3" + }, + "quantum yield": { + "type": "O3+hv->O2+O(3P)" + } + }, + { + "name": "jn2o", + "__reaction": "N2O + hv -> N2 + O(1D)", + "cross section": { + "type": "N2O+hv->N2+O(1D)" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jno2", + "__reaction": "NO2 + hv -> NO + O(3P)", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/NO2_1.nc" } + ], + "type": "NO2 tint" + }, + "quantum yield": { + "netcdf files": ["data/quantum_yields/NO2_1.nc"], + "type": "NO2 tint", + "lower extrapolation": { "type": "boundary" } + } + }, + { + "name": "jn2o5_a", + "__reaction": "N2O5 + hv -> NO2 + NO3", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/N2O5_1.nc" }, + { "file path": "data/cross_sections/N2O5_2.nc" } + ], + "type": "N2O5+hv->NO2+NO3" + }, + "quantum yield": { + "type": "base", + "netcdf files": [ "data/quantum_yields/N2O5_NO3_NO2.nc" ] + } + }, + { + "name": "jn2o5_b", + "__reaction": "N2O5 + hv -> NO + O + NO3", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/N2O5_1.nc" }, + { "file path": "data/cross_sections/N2O5_2.nc" } + ], + "type": "N2O5+hv->NO2+NO3" + }, + "quantum yield": { + "type": "base", + "netcdf files": [ "data/quantum_yields/N2O5_NO3_NO_O.nc" ] + } + }, + { + "name": "jhno3", + "__reaction": "HNO3 + hv -> OH + NO2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HNO3_1.nc" } + ], + "type": "HNO3+hv->OH+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jno3_a", + "__reaction": "NO3 + hv -> NO2 + O(3P)", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/NO3_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/NO3-NO2+O(3P)_1.nc" + ], + "type": "tint", + "lower extrapolation": { + "type": "constant", + "value": 1.0 + } + } + }, + { + "name": "jno3_b", + "__reaction": "NO3 + hv -> NO + O2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/NO3_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/NO3-NO+O2_1.nc" + ], + "type": "tint" + } + }, + { + "name": "jch3ooh", + "__reaction": "CH3OOH + hv -> CH3O + OH", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3OOH_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch2o_a", + "__reaction": "CH2O + hv -> H + HCO", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH2O_1.nc" } + ], + "type": "CH2O" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH2O_1.nc" + ], + "type": "base", + "lower extrapolation": { + "type": "boundary" + } + } + }, + { + "name": "jch2o_b", + "__reaction": "CH2O + hv -> H2 + CO", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH2O_1.nc" } + ], + "type": "CH2O" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH2O_1.nc" + ], + "type": "CH2O", + "lower extrapolation": { + "type": "boundary" + } + } + }, + { + "name": "jh2o2", + "__reaction": "H2O2 + hv -> OH + OH", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/H2O2_1.nc" } + ], + "type": "H2O2+hv->OH+OH" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3cho", + "__reaction": "CH3CHO + hv -> CH3 + HCO", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3CHO_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CH3CHO_1.nc" + ], + "type": "CH3CHO+hv->CH3+HCO" + } + }, + { + "name": "jpan", + "__reaction": "PAN + hv -> 0.6*CH3CO3 + 0.6*NO2 + 0.4*CH3O2 + 0.4*NO3 + 0.4*CO2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/PAN_1.nc" } + ], + "type": "CH3ONO2+hv->CH3O+NO2" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jmvk", + "__reaction": "MVK + hv -> 0.7*C3H6 + 0.7*CO + 0.3*CH3O2 + 0.3*CH3CO3", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/MVK_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "MVK+hv->Products" + } + }, + { + "name": "jacet", + "__reaction": "CH3COCH3 + hv -> CH3CO + CH3", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3COCH3_1.nc" } + ], + "type": "CH3COCH3+hv->CH3CO+CH3" + }, + "quantum yield": { + "type": "CH3COCH3+hv->CH3CO+CH3" + } + }, + { + "name": "jmgly", + "__reaction": "CH3COCHO + hv -> CH3CO3 + CO + HO2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3COCHO_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "CH3COCHO+hv->CH3CO+HCO" + } + }, + { + "name": "jglyald", + "__reaction": "GLYALD + hv -> 2*HO2 + CO + CH2O", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HOCH2CHO_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.5 + } + }, + { + "name": "jglyoxal", + "__reaction": "GLYOXAL + hv -> 2*CO + 2*HO2", + "__comments": "NOTE the products of this reaction don't exactly match", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CHOCHO_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "netcdf files": [ + "data/quantum_yields/CHOCHO-H2_CO_CO_1.nc" + ], + "type": "base", + "lower extrapolation": { + "type": "boundary" + } + } + }, + { + "name": "jbrcl", + "__reaction": "BrCl + hv -> Br + Cl", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/BrCl_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jbro", + "__reaction": "BrO + hv -> Br + O", + "cross section": { + "netcdf files": [ + { + "file path": "data/cross_sections/BrO_1.nc", + "interpolator": { + "type": "fractional target", + "fold in": true + } + } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jbrono2_a", + "__reaction": "BrONO2 + hv -> Br + NO3", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/BrONO2_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.85 + } + }, + { + "name": "jbrono2_b", + "__reaction": "BrONO2 + hv -> BrO + NO2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/BrONO2_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.15 + } + }, + { + "name": "jccl4", + "__reaction": "CCl4 + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CCl4_1.nc" } + ], + "type": "CCl4+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcf2clbr", + "__reaction": "CF2BrCl + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CF2BrCl_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcf3br", + "__reaction": "CF3Br + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CF3Br_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcfcl3", + "__reaction": "CCl3F + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CFC-11_1.nc" } + ], + "type": "CCl3F+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcfc113", + "__reaction": "CFC-113 + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CFC-113_1.nc" } + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcfc114", + "__reaction": "CFC-114 + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CFC-114_1.nc" } + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcfc115", + "__reaction": "CFC-115 + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CFC-115_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcf2cl2", + "__reaction": "CCl2F2 + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CFC-12_1.nc" } + ], + "type": "CCl3F+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch2br2", + "__reaction": "CH2BR2 + hv -> 2*BR", + "cross section": { + "type": "temperature based", + "netcdf file": "data/cross_sections/CH2BR2_1.nc", + "parameterization": { + "AA": [ -70.211776, 1.940326e-1, 2.726152e-3, -1.695472e-5, 2.500066e-8 ], + "BB": [ 2.899280, -4.327724e-2, 2.391599e-4, -5.807506e-7, 5.244883e-10 ], + "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], + "minimum wavelength": 210.0, + "maximum wavelength": 290.0, + "temperature ranges": [ + { + "maximum": 209.999999999999, + "fixed value": 210.0 + }, + { + "minimum": 210, + "maximum": 300 + }, + { + "minimum": 300.00000000001, + "fixed value": 300.0 + } + ] + } + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3br", + "__reaction": "CH3Br + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3Br_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3ccl3", + "__reaction": "CH3CCl3+hv->Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3CCl3_1.nc" } + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jch3cl", + "__reaction": "CH3Cl + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3Cl_1.nc" } + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jchbr3", + "__reaction": "CHBr3 + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CHBr3_1.nc" } + ], + "type": "CHBr3+hv->Products", + "lower extrapolation": { + "type": "boundary" + } + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcl2", + "__reaction": "Cl2 + hv -> Cl + Cl", + "cross section": { + "type": "Cl2+hv->Cl+Cl" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcl2o2", + "__reaction": "ClOOCl + hv -> Cl + ClOO", + "__comments": "TODO - this doesn't exactly match the products in TS1", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/ClOOCl_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jclo", + "__reaction": "ClO + hv -> Cl + O(1D)", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/ClO_1.nc" } + ], + "type": "tint" + }, + "quantum yield": { + "type": "ClO+hv->Cl+O(1D)" + } + }, + { + "name": "jclono2_a", + "__reaction": "ClONO2 + hv -> Cl + NO3", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/ClONO2_1.nc" } + ], + "type": "ClONO2" + }, + "quantum yield": { + "type": "ClONO2+hv->Cl+NO3" + } + }, + { + "name": "jclono2_b", + "__reaction": "ClONO2 + hv -> ClO + NO2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/ClONO2_1.nc" } + ], + "type": "ClONO2" + }, + "quantum yield": { + "type": "ClONO2+hv->ClO+NO2" + } + }, + { + "name": "jcof2", + "__reaction": "CF2O + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CF2O_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jcofcl", + "__reaction": "CClFO + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CClFO_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jh2402", + "__reaction": "H2402 + hv -> 2*BR + 2*COF2", + "__comments": "TUV data set name CF2BrCF2Br", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CF2BrCF2Br_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhcfc141b", + "__reaction": "HCFC-141b + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3CFCl2_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhcfc142b", + "__reaction": "HCFC-142b + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CH3CF2Cl_1.nc" } + ], + "type": "HCFC+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhcfc22", + "__reaction": "HCFC-22 + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/CHClF2_1.nc" } + ], + "type": "tint" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhcl", + "__reaction": "HCl + hv -> H + Cl", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HCl_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhobr", + "__reaction": "HOBr + hv -> OH + Br", + "cross section": { + "type": "HOBr+hv->OH+Br" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhocl", + "__reaction": "HOCl + hv -> HO + Cl", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HOCl_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "joclo", + "__reaction": "OClO + hv -> Products", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/OClO_1.nc" }, + { "file path": "data/cross_sections/OClO_2.nc" }, + { "file path": "data/cross_sections/OClO_3.nc" } + ], + "type": "OClO+hv->Products" + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jho2no2_a", + "__reaction": "HNO4 + hv -> OH + NO3", + "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HNO4_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.2 + } + }, + { + "name": "jho2no2_b", + "__reaction": "HNO4 + hv -> HO2 + NO2", + "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/HNO4_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.8 + } + }, + { + "name": "jmacr_a", + "__reaction": "CH2=C(CH3)CHO->1.34HO2+0.66MCO3+1.34CH2O+CH3CO3", + "__comments": "Methacrolein photolysis channel 1", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/Methacrolein_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.005 + } + }, + { + "name": "jmacr_b", + "__reaction": "CH2=C(CH3)CHO->0.66OH+1.34CO", + "__comments": "Methacrolein photolysis channel 2", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/Methacrolein_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.005 + } + }, + { + "name": "jhyac", + "__reaction": "CH2(OH)COCH3->CH3CO3+HO2+CH2O", + "__comments": "hydroxy acetone TODO: the products of this reaction differ from standalone TUV-x", + "cross section": { + "netcdf files": [ + { "file path": "data/cross_sections/Hydroxyacetone_1.nc" } + ], + "type": "base" + }, + "quantum yield": { + "type": "base", + "constant value": 0.65 + } + }, + { + "name": "jh2o_a", + "__reaction": "H2O + hv -> OH + H", + "cross section": { + "type": "base", + "merge data": true, + "netcdf files": [ + { + "file path": "data/cross_sections/H2O_1.nc", + "zero above": 183.0 + }, + { + "file path": "data/cross_sections/H2O_2.nc", + "zero below": 183.00000000001, + "zero above": 190.0 + }, + { + "file path": "data/cross_sections/H2O_3.nc", + "zero below": 190.00000000001 + } + ] + }, + "quantum yield" : { + "type": "base", + "netcdf files": [ "data/quantum_yields/H2O_H_OH.nc" ] + } + }, + { + "name": "jh2o_b", + "__reaction": "H2O + hv -> H2 + O1D", + "cross section": { + "type": "base", + "merge data": true, + "netcdf files": [ + { + "file path": "data/cross_sections/H2O_1.nc", + "zero above": 183.0 + }, + { + "file path": "data/cross_sections/H2O_2.nc", + "zero below": 183.00000000001, + "zero above": 190.0 + }, + { + "file path": "data/cross_sections/H2O_3.nc", + "zero below": 190.00000000001 + } + ] + }, + "quantum yield" : { + "type": "base", + "netcdf files": [ "data/quantum_yields/H2O_H2_O1D.nc" ] + } + }, + { + "name": "jh2o_c", + "__reaction": "H2O + hv -> 2*H + O", + "cross section": { + "type": "base", + "merge data": true, + "netcdf files": [ + { + "file path": "data/cross_sections/H2O_1.nc", + "zero above": 183.0 + }, + { + "file path": "data/cross_sections/H2O_2.nc", + "zero below": 183.00000000001, + "zero above": 190.0 + }, + { + "file path": "data/cross_sections/H2O_3.nc", + "zero below": 190.00000000001 + } + ] + }, + "quantum yield" : { + "type": "base", + "netcdf files": [ "data/quantum_yields/H2O_2H_O3P.nc" ] + } + }, + { + "name": "jch4_a", + "__reaction": "CH4 + hv -> H + CH3O2", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/CH4_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 0.55 + } + }, + { + "name": "jch4_b", + "__reaction": "CH4 + hv -> 1.44*H2 + 0.18*CH2O + 0.18*O + 0.33*OH + 0.33*H + 0.44*CO2 + 0.38*CO + 0.05*H2O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/CH4_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 0.45 + } + }, + { + "name": "jco2", + "__reaction": "CO2 + hv -> CO + O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/CO2_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhbr", + "__reaction": "HBR + hv -> BR + H", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/HBr_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jhf", + "__reaction": "HF + hv -> H + F", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/HF_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jsf6", + "__reaction": "SF6 + hv -> sink", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/SF6_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jh2so4", + "__reaction": "H2SO4 + hv -> SO3 + H2O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/H2SO4_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jocs", + "__reaction": "OCS + hv -> S + CO", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/OCS_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jso", + "__reaction": "SO + hv -> S + O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/SO_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jso2", + "__reaction": "SO2 + hv -> SO + O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/SO2_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + }, + { + "name": "jso3", + "__reaction": "SO3 + hv -> SO2 + O", + "cross section": { + "type": "base", + "netcdf files": [ + { "file path": "data/cross_sections/SO3_1.nc" } + ] + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } + } + ] + }, + "__CAM options": { + "aliasing": { + "default matching": "backup", + "pairs": [ + { + "to": "jno_i", + "__reaction": "NO + hv -> NOp + e", + "__comments": "TODO find this rate", + "from": "jo2_b", + "scale by": 0.0 + }, + { + "to": "jalknit", + "__reaction": "ALKNIT + hv -> NO2 + 0.4*CH3CHO + 0.1*CH2O + 0.25*CH3COCH3 + HO2 + 0.8*MEK", + "from": "jch3ooh" + }, + { + "to": "jpooh", + "__reaction": "POOH (C3H6OHOOH) + hv -> CH3CHO + CH2O + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jch3co3h", + "__reaction": "CH3COOOH + hv -> CH3O2 + OH + CO2", + "from": "jh2o2", + "scale by": 0.28 + }, + { + "to": "jmpan", + "__reaction": "MPAN + hv -> MCO3 + NO2", + "from": "jpan" + }, + { + "to": "jc2h5ooh", + "__reaction": "C2H5OOH + hv -> CH3CHO + HO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jc3h7ooh", + "__reaction": "C3H7OOH + hv -> 0.82*CH3COCH3 + OH + HO2", + "from": "jch3ooh" + }, + { + "to": "jc6h5ooh", + "__reaction": "C6H5OOH + hv -> PHENO + OH", + "from": "jch3ooh" + }, + { + "to": "jeooh", + "__reaction": "EOOH + hv -> EO + OH", + "from": "jch3ooh" + }, + { + "to": "jrooh", + "__reaction": "ROOH + hv -> CH3CO3 + CH2O + OH", + "from": "jch3ooh" + }, + { + "to": "jxooh", + "__reaction": "XOOH + hv -> OH", + "from": "jch3ooh" + }, + { + "to": "jonitr", + "__reaction": "ONITR + hv -> NO2", + "from": "jch3cho" + }, + { + "to": "jisopooh", + "__reaction": "ISOPOOH + hv -> 0.402*MVK + 0.288*MACR + 0.69*CH2O + HO2", + "from": "jch3ooh" + }, + { + "to": "jmek", + "__reaction": "MEK + hv -> CH3CO3 + C2H5O2", + "from": "jacet" + }, + { + "to": "jalkooh", + "__reaction": "ALKOOH + hv -> .4*CH3CHO + .1*CH2O + .25*CH3COCH3 + .9*HO2 + .8*MEK + OH", + "from": "jch3ooh" + }, + { + "to": "jbenzooh", + "__reaction": "BENZOOH + hv -> OH + GLYOXAL + 0.5*BIGALD1 + HO2", + "from": "jch3ooh" + }, + { + "to": "jbepomuc", + "__reaction": "BEPOMUC + hv -> BIGALD1 + 1.5*HO2 + 1.5*CO", + "from": "jno2", + "scale by": 0.1 + }, + { + "to": "jbigald", + "__reaction": "BIGALD + hv -> 0.45*CO + 0.13*GLYOXAL + 0.56*HO2 + 0.13*CH3CO3 + 0.18*CH3COCHO", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald1", + "__reaction": "BIGALD1 + hv -> 0.6*MALO2 + HO2", + "from": "jno2", + "scale by": 0.14 + }, + { + "to": "jbigald2", + "__reaction": "BIGALD2 + hv -> 0.6*HO2 + 0.6*DICARBO2", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald3", + "__reaction": "BIGALD3 + hv -> 0.6*HO2 + 0.6*CO + 0.6*MDIALO2", + "from": "jno2", + "scale by": 0.2 + }, + { + "to": "jbigald4", + "__reaction": "BIGALD4 + hv -> HO2 + CO + CH3COCHO + CH3CO3", + "from": "jno2", + "scale by": 0.006 + }, + { + "to": "jbzooh", + "__reaction": "BZOOH + hv -> BZALD + OH + HO2", + "from": "jch3ooh" + }, + { + "to": "jmekooh", + "__reaction": "MEKOOH + hv -> OH + CH3CO3 + CH3CHO", + "from": "jch3ooh" + }, + { + "to": "jtolooh", + "__reaction": "TOLOOH + hv -> OH + .45*GLYOXAL + .45*CH3COCHO + .9*BIGALD", + "from": "jch3ooh" + }, + { + "to": "jterpooh", + "__reaction": "TERPOOH + hv -> OH + .1*CH3COCH3 + HO2 + MVK + MACR", + "from": "jch3ooh" + }, + { + "to": "jhonitr", + "__reaction": "HONITR + hv -> NO2 + 0.67*HO2 + 0.33*CH3CHO + 0.33*CH2O + 0.33*CO + 0.33*GLYALD + 0.33*CH3CO3 + 0.17*HYAC + 0.17*CH3COCH3", + "from": "jch2o_a" + }, + { + "to": "jhpald", + "__reaction": "HPALD + hv -> BIGALD3 + OH + HO2", + "from": "jno2", + "scale by": 0.006 + }, + { + "to": "jisopnooh", + "__reaction": "ISOPNOOH + hv -> NO2 + HO2 + ISOPOOH", + "from": "jch3ooh" + }, + { + "to": "jnc4cho", + "__reaction": "NC4CHO + hv -> BIGALD3 + NO2 + HO2", + "from": "jch2o_a" + }, + { + "to": "jnoa", + "__reaction": "NOA + hv -> NO2 + CH2O + CH3CO3", + "from": "jch2o_a" + }, + { + "to": "jnterpooh", + "__reaction": "NTERPOOH + hv -> TERPROD1 + NO2 + OH", + "from": "jch3ooh" + }, + { + "to": "jphenooh", + "__reaction": "PHENOOH + hv -> OH + HO2 + 0.7*GLYOXAL", + "from": "jch3ooh" + }, + { + "to": "jtepomuc", + "__reaction": "TEPOMUC + hv -> 0.5*CH3CO3 + HO2 + 1.5*CO", + "from": "jno2", + "scale by": 0.1 + }, + { + "to": "jterp2ooh", + "__reaction": "TERP2OOH + hv -> OH + 0.375*CH2O + 0.3*CH3COCH3 + 0.25*CO + CO2 + TERPROD2 + HO2 + 0.25*GLYALD", + "from": "jch3ooh" + }, + { + "to": "jterpnit", + "__reaction": "TERPNIT + hv -> TERPROD1 + NO2 + HO2", + "from": "jch3ooh" + }, + { + "to": "jterprd1", + "__reaction": "TERPROD1 + hv -> HO2 + CO + TERPROD2", + "from": "jch3cho" + }, + { + "to": "jterprd2", + "__reaction": "TERPROD2 + hv -> 0.15*RO2 + 0.68*CH2O + 0.8*CO2 + 0.5*CH3COCH3 + 0.65*CH3CO3 + 1.2*HO2 + 1.7*CO", + "from": "jch3cho" + }, + { + "to": "jxylenooh", + "__reaction": "XYLENOOH + hv -> OH + HO2 + 0.34*GLYOXAL + 0.54*CH3COCHO + 0.06*BIGALD1 + 0.2*BIGALD2 + 0.15*BIGALD3 + 0.21*BIGALD4", + "from": "jch3ooh" + }, + { + "to": "jxylolooh", + "__reaction": "XYLOLOOH + hv -> OH + 0.17*GLYOXAL + 0.51*CH3COCHO + HO2", + "from": "jch3ooh" + }, + { + "to": "jsoa1_a1", + "__reaction": "soa1_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa1_a2", + "__reaction": "soa1_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa2_a1", + "__reaction": "soa2_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa2_a2", + "__reaction": "soa2_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa3_a1", + "__reaction": "soa3_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa3_a2", + "__reaction": "soa3_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa4_a1", + "__reaction": "soa4_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa4_a2", + "__reaction": "soa4_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa5_a1", + "__reaction": "soa5_a1 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + }, + { + "to": "jsoa5_a2", + "__reaction": "soa5_a2 + hv -> Products", + "from": "jno2", + "scale by": 0.0004 + } + ] + } + } +} diff --git a/src/chemistry/mozart/chemistry.F90 b/src/chemistry/mozart/chemistry.F90 index 046b669ddb..ff9f17db68 100644 --- a/src/chemistry/mozart/chemistry.F90 +++ b/src/chemistry/mozart/chemistry.F90 @@ -162,6 +162,7 @@ subroutine chem_register use short_lived_species, only : slvd_index, short_lived_map=>map, register_short_lived_species use cfc11star, only : register_cfc11star use mo_photo, only : photo_register + use mo_tuvx, only : tuvx_register, tuvx_active use mo_aurora, only : aurora_register use aero_model, only : aero_model_register use physics_buffer, only : pbuf_add_field, dtype_r8 @@ -311,7 +312,11 @@ subroutine chem_register call register_cfc11star() if ( waccmx_is('ionosphere') ) then - call photo_register() + if( tuvx_active ) then + call tuvx_register( ) + else + call photo_register( ) + end if call aurora_register() endif @@ -994,7 +999,7 @@ subroutine chem_timestep_init(phys_state,pbuf2d) use mo_aurora, only : aurora_timestep_init use mo_photo, only : photo_timestep_init - use mo_tuvx, only : tuvx_timestep_init + use mo_tuvx, only : tuvx_active, tuvx_timestep_init use cfc11star, only : update_cfc11star use physics_buffer, only : physics_buffer_desc @@ -1069,7 +1074,7 @@ subroutine chem_timestep_init(phys_state,pbuf2d) !----------------------------------------------------------------------------- ! ... setup the TUV-x profiles for this timestep !----------------------------------------------------------------------------- - call tuvx_timestep_init( ) + if( tuvx_active ) call tuvx_timestep_init( ) call update_cfc11star( pbuf2d, phys_state ) diff --git a/src/chemistry/mozart/mo_chemini.F90 b/src/chemistry/mozart/mo_chemini.F90 index 46ba96c933..b63a498864 100644 --- a/src/chemistry/mozart/mo_chemini.F90 +++ b/src/chemistry/mozart/mo_chemini.F90 @@ -48,7 +48,7 @@ subroutine chemini & use mo_srf_emissions, only : srf_emissions_inti use mo_sulf, only : sulf_inti use mo_photo, only : photo_inti - use mo_tuvx, only : tuvx_init + use mo_tuvx, only : tuvx_init, tuvx_active use mo_lightning, only : lightning_inti use mo_drydep, only : drydep_inti use mo_imp_sol, only : imp_slv_inti @@ -196,17 +196,16 @@ subroutine chemini & call euvac_init (euvac_file) call photo_inti( xs_coef_file, xs_short_file, xs_long_file, rsf_file, & - photon_file, electron_file, & - exo_coldens_file, photo_max_zen ) - + photon_file, electron_file, exo_coldens_file, photo_max_zen ) if (masterproc) write(iulog,*) 'chemini: after photo_inti on node ',iam !----------------------------------------------------------------------- ! ... initialize the TUV-x photolysis rate constant calculator !----------------------------------------------------------------------- - - call tuvx_init( ) - if (masterproc) write(iulog,*) 'chemini: after tuvx_init on node ',iam + if( tuvx_active ) then + call tuvx_init( photon_file, electron_file, photo_max_zen ) + if (masterproc) write(iulog,*) 'chemini: after tuvx_init on node ',iam + end if !----------------------------------------------------------------------- ! ... initialize ion production diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index 7274e4439a..b0313618b2 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -785,22 +785,22 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & call shr_orb_decl( calday, eccen, mvelpp, lambm0, obliqr , & delta, esfact ) - !if (tuvx_active) then + if (tuvx_active) then + !----------------------------------------------------------------- + ! ... get calculated photolysis rates from TUV-x + !----------------------------------------------------------------- + call tuvx_get_photo_rates( state, pbuf, ncol, lchnk, zmid, zint, & + tfld, ts, invariants, vmr, col_delta, & + asdir, zen_angle, esfact, & + reaction_rates(:,:,1:phtcnt) ) + else !----------------------------------------------------------------- ! ... lookup the photolysis rates from table !----------------------------------------------------------------- call table_photo( reaction_rates, pmid, pdel, tfld, zmid, zint, & col_dens, zen_angle, asdir, cwat, cldfr, & esfact, vmr, invariants, ncol, lchnk, pbuf ) - - !else - !----------------------------------------------------------------- - ! ... get calculated photolysis rates from TUV-x - !----------------------------------------------------------------- - call tuvx_get_photo_rates( state, pbuf, ncol, lchnk, zm, zi, & - tfld, ts, invariants, vmr, col_delta, & - asdir, zen_angle, esfact ) - !endif + endif do i = 1,phtcnt call outfld( tag_names(i), reaction_rates(:ncol,:,rxt_tag_map(i)), ncol, lchnk ) diff --git a/src/chemistry/mozart/mo_jeuv.F90 b/src/chemistry/mozart/mo_jeuv.F90 index c13255250e..b893ad9f20 100644 --- a/src/chemistry/mozart/mo_jeuv.F90 +++ b/src/chemistry/mozart/mo_jeuv.F90 @@ -80,6 +80,7 @@ subroutine jeuv_init (photon_file, electron_file, indexer) character(len=2) :: mstring character(len=7) :: jstring logical :: do_jeuv + logical, save :: is_initialized = .false. do_jeuv=.false. do_heating=.false. @@ -97,6 +98,11 @@ subroutine jeuv_init (photon_file, electron_file, indexer) endif enddo + ! If the module has already been initialized, return after + ! computing index map + if( is_initialized ) return + is_initialized = .true. + if (.not.do_jeuv) return if (solar_euv_data_active) then diff --git a/src/chemistry/mozart/mo_jshort.F90 b/src/chemistry/mozart/mo_jshort.F90 index aa47dffb31..65efa66de4 100644 --- a/src/chemistry/mozart/mo_jshort.F90 +++ b/src/chemistry/mozart/mo_jshort.F90 @@ -27,6 +27,7 @@ module mo_jshort public :: jshort public :: sphers public :: slant_col + public :: calc_jno public :: nj save diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 64e0e90f3f..a1adf776e4 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -3,6 +3,7 @@ !---------------------------------------------------------------------- module mo_tuvx + use musica_map, only : map_t use musica_string, only : string_t use ppgrid, only : pver ! number of vertical layers use shr_kind_mod, only : r8 => shr_kind_r8, cl=>shr_kind_cl @@ -16,6 +17,7 @@ module mo_tuvx private public :: tuvx_readnl + public :: tuvx_register public :: tuvx_init public :: tuvx_timestep_init public :: tuvx_get_photo_rates @@ -42,30 +44,58 @@ module mo_tuvx integer, parameter :: NUM_RADIATORS = 1 ! number of radiators that CAM will update at runtime integer, parameter :: RADIATOR_INDEX_AEROSOL = 1 ! Aerosol radiator index + ! Definition of the MS93 wavelength grid TODO add description of this + integer, parameter :: NUM_BINS_MS93 = 4 + real(kind=r8), parameter :: WAVELENGTH_EDGES_MS93(NUM_BINS_MS93+1) = & + (/ 181.6_r8, 183.1_r8, 184.6_r8, 190.2_r8, 192.5_r8 /) + ! Information needed to access CAM species state data + logical :: is_fixed_N2 = .false. ! indicates whether N2 concentrations are fixed + logical :: is_fixed_O = .false. ! indicates whether O concentrations are fixed logical :: is_fixed_O2 = .false. ! indicates whether O2 concentrations are fixed logical :: is_fixed_O3 = .false. ! indicates whether O3 concentrations are fixed + logical :: is_fixed_NO = .false. ! indicates whether NO concentrations are fixed + integer :: index_N2 = 0 ! index for N2 in concentration array + integer :: index_O = 0 ! index for O in concentration array integer :: index_O2 = 0 ! index for O2 in concentration array integer :: index_O3 = 0 ! index for O3 in concentration array + integer :: index_NO = 0 ! index for NO in concentration array ! Information needed to access aerosol optical properties logical :: aerosol_exists = .false. ! indicates whether aerosol optical properties are available + ! Information needed to set extended-UV photo rates + logical :: do_euv = .false. ! Indicates whether to calculate + ! extended-UV photo rates + integer :: ion_rates_pbuf_index = 0 ! Index in physics buffer for + ! ionization rates + + ! Information needed to do special NO photolysis rate calculation + logical :: do_jno = .false. ! Indicates whether to calculate jno + integer :: jno_index = 0 ! Index in tuvx_ptr::photo_rates_ array for jno + + ! Cutoff solar zenith angle for doing photolysis rate calculations [degrees] + integer :: max_sza = 0.0_r8 + ! TODO how should these paths be set and communicated to this wrapper? character(len=*), parameter :: wavelength_config_path = & - "data/grids/wavelength/combined.grid" + "data/grids/wavelength/cam.csv" logical, parameter :: enable_diagnostics = .true. ! TUV-x calculator for each OMP thread type :: tuvx_ptr type(core_t), pointer :: core_ => null( ) ! TUV-x calculator - integer :: n_photo_rates_ ! number of photo reactions in TUV-x + integer :: n_photo_rates_ = 0 ! number of photo reactions in TUV-x + integer :: n_euv_rates_ = 0 ! number of extreme-UV rates + integer :: n_special_rates_ = 0 ! number of special photo rates real(r8), allocatable :: photo_rates_(:,:,:) ! photolysis rate constants ! (column, vertical level, reaction) [s-1] + type(map_t) :: photo_rate_map_ ! map between TUV-x and CAM + ! photo rate constant arrays type(grid_updater_t) :: grids_(NUM_GRIDS) ! grid updaters type(profile_updater_t) :: profiles_(NUM_PROFILES) ! profile updaters type(radiator_updater_t) :: radiators_(NUM_RADIATORS) ! radiator updaters - real(r8) :: height_delta_(pver) ! change in height in each + real(r8) :: height_delta_(pver+1) ! change in height in each ! vertical layer (km) real(r8), allocatable :: wavelength_edges_(:) ! TUV-x wavelength bin edges (nm) real(r8), allocatable :: wavelength_values_(:) ! Working array for interface values @@ -75,6 +105,8 @@ module mo_tuvx real(r8), allocatable :: optical_depth_(:,:,:) ! (column, vertical level, wavelength) [unitless] real(r8), allocatable :: single_scattering_albedo_(:,:,:) ! (column, vertical level, wavelength) [unitless] real(r8), allocatable :: asymmetry_factor_(:,:,:) ! (column, vertical level, wavelength) [unitless] + real(r8) :: et_flux_ms93_(NUM_BINS_MS93) ! extraterrestrial flux on the MS93 grid + ! [photon cm-2 nm-1 s-1] end type tuvx_ptr type(tuvx_ptr), allocatable :: tuvx_ptrs(:) @@ -91,6 +123,23 @@ module mo_tuvx !================================================================================================ contains +!================================================================================================ + + !----------------------------------------------------------------------- + ! registers fields in the physics buffer + !----------------------------------------------------------------------- + subroutine tuvx_register( ) + + use mo_jeuv, only : nIonRates + use physics_buffer, only : pbuf_add_field, dtype_r8 + use ppgrid, only : pcols ! maximum number of columns + + ! add photo-ionization rates to physics buffer for WACCMX Ionosphere module + call pbuf_add_field( 'IonRates', 'physpkg', dtype_r8, (/ pcols, pver, nIonRates /), & + ion_rates_pbuf_index ) ! Ionization rates for O+, O2+, N+, N2+, NO+ + + end subroutine tuvx_register + !================================================================================================ !----------------------------------------------------------------------- @@ -156,17 +205,24 @@ end subroutine tuvx_readnl !----------------------------------------------------------------------- ! Initializes TUV-x for photolysis calculations !----------------------------------------------------------------------- - subroutine tuvx_init( ) + subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) #ifdef HAVE_MPI use mpi #endif + use cam_abortutils, only : endrun use cam_logfile, only : iulog ! log file output unit use mo_chem_utls, only : get_spc_ndx, get_inv_ndx + use mo_jeuv, only : neuv ! number of extreme-UV rates use musica_assert, only : assert_msg - use musica_mpi, only : musica_mpi_rank + use musica_config, only : config_t + use musica_mpi, only : musica_mpi_rank, & + musica_mpi_pack_size, & + musica_mpi_pack, & + musica_mpi_unpack use musica_string, only : string_t, to_char use ppgrid, only : pcols ! maximum number of columns + use shr_const_mod, only : pi => shr_const_pi use solar_irrad_data, only : has_spectrum use spmd_utils, only : main_task => masterprocid, & is_main_task => masterproc, & @@ -176,49 +232,99 @@ subroutine tuvx_init( ) use tuvx_profile_warehouse, only : profile_warehouse_t use tuvx_radiator_warehouse, only : radiator_warehouse_t + 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] + + character(len=*), parameter :: my_name = "TUV-x wrapper initialization" class(core_t), pointer :: core character, allocatable :: buffer(:) type(string_t) :: config_path + type(config_t) :: tuvx_config, cam_config, map_config + type(map_t) :: map class(grid_t), pointer :: height class(grid_warehouse_t), pointer :: cam_grids class(profile_warehouse_t), pointer :: cam_profiles class(radiator_warehouse_t), pointer :: cam_radiators integer :: pack_size, pos, i_core, i_err + logical :: disable_aerosols + type(string_t) :: required_keys(1), optional_keys(1) + logical, save :: is_initialized = .false. + + if( .not. tuvx_active ) return + if( is_initialized ) return + is_initialized = .true. - if (.not.tuvx_active) return + if( is_main_task ) write(iulog,*) "Beginning TUV-x Initialization" config_path = trim(tuvx_config_path) - if( is_main_task ) call log_initialization( ) + ! =============================== + ! CAM TUV-x configuration options + ! =============================== + required_keys(1) = "aliasing" + optional_keys(1) = "disable aerosols" #ifndef HAVE_MPI call assert_msg( 113937299, is_main_task, "Multiple tasks present without " & //"MPI support enabled for TUV-x" ) #endif + ! =============================================================== + ! set the maximum solar zenith angle to calculate photo rates for + ! =============================================================== + max_sza = max_solar_zenith_angle + if( max_sza <= 0.0_r8 .or. max_sza > 180.0_r8 ) then + call endrun( "TUV-x: max solar zenith angle must be between 0 and 180 degress" ) + end if + + ! ================================= + ! initialize the extended-UV module + ! ================================= + call initialize_euv( photon_file, electron_file, do_euv ) + ! ========================================================================== - ! Create the set of TUV-x grids and profiles that CAM will update at runtime + ! create the set of TUV-x grids and profiles that CAM will update at runtime ! ========================================================================== cam_grids => get_cam_grids( wavelength_config_path ) cam_profiles => get_cam_profiles( cam_grids ) cam_radiators => get_cam_radiators( cam_grids ) - ! ====================================================================== - ! construct a core on the primary process and pack it onto an MPI buffer - ! ====================================================================== + ! ================================================================== + ! construct a core and a map between TUV-x and CAM photolysis arrays + ! on the primary process and pack them onto an MPI buffer + ! ================================================================== if( is_main_task ) then + call tuvx_config%from_file( config_path%to_char( ) ) + call tuvx_config%get( "__CAM options", cam_config, my_name ) + call assert_msg( 973680295, & + cam_config%validate( required_keys, optional_keys ), & + "Bad configuration for CAM TUV-x options." ) + call cam_config%get( "disable aerosols", disable_aerosols, my_name, & + default = .false. ) + call cam_config%get( "aliasing", map_config, my_name ) core => core_t( config_path, cam_grids, cam_profiles, cam_radiators ) - pack_size = core%pack_size( mpicom ) + call set_photo_rate_map( core, map_config, do_euv, do_jno, jno_index, map ) + pack_size = core%pack_size( mpicom ) + & + map%pack_size( mpicom ) + & + musica_mpi_pack_size( do_jno, mpicom ) + & + musica_mpi_pack_size( jno_index, mpicom ) + & + musica_mpi_pack_size( disable_aerosols, mpicom ) allocate( buffer( pack_size ) ) pos = 0 call core%mpi_pack( buffer, pos, mpicom ) + call map%mpi_pack( buffer, pos, mpicom ) + call musica_mpi_pack( buffer, pos, do_jno, mpicom ) + call musica_mpi_pack( buffer, pos, jno_index, mpicom ) + call musica_mpi_pack( buffer, pos, disable_aerosols, mpicom ) deallocate( core ) end if #ifdef HAVE_MPI - ! ============================================ - ! broadcast the core data to all MPI processes - ! ============================================ + ! ==================================================== + ! broadcast the core and map data to all MPI processes + ! ==================================================== call mpi_bcast( pack_size, 1, MPI_INTEGER, main_task, mpicom, i_err ) if( i_err /= MPI_SUCCESS ) then write(iulog,*) "TUV-x MPI int bcast error" @@ -232,30 +338,40 @@ subroutine tuvx_init( ) end if #endif - ! ======================================================== - ! unpack the core for each OMP thread on every MPI process - ! ======================================================== + ! ================================================================ + ! unpack the core and map for each OMP thread on every MPI process + ! ================================================================ allocate( tuvx_ptrs( max_threads( ) ) ) do i_core = 1, size( tuvx_ptrs ) associate( tuvx => tuvx_ptrs( i_core ) ) allocate( tuvx%core_ ) pos = 0 call tuvx%core_%mpi_unpack( buffer, pos, mpicom ) - - ! ===================================================================== - ! Set up map between CAM and TUV-x photolysis reactions for each thread - ! ===================================================================== - call create_updaters( tuvx, cam_grids, cam_profiles, cam_radiators ) - - ! TEMPORARY FOR DEVELOPMENT + call tuvx%photo_rate_map_%mpi_unpack( buffer, pos, mpicom ) + call musica_mpi_unpack( buffer, pos, do_jno, mpicom ) + call musica_mpi_unpack( buffer, pos, jno_index, mpicom ) + call musica_mpi_unpack( buffer, pos, disable_aerosols, mpicom ) + + ! =================================================================== + ! Set up connections between CAM and TUV-x input data for each thread + ! =================================================================== + call create_updaters( tuvx, cam_grids, cam_profiles, cam_radiators, & + disable_aerosols ) + + ! =============================================================== + ! Create a working array for calculated photolysis rate constants + ! =============================================================== tuvx%n_photo_rates_ = tuvx%core_%number_of_photolysis_reactions( ) + if( do_euv ) tuvx%n_euv_rates_ = neuv + if( do_jno ) tuvx%n_special_rates_ = tuvx%n_special_rates_ + 1 height => tuvx%core_%get_grid( "height", "km" ) - allocate( tuvx%photo_rates_( pcols, height%ncells_ + 1, tuvx%n_photo_rates_ ) ) + allocate( tuvx%photo_rates_( pcols, height%ncells_ + 1, & + tuvx%n_photo_rates_ + tuvx%n_euv_rates_ + & + tuvx%n_special_rates_ ) ) deallocate( height ) end associate end do - deallocate( cam_grids ) deallocate( cam_profiles ) deallocate( cam_radiators ) @@ -263,12 +379,21 @@ subroutine tuvx_init( ) ! ============================================= ! Get index info for CAM species concentrations ! ============================================= + index_N2 = get_inv_ndx( 'N2' ) + is_fixed_N2 = index_N2 > 0 + if( .not. is_fixed_N2 ) index_N2 = get_spc_ndx( 'N2' ) + index_O = get_inv_ndx( 'O' ) + is_fixed_O = index_O > 0 + if( .not. is_fixed_O ) index_O = get_spc_ndx( 'O' ) index_O2 = get_inv_ndx( 'O2' ) is_fixed_O2 = index_O2 > 0 if( .not. is_fixed_O2 ) index_O2 = get_spc_ndx( 'O2' ) index_O3 = get_inv_ndx( 'O3' ) is_fixed_O3 = index_O3 > 0 if( .not. is_fixed_O3 ) index_O3 = get_spc_ndx( 'O3' ) + index_NO = get_inv_ndx( 'NO' ) + is_fixed_NO = index_NO > 0 + if( .not. is_fixed_NO ) index_NO = get_spc_ndx( 'NO' ) ! ==================================================== ! make sure extraterrestrial flux values are available @@ -281,6 +406,8 @@ subroutine tuvx_init( ) ! ============================================ call initialize_diagnostics( tuvx_ptrs( 1 ) ) + if( is_main_task ) call log_initialization( ) + end subroutine tuvx_init !================================================================================================ @@ -292,6 +419,8 @@ subroutine tuvx_timestep_init( ) integer :: i_thread + if( .not. tuvx_active ) return + do i_thread = 1, size( tuvx_ptrs ) associate( tuvx => tuvx_ptrs( i_thread ) ) call set_et_flux( tuvx ) @@ -308,10 +437,11 @@ end subroutine tuvx_timestep_init subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & height_int, temperature_mid, surface_temperature, fixed_species_conc, & species_vmr, exo_column_conc, surface_albedo, solar_zenith_angle, & - earth_sun_distance ) + earth_sun_distance, photolysis_rates ) use cam_logfile, only : iulog ! log info output unit - use chem_mods, only : gas_pcnst, & ! number of non-fixed species + use chem_mods, only : phtcnt, & ! number of photolysis reactions + gas_pcnst, & ! number of non-fixed species nfs, & ! number of fixed species nabscol ! number of absorbing species (radiators) use physics_types, only : physics_state @@ -324,35 +454,47 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & type(physics_state), target, intent(in) :: state type(physics_buffer_desc), pointer, intent(inout) :: pbuf(:) - integer, intent(in) :: ncol ! number of active columns on this thread - integer, intent(in) :: lchnk ! identifier for this thread - real(r8), intent(in) :: height_mid(pcols,pver) ! height at mid-points (km) - real(r8), intent(in) :: height_int(pcols,pver+1) ! height at interfaces (km) - real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) - real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) - real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities - ! (molecule cm-3) - real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing - ! ratios (mol mol-1) - real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! above column densities - ! (molecule cm-3) - real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) - real(r8), intent(in) :: solar_zenith_angle(ncol) ! solar zenith angle (radians) - real(r8), intent(in) :: earth_sun_distance ! Earth-Sun distance (AU) - - integer :: i_col ! column index - - if (.not.tuvx_active) return + integer, intent(in) :: ncol ! number of active columns on this thread + integer, intent(in) :: lchnk ! identifier for this thread + real(r8), intent(in) :: height_mid(ncol,pver) ! height at mid-points (km) + real(r8), intent(in) :: height_int(ncol,pver+1) ! height at interfaces (km) + real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) + real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) + real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities + ! (molecule cm-3) + real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) + real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! layer column densities + ! (molecule cm-2) + real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) + real(r8), intent(in) :: solar_zenith_angle(ncol) ! solar zenith angle (radians) + real(r8), intent(in) :: earth_sun_distance ! Earth-Sun distance (AU) + real(r8), intent(inout) :: photolysis_rates(ncol,pver,phtcnt) ! photolysis rate + ! constants (1/s) + + integer :: i_col ! column index + integer :: i_level ! vertical level index + real(r8) :: sza ! solar zenith angle [degrees] + + if( .not. tuvx_active ) return associate( tuvx => tuvx_ptrs( thread_id( ) ) ) + tuvx%photo_rates_(:,:,:) = 0.0_r8 + ! ============================================== - ! get aerosol optical properties for all columns + ! set aerosol optical properties for all columns ! ============================================== call get_aerosol_optical_properties( tuvx, state, pbuf ) do i_col = 1, ncol + ! =================================== + ! skip columns in near total darkness + ! =================================== + sza = solar_zenith_angle(i_col) * 180.0_r8 / pi + if( sza < 0.0_r8 .or. sza > max_sza ) cycle + ! =================== ! update grid heights ! =================== @@ -369,12 +511,52 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & ! =================================================== ! Calculate photolysis rate constants for this column ! =================================================== - call tuvx%core_%run( solar_zenith_angle = & - solar_zenith_angle(i_col) * 180.0_r8 / pi, & + call tuvx%core_%run( solar_zenith_angle = sza, & earth_sun_distance = earth_sun_distance, & photolysis_rate_constants = & - tuvx%photo_rates_(i_col,:,:) ) + tuvx%photo_rates_(i_col,:,1:tuvx%n_photo_rates_) ) + + ! ============================== + ! Calculate the extreme-UV rates + ! ============================== + if( do_euv ) then + associate( euv_begin => tuvx%n_photo_rates_ + 1, & + euv_end => tuvx%n_photo_rates_ + tuvx%n_euv_rates_ ) + call calculate_euv_rates( sza, & + fixed_species_conc(i_col,:,:), & + species_vmr(i_col,:,:), & + height_mid(i_col,:), & + height_int(i_col,:), & + tuvx%photo_rates_(i_col,2:pver+1,euv_begin:euv_end) ) + end associate + end if + + ! ============================= + ! Calculate special photo rates + ! ============================= + if( do_jno ) then + call calculate_jno( sza, & + tuvx%et_flux_ms93_, & + fixed_species_conc(i_col,:,:), & + species_vmr(i_col,:,:), & + height_int(i_col,:), & + tuvx%photo_rates_(i_col,2:pver+1,jno_index) ) + end if + end do + ! ============================================== + ! Filter negative rates TODO fix inputs to TUV-x + ! ============================================== + tuvx%photo_rates_(:,:,:) = max( 0.0_r8, tuvx%photo_rates_(:,:,:) ) + + ! ============================================ + ! Return the photolysis rates on the CAM grids + ! ============================================ + do i_col = 1, ncol + do i_level = 1, pver + call tuvx%photo_rate_map_%apply( tuvx%photo_rates_(i_col,pver-i_level+2,:), & + photolysis_rates(i_col,i_level,:) ) + end do end do call output_diagnostics( tuvx, ncol, lchnk ) @@ -459,7 +641,7 @@ subroutine log_initialization( ) is_main_task => masterproc if( is_main_task ) then - write(iulog,*) "Initializing TUV-x" + write(iulog,*) "Initialized TUV-x" #ifdef HAVE_MPI write(iulog,*) " - with MPI support on task "//trim( to_char( main_task ) ) #else @@ -478,10 +660,44 @@ subroutine log_initialization( ) else write(iulog,*) " - without on-line aerosols" end if + if( index_N2 > 0 ) write(iulog,*) " - including N2" + if( index_O > 0 ) write(iulog,*) " - including O" + if( index_O2 > 0 ) write(iulog,*) " - including O2" + if( index_O3 > 0 ) write(iulog,*) " - including O3" + if( index_NO > 0 ) write(iulog,*) " - including NO" + if( do_euv ) write(iulog,*) " - doing Extreme-UV calculations" + if( do_jno ) write(iulog,*) " - including special jno rate calculation" + write(iulog,*) " - max solar zenith angle [degrees]:", max_sza end if end subroutine log_initialization +!================================================================================================ + + !----------------------------------------------------------------------- + ! initializes the external extreme-UV module + !----------------------------------------------------------------------- + subroutine initialize_euv( photon_file, electron_file, do_euv ) + + use chem_mods, only : phtcnt ! number of CAM-Chem photolysis reactions + use mo_jeuv, only : jeuv_init ! extreme-UV initialization + + 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 + logical, intent(out) :: do_euv ! indicates whether extreme-UV + ! calculations are needed + + integer, allocatable :: euv_index_map(:) + + allocate( euv_index_map( phtcnt ) ) + euv_index_map(:) = 0 + call jeuv_init( photon_file, electron_file, euv_index_map ) + do_euv = any( euv_index_map(:) > 0 ) + + end subroutine initialize_euv + !================================================================================================ !----------------------------------------------------------------------- @@ -490,11 +706,12 @@ end subroutine log_initialization subroutine initialize_diagnostics( this ) use cam_history, only : addfld + use musica_assert, only : assert use musica_string, only : string_t type(tuvx_ptr), intent(in) :: this - type(string_t), allocatable :: labels(:) + type(string_t), allocatable :: labels(:), all_labels(:) integer :: i_label if( .not. enable_diagnostics ) then @@ -506,9 +723,17 @@ subroutine initialize_diagnostics( this ) ! add output for specific photolysis reaction rate constants ! ========================================================== labels = this%core_%photolysis_reaction_labels( ) - allocate( diagnostics( size( labels ) ) ) - do i_label = 1, size( labels ) - diagnostics( i_label )%name_ = trim( labels( i_label )%to_char( ) ) + allocate( all_labels( size( labels ) + this%n_special_rates_ ) ) + all_labels( 1 : size( labels ) ) = labels(:) + i_label = size( labels ) + 1 + if( do_jno ) then + all_labels( i_label ) = "jno" + i_label = i_label + 1 + end if + call assert( 522515214, i_label == size( all_labels ) + 1 ) + allocate( diagnostics( size( all_labels ) ) ) + do i_label = 1, size( all_labels ) + diagnostics( i_label )%name_ = trim( all_labels( i_label )%to_char( ) ) diagnostics( i_label )%index_ = i_label call addfld( "tuvx_"//diagnostics( i_label )%name_, (/ 'lev' /), 'A', 'sec-1', & 'photolysis rate constant' ) @@ -516,6 +741,114 @@ subroutine initialize_diagnostics( this ) end subroutine initialize_diagnostics +!================================================================================================ + + !----------------------------------------------------------------------- + ! Sets up a map between the TUV-x and CAM photolysis rate arrays + !----------------------------------------------------------------------- + subroutine set_photo_rate_map( core, config, do_euv, do_jno, jno_index, map ) + + use cam_logfile, only : iulog ! log info output unit + use chem_mods, only : phtcnt, & ! number of photolysis reactions + rxt_tag_lst ! labels for all chemical reactions + ! NOTE photolysis reactions are + ! expected to appear first + use mo_jeuv, only : neuv ! number of extreme-UV rates + use musica_config, only : config_t + use musica_string, only : string_t + + type(core_t), intent(in) :: core ! TUV-x core + type(config_t), intent(inout) :: config ! CAM<->TUV-x map configuration + logical, intent(in) :: do_euv ! indicates whether to include + ! extreme-UV rates in the mapping + logical, intent(out) :: do_jno ! indicates whether jno should be + ! calculated + integer, intent(out) :: jno_index ! index for jno in source photo + ! rate array + type(map_t), intent(out) :: map + + integer :: i_label, i_start, i_end + type(string_t) :: str_label + type(string_t), allocatable :: tuvx_labels(:), euv_labels(:), special_labels(:), & + all_labels(:), cam_labels(:) + + ! ================== + ! MOZART photo rates + ! ================== + allocate( cam_labels( phtcnt ) ) + do i_label = 1, phtcnt + cam_labels( i_label ) = trim( rxt_tag_lst( i_label ) ) + end do + + ! ================= + ! TUV-x photo rates + ! ================= + tuvx_labels = core%photolysis_reaction_labels( ) + + ! ====================== + ! Extreme-UV photo rates + ! ====================== + if( do_euv ) then + allocate( euv_labels( neuv ) ) + do i_label = 1, neuv + str_label = i_label + euv_labels( i_label ) = "jeuv_"//str_label + end do + else + allocate( euv_labels(0) ) + end if + + ! =============================== + ! Special photo rate calculations + ! =============================== + do_jno = .false. + jno_index = 0 + do i_label = 1, size( cam_labels ) + if( cam_labels( i_label ) == "jno" ) then + do_jno = .true. + exit + end if + end do + if( do_jno ) then + allocate( special_labels(1) ) + special_labels(1) = "jno" + else + allocate( special_labels(0) ) + end if + + ! ========================== + ! Combine photo rate sources + ! ========================== + allocate( all_labels( size( tuvx_labels ) + size( euv_labels ) + & + size( special_labels ) ) ) + i_end = 0 + if( size( tuvx_labels ) > 0 ) then + i_start = i_end + 1 + i_end = i_start + size( tuvx_labels ) - 1 + all_labels( i_start : i_end ) = tuvx_labels(:) + end if + if( size( euv_labels ) > 0 ) then + i_start = i_end + 1 + i_end = i_start + size( euv_labels ) - 1 + all_labels( i_start : i_end ) = euv_labels(:) + end if + if( size( special_labels ) > 0 ) then + i_start = i_end + 1 + i_end = i_start + size( special_labels ) - 1 + all_labels( i_start : i_end ) = special_labels(:) + jno_index = i_start + end if + + ! ========== + ! Create map + ! ========== + map = map_t( config, all_labels, cam_labels ) + write(iulog,*) + write(iulog,*) "TUV-x --> CAM-Chem photolysis rate constant map" + call map%print( all_labels, cam_labels, iulog ) + + end subroutine set_photo_rate_map + !================================================================================================ !----------------------------------------------------------------------- @@ -535,7 +868,7 @@ subroutine output_diagnostics( this, ncol, lchnk ) do i_diag = 1, size( diagnostics ) associate( diag => diagnostics( i_diag ) ) - call outfld( "tuvx_"//diag%name_, this%photo_rates_(:ncol,:,diag%index_), & + call outfld( "tuvx_"//diag%name_, this%photo_rates_(:ncol,pver+1:2:-1,diag%index_), & ncol, lchnk ) end associate end do @@ -569,7 +902,7 @@ function get_cam_grids( wavelength_path ) result( grids ) ! ========================= ! heights above the surface ! ========================= - host_grid => grid_from_host_t( "height", "km", pver ) + host_grid => grid_from_host_t( "height", "km", pver+1 ) call grids%add( host_grid ) deallocate( host_grid ) @@ -661,7 +994,7 @@ end function get_cam_profiles !================================================================================================ !----------------------------------------------------------------------- - ! Creates and loads a radiator warehouse with radiators that CAM + ! Creates and loads a radiator warehouse with radiators that CAM will ! update at runtime !----------------------------------------------------------------------- function get_cam_radiators( grids ) result( radiators ) @@ -706,7 +1039,7 @@ end function get_cam_radiators ! for runtime access of CAM data ! !----------------------------------------------------------------------- - subroutine create_updaters( this, grids, profiles, radiators ) + subroutine create_updaters( this, grids, profiles, radiators, disable_aerosols ) use modal_aer_opt, only : modal_aer_opt_init use ppgrid, only : pcols ! maximum number of columns @@ -726,6 +1059,7 @@ subroutine create_updaters( this, grids, profiles, radiators ) class(grid_warehouse_t), intent(in) :: grids class(profile_warehouse_t), intent(in) :: profiles class(radiator_warehouse_t), intent(in) :: radiators + logical, intent(in) :: disable_aerosols class(grid_t), pointer :: height, wavelength class(profile_t), pointer :: host_profile @@ -803,7 +1137,7 @@ subroutine create_updaters( this, grids, profiles, radiators ) ! intialize the aerosol optics module ! ==================================================================== call rad_cnst_get_info( 0, nmodes = n_modes ) - if( n_modes > 0 .and. .not. aerosol_exists ) then + if( n_modes > 0 .and. .not. aerosol_exists .and. .not. disable_aerosols ) then aerosol_exists = .true. call modal_aer_opt_init( ) else @@ -833,13 +1167,17 @@ end subroutine create_updaters ! TUV-x heights are "bottom-up" and require atmospheric constituent ! concentrations at interfaces. Therefore, CAM mid-points are used as ! TUV-x grid interfaces, with an additional layer introduced between - ! the surface and the lowest CAM mid-point. + ! the surface and the lowest CAM mid-point, and a layer at the + ! top of the TUV-x grid to hold species densities above the top CAM + ! mid-point. ! ! ---- (interface) ===== (mid-point) ! ! CAM TUV-x - ! ------(top)------ i_int = 1 (exo values) - ! ================= i_mid = 1 -------(top)------ i_int = pver + 1 + ! ------(top)------ i_int = 1 -------(top)------ i_int = pver + 2 + ! ************************ (exo values) ***************************** + ! ================== i_mid = pver + 1 + ! ================= i_mid = 1 ------------------ i_int = pver + 1 ! ----------------- i_int = 2 ================== i_mid = pver ! ------------------ i_int = pver ! || @@ -858,19 +1196,22 @@ subroutine set_heights( this, i_col, ncol, height_mid, height_int ) class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator integer, intent(in) :: i_col ! column to set conditions for integer, intent(in) :: ncol ! number of colums to calculated photolysis for - real(r8), intent(in) :: height_mid(pcols,pver) ! height above the surface at mid-points (km) - real(r8), intent(in) :: height_int(pcols,pver+1) ! height above the surface at interfaces (km) + real(r8), intent(in) :: height_mid(ncol,pver) ! height above the surface at mid-points (km) + real(r8), intent(in) :: height_int(ncol,pver+1) ! height above the surface at interfaces (km) integer :: i_level - real(r8) :: edges(pver+1) - real(r8) :: mid_points(pver) + real(r8) :: edges(pver+2) + real(r8) :: mid_points(pver+1) - edges(1) = 0.0_r8 + edges(1) = height_int(i_col,pver+1) edges(2:pver+1) = height_mid(i_col,pver:1:-1) - mid_points(1) = height_mid(i_col,pver) * 0.5_r8 + edges(pver+2) = height_int(i_col,1) + mid_points(1) = ( height_mid(i_col,pver) - height_int(i_col,pver+1) ) * 0.5_r8 & + + height_int(i_col,pver+1) mid_points(2:pver) = height_int(i_col,pver:2:-1) + mid_points(pver+1) = 0.5_r8 * ( edges(pver+1) + edges(pver+2) ) call this%grids_( GRID_INDEX_HEIGHT )%update( edges = edges, mid_points = mid_points ) - this%height_delta_(1:pver) = edges(2:pver+1) - edges(1:pver) + this%height_delta_(1:pver+1) = edges(2:pver+2) - edges(1:pver+1) end subroutine set_heights @@ -891,10 +1232,11 @@ subroutine set_temperatures( this, i_col, temperature_mid, surface_temperature ) real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) - real(r8) :: edges(pver+1) + real(r8) :: edges(pver+2) edges(1) = surface_temperature(i_col) edges(2:pver+1) = temperature_mid(i_col,pver:1:-1) + edges(pver+2) = temperature_mid(i_col,1) ! Use upper mid-point temperature for top edge call this%profiles_( PROFILE_INDEX_TEMPERATURE )%update( edge_values = edges ) end subroutine set_temperatures @@ -940,19 +1282,54 @@ subroutine set_et_flux( this ) ! (photon cm-2 nm-1 s-1) class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + real(r8) :: et_flux_orig(nbins) - integer :: n_tuvx_bins + integer :: n_tuvx_bins, i_bin - et_flux_orig(:) = sol_etf(:) * ( we(2:nbins+1) - we(1:nbins) ) + ! =============================================== + ! regrid normalized flux to TUV-x wavelength grid + !================================================ + et_flux_orig(:) = sol_etf(:) n_tuvx_bins = size(this%wavelength_mid_values_) call rebin( nbins, n_tuvx_bins, we, this%wavelength_edges_, et_flux_orig, & this%wavelength_mid_values_ ) - this%wavelength_values_(1) = this%wavelength_mid_values_(1) - this%wavelength_values_(2:n_tuvx_bins+1) = this%wavelength_mid_values_(1:n_tuvx_bins) + + ! ======================================================== + ! convert normalized flux to flux on TUV-x wavelength grid + ! ======================================================== + this%wavelength_mid_values_(:) = this%wavelength_mid_values_(:) * & + ( this%wavelength_edges_(2:n_tuvx_bins+1) - & + this%wavelength_edges_(1:n_tuvx_bins) ) + + ! ==================================== + ! estimate unused edge values for flux + ! ==================================== + this%wavelength_values_(1) = this%wavelength_mid_values_(1) - & + ( this%wavelength_mid_values_(2) - & + this%wavelength_mid_values_(1) ) * 0.5_r8 + do i_bin = 2, n_tuvx_bins + this%wavelength_values_(i_bin) = this%wavelength_mid_values_(i_bin-1) + & + ( this%wavelength_mid_values_(i_bin) - & + this%wavelength_mid_values_(i_bin-1) ) * 0.5_r8 + end do + this%wavelength_values_(n_tuvx_bins+1) = & + this%wavelength_mid_values_(n_tuvx_bins) + & + ( this%wavelength_mid_values_(n_tuvx_bins) - & + this%wavelength_mid_values_(n_tuvx_bins-1) ) * 0.5_r8 + + ! ============================ + ! update TUV-x ET flux profile + ! ============================ call this%profiles_( PROFILE_INDEX_ET_FLUX )%update( & mid_point_values = this%wavelength_mid_values_, & edge_values = this%wavelength_values_) + ! ====================================================================== + ! rebin extraterrestrial flux to MS93 grid for use with jno calculations + ! ====================================================================== + call rebin( nbins, NUM_BINS_MS93, we, WAVELENGTH_EDGES_MS93, et_flux_orig, & + this%et_flux_ms93_ ) + end subroutine set_et_flux !================================================================================================ @@ -983,9 +1360,9 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing ! ratios (mol mol-1) real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! above column densities - ! (molecule cm-3) + ! (molecule cm-2) - real(r8) :: edges(pver+1), densities(pver) + real(r8) :: edges(pver+2), densities(pver+1) real(r8) :: exo_val real(r8), parameter :: km2cm = 1.0e5 ! conversion from km to cm @@ -994,8 +1371,9 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species ! =========== edges(1) = fixed_species_conc(i_col,pver,indexm) edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,indexm) - densities(1:pver) = this%height_delta_(1:pver) * km2cm * & - sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) + edges(pver+2) = fixed_species_conc(i_col,1,indexm) ! use upper mid-point value for top edge + densities(1:pver+1) = this%height_delta_(1:pver+1) * km2cm * & + sqrt(edges(1:pver+1)) * sqrt(edges(2:pver+2)) call this%profiles_( PROFILE_INDEX_AIR )%update( & edge_values = edges, layer_densities = densities, & scale_height = 8.01_r8 ) ! scale height in [km] @@ -1006,20 +1384,22 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species if( is_fixed_O2 ) then edges(1) = fixed_species_conc(i_col,pver,index_O2) edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,index_O2) + edges(pver+2) = fixed_species_conc(i_col,1,index_O2) else if( index_O2 > 0 ) then edges(1) = species_vmr(i_col,pver,index_O2) * & fixed_species_conc(i_col,pver,indexm) edges(2:pver+1) = species_vmr(i_col,pver:1:-1,index_O2) * & fixed_species_conc(i_col,pver:1:-1,indexm) + edges(pver+2) = species_vmr(i_col,1,index_O2) * & + fixed_species_conc(i_col,1,indexm) else edges(:) = 0.0_r8 end if - exo_val = exo_column_conc(i_col,0,1) - densities(1:pver) = this%height_delta_(1:pver) * km2cm * & - sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) + densities(1:pver+1) = this%height_delta_(1:pver+1) * km2cm * & + sqrt(edges(1:pver+1)) * sqrt(edges(2:pver+2)) call this%profiles_( PROFILE_INDEX_O2 )%update( & edge_values = edges, layer_densities = densities, & - exo_density = exo_val ) + scale_height = 7.0_r8 ) ! ========== ! O3 profile @@ -1027,24 +1407,33 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species if( is_fixed_O3 ) then edges(1) = fixed_species_conc(i_col,pver,index_O3) edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,index_O3) + edges(pver+2) = fixed_species_conc(i_col,1,index_O3) else if( index_O3 > 0 ) then edges(1) = species_vmr(i_col,pver,index_O3) * & fixed_species_conc(i_col,pver,indexm) edges(2:pver+1) = species_vmr(i_col,pver:1:-1,index_O3) * & fixed_species_conc(i_col,pver:1:-1,indexm) + edges(pver+2) = species_vmr(i_col,1,index_O3) * & + fixed_species_conc(i_col,1,indexm) else edges(:) = 0.0_r8 end if - if( nabscol >= 2 ) then - exo_val = exo_column_conc(i_col,0,2) + if( nabscol >= 1 ) then + densities(1) = 0.5_r8 * exo_column_conc(i_col,pver,1) + densities(2:pver) = 0.5_r8 * ( exo_column_conc(i_col,pver-1:1:-1,1) & + + exo_column_conc(i_col,pver:2:-1,1) ) + densities(pver+1) = exo_column_conc(i_col,0,1) & + + 0.5_r8 * exo_column_conc(i_col,1,1) + call this%profiles_( PROFILE_INDEX_O3 )%update( & + edge_values = edges, layer_densities = densities, & + exo_density = exo_column_conc(i_col,0,1) ) else - exo_val = 0.0_r8 + densities(1:pver+1) = this%height_delta_(1:pver+1) * km2cm * & + ( edges(1:pver+1) + edges(2:pver+2) ) * 0.5_r8 + call this%profiles_( PROFILE_INDEX_O3 )%update( & + edge_values = edges, layer_densities = densities, & + scale_height = 7.0_r8 ) end if - densities(1:pver) = this%height_delta_(1:pver) * km2cm * & - sqrt(edges(1:pver)) + sqrt(edges(2:pver+1)) - call this%profiles_( PROFILE_INDEX_O3 )%update( & - edge_values = edges, layer_densities = densities, & - exo_density = exo_val ) ! =============== ! aerosol profile @@ -1131,6 +1520,12 @@ subroutine get_aerosol_optical_properties( this, state, pbuf ) call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & aer_tau_w_g(i_col,pver-i_level,:), this%asymmetry_factor_(i_col,i_level,:)) end do + this%optical_depth_(i_col,pver+1,:) = & + this%optical_depth_(i_col,pver,:) + this%single_scattering_albedo_(i_col,pver+1,:) = & + this%single_scattering_albedo_(i_col,pver,:) + this%asymmetry_factor_(i_col,pver+1,:) = & + this%asymmetry_factor_(i_col,pver,:) end do ! ================================================================ @@ -1174,6 +1569,167 @@ subroutine reorder_optics_array( optics_array ) end subroutine reorder_optics_array +!================================================================================================ + + !----------------------------------------------------------------------- + ! Calculates extreme-UV ionization rates + ! + ! NOTE This never includes an above-column layer + !----------------------------------------------------------------------- + subroutine calculate_euv_rates( solar_zenith_angle, fixed_species_conc, & + species_vmr, height_mid, height_int, euv_rates ) + + use chem_mods, only : gas_pcnst, & ! number of non-fixed species + nfs, & ! number of fixed species + indexm ! index for air density in fixed species array + use mo_jeuv, only : jeuv, neuv ! number of extreme-UV rates + use ref_pres, only : ptop_ref ! pressure at the top of the column (Pa) + + real(r8), intent(in) :: solar_zenith_angle ! degrees + real(r8), intent(in) :: fixed_species_conc(pver,max(1,nfs)) ! fixed species densities + ! (molecule cm-3) + real(r8), intent(in) :: species_vmr(pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) + real(r8), intent(in) :: height_mid(pver) ! height at mid-points (km) + real(r8), intent(in) :: height_int(pver+1) ! height at interfaces (km) + real(r8), intent(out) :: euv_rates(pver,neuv) ! calculated extreme-UV rates + + real(r8) :: o_dens(pver), o2_dens(pver), n2_dens(pver), height_arg(pver) + + ! ========== + ! N2 density + ! ========== + if( is_fixed_N2 ) then + n2_dens(:) = fixed_species_conc(:pver,index_N2) + else + n2_dens(:) = species_vmr(:pver,index_N2) * fixed_species_conc(:pver,indexm) + end if + + ! ========= + ! O density + ! ========= + if( is_fixed_O ) then + o_dens(:) = fixed_species_conc(:pver,index_O) + else + o_dens(:) = species_vmr(:pver,index_O) * fixed_species_conc(:pver,indexm) + end if + + ! ========== + ! O2 density + ! ========== + if( is_fixed_O2 ) then + o2_dens(:) = fixed_species_conc(:pver,index_O2) + else + o2_dens(:) = species_vmr(:pver,index_O2) * fixed_species_conc(:pver,indexm) + end if + + ! ======================= + ! special height argument + ! ======================= + height_arg(:) = height_mid(:) + + call jeuv( pver, solar_zenith_angle, o_dens, o2_dens, n2_dens, height_arg, euv_rates ) + + end subroutine calculate_euv_rates + +!================================================================================================ + + !----------------------------------------------------------------------- + ! Calculates NO photolysis rates + ! + ! NOTE: Always includes an above-column layer + !----------------------------------------------------------------------- + subroutine calculate_jno( solar_zenith_angle, et_flux, fixed_species_conc, species_vmr, & + height_int, jno ) + + use chem_mods, only : gas_pcnst, & ! number of non-fixed species + nfs, & ! number of fixed species + indexm ! index for air density in fixed species array + use mo_jshort, only : sphers, slant_col, calc_jno + use ref_pres, only : ptop_ref ! pressure at the top of the column (Pa) + + real(r8), intent(in) :: solar_zenith_angle ! degrees + real(r8), intent(in) :: et_flux(NUM_BINS_MS93) ! extraterrestrial flux MS93 grid + ! (photon cm-2 nm-1 s-1) + real(r8), intent(in) :: fixed_species_conc(pver,max(1,nfs)) ! fixed species densities + ! (molecule cm-3) + real(r8), intent(in) :: species_vmr(pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) + real(r8), intent(in) :: height_int(pver+1) ! height at interfaces (km) + real(r8), intent(out) :: jno(pver) ! calculated NO rate + + ! species column densities (molecule cm-3) + real(kind=r8) :: n2_dens(pver+1), o2_dens(pver+1), o3_dens(pver+1), no_dens(pver+1) + ! species slant column densities (molecule cm-2) + real(kind=r8) :: o2_slant(pver+1), o3_slant(pver+1), no_slant(pver+1) + ! working photo rate array + real(kind=r8) :: work_jno(pver+1) + ! parameters needed to calculate slant column densities + ! (see sphers routine description for details) + integer :: nid(pver+1) + real(kind=r8) :: dsdh(0:pver+1,pver+1) + ! layer thickness (cm) + real(kind=r8) :: delz(pver+1) + ! conversion from km to cm + real(kind=r8), parameter :: km2cm = 1.0e5_r8 + + ! ========== + ! N2 density + ! ========== + if( is_fixed_N2 ) then + n2_dens(2:) = fixed_species_conc(:pver,index_N2) + else + n2_dens(2:) = species_vmr(:pver,index_N2) * fixed_species_conc(:pver,indexm) + end if + n2_dens(1) = n2_dens(2) * 0.9_r8 + + ! ========== + ! O2 density + ! ========== + if( is_fixed_O2 ) then + o2_dens(2:) = fixed_species_conc(:pver,index_O2) + else + o2_dens(2:) = species_vmr(:pver,index_O2) * fixed_species_conc(:pver,indexm) + end if + o2_dens(1) = o2_dens(2) * 7.0_r8 / ( height_int(1) - height_int(2) ) + + ! ========== + ! O3 density + ! ========== + if( is_fixed_O3 ) then + o3_dens(2:) = fixed_species_conc(:pver,index_O3) + else + o3_dens(2:) = species_vmr(:pver,index_O3) * fixed_species_conc(:pver,indexm) + end if + o3_dens(1) = o3_dens(2) * 7.0_r8 / ( height_int(1) - height_int(2) ) + + ! ========== + ! NO density + ! ========== + if( is_fixed_NO ) then + no_dens(2:) = fixed_species_conc(:pver,index_NO) + else + no_dens(2:) = species_vmr(:pver,index_NO) * fixed_species_conc(:pver,indexm) + end if + no_dens(1) = no_dens(2) * 0.9_r8 + + ! ================================ + ! calculate slant column densities + ! ================================ + call sphers( pver+1, height_int, solar_zenith_angle, dsdh, nid ) + delz(1:pver) = km2cm * ( height_int(1:pver) - height_int(2:pver+1) ) + call slant_col( pver+1, delz, dsdh, nid, o2_dens, o2_slant ) + call slant_col( pver+1, delz, dsdh, nid, o3_dens, o3_slant ) + call slant_col( pver+1, delz, dsdh, nid, no_dens, no_slant ) + + ! ========================================= + ! calculate the NO photolysis rate constant + ! ========================================= + call calc_jno( pver+1, et_flux, n2_dens, o2_slant, o3_slant, no_slant, work_jno ) + jno(:) = work_jno(:pver) + + end subroutine calculate_jno + !================================================================================================ end module mo_tuvx diff --git a/src/chemistry/pp_trop_strat_mam4_vbs/mo_imp_sol.F90 b/src/chemistry/pp_trop_strat_mam4_vbs/mo_imp_sol.F90 index 98cadb9050..bfcbed423c 100644 --- a/src/chemistry/pp_trop_strat_mam4_vbs/mo_imp_sol.F90 +++ b/src/chemistry/pp_trop_strat_mam4_vbs/mo_imp_sol.F90 @@ -382,8 +382,9 @@ subroutine imp_sol( base_sol, reaction_rates, het_rates, extfrc, delt, & end if base_sol_blk(i,:) = sbase_sol_blk(i,:) else - write(iulog,'('' imp_sol: step failed to converge @ (lchnk,vctrpos,nstep,dt,time) = '',3i8,1p,2g15.7)') & - lchnk,ofl+i-1,nstep,dt(i),interval_done+dt(i) + write(iulog,'('' imp_sol: step failed to converge '')') + !write(iulog,'('' imp_sol: step failed to converge @ (lchnk,vctrpos,nstep,dt,time) = '',3i8,1p,2g15.7)') & + ! lchnk,ofl+i-1,nstep,dt(i),interval_done+dt(i) do m = 1,clscnt4 if( .not. spc_conv_blk(i,m) ) then write(iulog,'(1x,a16,1x,1pe10.3)') solsym(clsmap(m,4)), max_delta(m) From 23c82529b52155ddfb5eb4c1f619bef4dd28c383 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 4 Apr 2023 10:50:02 -0600 Subject: [PATCH 70/84] switching to a build of musica (#8) * switching to a build of musica * disabling tests and explicitly setting a build of TUVX * updating musica branch * update path to tuv-x data --------- Co-authored-by: Matthew Dawson --- .gitignore | 3 +- Externals_CAM.cfg | 13 +-- cime_config/buildlib | 171 ++++++++----------------------- cime_config/buildnml | 3 +- cime_config/config_component.xml | 2 +- 5 files changed, 47 insertions(+), 145 deletions(-) diff --git a/.gitignore b/.gitignore index 39fd22862a..349bb8e9f3 100644 --- a/.gitignore +++ b/.gitignore @@ -14,8 +14,7 @@ src/dynamics/fv3/atmos_cubed_sphere libraries/FMS libraries/mct libraries/parallelio -libraries/tuv-x -libraries/musica-core +libraries/musica libraries/json-fortran src/atmos_phys src/dynamics/mpas/dycore diff --git a/Externals_CAM.cfg b/Externals_CAM.cfg index 9156837aef..75cd48bda4 100644 --- a/Externals_CAM.cfg +++ b/Externals_CAM.cfg @@ -69,17 +69,10 @@ sparse = ../.mpas_sparse_checkout hash = ff76a231 required = True -[tuv-x] -local_path = libraries/tuv-x +[musica] +local_path = libraries/musica protocol = git -repo_url = https://github.com/NCAR/tuv-x.git -branch = main -required = True - -[musica-core] -local_path = libraries/musica-core -protocol = git -repo_url = https://github.com/NCAR/musica-core.git +repo_url = https://github.com/NCAR/musica.git branch = main required = True diff --git a/cime_config/buildlib b/cime_config/buildlib index c6ccbd9a1c..2cbe1e9281 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -221,21 +221,22 @@ def _json_fortran_install_dir(libroot): return jsonpaths[0] ############################################################################### -def _build_musica_core(caseroot, libroot, bldroot): +def _build_musica(caseroot, libroot, bldroot): ############################################################################### -# Builds the musica-core support library for TUV-x and updates the case -# variables used to set the include paths and linked libraries +# Builds the musica library, including TUV-x, musica, and MICM +# and updates the case variables used to set the include paths +# and linked libraries build = EnvBuild(case_root=caseroot) with Case(caseroot) as case: - bldpath = os.path.join(bldroot, "musica-core") + bldpath = os.path.join(bldroot, "musica") if not os.path.exists(bldpath): os.makedirs(bldpath) jsoninc = _json_fortran_include_dir(libroot) jsonlib = _json_fortran_lib_path(libroot) srcpath = os.path.abspath(os.path.join(case.get_value("COMP_ROOT_DIR_ATM"), \ - "libraries", "musica-core", "")) - logger.info("Building musica-core in {} from source in {}\n".format(bldpath, srcpath)) + "libraries", "musica", "")) + logger.info("Building musica in {} from source in {}\n".format(bldpath, srcpath)) arg_dict = _cmake_default_args(caseroot) if build.get_value("MPILIB") == "mpi-serial": @@ -250,6 +251,9 @@ def _build_musica_core(caseroot, libroot, bldroot): cmake_args += "-DENABLE_UTIL_ONLY=ON " cmake_args += "-DCMAKE_C_COMPILER_WORKS=1 " cmake_args += "-DCMAKE_CXX_COMPILER_WORKS=1 " + cmake_args += "-DENABLE_MICM=OFF " + cmake_args += "-DENABLE_TUVX=ON " + cmake_args += "-DENABLE_TESTS=OFF " cmake_args += "-DENABLE_COVERAGE=OFF " cmake_args += "-DJSON_INCLUDE_DIR={} ".format(jsoninc) cmake_args += "-DJSON_LIB={} ".format(jsonlib) @@ -261,170 +265,75 @@ def _build_musica_core(caseroot, libroot, bldroot): _run_cmd(case.get_value("GMAKE"), bldpath) _run_cmd("{} install".format(case.get_value("GMAKE")), bldpath) - # add musica-core to include paths + # add musica to include paths incldir = os.environ.get('USER_INCLDIR') if incldir is None: incldir = '' os.environ['USER_INCLDIR'] = incldir + \ - " -I{} ".format(_musica_core_include_dir(libroot)) + " -I{} ".format(_musica_include_dir(libroot)) # create simlink to library in folder CIME expects libraries to be in - dst = os.path.join(libroot, "libmusicacore.a") + dst = os.path.join(libroot, "libmusica.a") if os.path.isfile(dst): os.remove(dst) - os.symlink(_musica_core_lib_path(libroot), dst) + os.symlink(_musica_lib_path(libroot), dst) ############################################################################### -def _musica_core_include_dir(libroot): +def _musica_include_dir(libroot): ############################################################################### -# Returns the path to the musica-core include directory +# Returns the path to the musica include directory - coreinc = os.path.join(_musica_core_install_dir(libroot), "include", "") + coreinc = os.path.join(_musica_install_dir(libroot), "include", "") expect(os.path.exists(coreinc), \ - "musica-core include directory not found at {}".format(coreinc)) + "musica include directory not found at {}".format(coreinc)) return coreinc ############################################################################### -def _musica_core_lib_path(libroot): +def _musica_lib_path(libroot): ############################################################################### -# Returns the path to the musica-core library +# Returns the path to the musica library - corelib = os.path.join(_musica_core_install_dir(libroot), "lib64", "libmusicacore.a") + corelib = os.path.join(_musica_install_dir(libroot), "lib64", "libmusica.a") if not os.path.exists(corelib): - corelib = os.path.join(_musica_core_install_dir(libroot), "lib", "libmusicacore.a") + corelib = os.path.join(_musica_install_dir(libroot), "lib", "libmusica.a") expect(os.path.exists(corelib), \ - "musica-core library not found at {}".format(corelib)) + "musica library not found at {}".format(corelib)) return corelib ############################################################################### -def _musica_core_install_dir(libroot): -############################################################################### -# Returns the path to the musica-core install directory - - corepaths = glob(os.path.join(libroot, "musicacore*")) - expect(len(corepaths)>0, \ - "musica-core not found at {}".format(libroot)) - expect(len(corepaths)<2, \ - "Multiple musica-core versions found at {}".format(libroot)) - expect(os.path.exists(corepaths[0]), \ - "musica-core install directory not found at {}".format(corepaths[0])) - return corepaths[0] - -############################################################################### -def _musica_core_package_dir(libroot): +def _musica_install_dir(libroot): ############################################################################### -# Returns the path to the musica-core CMake package +# Returns the path to the musica install directory - corepaths = glob(os.path.join(libroot, "musicacore*", "cmake", "musicacore*" )) + corepaths = glob(os.path.join(libroot, "musica*")) expect(len(corepaths)>0, \ - "musica-core package not found at {}".format(libroot)) + "musica not found at {}".format(libroot)) expect(len(corepaths)<2, \ - "Multiple musica-core versions found at {}".format(libroot)) + "Multiple musica versions found at {}".format(libroot)) expect(os.path.exists(corepaths[0]), \ - "musica-core package directory not found at {}".format(corepaths[0])) + "musica install directory not found at {}".format(corepaths[0])) return corepaths[0] ############################################################################### -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 - - build = EnvBuild(case_root=caseroot) - with Case(caseroot) as case: - bldpath = os.path.join(bldroot, "tuv-x") - if not os.path.exists(bldpath): - os.makedirs(bldpath) - jsoninc = _json_fortran_include_dir(libroot) - jsonlib = _json_fortran_lib_path(libroot) - corepackage = _musica_core_package_dir(libroot) - srcpath = os.path.abspath(os.path.join(case.get_value("COMP_ROOT_DIR_ATM"), \ - "libraries", "tuv-x", "")) - logger.info("Building TUV-x in {} from source in {}\n".format(bldpath, srcpath)) - - arg_dict = _cmake_default_args(caseroot) - if build.get_value("MPILIB") == "mpi-serial": - cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["SFC"]) - cmake_args += "-DCMAKE_C_COMPILER={} ".format(arg_dict["SCC"]) - else: - cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["MPIFC"]) - cmake_args += "-DCMAKE_C_COMPILER={} ".format(arg_dict["MPICC"]) - cmake_args += "-DENABLE_MPI:BOOL=TRUE " - if case.get_value("DEBUG"): - cmake_args += "-DCMAKE_BUILD_TYPE=Debug " - else: - cmake_args += "-DCMAKE_BUILD_TYPE=Release " - cmake_args += "-DCMAKE_C_COMPILER_WORKS=1 " - cmake_args += "-DCMAKE_CXX_COMPILER_WORKS=1 " - cmake_args += "-DENABLE_COVERAGE=OFF " - cmake_args += "-DENABLE_TESTS=OFF " - cmake_args += "-DJSON_INCLUDE_DIR={} ".format(jsoninc) - cmake_args += "-DJSON_LIB={} ".format(jsonlib) - cmake_args += "-DCMAKE_PREFIX_PATH={} ".format(corepackage) - cmake_args += "-DCMAKE_Fortran_FLAGS='{}' ".format(arg_dict["FFLAGS"]) - cmake_args += "-DCMAKE_INSTALL_PREFIX='{}' ".format(libroot) - cmake_args += srcpath - - _run_cmd("cmake {}".format(cmake_args), bldpath) - _run_cmd(case.get_value("GMAKE"), bldpath) - _run_cmd("{} install".format(case.get_value("GMAKE")), bldpath) - - # add TUV-x to include paths - incldir = os.environ.get('USER_INCLDIR') - if incldir is None: - incldir = '' - os.environ['USER_INCLDIR'] = incldir + \ - " -I{} ".format(os.path.join(bldroot, "tuv-x", "include")) - - # create simlink 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) - -############################################################################### -def _tuvx_include_dir(libroot): -############################################################################### -# Returns the path to the TUV-x include directory - - tuvxinc = os.path.join(_tuvx_install_dir(libroot), "include", "") - expect(os.path.exists(tuvxinc), \ - "TUV-x include directory not found at {}".format(tuvxinc)) - return tuvxinc - -############################################################################### -def _tuvx_lib_path(libroot): -############################################################################### -# Returns the path to the TUV-x library - - tuvxlib = os.path.join(_tuvx_install_dir(libroot), "lib64", "libtuvx.a") - if not os.path.exists(tuvxlib): - tuvxlib = os.path.join(_tuvx_install_dir(libroot), "lib", "libtuvx.a") - expect(os.path.exists(tuvxlib), \ - "TUV-x library not found at {}".format(tuvxlib)) - return tuvxlib - -############################################################################### -def _tuvx_install_dir(libroot): +def _musica_package_dir(libroot): ############################################################################### -# Returns the path to the TUV-x install directory +# Returns the path to the musica CMake package - tuvxpaths = glob(os.path.join(libroot, "tuvx*")) - expect(len(tuvxpaths)>0, \ - "TUV-x not found at {}".format(libroot)) - expect(len(tuvxpaths)<2, \ - "Multiple TUV-x versions found at {}".format(libroot)) - expect(os.path.exists(tuvxpaths[0]), \ - "TUV-x install directory not found at {}".format(tuvxpaths[0])) - return tuvxpaths[0] + paths = glob(os.path.join(libroot, "musica*", "cmake", "musica*" )) + expect(len(paths)>0, \ + "musica package not found at {}".format(libroot)) + expect(len(paths)<2, \ + "Multiple musica versions found at {}".format(libroot)) + expect(os.path.exists(paths[0]), \ + "musica package directory not found at {}".format(paths[0])) + return paths[0] ############################################################################### def _main_func(): caseroot, libroot, bldroot = parse_input(sys.argv) _build_json_fortran(caseroot, libroot, bldroot) - _build_musica_core(caseroot, libroot, bldroot) - _build_tuvx(caseroot, libroot, bldroot) + _build_musica(caseroot, libroot, bldroot) _build_fms(caseroot, libroot, bldroot) _build_cam(caseroot, libroot, bldroot) diff --git a/cime_config/buildnml b/cime_config/buildnml index 9b2c7bcb22..765eb3e913 100755 --- a/cime_config/buildnml +++ b/cime_config/buildnml @@ -203,7 +203,8 @@ def buildnml(case, caseroot, compname): dest_data = os.path.join(rundir, "data") if os.path.exists(dest_data): shutil.rmtree(dest_data) - shutil.copytree(os.path.join(srcroot, "libraries", "tuv-x", "data"), dest_data) + shutil.copytree(os.path.join(srcroot, "libraries", "musica", "lib", \ + "tuv-x", "data"), dest_data) shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_MOZART.json"), \ os.path.join(rundir, "tuvx_MOZART.json")) shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_MOZART_TS1.json"), \ diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index 8f742460d6..12569487d0 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -193,7 +193,7 @@ char - -ltuvx -lmusicacore -ljsonfortran + -lmusica -ljsonfortran build_component_cam env_build.xml From 1919ddd85c80e098a33eb598eaf5f80b6442b1c1 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Tue, 4 Apr 2023 13:40:18 -0600 Subject: [PATCH 71/84] fix duplicated code from merge; remove first time step filter for using TUV-x --- src/chemistry/mozart/mo_gas_phase_chemdr.F90 | 5 ++-- src/chemistry/mozart/mo_tuvx.F90 | 26 -------------------- 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 index 991ee8ff90..3153ef1be5 100644 --- a/src/chemistry/mozart/mo_gas_phase_chemdr.F90 +++ b/src/chemistry/mozart/mo_gas_phase_chemdr.F90 @@ -250,8 +250,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & use chem_mods, only : nabscol, nfs, indexm, clscnt4 use physconst, only : rga use mo_photo, only : set_ub_col, setcol, table_photo - use mo_tuvx, only : tuvx_get_photo_rates, tuvx_active, & - tuvx_is_first_time_step + use mo_tuvx, only : tuvx_get_photo_rates, tuvx_active use mo_exp_sol, only : exp_sol use mo_imp_sol, only : imp_sol use mo_setrxt, only : setrxt @@ -786,7 +785,7 @@ subroutine gas_phase_chemdr(lchnk, ncol, imozart, q, & call shr_orb_decl( calday, eccen, mvelpp, lambm0, obliqr , & delta, esfact ) - if (tuvx_active .and. .not. tuvx_is_first_time_step) then + if (tuvx_active) then !----------------------------------------------------------------- ! ... get calculated photolysis rates from TUV-x !----------------------------------------------------------------- diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 6dcd84eb82..2881565c31 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -23,7 +23,6 @@ module mo_tuvx public :: tuvx_get_photo_rates public :: tuvx_finalize public :: tuvx_active - public :: tuvx_is_first_time_step ! Inidices for grid updaters integer, parameter :: NUM_GRIDS = 2 ! number of grids that CAM will update at runtime @@ -46,11 +45,6 @@ module mo_tuvx integer, parameter :: RADIATOR_INDEX_AEROSOL = 1 ! Aerosol radiator index integer, parameter :: RADIATOR_INDEX_CLOUDS = 2 ! Cloud radiator index - ! Definition of the MS93 wavelength grid TODO add description of this - integer, parameter :: NUM_BINS_MS93 = 4 - real(kind=r8), parameter :: WAVELENGTH_EDGES_MS93(NUM_BINS_MS93+1) = & - (/ 181.6_r8, 183.1_r8, 184.6_r8, 190.2_r8, 192.5_r8 /) - ! Definition of the MS93 wavelength grid TODO add description of this integer, parameter :: NUM_BINS_MS93 = 4 real(kind=r8), parameter :: WAVELENGTH_EDGES_MS93(NUM_BINS_MS93+1) = & @@ -89,19 +83,6 @@ module mo_tuvx ! Cutoff solar zenith angle for doing photolysis rate calculations [degrees] integer :: max_sza = 0.0_r8 - ! Information needed to set extended-UV photo rates - logical :: do_euv = .false. ! Indicates whether to calculate - ! extended-UV photo rates - integer :: ion_rates_pbuf_index = 0 ! Index in physics buffer for - ! ionization rates - - ! Information needed to do special NO photolysis rate calculation - logical :: do_jno = .false. ! Indicates whether to calculate jno - integer :: jno_index = 0 ! Index in tuvx_ptr::photo_rates_ array for jno - - ! Cutoff solar zenith angle for doing photolysis rate calculations [degrees] - integer :: max_sza = 0.0_r8 - ! TODO how should these paths be set and communicated to this wrapper? character(len=*), parameter :: wavelength_config_path = & "data/grids/wavelength/cam.csv" @@ -145,7 +126,6 @@ module mo_tuvx ! namelist options character(len=cl) :: tuvx_config_path = 'NONE' ! absolute path to TUVX configuration file logical, protected :: tuvx_active = .false. - logical, protected :: tuvx_is_first_time_step = .true. !================================================================================================ contains @@ -450,12 +430,6 @@ end subroutine tuvx_init subroutine tuvx_timestep_init( ) integer :: i_thread - integer, save :: n_time_step = 0 - - if( .not. tuvx_active ) return - - if( n_time_step > 0 ) tuvx_is_first_time_step = .false. - n_time_step = n_time_step + 1 if( .not. tuvx_active ) return From 889e146013125ba8b78f433cf019a470c7019b43 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Tue, 4 Apr 2023 14:08:29 -0600 Subject: [PATCH 72/84] disable TUV-x until ready for use in CAM --- src/chemistry/mozart/mo_tuvx.F90 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 2881565c31..1742bdec42 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -216,11 +216,10 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) #ifdef HAVE_MPI use mpi #endif - use cam_abortutils, only : endrun use cam_logfile, only : iulog ! log file output unit use mo_chem_utls, only : get_spc_ndx, get_inv_ndx use mo_jeuv, only : neuv ! number of extreme-UV rates - use musica_assert, only : assert_msg + use musica_assert, only : assert_msg, die_msg use musica_config, only : config_t use musica_mpi, only : musica_mpi_rank, & musica_mpi_pack_size, & @@ -262,6 +261,8 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) if( is_initialized ) return is_initialized = .true. + call die_msg( 121631567, "TUV-x is not yet ready for use in CAM" ) + if( is_main_task ) write(iulog,*) "Beginning TUV-x Initialization" config_path = trim(tuvx_config_path) @@ -283,7 +284,7 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) ! =============================================================== max_sza = max_solar_zenith_angle if( max_sza <= 0.0_r8 .or. max_sza > 180.0_r8 ) then - call endrun( "TUV-x: max solar zenith angle must be between 0 and 180 degress" ) + call die_msg( 723815691, "TUV-x max solar zenith angle must be between 0 and 180 degress" ) end if ! ================================= From 561d2266804749ef41a57345bc6211ffccac9a48 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Tue, 4 Apr 2023 14:48:33 -0600 Subject: [PATCH 73/84] update comments --- src/chemistry/mozart/mo_tuvx.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 1742bdec42..63e24480ef 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -563,9 +563,9 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & end if end do - ! ============================================== - ! Filter negative rates TODO fix inputs to TUV-x - ! ============================================== + ! ===================== + ! Filter negative rates + ! ===================== tuvx%photo_rates_(:,:,:) = max( 0.0_r8, tuvx%photo_rates_(:,:,:) ) ! ============================================ From c572a6881f5929064e8c12f93ab707f27af9b904 Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Wed, 5 Apr 2023 08:44:13 -0600 Subject: [PATCH 74/84] revert changes to chem-pp code --- src/chemistry/pp_trop_strat_mam4_vbs/mo_imp_sol.F90 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/chemistry/pp_trop_strat_mam4_vbs/mo_imp_sol.F90 b/src/chemistry/pp_trop_strat_mam4_vbs/mo_imp_sol.F90 index bfcbed423c..98cadb9050 100644 --- a/src/chemistry/pp_trop_strat_mam4_vbs/mo_imp_sol.F90 +++ b/src/chemistry/pp_trop_strat_mam4_vbs/mo_imp_sol.F90 @@ -382,9 +382,8 @@ subroutine imp_sol( base_sol, reaction_rates, het_rates, extfrc, delt, & end if base_sol_blk(i,:) = sbase_sol_blk(i,:) else - write(iulog,'('' imp_sol: step failed to converge '')') - !write(iulog,'('' imp_sol: step failed to converge @ (lchnk,vctrpos,nstep,dt,time) = '',3i8,1p,2g15.7)') & - ! lchnk,ofl+i-1,nstep,dt(i),interval_done+dt(i) + write(iulog,'('' imp_sol: step failed to converge @ (lchnk,vctrpos,nstep,dt,time) = '',3i8,1p,2g15.7)') & + lchnk,ofl+i-1,nstep,dt(i),interval_done+dt(i) do m = 1,clscnt4 if( .not. spc_conv_blk(i,m) ) then write(iulog,'(1x,a16,1x,1pe10.3)') solsym(clsmap(m,4)), max_delta(m) From ab79052025d30e3db9d2411c751e1d3d4c0e6d84 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 6 Oct 2023 11:58:21 -0600 Subject: [PATCH 75/84] disable aerosols temporarily --- src/chemistry/mozart/mo_tuvx.F90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 63e24480ef..f7d51fd651 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -1073,7 +1073,6 @@ end function get_cam_radiators subroutine create_updaters( this, grids, profiles, radiators, disable_aerosols, & disable_clouds ) - use modal_aer_opt, only : modal_aer_opt_init use ppgrid, only : pcols ! maximum number of columns use rad_constituents, only : rad_cnst_get_info use musica_assert, only : assert @@ -1172,7 +1171,9 @@ subroutine create_updaters( this, grids, profiles, radiators, disable_aerosols, call rad_cnst_get_info( 0, nmodes = n_modes ) if( n_modes > 0 .and. .not. do_aerosol .and. .not. disable_aerosols ) then do_aerosol = .true. - call modal_aer_opt_init( ) + do_aerosol = .false. ! temporarily disable aerosols + ! TODO update to use new aerosol_optics class + ! call modal_aer_opt_init( ) else ! TODO are there default aerosol optical properties that should be used ! when an aerosol module is not available? From 34a48a3e8bd250dd1ab74e0793111ffd49d03574 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 6 Oct 2023 15:51:39 -0600 Subject: [PATCH 76/84] update externals --- Externals.cfg | 2 +- Externals_CAM.cfg | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/Externals.cfg b/Externals.cfg index d1fb18b7ba..7dfd41a073 100644 --- a/Externals.cfg +++ b/Externals.cfg @@ -23,7 +23,7 @@ required = True [cmeps] tag = cmeps0.14.39 protocol = git -repo_url = https://github.com/mattldawson/CMEPS.git +repo_url = https://github.com/ESCOMP/CMEPS.git local_path = components/cmeps required = True diff --git a/Externals_CAM.cfg b/Externals_CAM.cfg index 61a6852d69..a1189614b8 100644 --- a/Externals_CAM.cfg +++ b/Externals_CAM.cfg @@ -90,20 +90,6 @@ repo_url = https://github.com/jacobwilliams/json-fortran.git tag = 8.2.1 required = True -[musica] -local_path = libraries/musica -protocol = git -repo_url = https://github.com/NCAR/musica.git -branch = main -required = True - -[json-fortran] -local_path = libraries/json-fortran -protocol = git -repo_url = https://github.com/jacobwilliams/json-fortran.git -tag = 8.2.1 -required = True - [hemco] local_path = src/hemco tag = hemco-cesm1_2_1_hemco3_6_3_cesm From 93983f6cfdf1a86174cfd283615b7801461bfbec Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Thu, 1 Feb 2024 12:17:35 -0700 Subject: [PATCH 77/84] update TUV-x --- .gitignore | 3 + Externals_CAM.cfg | 2 +- cime_config/tuvx_MOZART_TS1.json | 634 ++++-- src/chemistry/mozart/mo_tuvx.F90 | 3228 +++++++++++++++--------------- 4 files changed, 2132 insertions(+), 1735 deletions(-) diff --git a/.gitignore b/.gitignore index e99ea80cc4..54898a9808 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,9 @@ src/hemco buildnmlc buildcppc +# ignore ide configs +.vscode + # Ignore editor temporaries and backups *~ .#* diff --git a/Externals_CAM.cfg b/Externals_CAM.cfg index a7e10620ab..e7866ae5af 100644 --- a/Externals_CAM.cfg +++ b/Externals_CAM.cfg @@ -75,7 +75,7 @@ required = True local_path = libraries/musica protocol = git repo_url = https://github.com/NCAR/musica.git -branch = main +branch = develop-update-tuvx required = True [json-fortran] diff --git a/cime_config/tuvx_MOZART_TS1.json b/cime_config/tuvx_MOZART_TS1.json index 666cee23b7..5a37185017 100644 --- a/cime_config/tuvx_MOZART_TS1.json +++ b/cime_config/tuvx_MOZART_TS1.json @@ -70,7 +70,8 @@ "name": "jo2_a", "__reaction": "O2 + hv -> O + O1D", "cross section": { - "netcdf files": [ + "apply O2 bands": true, + "netcdf files": [ { "file path": "data/cross_sections/O2_1.nc", "lower extrapolation": { "type": "boundary" }, @@ -215,7 +216,7 @@ "__reaction": "HNO3 + hv -> OH + NO2", "cross section": { "netcdf files": [ - { "file path": "data/cross_sections/HNO3_1.nc" } + { "file path": "data/cross_sections/HNO3_JPL06.nc" } ], "type": "HNO3+hv->OH+NO2" }, @@ -373,14 +374,37 @@ "name": "jacet", "__reaction": "CH3COCH3 + hv -> CH3CO + CH3", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CH3COCH3_1.nc" } - ], - "type": "CH3COCH3+hv->CH3CO+CH3" - }, - "quantum yield": { - "type": "CH3COCH3+hv->CH3CO+CH3" - } + "type": "temperature based", + "netcdf file": "data/cross_sections/CH3CL_JPL06.nc", + "parameterization": { + "AA": [ -299.80, 5.1047, -3.3630e-2, 9.5805e-5, -1.0135e-7 ], + "BB": [ -7.1727, 1.4837e-1, -1.1463e-3, 3.9188e-6, -4.9994e-9 ], + "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], + "minimum wavelength": 174.1, + "maximum wavelength": 216.0, + "base temperature": 273.0, + "base wavelength": 0.0, + "logarithm": "base 10", + "temperature ranges": [ + { + "maximum": 209.999999999999, + "fixed value": 210.0 + }, + { + "minimum": 210, + "maximum": 300 + }, + { + "minimum": 300.00000000001, + "fixed value": 300.0 + } + ] + } + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } }, { "name": "jmgly", @@ -449,11 +473,7 @@ "cross section": { "netcdf files": [ { - "file path": "data/cross_sections/BrO_1.nc", - "interpolator": { - "type": "fractional target", - "fold in": true - } + "file path": "data/cross_sections/BRO_JPL06.nc" } ], "type": "base" @@ -467,11 +487,29 @@ "name": "jbrono2_a", "__reaction": "BrONO2 + hv -> Br + NO3", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/BrONO2_1.nc" } - ], - "type": "base" - }, + "type": "temperature based", + "parameterization": { + "type": "TAYLOR_SERIES", + "netcdf file": { + "file path": "data/cross_sections/BRONO2_JPL06.nc" + }, + "base temperature": 296.0, + "temperature ranges": [ + { + "maximum": 199.999999999999, + "fixed value": 200.0 + }, + { + "minimum": 200.0, + "maximum": 296.0 + }, + { + "minimum": 296.00000000001, + "fixed value": 296.0 + } + ] + } + }, "quantum yield": { "type": "base", "constant value": 0.85 @@ -481,11 +519,29 @@ "name": "jbrono2_b", "__reaction": "BrONO2 + hv -> BrO + NO2", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/BrONO2_1.nc" } - ], - "type": "base" - }, + "type": "temperature based", + "parameterization": { + "type": "TAYLOR_SERIES", + "netcdf file": { + "file path": "data/cross_sections/BRONO2_JPL06.nc" + }, + "base temperature": 296.0, + "temperature ranges": [ + { + "maximum": 199.999999999999, + "fixed value": 200.0 + }, + { + "minimum": 200.0, + "maximum": 296.0 + }, + { + "minimum": 296.00000000001, + "fixed value": 296.0 + } + ] + } + }, "quantum yield": { "type": "base", "constant value": 0.15 @@ -523,11 +579,33 @@ "name": "jcf3br", "__reaction": "CF3Br + hv -> Products", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CF3Br_1.nc" } - ], - "type": "base" - }, + "type": "temperature based", + "netcdf file": "data/cross_sections/H1301_JPL06.nc", + "parameterization": { + "AA": [ 62.563, -2.0068, 1.6592e-2, -5.6465e-5, 6.7459e-8 ], + "BB": [ -9.1755e-1, 1.8575e-2, -1.3857e-4, 4.5066e-7, -5.3803e-10 ], + "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], + "minimum wavelength": 178.0, + "maximum wavelength": 280.0, + "base temperature": 273.0, + "base wavelength": 0.0, + "logarithm": "base 10", + "temperature ranges": [ + { + "maximum": 209.999999999999, + "fixed value": 210.0 + }, + { + "minimum": 210.0, + "maximum": 300.0 + }, + { + "minimum": 300.00000000001, + "fixed value": 300.0 + } + ] + } + }, "quantum yield": { "type": "base", "constant value": 1.0 @@ -537,11 +615,33 @@ "name": "jcfcl3", "__reaction": "CCl3F + hv -> Products", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CFC-11_1.nc" } - ], - "type": "CCl3F+hv->Products" - }, + "type": "temperature based", + "netcdf file": "data/cross_sections/CFCL3_JPL06.nc", + "parameterization": { + "AA": [ -84.611, 7.9551e-1, -2.0550e-3, -4.4812e-6, 1.5838e-8 ], + "BB": [ -5.7912, 1.1689e-1, -8.8069e-4, 2.9335e-6, -3.6421e-9 ], + "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], + "minimum wavelength": 174.1, + "maximum wavelength": 230.0, + "base temperature": 273.0, + "base wavelength": 0.0, + "logarithm": "base 10", + "temperature ranges": [ + { + "maximum": 209.999999999999, + "fixed value": 210.0 + }, + { + "minimum": 210, + "maximum": 300 + }, + { + "minimum": 300.00000000001, + "fixed value": 300.0 + } + ] + } + }, "quantum yield": { "type": "base", "constant value": 1.0 @@ -551,11 +651,33 @@ "name": "jcfc113", "__reaction": "CFC-113 + hv -> Products", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CFC-113_1.nc" } - ], - "type": "tint" - }, + "type": "temperature based", + "netcdf file": "data/cross_sections/CFC113_JPL06.nc", + "parameterization": { + "AA": [ -1087.9, 20.004, -1.3920e-1, 4.2828e-4, -4.9384e-7 ], + "BB": [ 12.493, -2.3937e-1, 1.7142e-3, -5.4393e-6, 6.4548e-9 ], + "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], + "minimum wavelength": 182.0, + "maximum wavelength": 230.0, + "base temperature": 273.0, + "base wavelength": 0.0, + "logarithm": "base 10", + "temperature ranges": [ + { + "maximum": 209.999999999999, + "fixed value": 210.0 + }, + { + "minimum": 210, + "maximum": 300 + }, + { + "minimum": 300.00000000001, + "fixed value": 300.0 + } + ] + } + }, "quantum yield": { "type": "base", "constant value": 1.0 @@ -565,11 +687,33 @@ "name": "jcfc114", "__reaction": "CFC-114 + hv -> Products", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CFC-114_1.nc" } - ], - "type": "tint" - }, + "type": "temperature based", + "netcdf file": "data/cross_sections/CFC114_JPL10.nc", + "parameterization": { + "AA": [ -160.50, 2.4807, -1.5202e-2, 3.8412e-5, -3.4373e-8 ], + "BB": [ -1.5296, 3.5248e-2, -2.9951e-4, 1.1129e-6, -1.5259e-9 ], + "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], + "minimum wavelength": 172.0, + "maximum wavelength": 220.0, + "base temperature": 273.0, + "base wavelength": 0.0, + "logarithm": "base 10", + "temperature ranges": [ + { + "maximum": 209.999999999999, + "fixed value": 210.0 + }, + { + "minimum": 210, + "maximum": 300 + }, + { + "minimum": 300.00000000001, + "fixed value": 300.0 + } + ] + } + }, "quantum yield": { "type": "base", "constant value": 1.0 @@ -579,11 +723,11 @@ "name": "jcfc115", "__reaction": "CFC-115 + hv -> Products", "cross section": { + "type": "base", "netcdf files": [ - { "file path": "data/cross_sections/CFC-115_1.nc" } - ], - "type": "base" - }, + { "file path": "data/cross_sections/CFC115_JPL10.nc" } + ] + }, "quantum yield": { "type": "base", "constant value": 1.0 @@ -593,11 +737,33 @@ "name": "jcf2cl2", "__reaction": "CCl2F2 + hv -> Products", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CFC-12_1.nc" } - ], - "type": "CCl3F+hv->Products" - }, + "type": "temperature based", + "netcdf file": "data/cross_sections/CF2CL2_JPL06.nc", + "parameterization": { + "AA": [ -43.8954569, -2.403597e-1, -4.2619e-4, 9.8743e-6, 0.0 ], + "BB": [ 4.8438e-3, 4.96145e-4, -5.6953e-6, 0.0, 0.0 ], + "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], + "minimum wavelength": 200.0, + "maximum wavelength": 231.0, + "base temperature": 296.0, + "base wavelength": 200.0, + "logarithm": "natural", + "temperature ranges": [ + { + "maximum": 219.999999999999, + "fixed value": 220.0 + }, + { + "minimum": 220, + "maximum": 296 + }, + { + "minimum": 296.00000000001, + "fixed value": 296.0 + } + ] + } + }, "quantum yield": { "type": "base", "constant value": 1.0 @@ -615,6 +781,9 @@ "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], "minimum wavelength": 210.0, "maximum wavelength": 290.0, + "base temperature": 273.0, + "base wavelength": 0.0, + "logarithm": "base 10", "temperature ranges": [ { "maximum": 209.999999999999, @@ -640,11 +809,33 @@ "name": "jch3br", "__reaction": "CH3Br + hv -> Products", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CH3Br_1.nc" } - ], - "type": "base" - }, + "type": "temperature based", + "netcdf file": "data/cross_sections/CH3BR_JPL06.nc", + "parameterization": { + "AA": [ 46.520, -1.4580, 1.1469e-2, -3.7627e-5, 4.3264e-8 ], + "BB": [ 9.3408e-1, -1.6887e-2, 1.1487e-4, -3.4881e-7, 3.9945e-10 ], + "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], + "minimum wavelength": 200.0, + "maximum wavelength": 280.0, + "base temperature": 273.0, + "base wavelength": 0.0, + "logarithm": "base 10", + "temperature ranges": [ + { + "maximum": 209.999999999999, + "fixed value": 210.0 + }, + { + "minimum": 210, + "maximum": 300 + }, + { + "minimum": 300.00000000001, + "fixed value": 300.0 + } + ] + } + }, "quantum yield": { "type": "base", "constant value": 1.0 @@ -682,14 +873,29 @@ "name": "jchbr3", "__reaction": "CHBr3 + hv -> Products", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CHBr3_1.nc" } - ], - "type": "CHBr3+hv->Products", - "lower extrapolation": { - "type": "boundary" + "type": "temperature based", + "netcdf file": "data/cross_sections/CHBR3_JPL10.nc", + "parameterization": { + "AA": [ -32.6067, 0.10308, 6.39e-5, -7.7392e-7, -2.2513e-9, 6.1376e-12 ], + "BB": [ 0.1582, -0.0014758, 3.8058e-6, 9.187e-10, -1.0772e-11, 0.0 ], + "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 ], + "minimum wavelength": 260.0, + "maximum wavelength": 362.0, + "base temperature": 296.0, + "base wavelength": 0.0, + "logarithm": "base 10", + "invert temperature offset": true, + "temperature ranges": [ + { + "maximum": 259.999999999999, + "fixed value": 260.0 + }, + { + "minimum": 260.0 + } + ] } - }, + }, "quantum yield": { "type": "base", "constant value": 1.0 @@ -712,7 +918,7 @@ "__comments": "TODO - this doesn't exactly match the products in TS1", "cross section": { "netcdf files": [ - { "file path": "data/cross_sections/ClOOCl_1.nc" } + { "file path": "data/cross_sections/CL2O2_JPL10.nc" } ], "type": "base" }, @@ -723,15 +929,16 @@ }, { "name": "jclo", - "__reaction": "ClO + hv -> Cl + O(1D)", + "__reaction": "ClO + hv -> Cl + O", "cross section": { "netcdf files": [ - { "file path": "data/cross_sections/ClO_1.nc" } + { "file path": "data/cross_sections/CLO_JPL06.nc" } ], - "type": "tint" + "type": "base" }, "quantum yield": { - "type": "ClO+hv->Cl+O(1D)" + "type": "base", + "constant value": 1.0 } }, { @@ -793,11 +1000,33 @@ "__reaction": "H2402 + hv -> 2*BR + 2*COF2", "__comments": "TUV data set name CF2BrCF2Br", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CF2BrCF2Br_1.nc" } - ], - "type": "base" - }, + "type": "temperature based", + "netcdf file": "data/cross_sections/H2402_JPL06.nc", + "parameterization": { + "AA": [ 34.026, -1.152616, 8.959798e-3, -2.9089e-5, 3.307212e-8 ], + "BB": [ 4.010664e-1, -8.358968e-3, 6.415741e-5, -2.157554e-7, 2.691871e-10 ], + "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], + "minimum wavelength": 190.0, + "maximum wavelength": 290.0, + "base temperature": 273.0, + "base wavelength": 0.0, + "logarithm": "base 10", + "temperature ranges": [ + { + "maximum": 209.999999999999, + "fixed value": 210.0 + }, + { + "minimum": 210.0, + "maximum": 300.0 + }, + { + "minimum": 300.00000000001, + "fixed value": 300.0 + } + ] + } + }, "quantum yield": { "type": "base", "constant value": 1.0 @@ -807,11 +1036,33 @@ "name": "jhcfc141b", "__reaction": "HCFC-141b + hv -> Products", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CH3CFCl2_1.nc" } - ], - "type": "base" - }, + "type": "temperature based", + "netcdf file": "data/cross_sections/HCFC141b_JPL10.nc", + "parameterization": { + "AA": [ -682.913042, 12.122290, -8.187699e-2, 2.437244e-4, -2.719103e-7 ], + "BB": [ 4.074747, -8.053899e-2, 5.946552e-4, -1.945048e-6, 2.380143e-9 ], + "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], + "minimum wavelength": 172.0, + "maximum wavelength": 240.0, + "base temperature": 273.0, + "base wavelength": 0.0, + "logarithm": "base 10", + "temperature ranges": [ + { + "maximum": 209.999999999999, + "fixed value": 210.0 + }, + { + "minimum": 210.0, + "maximum": 300.0 + }, + { + "minimum": 300.00000000001, + "fixed value": 300.0 + } + ] + } + }, "quantum yield": { "type": "base", "constant value": 1.0 @@ -821,11 +1072,33 @@ "name": "jhcfc142b", "__reaction": "HCFC-142b + hv -> Products", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CH3CF2Cl_1.nc" } - ], - "type": "HCFC+hv->Products" - }, + "type": "temperature based", + "netcdf file": "data/cross_sections/HCFC142b_JPL10.nc", + "parameterization": { + "AA": [ -328.092008, 6.342799, -4.810362e-2, 1.611991e-4, -2.042613e-7 ], + "BB": [ 4.289533e-1, -9.042817e-3, 7.018009e-5, -2.389064e-7, 3.039799e-10 ], + "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], + "minimum wavelength": 172.0, + "maximum wavelength": 230.0, + "base temperature": 273.0, + "base wavelength": 0.0, + "logarithm": "base 10", + "temperature ranges": [ + { + "maximum": 209.999999999999, + "fixed value": 210.0 + }, + { + "minimum": 210.0, + "maximum": 300.0 + }, + { + "minimum": 300.00000000001, + "fixed value": 300.0 + } + ] + } + }, "quantum yield": { "type": "base", "constant value": 1.0 @@ -835,11 +1108,43 @@ "name": "jhcfc22", "__reaction": "HCFC-22 + hv -> Products", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CHClF2_1.nc" } - ], - "type": "tint" - }, + "type": "temperature based", + "netcdf file": "data/cross_sections/HCFC22_JPL06.nc", + "parameterization wavelength grid": { + "name": "custom wavelengths", + "type": "from config file", + "units": "nm", + "values": [ + 169.0, 171.0, 173.0, 175.0, 177.0, 179.0, 181.0, 183.0, 185.0, + 187.0, 189.0, 191.0, 193.0, 195.0, 197.0, 199.0, 201.0, 203.0, + 205.0, 207.0, 209.0, 211.0, 213.0, 215.0, 217.0, 219.0, 221.0 + ] + }, + "parameterization": { + "AA": [ -106.029, 1.5038, -8.2476e-3, 1.4206e-5 ], + "BB": [ -1.3399e-1, 2.7405e-3, -1.8028e-5, 3.8504e-8 ], + "lp": [ 0.0, 1.0, 2.0, 3.0 ], + "minimum wavelength": 174.0, + "maximum wavelength": 204.0, + "base temperature": 273.0, + "base wavelength": 0.0, + "logarithm": "base 10", + "temperature ranges": [ + { + "maximum": 209.999999999999, + "fixed value": 210.0 + }, + { + "minimum": 210.0, + "maximum": 300.0 + }, + { + "minimum": 300.00000000001, + "fixed value": 300.0 + } + ] + } + }, "quantum yield": { "type": "base", "constant value": 1.0 @@ -903,32 +1208,84 @@ { "name": "jho2no2_a", "__reaction": "HNO4 + hv -> OH + NO3", - "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/HNO4_1.nc" } - ], - "type": "base" - }, - "quantum yield": { + "type": "temperature based", + "netcdf file": "data/cross_sections/HO2NO2_JPL06.nc", + "parameterization": { + "type": "BURKHOLDER", + "netcdf file": { + "file path": "data/cross_sections/HO2NO2_temp_JPL06.nc" + }, + "A": -988.0, + "B": 0.69, + "temperature ranges": [ + { + "maximum": 279.999999999999, + "fixed value": 280.0 + }, + { + "minimum": 280.0, + "maximum": 350.0 + }, + { + "minimum": 350.00000000001, + "fixed value": 350.0 + } + ] + } + }, + "quantum yield": { "type": "base", - "constant value": 0.2 - } + "constant value": 0.30, + "override bands": [ + { + "band": "range", + "minimum wavelength": 200.0, + "value": 0.20 + } + ] + } }, { "name": "jho2no2_b", "__reaction": "HNO4 + hv -> HO2 + NO2", - "__comments": "TODO Doug's data sets have special temperature dependence - need new type?", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/HNO4_1.nc" } - ], - "type": "base" - }, - "quantum yield": { - "type": "base", - "constant value": 0.8 - } + "type": "temperature based", + "netcdf file": "data/cross_sections/HO2NO2_JPL06.nc", + "parameterization": { + "type": "BURKHOLDER", + "netcdf file": { + "file path": "data/cross_sections/HO2NO2_temp_JPL06.nc" + }, + "A": -988.0, + "B": 0.69, + "temperature ranges": [ + { + "maximum": 279.999999999999, + "fixed value": 280.0 + }, + { + "minimum": 280.0, + "maximum": 350.0 + }, + { + "minimum": 350.00000000001, + "fixed value": 350.0 + } + ] + } + }, + "quantum yield": { + "type": "base", + "constant value": 0.70, + "override bands": [ + { + "band": "range", + "minimum wavelength": 200.0, + "value": 0.80 + } + ] + } }, { "name": "jmacr_a", @@ -1067,7 +1424,7 @@ }, "quantum yield": { "type": "base", - "constant value": 0.55 + "constant value": 0.45 } }, { @@ -1081,7 +1438,7 @@ }, "quantum yield": { "type": "base", - "constant value": 0.45 + "constant value": 0.55 } }, { @@ -1145,14 +1502,34 @@ "__reaction": "H2SO4 + hv -> SO3 + H2O", "cross section": { "type": "base", - "netcdf files": [ - { "file path": "data/cross_sections/H2SO4_1.nc" } - ] - }, + "data": { + "default value": 0.0, + "point values": [ + { "wavelength": 121.65, "value": 6.3e-17 }, + { "wavelength": 525.0, "value": 1.43e-26 }, + { "wavelength": 625.0, "value": 1.8564e-25 }, + { "wavelength": 725.0, "value": 3.086999e-24 } + ] + } + }, "quantum yield": { - "type": "base", - "constant value": 1.0 - } + "type": "H2SO4 Mills", + "netcdf files": [ + "data/quantum_yields/H2SO4_mills.nc" + ], + "parameterized wavelengths": [ + 525, + 625, + 725 + ], + "collision interval s": [ + 1.1e-9, + 8.9e-9, + 1.7e-7 + ], + "molecular diameter m": 4.18e-10, + "molecular weight kg mol-1": 98.078479e-3 + } }, { "name": "jocs", @@ -1188,7 +1565,7 @@ "cross section": { "type": "base", "netcdf files": [ - { "file path": "data/cross_sections/SO2_1.nc" } + { "file path": "data/cross_sections/SO2_Mills.nc" } ] }, "quantum yield": { @@ -1209,6 +1586,23 @@ "type": "base", "constant value": 1.0 } + }, + { + "name": "jno_i", + "__reaction": "NO + hv -> NOp + e", + "cross section": { + "type": "base", + "data": { + "default value": 0.0, + "point values": [ + { "wavelength": 121.65, "value": 2.0e-18 } + ] + } + }, + "quantum yield": { + "type": "base", + "constant value": 1.0 + } } ] }, diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index f7d51fd651..52c45aadf0 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -3,605 +3,605 @@ !---------------------------------------------------------------------- module mo_tuvx - use musica_map, only : map_t - use musica_string, only : string_t - use ppgrid, only : pver ! number of vertical layers - 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 - use tuvx_radiator_from_host, only : radiator_updater_t - - implicit none - - private - - public :: tuvx_readnl - public :: tuvx_register - public :: tuvx_init - public :: tuvx_timestep_init - public :: tuvx_get_photo_rates - public :: tuvx_finalize - public :: tuvx_active - - ! 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 - integer, parameter :: GRID_INDEX_WAVELENGTH = 2 ! Wavelength grid index - - ! Indices for profile updaters - integer, parameter :: NUM_PROFILES = 8 ! number of profiles that CAM will update at runtime - integer, parameter :: PROFILE_INDEX_TEMPERATURE = 1 ! Temperature profile index - integer, parameter :: PROFILE_INDEX_ALBEDO = 2 ! Surface albedo profile index - integer, parameter :: PROFILE_INDEX_ET_FLUX = 3 ! Extraterrestrial flux profile index - integer, parameter :: PROFILE_INDEX_AIR = 4 ! Air density profile index - integer, parameter :: PROFILE_INDEX_O3 = 5 ! Ozone profile index - integer, parameter :: PROFILE_INDEX_O2 = 6 ! Molecular oxygen profile index - integer, parameter :: PROFILE_INDEX_SO2 = 7 ! Sulfur dioxide profile index - integer, parameter :: PROFILE_INDEX_NO2 = 8 ! Nitrogen dioxide profile index - - ! Indices for radiator updaters - integer, parameter :: NUM_RADIATORS = 2 ! number of radiators that CAM will update at runtime - integer, parameter :: RADIATOR_INDEX_AEROSOL = 1 ! Aerosol radiator index - integer, parameter :: RADIATOR_INDEX_CLOUDS = 2 ! Cloud radiator index - - ! Definition of the MS93 wavelength grid TODO add description of this - integer, parameter :: NUM_BINS_MS93 = 4 - real(kind=r8), parameter :: WAVELENGTH_EDGES_MS93(NUM_BINS_MS93+1) = & + use musica_map, only : map_t + use musica_string, only : string_t + use ppgrid, only : pver ! number of vertical layers + 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 + use tuvx_radiator_from_host, only : radiator_updater_t + + implicit none + + private + + public :: tuvx_readnl + public :: tuvx_register + public :: tuvx_init + public :: tuvx_timestep_init + public :: tuvx_get_photo_rates + public :: tuvx_finalize + public :: tuvx_active + + ! 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 + integer, parameter :: GRID_INDEX_WAVELENGTH = 2 ! Wavelength grid index + + ! Indices for profile updaters + integer, parameter :: NUM_PROFILES = 8 ! number of profiles that CAM will update at runtime + integer, parameter :: PROFILE_INDEX_TEMPERATURE = 1 ! Temperature profile index + integer, parameter :: PROFILE_INDEX_ALBEDO = 2 ! Surface albedo profile index + integer, parameter :: PROFILE_INDEX_ET_FLUX = 3 ! Extraterrestrial flux profile index + integer, parameter :: PROFILE_INDEX_AIR = 4 ! Air density profile index + integer, parameter :: PROFILE_INDEX_O3 = 5 ! Ozone profile index + integer, parameter :: PROFILE_INDEX_O2 = 6 ! Molecular oxygen profile index + integer, parameter :: PROFILE_INDEX_SO2 = 7 ! Sulfur dioxide profile index + integer, parameter :: PROFILE_INDEX_NO2 = 8 ! Nitrogen dioxide profile index + + ! Indices for radiator updaters + integer, parameter :: NUM_RADIATORS = 2 ! number of radiators that CAM will update at runtime + integer, parameter :: RADIATOR_INDEX_AEROSOL = 1 ! Aerosol radiator index + integer, parameter :: RADIATOR_INDEX_CLOUDS = 2 ! Cloud radiator index + + ! Definition of the MS93 wavelength grid TODO add description of this + integer, parameter :: NUM_BINS_MS93 = 4 + real(kind=r8), parameter :: WAVELENGTH_EDGES_MS93(NUM_BINS_MS93+1) = & (/ 181.6_r8, 183.1_r8, 184.6_r8, 190.2_r8, 192.5_r8 /) - ! Information needed to access CAM species state data - logical :: is_fixed_N2 = .false. ! indicates whether N2 concentrations are fixed - logical :: is_fixed_O = .false. ! indicates whether O concentrations are fixed - logical :: is_fixed_O2 = .false. ! indicates whether O2 concentrations are fixed - logical :: is_fixed_O3 = .false. ! indicates whether O3 concentrations are fixed - logical :: is_fixed_NO = .false. ! indicates whether NO concentrations are fixed - integer :: index_N2 = 0 ! index for N2 in concentration array - integer :: index_O = 0 ! index for O in concentration array - integer :: index_O2 = 0 ! index for O2 in concentration array - integer :: index_O3 = 0 ! index for O3 in concentration array - integer :: index_NO = 0 ! index for NO in concentration array - - ! Information needed to access aerosol and cloud optical properties - logical :: do_aerosol = .false. ! indicates whether aerosol optical properties - ! are available and should be used in radiative - ! transfer calculations - logical :: do_clouds = .false. ! indicates whether cloud optical properties - ! should be calculated and used in radiative - ! transfer calculations - - ! Information needed to set extended-UV photo rates - logical :: do_euv = .false. ! Indicates whether to calculate - ! extended-UV photo rates - integer :: ion_rates_pbuf_index = 0 ! Index in physics buffer for - ! ionization rates - - ! Information needed to do special NO photolysis rate calculation - logical :: do_jno = .false. ! Indicates whether to calculate jno - integer :: jno_index = 0 ! Index in tuvx_ptr::photo_rates_ array for jno - - ! Cutoff solar zenith angle for doing photolysis rate calculations [degrees] - integer :: max_sza = 0.0_r8 - - ! TODO how should these paths be set and communicated to this wrapper? - character(len=*), parameter :: wavelength_config_path = & - "data/grids/wavelength/cam.csv" - logical, parameter :: enable_diagnostics = .true. - - ! TUV-x calculator for each OMP thread - type :: tuvx_ptr - type(core_t), pointer :: core_ => null( ) ! TUV-x calculator - integer :: n_photo_rates_ = 0 ! number of photo reactions in TUV-x - integer :: n_euv_rates_ = 0 ! number of extreme-UV rates - integer :: n_special_rates_ = 0 ! number of special photo rates - real(r8), allocatable :: photo_rates_(:,:,:) ! photolysis rate constants - ! (column, vertical level, reaction) [s-1] - type(map_t) :: photo_rate_map_ ! map between TUV-x and CAM - ! photo rate constant arrays - type(grid_updater_t) :: grids_(NUM_GRIDS) ! grid updaters - type(profile_updater_t) :: profiles_(NUM_PROFILES) ! profile updaters - type(radiator_updater_t) :: radiators_(NUM_RADIATORS) ! radiator updaters - real(r8) :: height_delta_(pver+1) ! change in height in each - ! vertical layer (km) - real(r8), allocatable :: wavelength_edges_(:) ! TUV-x wavelength bin edges (nm) - real(r8), allocatable :: wavelength_values_(:) ! Working array for interface values - ! on the TUV-x wavelength grid - real(r8), allocatable :: wavelength_mid_values_(:) ! Working array for mid-point values - ! on the TUV-x wavelength grid - real(r8), allocatable :: optical_depth_(:,:,:) ! (column, vertical level, wavelength) [unitless] - real(r8), allocatable :: single_scattering_albedo_(:,:,:) ! (column, vertical level, wavelength) [unitless] - real(r8), allocatable :: asymmetry_factor_(:,:,:) ! (column, vertical level, wavelength) [unitless] - real(r8) :: et_flux_ms93_(NUM_BINS_MS93) ! extraterrestrial flux on the MS93 grid - ! [photon cm-2 nm-1 s-1] - end type tuvx_ptr - type(tuvx_ptr), allocatable :: tuvx_ptrs(:) - - ! Diagnostic photolysis rate constant output - type :: diagnostic_t - character(len=:), allocatable :: name_ ! Name of the output field - integer :: index_ ! index of the photolysis rate constant from TUV-x - end type diagnostic_t - type(diagnostic_t), allocatable :: diagnostics(:) - - ! namelist options - character(len=cl) :: tuvx_config_path = 'NONE' ! absolute path to TUVX configuration file - logical, protected :: tuvx_active = .false. + ! Information needed to access CAM species state data + logical :: is_fixed_N2 = .false. ! indicates whether N2 concentrations are fixed + logical :: is_fixed_O = .false. ! indicates whether O concentrations are fixed + logical :: is_fixed_O2 = .false. ! indicates whether O2 concentrations are fixed + logical :: is_fixed_O3 = .false. ! indicates whether O3 concentrations are fixed + logical :: is_fixed_NO = .false. ! indicates whether NO concentrations are fixed + integer :: index_N2 = 0 ! index for N2 in concentration array + integer :: index_O = 0 ! index for O in concentration array + integer :: index_O2 = 0 ! index for O2 in concentration array + integer :: index_O3 = 0 ! index for O3 in concentration array + integer :: index_NO = 0 ! index for NO in concentration array + + ! Information needed to access aerosol and cloud optical properties + logical :: do_aerosol = .false. ! indicates whether aerosol optical properties + ! are available and should be used in radiative + ! transfer calculations + logical :: do_clouds = .false. ! indicates whether cloud optical properties + ! should be calculated and used in radiative + ! transfer calculations + + ! Information needed to set extended-UV photo rates + logical :: do_euv = .false. ! Indicates whether to calculate + ! extended-UV photo rates + integer :: ion_rates_pbuf_index = 0 ! Index in physics buffer for + ! ionization rates + + ! Information needed to do special NO photolysis rate calculation + logical :: do_jno = .false. ! Indicates whether to calculate jno + integer :: jno_index = 0 ! Index in tuvx_ptr::photo_rates_ array for jno + + ! Cutoff solar zenith angle for doing photolysis rate calculations [degrees] + integer :: max_sza = 0.0_r8 + + ! TODO how should these paths be set and communicated to this wrapper? + character(len=*), parameter :: wavelength_config_path = & + "data/grids/wavelength/cam.csv" + logical, parameter :: enable_diagnostics = .true. + + ! TUV-x calculator for each OMP thread + type :: tuvx_ptr + type(core_t), pointer :: core_ => null( ) ! TUV-x calculator + integer :: n_photo_rates_ = 0 ! number of photo reactions in TUV-x + integer :: n_euv_rates_ = 0 ! number of extreme-UV rates + integer :: n_special_rates_ = 0 ! number of special photo rates + real(r8), allocatable :: photo_rates_(:,:,:) ! photolysis rate constants + ! (column, vertical level, reaction) [s-1] + type(map_t) :: photo_rate_map_ ! map between TUV-x and CAM + ! photo rate constant arrays + type(grid_updater_t) :: grids_(NUM_GRIDS) ! grid updaters + type(profile_updater_t) :: profiles_(NUM_PROFILES) ! profile updaters + type(radiator_updater_t) :: radiators_(NUM_RADIATORS) ! radiator updaters + real(r8) :: height_delta_(pver+1) ! change in height in each + ! vertical layer (km) + real(r8), allocatable :: wavelength_edges_(:) ! TUV-x wavelength bin edges (nm) + real(r8), allocatable :: wavelength_values_(:) ! Working array for interface values + ! on the TUV-x wavelength grid + real(r8), allocatable :: wavelength_mid_values_(:) ! Working array for mid-point values + ! on the TUV-x wavelength grid + real(r8), allocatable :: optical_depth_(:,:,:) ! (column, vertical level, wavelength) [unitless] + real(r8), allocatable :: single_scattering_albedo_(:,:,:) ! (column, vertical level, wavelength) [unitless] + real(r8), allocatable :: asymmetry_factor_(:,:,:) ! (column, vertical level, wavelength) [unitless] + real(r8) :: et_flux_ms93_(NUM_BINS_MS93) ! extraterrestrial flux on the MS93 grid + ! [photon cm-2 nm-1 s-1] + end type tuvx_ptr + type(tuvx_ptr), allocatable :: tuvx_ptrs(:) + + ! Diagnostic photolysis rate constant output + type :: diagnostic_t + character(len=:), allocatable :: name_ ! Name of the output field + integer :: index_ ! index of the photolysis rate constant from TUV-x + end type diagnostic_t + type(diagnostic_t), allocatable :: diagnostics(:) + + ! namelist options + character(len=cl) :: tuvx_config_path = 'NONE' ! absolute path to TUVX configuration file + logical, protected :: tuvx_active = .false. !================================================================================================ contains !================================================================================================ - !----------------------------------------------------------------------- - ! registers fields in the physics buffer - !----------------------------------------------------------------------- - subroutine tuvx_register( ) + !----------------------------------------------------------------------- + ! registers fields in the physics buffer + !----------------------------------------------------------------------- + subroutine tuvx_register( ) - use mo_jeuv, only : nIonRates - use physics_buffer, only : pbuf_add_field, dtype_r8 - use ppgrid, only : pcols ! maximum number of columns + use mo_jeuv, only : nIonRates + use physics_buffer, only : pbuf_add_field, dtype_r8 + use ppgrid, only : pcols ! maximum number of columns - ! add photo-ionization rates to physics buffer for WACCMX Ionosphere module - call pbuf_add_field( 'IonRates', 'physpkg', dtype_r8, (/ pcols, pver, nIonRates /), & - ion_rates_pbuf_index ) ! Ionization rates for O+, O2+, N+, N2+, NO+ + ! add photo-ionization rates to physics buffer for WACCMX Ionosphere module + call pbuf_add_field( 'IonRates', 'physpkg', dtype_r8, (/ pcols, pver, nIonRates /), & + ion_rates_pbuf_index ) ! Ionization rates for O+, O2+, N+, N2+, NO+ - end subroutine tuvx_register + end subroutine tuvx_register !================================================================================================ - !----------------------------------------------------------------------- - ! read namelist options - !----------------------------------------------------------------------- - subroutine tuvx_readnl(nlfile) + !----------------------------------------------------------------------- + ! read namelist options + !----------------------------------------------------------------------- + subroutine tuvx_readnl(nlfile) #ifdef HAVE_MPI - use mpi + use mpi #endif - use cam_abortutils, only : endrun - use cam_logfile, only : iulog ! log file output unit - use namelist_utils, only : find_group_name - use spmd_utils, only : mpicom, is_main_task => masterproc, & - main_task => masterprocid - - character(len=*), intent(in) :: nlfile ! filepath for file containing namelist input - - integer :: unitn, ierr - character(len=*), parameter :: subname = 'tuvx_readnl' - - ! =================== - ! Namelist definition - ! =================== - namelist /tuvx_opts/ tuvx_active, tuvx_config_path - - ! ============= - ! Read namelist - ! ============= - if (is_main_task) then - open( newunit=unitn, file=trim(nlfile), status='old' ) - call find_group_name(unitn, 'tuvx_opts', status=ierr) - if (ierr == 0) then - read(unitn, tuvx_opts, iostat=ierr) - if (ierr /= 0) then - call endrun(subname // ':: ERROR reading namelist') - end if - end if - close(unitn) - end if - - ! ============================ - ! Broadcast namelist variables - ! ============================ + use cam_abortutils, only : endrun + use cam_logfile, only : iulog ! log file output unit + use namelist_utils, only : find_group_name + use spmd_utils, only : mpicom, is_main_task => masterproc, & + main_task => masterprocid + + character(len=*), intent(in) :: nlfile ! filepath for file containing namelist input + + integer :: unitn, ierr + character(len=*), parameter :: subname = 'tuvx_readnl' + + ! =================== + ! Namelist definition + ! =================== + namelist /tuvx_opts/ tuvx_active, tuvx_config_path + + ! ============= + ! Read namelist + ! ============= + if (is_main_task) then + open( newunit=unitn, file=trim(nlfile), status='old' ) + call find_group_name(unitn, 'tuvx_opts', status=ierr) + if (ierr == 0) then + read(unitn, tuvx_opts, iostat=ierr) + if (ierr /= 0) then + call endrun(subname // ':: ERROR reading namelist') + end if + end if + close(unitn) + end if + + ! ============================ + ! Broadcast namelist variables + ! ============================ #ifdef HAVE_MPI - call mpi_bcast(tuvx_config_path, len(tuvx_config_path), mpi_character, main_task, mpicom, ierr) - call mpi_bcast(tuvx_active, 1, mpi_logical, main_task, mpicom, ierr) + call mpi_bcast(tuvx_config_path, len(tuvx_config_path), mpi_character, main_task, mpicom, ierr) + call mpi_bcast(tuvx_active, 1, mpi_logical, main_task, mpicom, ierr) #endif - if (tuvx_active .and. tuvx_config_path == 'NONE') then - call endrun(subname // ' : must set tuvx_config_path when TUV-X is active') - end if + if (tuvx_active .and. tuvx_config_path == 'NONE') then + call endrun(subname // ' : must set tuvx_config_path when TUV-X is active') + end if - if (is_main_task) then - write(iulog,*) 'tuvx_readnl: tuvx_config_path = ', trim(tuvx_config_path) - write(iulog,*) 'tuvx_readnl: tuvx_active = ', tuvx_active - end if + if (is_main_task) then + write(iulog,*) 'tuvx_readnl: tuvx_config_path = ', trim(tuvx_config_path) + write(iulog,*) 'tuvx_readnl: tuvx_active = ', tuvx_active + end if - end subroutine tuvx_readnl + end subroutine tuvx_readnl !================================================================================================ - !----------------------------------------------------------------------- - ! Initializes TUV-x for photolysis calculations - !----------------------------------------------------------------------- - subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) + !----------------------------------------------------------------------- + ! Initializes TUV-x for photolysis calculations + !----------------------------------------------------------------------- + subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) #ifdef HAVE_MPI - use mpi + use mpi #endif - use cam_logfile, only : iulog ! log file output unit - use mo_chem_utls, only : get_spc_ndx, get_inv_ndx - use mo_jeuv, only : neuv ! number of extreme-UV rates - use musica_assert, only : assert_msg, die_msg - use musica_config, only : config_t - use musica_mpi, only : musica_mpi_rank, & - musica_mpi_pack_size, & - musica_mpi_pack, & - musica_mpi_unpack - use musica_string, only : string_t, to_char - use ppgrid, only : pcols ! maximum number of columns - use shr_const_mod, only : pi => shr_const_pi - use solar_irrad_data, only : has_spectrum - use spmd_utils, only : main_task => masterprocid, & - is_main_task => masterproc, & - mpicom - use tuvx_grid, only : grid_t - use tuvx_grid_warehouse, only : grid_warehouse_t - use tuvx_profile_warehouse, only : profile_warehouse_t - use tuvx_radiator_warehouse, only : radiator_warehouse_t - - 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] - - character(len=*), parameter :: my_name = "TUV-x wrapper initialization" - class(core_t), pointer :: core - character, allocatable :: buffer(:) - type(string_t) :: config_path - type(config_t) :: tuvx_config, cam_config, map_config - type(map_t) :: map - class(grid_t), pointer :: height - class(grid_warehouse_t), pointer :: cam_grids - class(profile_warehouse_t), pointer :: cam_profiles - class(radiator_warehouse_t), pointer :: cam_radiators - integer :: pack_size, pos, i_core, i_err - logical :: disable_aerosols, disable_clouds - type(string_t) :: required_keys(1), optional_keys(2) - logical, save :: is_initialized = .false. - - if( .not. tuvx_active ) return - if( is_initialized ) return - is_initialized = .true. - - call die_msg( 121631567, "TUV-x is not yet ready for use in CAM" ) - - if( is_main_task ) write(iulog,*) "Beginning TUV-x Initialization" - - config_path = trim(tuvx_config_path) - - ! =============================== - ! CAM TUV-x configuration options - ! =============================== - required_keys(1) = "aliasing" - optional_keys(1) = "disable aerosols" - optional_keys(2) = "disable clouds" + use cam_logfile, only : iulog ! log file output unit + use mo_chem_utls, only : get_spc_ndx, get_inv_ndx + use mo_jeuv, only : neuv ! number of extreme-UV rates + use musica_assert, only : assert_msg, die_msg + use musica_config, only : config_t + use musica_mpi, only : musica_mpi_rank, & + musica_mpi_pack_size, & + musica_mpi_pack, & + musica_mpi_unpack + use musica_string, only : string_t, to_char + use ppgrid, only : pcols ! maximum number of columns + use shr_const_mod, only : pi => shr_const_pi + use solar_irrad_data, only : has_spectrum + use spmd_utils, only : main_task => masterprocid, & + is_main_task => masterproc, & + mpicom + use tuvx_grid, only : grid_t + use tuvx_grid_warehouse, only : grid_warehouse_t + use tuvx_profile_warehouse, only : profile_warehouse_t + use tuvx_radiator_warehouse, only : radiator_warehouse_t + + 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] + + character(len=*), parameter :: my_name = "TUV-x wrapper initialization" + class(core_t), pointer :: core + character, allocatable :: buffer(:) + type(string_t) :: config_path + type(config_t) :: tuvx_config, cam_config, map_config + type(map_t) :: map + class(grid_t), pointer :: height + class(grid_warehouse_t), pointer :: cam_grids + class(profile_warehouse_t), pointer :: cam_profiles + class(radiator_warehouse_t), pointer :: cam_radiators + integer :: pack_size, pos, i_core, i_err + logical :: disable_aerosols, disable_clouds + type(string_t) :: required_keys(1), optional_keys(2) + logical, save :: is_initialized = .false. + + if( .not. tuvx_active ) return + if( is_initialized ) return + is_initialized = .true. + + ! call die_msg( 121631567, "TUV-x is not yet ready for use in CAM" ) + + if( is_main_task ) write(iulog,*) "Beginning TUV-x Initialization" + + config_path = trim(tuvx_config_path) + + ! =============================== + ! CAM TUV-x configuration options + ! =============================== + required_keys(1) = "aliasing" + optional_keys(1) = "disable aerosols" + optional_keys(2) = "disable clouds" #ifndef HAVE_MPI - call assert_msg( 113937299, is_main_task, "Multiple tasks present without " & - //"MPI support enabled for TUV-x" ) + call assert_msg( 113937299, is_main_task, "Multiple tasks present without " & + //"MPI support enabled for TUV-x" ) #endif - ! =============================================================== - ! set the maximum solar zenith angle to calculate photo rates for - ! =============================================================== - max_sza = max_solar_zenith_angle - if( max_sza <= 0.0_r8 .or. max_sza > 180.0_r8 ) then - call die_msg( 723815691, "TUV-x max solar zenith angle must be between 0 and 180 degress" ) - end if - - ! ================================= - ! initialize the extended-UV module - ! ================================= - call initialize_euv( photon_file, electron_file, do_euv ) - - ! ========================================================================== - ! create the set of TUV-x grids and profiles that CAM will update at runtime - ! ========================================================================== - cam_grids => get_cam_grids( wavelength_config_path ) - cam_profiles => get_cam_profiles( cam_grids ) - cam_radiators => get_cam_radiators( cam_grids ) - - ! ================================================================== - ! construct a core and a map between TUV-x and CAM photolysis arrays - ! on the primary process and pack them onto an MPI buffer - ! ================================================================== - if( is_main_task ) then - call tuvx_config%from_file( config_path%to_char( ) ) - call tuvx_config%get( "__CAM options", cam_config, my_name ) - call assert_msg( 973680295, & - cam_config%validate( required_keys, optional_keys ), & - "Bad configuration for CAM TUV-x options." ) - call cam_config%get( "disable aerosols", disable_aerosols, my_name, & - default = .false. ) - call cam_config%get( "disable clouds", disable_clouds, my_name, & - default = .false. ) - call cam_config%get( "aliasing", map_config, my_name ) - core => core_t( config_path, cam_grids, cam_profiles, cam_radiators ) - call set_photo_rate_map( core, map_config, do_euv, do_jno, jno_index, map ) - pack_size = core%pack_size( mpicom ) + & - map%pack_size( mpicom ) + & - musica_mpi_pack_size( do_jno, mpicom ) + & - musica_mpi_pack_size( jno_index, mpicom ) + & - musica_mpi_pack_size( disable_aerosols, mpicom ) + & - musica_mpi_pack_size( disable_clouds, mpicom ) - allocate( buffer( pack_size ) ) - pos = 0 - call core%mpi_pack( buffer, pos, mpicom ) - call map%mpi_pack( buffer, pos, mpicom ) - call musica_mpi_pack( buffer, pos, do_jno, mpicom ) - call musica_mpi_pack( buffer, pos, jno_index, mpicom ) - call musica_mpi_pack( buffer, pos, disable_aerosols, mpicom ) - call musica_mpi_pack( buffer, pos, disable_clouds, mpicom ) - deallocate( core ) - end if + ! =============================================================== + ! set the maximum solar zenith angle to calculate photo rates for + ! =============================================================== + max_sza = max_solar_zenith_angle + if( max_sza <= 0.0_r8 .or. max_sza > 180.0_r8 ) then + call die_msg( 723815691, "TUV-x max solar zenith angle must be between 0 and 180 degress" ) + end if + + ! ================================= + ! initialize the extended-UV module + ! ================================= + call initialize_euv( photon_file, electron_file, do_euv ) + + ! ========================================================================== + ! create the set of TUV-x grids and profiles that CAM will update at runtime + ! ========================================================================== + cam_grids => get_cam_grids( wavelength_config_path ) + cam_profiles => get_cam_profiles( cam_grids ) + cam_radiators => get_cam_radiators( cam_grids ) + + ! ================================================================== + ! construct a core and a map between TUV-x and CAM photolysis arrays + ! on the primary process and pack them onto an MPI buffer + ! ================================================================== + if( is_main_task ) then + call tuvx_config%from_file( config_path%to_char( ) ) + call tuvx_config%get( "__CAM options", cam_config, my_name ) + call assert_msg( 973680295, & + cam_config%validate( required_keys, optional_keys ), & + "Bad configuration for CAM TUV-x options." ) + call cam_config%get( "disable aerosols", disable_aerosols, my_name, & + default = .false. ) + call cam_config%get( "disable clouds", disable_clouds, my_name, & + default = .false. ) + call cam_config%get( "aliasing", map_config, my_name ) + core => core_t( config_path, cam_grids, cam_profiles, cam_radiators ) + call set_photo_rate_map( core, map_config, do_euv, do_jno, jno_index, map ) + pack_size = core%pack_size( mpicom ) + & + map%pack_size( mpicom ) + & + musica_mpi_pack_size( do_jno, mpicom ) + & + musica_mpi_pack_size( jno_index, mpicom ) + & + musica_mpi_pack_size( disable_aerosols, mpicom ) + & + musica_mpi_pack_size( disable_clouds, mpicom ) + allocate( buffer( pack_size ) ) + pos = 0 + call core%mpi_pack( buffer, pos, mpicom ) + call map%mpi_pack( buffer, pos, mpicom ) + call musica_mpi_pack( buffer, pos, do_jno, mpicom ) + call musica_mpi_pack( buffer, pos, jno_index, mpicom ) + call musica_mpi_pack( buffer, pos, disable_aerosols, mpicom ) + call musica_mpi_pack( buffer, pos, disable_clouds, mpicom ) + deallocate( core ) + end if #ifdef HAVE_MPI - ! ==================================================== - ! broadcast the core and map data to all MPI processes - ! ==================================================== - call mpi_bcast( pack_size, 1, MPI_INTEGER, main_task, mpicom, i_err ) - if( i_err /= MPI_SUCCESS ) then - write(iulog,*) "TUV-x MPI int bcast error" - call mpi_abort( mpicom, 1, i_err ) - end if - if( .not. is_main_task ) allocate( buffer( pack_size ) ) - call mpi_bcast( buffer, pack_size, MPI_CHARACTER, main_task, mpicom, i_err ) - if( i_err /= MPI_SUCCESS ) then - write(iulog,*) "TUV-x MPI char array bcast error" - call mpi_abort( mpicom, 1, i_err ) - end if + ! ==================================================== + ! broadcast the core and map data to all MPI processes + ! ==================================================== + call mpi_bcast( pack_size, 1, MPI_INTEGER, main_task, mpicom, i_err ) + if( i_err /= MPI_SUCCESS ) then + write(iulog,*) "TUV-x MPI int bcast error" + call mpi_abort( mpicom, 1, i_err ) + end if + if( .not. is_main_task ) allocate( buffer( pack_size ) ) + call mpi_bcast( buffer, pack_size, MPI_CHARACTER, main_task, mpicom, i_err ) + if( i_err /= MPI_SUCCESS ) then + write(iulog,*) "TUV-x MPI char array bcast error" + call mpi_abort( mpicom, 1, i_err ) + end if #endif - ! ================================================================ - ! unpack the core and map for each OMP thread on every MPI process - ! ================================================================ - allocate( tuvx_ptrs( max_threads( ) ) ) - do i_core = 1, size( tuvx_ptrs ) - associate( tuvx => tuvx_ptrs( i_core ) ) - allocate( tuvx%core_ ) - pos = 0 - call tuvx%core_%mpi_unpack( buffer, pos, mpicom ) - call tuvx%photo_rate_map_%mpi_unpack( buffer, pos, mpicom ) - call musica_mpi_unpack( buffer, pos, do_jno, mpicom ) - call musica_mpi_unpack( buffer, pos, jno_index, mpicom ) - call musica_mpi_unpack( buffer, pos, disable_aerosols, mpicom ) - call musica_mpi_unpack( buffer, pos, disable_clouds, mpicom ) - - ! =================================================================== - ! Set up connections between CAM and TUV-x input data for each thread - ! =================================================================== - call create_updaters( tuvx, cam_grids, cam_profiles, cam_radiators, & - disable_aerosols, disable_clouds ) + ! ================================================================ + ! unpack the core and map for each OMP thread on every MPI process + ! ================================================================ + allocate( tuvx_ptrs( max_threads( ) ) ) + do i_core = 1, size( tuvx_ptrs ) + associate( tuvx => tuvx_ptrs( i_core ) ) + allocate( tuvx%core_ ) + pos = 0 + call tuvx%core_%mpi_unpack( buffer, pos, mpicom ) + call tuvx%photo_rate_map_%mpi_unpack( buffer, pos, mpicom ) + call musica_mpi_unpack( buffer, pos, do_jno, mpicom ) + call musica_mpi_unpack( buffer, pos, jno_index, mpicom ) + call musica_mpi_unpack( buffer, pos, disable_aerosols, mpicom ) + call musica_mpi_unpack( buffer, pos, disable_clouds, mpicom ) + + ! =================================================================== + ! Set up connections between CAM and TUV-x input data for each thread + ! =================================================================== + call create_updaters( tuvx, cam_grids, cam_profiles, cam_radiators, & + disable_aerosols, disable_clouds ) + + ! =============================================================== + ! Create a working array for calculated photolysis rate constants + ! =============================================================== + tuvx%n_photo_rates_ = tuvx%core_%number_of_photolysis_reactions( ) + if( do_euv ) tuvx%n_euv_rates_ = neuv + if( do_jno ) tuvx%n_special_rates_ = tuvx%n_special_rates_ + 1 + height => tuvx%core_%get_grid( "height", "km" ) + allocate( tuvx%photo_rates_( pcols, height%ncells_ + 1, & + tuvx%n_photo_rates_ + tuvx%n_euv_rates_ + & + tuvx%n_special_rates_ ) ) + deallocate( height ) + + end associate + end do + deallocate( cam_grids ) + deallocate( cam_profiles ) + deallocate( cam_radiators ) + + ! ============================================= + ! Get index info for CAM species concentrations + ! ============================================= + index_N2 = get_inv_ndx( 'N2' ) + is_fixed_N2 = index_N2 > 0 + if( .not. is_fixed_N2 ) index_N2 = get_spc_ndx( 'N2' ) + index_O = get_inv_ndx( 'O' ) + is_fixed_O = index_O > 0 + if( .not. is_fixed_O ) index_O = get_spc_ndx( 'O' ) + index_O2 = get_inv_ndx( 'O2' ) + is_fixed_O2 = index_O2 > 0 + if( .not. is_fixed_O2 ) index_O2 = get_spc_ndx( 'O2' ) + index_O3 = get_inv_ndx( 'O3' ) + is_fixed_O3 = index_O3 > 0 + if( .not. is_fixed_O3 ) index_O3 = get_spc_ndx( 'O3' ) + index_NO = get_inv_ndx( 'NO' ) + is_fixed_NO = index_NO > 0 + if( .not. is_fixed_NO ) index_NO = get_spc_ndx( 'NO' ) + + ! ==================================================== + ! make sure extraterrestrial flux values are available + ! ==================================================== + call assert_msg( 170693514, has_spectrum, & + "Solar irradiance spectrum needed for TUV-x" ) - ! =============================================================== - ! Create a working array for calculated photolysis rate constants - ! =============================================================== - tuvx%n_photo_rates_ = tuvx%core_%number_of_photolysis_reactions( ) - if( do_euv ) tuvx%n_euv_rates_ = neuv - if( do_jno ) tuvx%n_special_rates_ = tuvx%n_special_rates_ + 1 - height => tuvx%core_%get_grid( "height", "km" ) - allocate( tuvx%photo_rates_( pcols, height%ncells_ + 1, & - tuvx%n_photo_rates_ + tuvx%n_euv_rates_ + & - tuvx%n_special_rates_ ) ) - deallocate( height ) + ! ============================================ + ! set up diagnostic output of photolysis rates + ! ============================================ + call initialize_diagnostics( tuvx_ptrs( 1 ) ) - end associate - end do - deallocate( cam_grids ) - deallocate( cam_profiles ) - deallocate( cam_radiators ) - - ! ============================================= - ! Get index info for CAM species concentrations - ! ============================================= - index_N2 = get_inv_ndx( 'N2' ) - is_fixed_N2 = index_N2 > 0 - if( .not. is_fixed_N2 ) index_N2 = get_spc_ndx( 'N2' ) - index_O = get_inv_ndx( 'O' ) - is_fixed_O = index_O > 0 - if( .not. is_fixed_O ) index_O = get_spc_ndx( 'O' ) - index_O2 = get_inv_ndx( 'O2' ) - is_fixed_O2 = index_O2 > 0 - if( .not. is_fixed_O2 ) index_O2 = get_spc_ndx( 'O2' ) - index_O3 = get_inv_ndx( 'O3' ) - is_fixed_O3 = index_O3 > 0 - if( .not. is_fixed_O3 ) index_O3 = get_spc_ndx( 'O3' ) - index_NO = get_inv_ndx( 'NO' ) - is_fixed_NO = index_NO > 0 - if( .not. is_fixed_NO ) index_NO = get_spc_ndx( 'NO' ) - - ! ==================================================== - ! make sure extraterrestrial flux values are available - ! ==================================================== - call assert_msg( 170693514, has_spectrum, & - "Solar irradiance spectrum needed for TUV-x" ) - - ! ============================================ - ! set up diagnostic output of photolysis rates - ! ============================================ - call initialize_diagnostics( tuvx_ptrs( 1 ) ) - - if( is_main_task ) call log_initialization( ) - - end subroutine tuvx_init + if( is_main_task ) call log_initialization( ) + + end subroutine tuvx_init !================================================================================================ - !----------------------------------------------------------------------- - ! Updates TUV-x profiles that depend on time but not space - !----------------------------------------------------------------------- - subroutine tuvx_timestep_init( ) + !----------------------------------------------------------------------- + ! Updates TUV-x profiles that depend on time but not space + !----------------------------------------------------------------------- + subroutine tuvx_timestep_init( ) - integer :: i_thread + integer :: i_thread - if( .not. tuvx_active ) return + if( .not. tuvx_active ) return - do i_thread = 1, size( tuvx_ptrs ) - associate( tuvx => tuvx_ptrs( i_thread ) ) - call set_et_flux( tuvx ) - end associate - end do + do i_thread = 1, size( tuvx_ptrs ) + associate( tuvx => tuvx_ptrs( i_thread ) ) + call set_et_flux( tuvx ) + end associate + end do - end subroutine tuvx_timestep_init + end subroutine tuvx_timestep_init !================================================================================================ - !----------------------------------------------------------------------- - ! Calculates and returns photolysis rate constants - !----------------------------------------------------------------------- - subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & + !----------------------------------------------------------------------- + ! Calculates and returns photolysis rate constants + !----------------------------------------------------------------------- + subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & height_int, temperature_mid, surface_temperature, fixed_species_conc, & species_vmr, exo_column_conc, surface_albedo, solar_zenith_angle, & earth_sun_distance, pressure_delta, cloud_fraction, liquid_water_content, & photolysis_rates ) - use cam_logfile, only : iulog ! log info output unit - use chem_mods, only : phtcnt, & ! number of photolysis reactions - gas_pcnst, & ! number of non-fixed species - nfs, & ! number of fixed species - nabscol ! number of absorbing species (radiators) - use physics_types, only : physics_state - use physics_buffer, only : physics_buffer_desc - use ppgrid, only : pcols ! maximum number of columns - use shr_const_mod, only : pi => shr_const_pi - use spmd_utils, only : main_task => masterprocid, & - is_main_task => masterproc, & - mpicom - - type(physics_state), target, intent(in) :: state - type(physics_buffer_desc), pointer, intent(inout) :: pbuf(:) - integer, intent(in) :: ncol ! number of active columns on this thread - integer, intent(in) :: lchnk ! identifier for this thread - real(r8), intent(in) :: height_mid(ncol,pver) ! height at mid-points (km) - real(r8), intent(in) :: height_int(ncol,pver+1) ! height at interfaces (km) - real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) - real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) - real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities - ! (molecule cm-3) - real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing - ! ratios (mol mol-1) - real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! layer column densities - ! (molecule cm-2) - real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) - real(r8), intent(in) :: solar_zenith_angle(ncol) ! solar zenith angle (radians) - real(r8), intent(in) :: earth_sun_distance ! Earth-Sun distance (AU) - real(r8), intent(in) :: pressure_delta(pcols,pver) ! pressure delta about midpoints (Pa) - real(r8), intent(in) :: cloud_fraction(ncol,pver) ! cloud fraction (unitless) - 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) - - integer :: i_col ! column index - integer :: i_level ! vertical level index - real(r8) :: sza ! solar zenith angle [degrees] - - if( .not. tuvx_active ) return - - associate( tuvx => tuvx_ptrs( thread_id( ) ) ) - - tuvx%photo_rates_(:,:,:) = 0.0_r8 - - ! ============================================== - ! set aerosol optical properties for all columns - ! ============================================== - call get_aerosol_optical_properties( tuvx, state, pbuf ) - - do i_col = 1, ncol - - ! =================================== - ! skip columns in near total darkness - ! =================================== - sza = solar_zenith_angle(i_col) * 180.0_r8 / pi - if( sza < 0.0_r8 .or. sza > max_sza ) cycle - - ! =================== - ! update grid heights - ! =================== - call set_heights( tuvx, i_col, ncol, height_mid, height_int ) - - ! ======================================= - ! set conditions for this column in TUV-x - ! ======================================= - call set_temperatures( tuvx, i_col, temperature_mid, surface_temperature ) - call set_surface_albedo( tuvx, i_col, surface_albedo ) - call set_radiator_profiles( tuvx, i_col, ncol, fixed_species_conc, & - species_vmr, exo_column_conc, & - pressure_delta(1:ncol,:), cloud_fraction, & - liquid_water_content ) - - ! =================================================== - ! Calculate photolysis rate constants for this column - ! =================================================== - call tuvx%core_%run( solar_zenith_angle = sza, & - earth_sun_distance = earth_sun_distance, & - photolysis_rate_constants = & - tuvx%photo_rates_(i_col,:,1:tuvx%n_photo_rates_) ) - - ! ============================== - ! Calculate the extreme-UV rates - ! ============================== - if( do_euv ) then - associate( euv_begin => tuvx%n_photo_rates_ + 1, & - euv_end => tuvx%n_photo_rates_ + tuvx%n_euv_rates_ ) - call calculate_euv_rates( sza, & - fixed_species_conc(i_col,:,:), & - species_vmr(i_col,:,:), & - height_mid(i_col,:), & - height_int(i_col,:), & - tuvx%photo_rates_(i_col,2:pver+1,euv_begin:euv_end) ) - end associate - end if - - ! ============================= - ! Calculate special photo rates - ! ============================= - if( do_jno ) then - call calculate_jno( sza, & - tuvx%et_flux_ms93_, & - fixed_species_conc(i_col,:,:), & - species_vmr(i_col,:,:), & - height_int(i_col,:), & - tuvx%photo_rates_(i_col,2:pver+1,jno_index) ) - end if - end do - - ! ===================== - ! Filter negative rates - ! ===================== - tuvx%photo_rates_(:,:,:) = max( 0.0_r8, tuvx%photo_rates_(:,:,:) ) + use cam_logfile, only : iulog ! log info output unit + use chem_mods, only : phtcnt, & ! number of photolysis reactions + gas_pcnst, & ! number of non-fixed species + nfs, & ! number of fixed species + nabscol ! number of absorbing species (radiators) + use physics_types, only : physics_state + use physics_buffer, only : physics_buffer_desc + use ppgrid, only : pcols ! maximum number of columns + use shr_const_mod, only : pi => shr_const_pi + use spmd_utils, only : main_task => masterprocid, & + is_main_task => masterproc, & + mpicom + + type(physics_state), target, intent(in) :: state + type(physics_buffer_desc), pointer, intent(inout) :: pbuf(:) + integer, intent(in) :: ncol ! number of active columns on this thread + integer, intent(in) :: lchnk ! identifier for this thread + real(r8), intent(in) :: height_mid(ncol,pver) ! height at mid-points (km) + real(r8), intent(in) :: height_int(ncol,pver+1) ! height at interfaces (km) + real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) + real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) + real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities + ! (molecule cm-3) + real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) + real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! layer column densities + ! (molecule cm-2) + real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) + real(r8), intent(in) :: solar_zenith_angle(ncol) ! solar zenith angle (radians) + real(r8), intent(in) :: earth_sun_distance ! Earth-Sun distance (AU) + real(r8), intent(in) :: pressure_delta(pcols,pver) ! pressure delta about midpoints (Pa) + real(r8), intent(in) :: cloud_fraction(ncol,pver) ! cloud fraction (unitless) + 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) + + integer :: i_col ! column index + integer :: i_level ! vertical level index + real(r8) :: sza ! solar zenith angle [degrees] + + if( .not. tuvx_active ) return + + associate( tuvx => tuvx_ptrs( thread_id( ) ) ) + + tuvx%photo_rates_(:,:,:) = 0.0_r8 + + ! ============================================== + ! set aerosol optical properties for all columns + ! ============================================== + call get_aerosol_optical_properties( tuvx, state, pbuf ) + + do i_col = 1, ncol + + ! =================================== + ! skip columns in near total darkness + ! =================================== + sza = solar_zenith_angle(i_col) * 180.0_r8 / pi + if( sza < 0.0_r8 .or. sza > max_sza ) cycle + + ! =================== + ! update grid heights + ! =================== + call set_heights( tuvx, i_col, ncol, height_mid, height_int ) + + ! ======================================= + ! set conditions for this column in TUV-x + ! ======================================= + call set_temperatures( tuvx, i_col, temperature_mid, surface_temperature ) + call set_surface_albedo( tuvx, i_col, surface_albedo ) + call set_radiator_profiles( tuvx, i_col, ncol, fixed_species_conc, & + species_vmr, exo_column_conc, & + pressure_delta(1:ncol,:), cloud_fraction, & + liquid_water_content ) + + ! =================================================== + ! Calculate photolysis rate constants for this column + ! =================================================== + call tuvx%core_%run( solar_zenith_angle = sza, & + earth_sun_distance = earth_sun_distance, & + photolysis_rate_constants = & + tuvx%photo_rates_(i_col,:,1:tuvx%n_photo_rates_) ) + + ! ============================== + ! Calculate the extreme-UV rates + ! ============================== + if( do_euv ) then + associate( euv_begin => tuvx%n_photo_rates_ + 1, & + euv_end => tuvx%n_photo_rates_ + tuvx%n_euv_rates_ ) + call calculate_euv_rates( sza, & + fixed_species_conc(i_col,:,:), & + species_vmr(i_col,:,:), & + height_mid(i_col,:), & + height_int(i_col,:), & + tuvx%photo_rates_(i_col,2:pver+1,euv_begin:euv_end) ) + end associate + end if + + ! ============================= + ! Calculate special photo rates + ! ============================= + if( do_jno ) then + call calculate_jno( sza, & + tuvx%et_flux_ms93_, & + fixed_species_conc(i_col,:,:), & + species_vmr(i_col,:,:), & + height_int(i_col,:), & + tuvx%photo_rates_(i_col,2:pver+1,jno_index) ) + end if + end do + + ! ===================== + ! Filter negative rates + ! ===================== + tuvx%photo_rates_(:,:,:) = max( 0.0_r8, tuvx%photo_rates_(:,:,:) ) + + ! ============================================ + ! Return the photolysis rates on the CAM grids + ! ============================================ + do i_col = 1, ncol + do i_level = 1, pver + call tuvx%photo_rate_map_%apply( tuvx%photo_rates_(i_col,pver-i_level+2,:), & + photolysis_rates(i_col,i_level,:) ) + end do + end do + + call output_diagnostics( tuvx, ncol, lchnk ) - ! ============================================ - ! Return the photolysis rates on the CAM grids - ! ============================================ - do i_col = 1, ncol - do i_level = 1, pver - call tuvx%photo_rate_map_%apply( tuvx%photo_rates_(i_col,pver-i_level+2,:), & - photolysis_rates(i_col,i_level,:) ) - end do - end do - - call output_diagnostics( tuvx, ncol, lchnk ) - - end associate + end associate - end subroutine tuvx_get_photo_rates + end subroutine tuvx_get_photo_rates !================================================================================================ - !----------------------------------------------------------------------- - ! Cleans up memory associated with TUV-x calculators - !----------------------------------------------------------------------- - subroutine tuvx_finalize( ) + !----------------------------------------------------------------------- + ! Cleans up memory associated with TUV-x calculators + !----------------------------------------------------------------------- + subroutine tuvx_finalize( ) - integer :: i_core + integer :: i_core - if( allocated( tuvx_ptrs ) ) then - do i_core = 1, size( tuvx_ptrs ) - associate( tuvx => tuvx_ptrs( i_core ) ) - if( associated( tuvx%core_ ) ) deallocate( tuvx%core_ ) - end associate - end do - end if + if( allocated( tuvx_ptrs ) ) then + do i_core = 1, size( tuvx_ptrs ) + associate( tuvx => tuvx_ptrs( i_core ) ) + if( associated( tuvx%core_ ) ) deallocate( tuvx%core_ ) + end associate + end do + end if - end subroutine tuvx_finalize + end subroutine tuvx_finalize !================================================================================================ !================================================================================================ @@ -611,1201 +611,1201 @@ end subroutine tuvx_finalize !================================================================================================ !================================================================================================ - !----------------------------------------------------------------------- - ! Returns the id of the current OpenMP thread, or 1 if not - ! using OpenMP (1 <= id <= max_threads()) - !----------------------------------------------------------------------- - integer function thread_id( ) + !----------------------------------------------------------------------- + ! Returns the id of the current OpenMP thread, or 1 if not + ! using OpenMP (1 <= id <= max_threads()) + !----------------------------------------------------------------------- + integer function thread_id( ) #ifdef _OPENMP - use omp_lib, only : omp_get_thread_num + use omp_lib, only : omp_get_thread_num #endif #ifdef _OPENMP - thread_id = 1 + omp_get_thread_num( ) + thread_id = 1 + omp_get_thread_num( ) #else - thread_id = 1 + thread_id = 1 #endif - end function thread_id + end function thread_id !================================================================================================ - !----------------------------------------------------------------------- - ! Returns the number of threads available for calculations at - ! runtime - !----------------------------------------------------------------------- - integer function max_threads( ) + !----------------------------------------------------------------------- + ! Returns the number of threads available for calculations at + ! runtime + !----------------------------------------------------------------------- + integer function max_threads( ) #ifdef _OPENMP - use omp_lib, only : omp_get_max_threads + use omp_lib, only : omp_get_max_threads #endif #ifdef _OPENMP - max_threads = omp_get_max_threads( ) + max_threads = omp_get_max_threads( ) #else - max_threads = 1 + max_threads = 1 #endif - end function max_threads + end function max_threads !================================================================================================ - !----------------------------------------------------------------------- - ! Prints initialization conditions to the log file - !----------------------------------------------------------------------- - subroutine log_initialization( ) + !----------------------------------------------------------------------- + ! Prints initialization conditions to the log file + !----------------------------------------------------------------------- + subroutine log_initialization( ) - use cam_logfile, only : iulog ! log info output unit - use musica_string, only : to_char - use spmd_utils, only : main_task => masterprocid, & - is_main_task => masterproc + use cam_logfile, only : iulog ! log info output unit + use musica_string, only : to_char + use spmd_utils, only : main_task => masterprocid, & + is_main_task => masterproc - if( is_main_task ) then - write(iulog,*) "Initialized TUV-x" + if( is_main_task ) then + write(iulog,*) "Initialized TUV-x" #ifdef HAVE_MPI - write(iulog,*) " - with MPI support on task "//trim( to_char( main_task ) ) + write(iulog,*) " - with MPI support on task "//trim( to_char( main_task ) ) #else - write(iulog,*) " - without MPI support" + write(iulog,*) " - without MPI support" #endif #ifdef _OPENMP - write(iulog,*) " - with OpenMP support for "// & - trim( to_char( max_threads( ) ) )//" threads, on thread " & - //trim( to_char( thread_id( ) ) ) + write(iulog,*) " - with OpenMP support for "// & + trim( to_char( max_threads( ) ) )//" threads, on thread " & + //trim( to_char( thread_id( ) ) ) #else - write(iulog,*) " - without OpenMP support" + write(iulog,*) " - without OpenMP support" #endif - write(iulog,*) " - with configuration file: '"//trim( tuvx_config_path )//"'" - if( do_aerosol ) then - write(iulog,*) " - with on-line aerosols" - else - write(iulog,*) " - without on-line aerosols" - end if - if( do_clouds ) then - write(iulog,*) " - with on-line clouds" - else - write(iulog,*) " - without on-line clouds" + write(iulog,*) " - with configuration file: '"//trim( tuvx_config_path )//"'" + if( do_aerosol ) then + write(iulog,*) " - with on-line aerosols" + else + write(iulog,*) " - without on-line aerosols" + end if + if( do_clouds ) then + write(iulog,*) " - with on-line clouds" + else + write(iulog,*) " - without on-line clouds" + end if + if( index_N2 > 0 ) write(iulog,*) " - including N2" + if( index_O > 0 ) write(iulog,*) " - including O" + if( index_O2 > 0 ) write(iulog,*) " - including O2" + if( index_O3 > 0 ) write(iulog,*) " - including O3" + if( index_NO > 0 ) write(iulog,*) " - including NO" + if( do_euv ) write(iulog,*) " - doing Extreme-UV calculations" + if( do_jno ) write(iulog,*) " - including special jno rate calculation" + write(iulog,*) " - max solar zenith angle [degrees]:", max_sza end if - if( index_N2 > 0 ) write(iulog,*) " - including N2" - if( index_O > 0 ) write(iulog,*) " - including O" - if( index_O2 > 0 ) write(iulog,*) " - including O2" - if( index_O3 > 0 ) write(iulog,*) " - including O3" - if( index_NO > 0 ) write(iulog,*) " - including NO" - if( do_euv ) write(iulog,*) " - doing Extreme-UV calculations" - if( do_jno ) write(iulog,*) " - including special jno rate calculation" - write(iulog,*) " - max solar zenith angle [degrees]:", max_sza - end if - - end subroutine log_initialization + + end subroutine log_initialization !================================================================================================ - !----------------------------------------------------------------------- - ! initializes the external extreme-UV module - !----------------------------------------------------------------------- - subroutine initialize_euv( photon_file, electron_file, do_euv ) + !----------------------------------------------------------------------- + ! initializes the external extreme-UV module + !----------------------------------------------------------------------- + subroutine initialize_euv( photon_file, electron_file, do_euv ) - use chem_mods, only : phtcnt ! number of CAM-Chem photolysis reactions - use mo_jeuv, only : jeuv_init ! extreme-UV initialization + use chem_mods, only : phtcnt ! number of CAM-Chem photolysis reactions + use mo_jeuv, only : jeuv_init ! extreme-UV initialization - 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 - logical, intent(out) :: do_euv ! indicates whether extreme-UV - ! calculations are needed + 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 + logical, intent(out) :: do_euv ! indicates whether extreme-UV + ! calculations are needed - integer, allocatable :: euv_index_map(:) + integer, allocatable :: euv_index_map(:) - allocate( euv_index_map( phtcnt ) ) - euv_index_map(:) = 0 - call jeuv_init( photon_file, electron_file, euv_index_map ) - do_euv = any( euv_index_map(:) > 0 ) + allocate( euv_index_map( phtcnt ) ) + euv_index_map(:) = 0 + call jeuv_init( photon_file, electron_file, euv_index_map ) + do_euv = any( euv_index_map(:) > 0 ) - end subroutine initialize_euv + end subroutine initialize_euv !================================================================================================ - !----------------------------------------------------------------------- - ! Registers fields for diagnostic output - !----------------------------------------------------------------------- - subroutine initialize_diagnostics( this ) - - use cam_history, only : addfld - use musica_assert, only : assert - use musica_string, only : string_t - - type(tuvx_ptr), intent(in) :: this - - type(string_t), allocatable :: labels(:), all_labels(:) - integer :: i_label - - if( .not. enable_diagnostics ) then - allocate( diagnostics( 0 ) ) - return - end if - - ! ========================================================== - ! add output for specific photolysis reaction rate constants - ! ========================================================== - labels = this%core_%photolysis_reaction_labels( ) - allocate( all_labels( size( labels ) + this%n_special_rates_ ) ) - all_labels( 1 : size( labels ) ) = labels(:) - i_label = size( labels ) + 1 - if( do_jno ) then - all_labels( i_label ) = "jno" - i_label = i_label + 1 - end if - call assert( 522515214, i_label == size( all_labels ) + 1 ) - allocate( diagnostics( size( all_labels ) ) ) - do i_label = 1, size( all_labels ) - diagnostics( i_label )%name_ = trim( all_labels( i_label )%to_char( ) ) - diagnostics( i_label )%index_ = i_label - call addfld( "tuvx_"//diagnostics( i_label )%name_, (/ 'lev' /), 'A', 'sec-1', & - 'photolysis rate constant' ) - end do - - end subroutine initialize_diagnostics + !----------------------------------------------------------------------- + ! Registers fields for diagnostic output + !----------------------------------------------------------------------- + subroutine initialize_diagnostics( this ) + + use cam_history, only : addfld + use musica_assert, only : assert + use musica_string, only : string_t + + type(tuvx_ptr), intent(in) :: this + + type(string_t), allocatable :: labels(:), all_labels(:) + integer :: i_label + + if( .not. enable_diagnostics ) then + allocate( diagnostics( 0 ) ) + return + end if + + ! ========================================================== + ! add output for specific photolysis reaction rate constants + ! ========================================================== + labels = this%core_%photolysis_reaction_labels( ) + allocate( all_labels( size( labels ) + this%n_special_rates_ ) ) + all_labels( 1 : size( labels ) ) = labels(:) + i_label = size( labels ) + 1 + if( do_jno ) then + all_labels( i_label ) = "jno" + i_label = i_label + 1 + end if + call assert( 522515214, i_label == size( all_labels ) + 1 ) + allocate( diagnostics( size( all_labels ) ) ) + do i_label = 1, size( all_labels ) + diagnostics( i_label )%name_ = trim( all_labels( i_label )%to_char( ) ) + diagnostics( i_label )%index_ = i_label + call addfld( "tuvx_"//diagnostics( i_label )%name_, (/ 'lev' /), 'A', 'sec-1', & + 'photolysis rate constant' ) + end do + + end subroutine initialize_diagnostics !================================================================================================ - !----------------------------------------------------------------------- - ! Sets up a map between the TUV-x and CAM photolysis rate arrays - !----------------------------------------------------------------------- - subroutine set_photo_rate_map( core, config, do_euv, do_jno, jno_index, map ) - - use cam_logfile, only : iulog ! log info output unit - use chem_mods, only : phtcnt, & ! number of photolysis reactions - rxt_tag_lst ! labels for all chemical reactions - ! NOTE photolysis reactions are - ! expected to appear first - use mo_jeuv, only : neuv ! number of extreme-UV rates - use musica_config, only : config_t - use musica_string, only : string_t - - type(core_t), intent(in) :: core ! TUV-x core - type(config_t), intent(inout) :: config ! CAM<->TUV-x map configuration - logical, intent(in) :: do_euv ! indicates whether to include - ! extreme-UV rates in the mapping - logical, intent(out) :: do_jno ! indicates whether jno should be - ! calculated - integer, intent(out) :: jno_index ! index for jno in source photo - ! rate array - type(map_t), intent(out) :: map - - integer :: i_label, i_start, i_end - type(string_t) :: str_label - type(string_t), allocatable :: tuvx_labels(:), euv_labels(:), special_labels(:), & - all_labels(:), cam_labels(:) - - ! ================== - ! MOZART photo rates - ! ================== - allocate( cam_labels( phtcnt ) ) - do i_label = 1, phtcnt - cam_labels( i_label ) = trim( rxt_tag_lst( i_label ) ) - end do - - ! ================= - ! TUV-x photo rates - ! ================= - tuvx_labels = core%photolysis_reaction_labels( ) - - ! ====================== - ! Extreme-UV photo rates - ! ====================== - if( do_euv ) then - allocate( euv_labels( neuv ) ) - do i_label = 1, neuv - str_label = i_label - euv_labels( i_label ) = "jeuv_"//str_label + !----------------------------------------------------------------------- + ! Sets up a map between the TUV-x and CAM photolysis rate arrays + !----------------------------------------------------------------------- + subroutine set_photo_rate_map( core, config, do_euv, do_jno, jno_index, map ) + + use cam_logfile, only : iulog ! log info output unit + use chem_mods, only : phtcnt, & ! number of photolysis reactions + rxt_tag_lst ! labels for all chemical reactions + ! NOTE photolysis reactions are + ! expected to appear first + use mo_jeuv, only : neuv ! number of extreme-UV rates + use musica_config, only : config_t + use musica_string, only : string_t + + type(core_t), intent(in) :: core ! TUV-x core + type(config_t), intent(inout) :: config ! CAM<->TUV-x map configuration + logical, intent(in) :: do_euv ! indicates whether to include + ! extreme-UV rates in the mapping + logical, intent(out) :: do_jno ! indicates whether jno should be + ! calculated + integer, intent(out) :: jno_index ! index for jno in source photo + ! rate array + type(map_t), intent(out) :: map + + integer :: i_label, i_start, i_end + type(string_t) :: str_label + type(string_t), allocatable :: tuvx_labels(:), euv_labels(:), special_labels(:), & + all_labels(:), cam_labels(:) + + ! ================== + ! MOZART photo rates + ! ================== + allocate( cam_labels( phtcnt ) ) + do i_label = 1, phtcnt + cam_labels( i_label ) = trim( rxt_tag_lst( i_label ) ) + end do + + ! ================= + ! TUV-x photo rates + ! ================= + tuvx_labels = core%photolysis_reaction_labels( ) + + ! ====================== + ! Extreme-UV photo rates + ! ====================== + if( do_euv ) then + allocate( euv_labels( neuv ) ) + do i_label = 1, neuv + str_label = i_label + euv_labels( i_label ) = "jeuv_"//str_label + end do + else + allocate( euv_labels(0) ) + end if + + ! =============================== + ! Special photo rate calculations + ! =============================== + do_jno = .false. + jno_index = 0 + do i_label = 1, size( cam_labels ) + if( cam_labels( i_label ) == "jno" ) then + do_jno = .true. + exit + end if end do - else - allocate( euv_labels(0) ) - end if - - ! =============================== - ! Special photo rate calculations - ! =============================== - do_jno = .false. - jno_index = 0 - do i_label = 1, size( cam_labels ) - if( cam_labels( i_label ) == "jno" ) then - do_jno = .true. - exit + if( do_jno ) then + allocate( special_labels(1) ) + special_labels(1) = "jno" + else + allocate( special_labels(0) ) end if - end do - if( do_jno ) then - allocate( special_labels(1) ) - special_labels(1) = "jno" - else - allocate( special_labels(0) ) - end if - - ! ========================== - ! Combine photo rate sources - ! ========================== - allocate( all_labels( size( tuvx_labels ) + size( euv_labels ) + & - size( special_labels ) ) ) - i_end = 0 - if( size( tuvx_labels ) > 0 ) then - i_start = i_end + 1 - i_end = i_start + size( tuvx_labels ) - 1 - all_labels( i_start : i_end ) = tuvx_labels(:) - end if - if( size( euv_labels ) > 0 ) then - i_start = i_end + 1 - i_end = i_start + size( euv_labels ) - 1 - all_labels( i_start : i_end ) = euv_labels(:) - end if - if( size( special_labels ) > 0 ) then - i_start = i_end + 1 - i_end = i_start + size( special_labels ) - 1 - all_labels( i_start : i_end ) = special_labels(:) - jno_index = i_start - end if - - ! ========== - ! Create map - ! ========== - map = map_t( config, all_labels, cam_labels ) - write(iulog,*) - write(iulog,*) "TUV-x --> CAM-Chem photolysis rate constant map" - call map%print( all_labels, cam_labels, iulog ) - - end subroutine set_photo_rate_map + + ! ========================== + ! Combine photo rate sources + ! ========================== + allocate( all_labels( size( tuvx_labels ) + size( euv_labels ) + & + size( special_labels ) ) ) + i_end = 0 + if( size( tuvx_labels ) > 0 ) then + i_start = i_end + 1 + i_end = i_start + size( tuvx_labels ) - 1 + all_labels( i_start : i_end ) = tuvx_labels(:) + end if + if( size( euv_labels ) > 0 ) then + i_start = i_end + 1 + i_end = i_start + size( euv_labels ) - 1 + all_labels( i_start : i_end ) = euv_labels(:) + end if + if( size( special_labels ) > 0 ) then + i_start = i_end + 1 + i_end = i_start + size( special_labels ) - 1 + all_labels( i_start : i_end ) = special_labels(:) + jno_index = i_start + end if + + ! ========== + ! Create map + ! ========== + map = map_t( config, all_labels, cam_labels ) + write(iulog,*) + write(iulog,*) "TUV-x --> CAM-Chem photolysis rate constant map" + call map%print( all_labels, cam_labels, iulog ) + + end subroutine set_photo_rate_map !================================================================================================ - !----------------------------------------------------------------------- - ! Outputs diagnostic information for the current time step - !----------------------------------------------------------------------- - subroutine output_diagnostics( this, ncol, lchnk ) + !----------------------------------------------------------------------- + ! Outputs diagnostic information for the current time step + !----------------------------------------------------------------------- + subroutine output_diagnostics( this, ncol, lchnk ) - use cam_history, only : outfld + use cam_history, only : outfld - type(tuvx_ptr), intent(in) :: this - integer, intent(in) :: ncol ! number of active columns on this thread - integer, intent(in) :: lchnk ! identifier for this thread + type(tuvx_ptr), intent(in) :: this + integer, intent(in) :: ncol ! number of active columns on this thread + integer, intent(in) :: lchnk ! identifier for this thread - integer :: i_diag + integer :: i_diag - if( .not. enable_diagnostics ) return + if( .not. enable_diagnostics ) return - do i_diag = 1, size( diagnostics ) - associate( diag => diagnostics( i_diag ) ) - call outfld( "tuvx_"//diag%name_, this%photo_rates_(:ncol,pver+1:2:-1,diag%index_), & - ncol, lchnk ) - end associate - end do + do i_diag = 1, size( diagnostics ) + associate( diag => diagnostics( i_diag ) ) + call outfld( "tuvx_"//diag%name_, this%photo_rates_(:ncol,pver+1:2:-1,diag%index_), & + ncol, lchnk ) + end associate + end do - end subroutine output_diagnostics + end subroutine output_diagnostics !================================================================================================ - !----------------------------------------------------------------------- - ! Creates and loads a grid warehouse with grids that CAM will - ! update at runtime - !----------------------------------------------------------------------- - function get_cam_grids( wavelength_path ) result( grids ) - - use musica_assert, only : assert_msg - use musica_config, only : config_t - use tuvx_grid, only : grid_t - use tuvx_grid_factory, only : grid_builder - use tuvx_grid_from_host, only : grid_from_host_t - use tuvx_grid_warehouse, only : grid_warehouse_t - - character(len=*), intent(in) :: wavelength_path ! path to the wavelength data file - class(grid_warehouse_t), pointer :: grids ! collection of grids to be updated by CAM - - character(len=*), parameter :: my_name = "CAM grid creator" - class(grid_t), pointer :: host_grid - type(config_t) :: config - - grids => grid_warehouse_t( ) - - ! ========================= - ! heights above the surface - ! ========================= - host_grid => grid_from_host_t( "height", "km", pver+1 ) - call grids%add( host_grid ) - deallocate( host_grid ) - - ! ============================================ - ! wavelengths (will not be updated at runtime) - ! ============================================ - call config%empty( ) - call config%add( "type", "from csv file", my_name ) - call config%add( "name", "wavelength", my_name ) - call config%add( "units", "nm", my_name ) - call config%add( "file path", wavelength_path, my_name ) - host_grid => grid_builder( config ) - call grids%add( host_grid ) - deallocate( host_grid ) - - end function get_cam_grids + !----------------------------------------------------------------------- + ! Creates and loads a grid warehouse with grids that CAM will + ! update at runtime + !----------------------------------------------------------------------- + function get_cam_grids( wavelength_path ) result( grids ) -!================================================================================================ + use musica_assert, only : assert_msg + use musica_config, only : config_t + use tuvx_grid, only : grid_t + use tuvx_grid_factory, only : grid_builder + use tuvx_grid_from_host, only : grid_from_host_t + use tuvx_grid_warehouse, only : grid_warehouse_t - !----------------------------------------------------------------------- - ! Creates and loads a profile warehouse with profiles that CAM - ! will update at runtime - !----------------------------------------------------------------------- - function get_cam_profiles( grids ) result( profiles ) - - use tuvx_grid, only : grid_t - use tuvx_grid_warehouse, only : grid_warehouse_t - use tuvx_profile_from_host, only : profile_from_host_t - use tuvx_profile_warehouse, only : profile_warehouse_t - - class(grid_warehouse_t), intent(in) :: grids ! CAM grids used in TUV-x - class(profile_warehouse_t), pointer :: profiles ! collection of profiles to be updated by CAM - - class(profile_from_host_t), pointer :: host_profile - class(grid_t), pointer :: height, wavelength - - profiles => profile_warehouse_t( ) - height => grids%get_grid( "height", "km" ) - wavelength => grids%get_grid( "wavelength", "nm" ) - - ! ================================== - ! Temperature profile on height grid - ! ================================== - host_profile => profile_from_host_t( "temperature", "K", height%size( ) ) - call profiles%add( host_profile ) - deallocate( host_profile ) - - ! ================================= - ! Surface albedo on wavelength grid - ! ================================= - host_profile => profile_from_host_t( "surface albedo", "none", wavelength%size( ) ) - call profiles%add( host_profile ) - deallocate( host_profile ) - - ! ======================================== - ! Extraterrestrial flux on wavelength grid - ! ======================================== - host_profile => profile_from_host_t( "extraterrestrial flux", "photon cm-2 s-1", & - wavelength%size( ) ) - call profiles%add( host_profile ) - deallocate( host_profile ) - - ! =========== - ! Air profile - ! =========== - host_profile => profile_from_host_t( "air", "molecule cm-3", height%size( ) ) - call profiles%add( host_profile ) - deallocate( host_profile ) - - ! ========== - ! O3 profile - ! ========== - host_profile => profile_from_host_t( "O3", "molecule cm-3", height%size( ) ) - call profiles%add( host_profile ) - deallocate( host_profile ) - - ! ========== - ! O2 profile - ! ========== - host_profile => profile_from_host_t( "O2", "molecule cm-3", height%size( ) ) - call profiles%add( host_profile ) - deallocate( host_profile ) - - deallocate( height ) - deallocate( wavelength ) - - end function get_cam_profiles + character(len=*), intent(in) :: wavelength_path ! path to the wavelength data file + class(grid_warehouse_t), pointer :: grids ! collection of grids to be updated by CAM -!================================================================================================ + character(len=*), parameter :: my_name = "CAM grid creator" + class(grid_t), pointer :: host_grid + type(config_t) :: config + + grids => grid_warehouse_t( ) + + ! ========================= + ! heights above the surface + ! ========================= + host_grid => grid_from_host_t( "height", "km", pver+1 ) + call grids%add( host_grid ) + deallocate( host_grid ) + + ! ============================================ + ! wavelengths (will not be updated at runtime) + ! ============================================ + call config%empty( ) + call config%add( "type", "from csv file", my_name ) + call config%add( "name", "wavelength", my_name ) + call config%add( "units", "nm", my_name ) + call config%add( "file path", wavelength_path, my_name ) + host_grid => grid_builder( config ) + call grids%add( host_grid ) + deallocate( host_grid ) - !----------------------------------------------------------------------- - ! Creates and loads a radiator warehouse with radiators that CAM will - ! update at runtime - !----------------------------------------------------------------------- - function get_cam_radiators( grids ) result( radiators ) + end function get_cam_grids - use tuvx_grid, only : grid_t - use tuvx_grid_warehouse, only : grid_warehouse_t - use tuvx_radiator_from_host, only : radiator_from_host_t - use tuvx_radiator_warehouse, only : radiator_warehouse_t +!================================================================================================ - type(grid_warehouse_t), intent(in) :: grids ! CAM grids used in TUV-x - class(radiator_warehouse_t), pointer :: radiators ! collection of radiators to be updated by CAM + !----------------------------------------------------------------------- + ! Creates and loads a profile warehouse with profiles that CAM + ! will update at runtime + !----------------------------------------------------------------------- + function get_cam_profiles( grids ) result( profiles ) + + use tuvx_grid, only : grid_t + use tuvx_grid_warehouse, only : grid_warehouse_t + use tuvx_profile_from_host, only : profile_from_host_t + use tuvx_profile_warehouse, only : profile_warehouse_t + + class(grid_warehouse_t), intent(in) :: grids ! CAM grids used in TUV-x + class(profile_warehouse_t), pointer :: profiles ! collection of profiles to be updated by CAM + + class(profile_from_host_t), pointer :: host_profile + class(grid_t), pointer :: height, wavelength + + profiles => profile_warehouse_t( ) + height => grids%get_grid( "height", "km" ) + wavelength => grids%get_grid( "wavelength", "nm" ) + + ! ================================== + ! Temperature profile on height grid + ! ================================== + host_profile => profile_from_host_t( "temperature", "K", height%size( ) ) + call profiles%add( host_profile ) + deallocate( host_profile ) + + ! ================================= + ! Surface albedo on wavelength grid + ! ================================= + host_profile => profile_from_host_t( "surface albedo", "none", wavelength%size( ) ) + call profiles%add( host_profile ) + deallocate( host_profile ) + + ! ======================================== + ! Extraterrestrial flux on wavelength grid + ! ======================================== + host_profile => profile_from_host_t( "extraterrestrial flux", "photon cm-2 s-1", & + wavelength%size( ) ) + call profiles%add( host_profile ) + deallocate( host_profile ) + + ! =========== + ! Air profile + ! =========== + host_profile => profile_from_host_t( "air", "molecule cm-3", height%size( ) ) + call profiles%add( host_profile ) + deallocate( host_profile ) + + ! ========== + ! O3 profile + ! ========== + host_profile => profile_from_host_t( "O3", "molecule cm-3", height%size( ) ) + call profiles%add( host_profile ) + deallocate( host_profile ) + + ! ========== + ! O2 profile + ! ========== + host_profile => profile_from_host_t( "O2", "molecule cm-3", height%size( ) ) + call profiles%add( host_profile ) + deallocate( host_profile ) - class(radiator_from_host_t), pointer :: host_radiator - class(grid_t), pointer :: height, wavelength + deallocate( height ) + deallocate( wavelength ) - radiators => radiator_warehouse_t( ) - height => grids%get_grid( "height", "km" ) - wavelength => grids%get_grid( "wavelength", "nm" ) + end function get_cam_profiles - ! ================ - ! Aerosol radiator - ! ================ - host_radiator => radiator_from_host_t( "aerosol", height, wavelength ) - call radiators%add( host_radiator ) - deallocate( host_radiator ) +!================================================================================================ - ! ============== - ! Cloud radiator - ! ============== - host_radiator => radiator_from_host_t( "clouds", height, wavelength ) - call radiators%add( host_radiator ) - deallocate( host_radiator ) + !----------------------------------------------------------------------- + ! Creates and loads a radiator warehouse with radiators that CAM will + ! update at runtime + !----------------------------------------------------------------------- + function get_cam_radiators( grids ) result( radiators ) + + use tuvx_grid, only : grid_t + use tuvx_grid_warehouse, only : grid_warehouse_t + use tuvx_radiator_from_host, only : radiator_from_host_t + use tuvx_radiator_warehouse, only : radiator_warehouse_t + + type(grid_warehouse_t), intent(in) :: grids ! CAM grids used in TUV-x + class(radiator_warehouse_t), pointer :: radiators ! collection of radiators to be updated by CAM + + class(radiator_from_host_t), pointer :: host_radiator + class(grid_t), pointer :: height, wavelength + + radiators => radiator_warehouse_t( ) + height => grids%get_grid( "height", "km" ) + wavelength => grids%get_grid( "wavelength", "nm" ) + + ! ================ + ! Aerosol radiator + ! ================ + host_radiator => radiator_from_host_t( "aerosol", height, wavelength ) + call radiators%add( host_radiator ) + deallocate( host_radiator ) + + ! ============== + ! Cloud radiator + ! ============== + host_radiator => radiator_from_host_t( "clouds", height, wavelength ) + call radiators%add( host_radiator ) + deallocate( host_radiator ) - deallocate( height ) - deallocate( wavelength ) + deallocate( height ) + deallocate( wavelength ) - end function get_cam_radiators + end function get_cam_radiators !================================================================================================ - !----------------------------------------------------------------------- - ! Forms connections between CAM and TUV-x data structures - ! - ! - creates updaters for each grid and profile that CAM will use - ! to update TUV-x at each timestep. - ! - allocates working arrays when needed for interpolation between - ! grids - ! - initializes CAM modules if necessary and sets parameters needed - ! for runtime access of CAM data - ! - !----------------------------------------------------------------------- - subroutine create_updaters( this, grids, profiles, radiators, disable_aerosols, & - disable_clouds ) - - use ppgrid, only : pcols ! maximum number of columns - use rad_constituents, only : rad_cnst_get_info - use musica_assert, only : assert - use tuvx_grid, only : grid_t - use tuvx_grid_from_host, only : grid_updater_t - use tuvx_grid_warehouse, only : grid_warehouse_t - use tuvx_profile, only : profile_t - use tuvx_profile_from_host, only : profile_updater_t - use tuvx_profile_warehouse, only : profile_warehouse_t - use tuvx_radiator, only : radiator_t - use tuvx_radiator_from_host, only : radiator_updater_t - use tuvx_radiator_warehouse, only : radiator_warehouse_t - - class(tuvx_ptr), intent(inout) :: this - class(grid_warehouse_t), intent(in) :: grids - class(profile_warehouse_t), intent(in) :: profiles - class(radiator_warehouse_t), intent(in) :: radiators - logical, intent(in) :: disable_aerosols - logical, intent(in) :: disable_clouds - - class(grid_t), pointer :: height, wavelength - class(profile_t), pointer :: host_profile - class(radiator_t), pointer :: host_radiator - integer :: n_modes - logical :: found - - ! ============= - ! Grid updaters - ! ============= - - height => grids%get_grid( "height", "km" ) - this%grids_( GRID_INDEX_HEIGHT ) = this%core_%get_updater( height, found ) - call assert( 213798815, found ) - - ! ============================================ - ! wavelength grid cannot be updated at runtime - ! ============================================ - wavelength => grids%get_grid( "wavelength", "nm" ) - allocate( this%wavelength_edges_( wavelength%size( ) + 1 ) ) - allocate( this%wavelength_values_( wavelength%size( ) + 1 ) ) - allocate( this%wavelength_mid_values_( wavelength%size( ) ) ) - this%wavelength_edges_(:) = wavelength%edge_(:) - - ! ============================== - ! optical property working array - ! ============================== - allocate( this%optical_depth_( pcols, height%size( ), wavelength%size( ) ) ) - allocate( this%single_scattering_albedo_( pcols, height%size( ), wavelength%size( ) ) ) - allocate( this%asymmetry_factor_( pcols, height%size( ), wavelength%size( ) ) ) - - deallocate( height ) - deallocate( wavelength ) - - ! ================ - ! Profile updaters - ! ================ - - host_profile => profiles%get_profile( "temperature", "K" ) - this%profiles_( PROFILE_INDEX_TEMPERATURE ) = this%core_%get_updater( host_profile, found ) - call assert( 418735162, found ) - deallocate( host_profile ) - - host_profile => profiles%get_profile( "surface albedo", "none" ) - this%profiles_( PROFILE_INDEX_ALBEDO ) = this%core_%get_updater( host_profile, found ) - call assert( 720785186, found ) - deallocate( host_profile ) - - host_profile => profiles%get_profile( "extraterrestrial flux", "photon cm-2 s-1" ) - this%profiles_( PROFILE_INDEX_ET_FLUX ) = this%core_%get_updater( host_profile, found ) - call assert( 550628282, found ) - deallocate( host_profile ) - - host_profile => profiles%get_profile( "air", "molecule cm-3" ) - this%profiles_( PROFILE_INDEX_AIR ) = this%core_%get_updater( host_profile, found ) - call assert( 380471378, found ) - deallocate( host_profile ) - - host_profile => profiles%get_profile( "O3", "molecule cm-3" ) - this%profiles_( PROFILE_INDEX_O3 ) = this%core_%get_updater( host_profile, found ) - call assert( 210314474, found ) - deallocate( host_profile ) - - host_profile => profiles%get_profile( "O2", "molecule cm-3" ) - this%profiles_( PROFILE_INDEX_O2 ) = this%core_%get_updater( host_profile, found ) - call assert( 105165970, found ) - deallocate( host_profile ) - - ! ================= - ! radiator updaters - ! ================= - - ! ==================================================================== - ! determine if aerosol optical properties will be available, and if so - ! intialize the aerosol optics module - ! ==================================================================== - call rad_cnst_get_info( 0, nmodes = n_modes ) - if( n_modes > 0 .and. .not. do_aerosol .and. .not. disable_aerosols ) then - do_aerosol = .true. - do_aerosol = .false. ! temporarily disable aerosols - ! TODO update to use new aerosol_optics class - ! call modal_aer_opt_init( ) - else - ! TODO are there default aerosol optical properties that should be used - ! when an aerosol module is not available? - this%optical_depth_(:,:,:) = 0.0_r8 - this%single_scattering_albedo_(:,:,:) = 0.0_r8 - this%asymmetry_factor_(:,:,:) = 0.0_r8 - end if - host_radiator => radiators%get_radiator( "aerosol" ) - this%radiators_( RADIATOR_INDEX_AEROSOL ) = & - this%core_%get_updater( host_radiator, found ) - call assert( 675200430, found ) - nullify( host_radiator ) - - ! ===================================== - ! get an updater for the cloud radiator - ! ===================================== - do_clouds = .not. disable_clouds - host_radiator => radiators%get_radiator( "clouds" ) - this%radiators_( RADIATOR_INDEX_CLOUDS ) = & - this%core_%get_updater( host_radiator, found ) - call assert( 993715720, found ) - nullify( host_radiator ) - - end subroutine create_updaters + !----------------------------------------------------------------------- + ! Forms connections between CAM and TUV-x data structures + ! + ! - creates updaters for each grid and profile that CAM will use + ! to update TUV-x at each timestep. + ! - allocates working arrays when needed for interpolation between + ! grids + ! - initializes CAM modules if necessary and sets parameters needed + ! for runtime access of CAM data + ! + !----------------------------------------------------------------------- + subroutine create_updaters( this, grids, profiles, radiators, disable_aerosols, & + disable_clouds ) + + use ppgrid, only : pcols ! maximum number of columns + use rad_constituents, only : rad_cnst_get_info + use musica_assert, only : assert + use tuvx_grid, only : grid_t + use tuvx_grid_from_host, only : grid_updater_t + use tuvx_grid_warehouse, only : grid_warehouse_t + use tuvx_profile, only : profile_t + use tuvx_profile_from_host, only : profile_updater_t + use tuvx_profile_warehouse, only : profile_warehouse_t + use tuvx_radiator, only : radiator_t + use tuvx_radiator_from_host, only : radiator_updater_t + use tuvx_radiator_warehouse, only : radiator_warehouse_t + + class(tuvx_ptr), intent(inout) :: this + class(grid_warehouse_t), intent(in) :: grids + class(profile_warehouse_t), intent(in) :: profiles + class(radiator_warehouse_t), intent(in) :: radiators + logical, intent(in) :: disable_aerosols + logical, intent(in) :: disable_clouds + + class(grid_t), pointer :: height, wavelength + class(profile_t), pointer :: host_profile + class(radiator_t), pointer :: host_radiator + integer :: n_modes + logical :: found + + ! ============= + ! Grid updaters + ! ============= + + height => grids%get_grid( "height", "km" ) + this%grids_( GRID_INDEX_HEIGHT ) = this%core_%get_updater( height, found ) + call assert( 213798815, found ) + + ! ============================================ + ! wavelength grid cannot be updated at runtime + ! ============================================ + wavelength => grids%get_grid( "wavelength", "nm" ) + allocate( this%wavelength_edges_( wavelength%size( ) + 1 ) ) + allocate( this%wavelength_values_( wavelength%size( ) + 1 ) ) + allocate( this%wavelength_mid_values_( wavelength%size( ) ) ) + this%wavelength_edges_(:) = wavelength%edge_(:) + + ! ============================== + ! optical property working array + ! ============================== + allocate( this%optical_depth_( pcols, height%size( ), wavelength%size( ) ) ) + allocate( this%single_scattering_albedo_( pcols, height%size( ), wavelength%size( ) ) ) + allocate( this%asymmetry_factor_( pcols, height%size( ), wavelength%size( ) ) ) + + deallocate( height ) + deallocate( wavelength ) + + ! ================ + ! Profile updaters + ! ================ + + host_profile => profiles%get_profile( "temperature", "K" ) + this%profiles_( PROFILE_INDEX_TEMPERATURE ) = this%core_%get_updater( host_profile, found ) + call assert( 418735162, found ) + deallocate( host_profile ) + + host_profile => profiles%get_profile( "surface albedo", "none" ) + this%profiles_( PROFILE_INDEX_ALBEDO ) = this%core_%get_updater( host_profile, found ) + call assert( 720785186, found ) + deallocate( host_profile ) + + host_profile => profiles%get_profile( "extraterrestrial flux", "photon cm-2 s-1" ) + this%profiles_( PROFILE_INDEX_ET_FLUX ) = this%core_%get_updater( host_profile, found ) + call assert( 550628282, found ) + deallocate( host_profile ) + + host_profile => profiles%get_profile( "air", "molecule cm-3" ) + this%profiles_( PROFILE_INDEX_AIR ) = this%core_%get_updater( host_profile, found ) + call assert( 380471378, found ) + deallocate( host_profile ) + + host_profile => profiles%get_profile( "O3", "molecule cm-3" ) + this%profiles_( PROFILE_INDEX_O3 ) = this%core_%get_updater( host_profile, found ) + call assert( 210314474, found ) + deallocate( host_profile ) + + host_profile => profiles%get_profile( "O2", "molecule cm-3" ) + this%profiles_( PROFILE_INDEX_O2 ) = this%core_%get_updater( host_profile, found ) + call assert( 105165970, found ) + deallocate( host_profile ) + + ! ================= + ! radiator updaters + ! ================= + + ! ==================================================================== + ! determine if aerosol optical properties will be available, and if so + ! intialize the aerosol optics module + ! ==================================================================== + call rad_cnst_get_info( 0, nmodes = n_modes ) + if( n_modes > 0 .and. .not. do_aerosol .and. .not. disable_aerosols ) then + do_aerosol = .true. + do_aerosol = .false. ! temporarily disable aerosols + ! TODO update to use new aerosol_optics class + ! call modal_aer_opt_init( ) + else + ! TODO are there default aerosol optical properties that should be used + ! when an aerosol module is not available? + this%optical_depth_(:,:,:) = 0.0_r8 + this%single_scattering_albedo_(:,:,:) = 0.0_r8 + this%asymmetry_factor_(:,:,:) = 0.0_r8 + end if + host_radiator => radiators%get_radiator( "aerosol" ) + this%radiators_( RADIATOR_INDEX_AEROSOL ) = & + this%core_%get_updater( host_radiator, found ) + call assert( 675200430, found ) + nullify( host_radiator ) + + ! ===================================== + ! get an updater for the cloud radiator + ! ===================================== + do_clouds = .not. disable_clouds + host_radiator => radiators%get_radiator( "clouds" ) + this%radiators_( RADIATOR_INDEX_CLOUDS ) = & + this%core_%get_updater( host_radiator, found ) + call assert( 993715720, found ) + nullify( host_radiator ) + + end subroutine create_updaters !================================================================================================ - !----------------------------------------------------------------------- - ! Sets the height values in TUV-x for the given column - ! - ! NOTE: This function must be called before updating any profile data. - ! - ! CAM to TUV-x height grid mapping - ! - ! TUV-x heights are "bottom-up" and require atmospheric constituent - ! concentrations at interfaces. Therefore, CAM mid-points are used as - ! TUV-x grid interfaces, with an additional layer introduced between - ! the surface and the lowest CAM mid-point, and a layer at the - ! top of the TUV-x grid to hold species densities above the top CAM - ! mid-point. - ! - ! ---- (interface) ===== (mid-point) - ! - ! CAM TUV-x - ! ------(top)------ i_int = 1 -------(top)------ i_int = pver + 2 - ! ************************ (exo values) ***************************** - ! ================== i_mid = pver + 1 - ! ================= i_mid = 1 ------------------ i_int = pver + 1 - ! ----------------- i_int = 2 ================== i_mid = pver - ! ------------------ i_int = pver - ! || - ! || || - ! || - ! ----------------- i_int = pver - ! ================= i_imd = pver ------------------ i_int = 2 - ! ================== i_mid = 1 - ! -----(ground)---- i_int = pver+1 -----(ground)----- i_int = 1 - ! - !----------------------------------------------------------------------- - subroutine set_heights( this, i_col, ncol, height_mid, height_int ) - - use ppgrid, only : pcols ! maximum number of columns - - class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator - integer, intent(in) :: i_col ! column to set conditions for - integer, intent(in) :: ncol ! number of colums to calculated photolysis for - real(r8), intent(in) :: height_mid(ncol,pver) ! height above the surface at mid-points (km) - real(r8), intent(in) :: height_int(ncol,pver+1) ! height above the surface at interfaces (km) - - integer :: i_level - real(r8) :: edges(pver+2) - real(r8) :: mid_points(pver+1) - - edges(1) = height_int(i_col,pver+1) - edges(2:pver+1) = height_mid(i_col,pver:1:-1) - edges(pver+2) = height_int(i_col,1) - mid_points(1) = ( height_mid(i_col,pver) - height_int(i_col,pver+1) ) * 0.5_r8 & - + height_int(i_col,pver+1) - mid_points(2:pver) = height_int(i_col,pver:2:-1) - mid_points(pver+1) = 0.5_r8 * ( edges(pver+1) + edges(pver+2) ) - call this%grids_( GRID_INDEX_HEIGHT )%update( edges = edges, mid_points = mid_points ) - this%height_delta_(1:pver+1) = edges(2:pver+2) - edges(1:pver+1) - - end subroutine set_heights + !----------------------------------------------------------------------- + ! Sets the height values in TUV-x for the given column + ! + ! NOTE: This function must be called before updating any profile data. + ! + ! CAM to TUV-x height grid mapping + ! + ! TUV-x heights are "bottom-up" and require atmospheric constituent + ! concentrations at interfaces. Therefore, CAM mid-points are used as + ! TUV-x grid interfaces, with an additional layer introduced between + ! the surface and the lowest CAM mid-point, and a layer at the + ! top of the TUV-x grid to hold species densities above the top CAM + ! mid-point. + ! + ! ---- (interface) ===== (mid-point) + ! + ! CAM TUV-x + ! ------(top)------ i_int = 1 -------(top)------ i_int = pver + 2 + ! ************************ (exo values) ***************************** + ! ================== i_mid = pver + 1 + ! ================= i_mid = 1 ------------------ i_int = pver + 1 + ! ----------------- i_int = 2 ================== i_mid = pver + ! ------------------ i_int = pver + ! || + ! || || + ! || + ! ----------------- i_int = pver + ! ================= i_imd = pver ------------------ i_int = 2 + ! ================== i_mid = 1 + ! -----(ground)---- i_int = pver+1 -----(ground)----- i_int = 1 + ! + !----------------------------------------------------------------------- + subroutine set_heights( this, i_col, ncol, height_mid, height_int ) + + use ppgrid, only : pcols ! maximum number of columns + + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! column to set conditions for + integer, intent(in) :: ncol ! number of colums to calculated photolysis for + real(r8), intent(in) :: height_mid(ncol,pver) ! height above the surface at mid-points (km) + real(r8), intent(in) :: height_int(ncol,pver+1) ! height above the surface at interfaces (km) + + integer :: i_level + real(r8) :: edges(pver+2) + real(r8) :: mid_points(pver+1) + + edges(1) = height_int(i_col,pver+1) + edges(2:pver+1) = height_mid(i_col,pver:1:-1) + edges(pver+2) = height_int(i_col,1) + mid_points(1) = ( height_mid(i_col,pver) - height_int(i_col,pver+1) ) * 0.5_r8 & + + height_int(i_col,pver+1) + mid_points(2:pver) = height_int(i_col,pver:2:-1) + mid_points(pver+1) = 0.5_r8 * ( edges(pver+1) + edges(pver+2) ) + call this%grids_( GRID_INDEX_HEIGHT )%update( edges = edges, mid_points = mid_points ) + this%height_delta_(1:pver+1) = edges(2:pver+2) - edges(1:pver+1) + + end subroutine set_heights !================================================================================================ - !----------------------------------------------------------------------- - ! Sets the temperatures in TUV-x for the given column - ! - ! See description of `set_heights` for CAM <-> TUV-x vertical grid - ! mapping. - !----------------------------------------------------------------------- - subroutine set_temperatures( this, i_col, temperature_mid, surface_temperature ) + !----------------------------------------------------------------------- + ! Sets the temperatures in TUV-x for the given column + ! + ! See description of `set_heights` for CAM <-> TUV-x vertical grid + ! mapping. + !----------------------------------------------------------------------- + subroutine set_temperatures( this, i_col, temperature_mid, surface_temperature ) - use ppgrid, only : pcols ! maximum number of columns + use ppgrid, only : pcols ! maximum number of columns - class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator - integer, intent(in) :: i_col ! column to set conditions for - real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) - real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! column to set conditions for + real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) + real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) - real(r8) :: edges(pver+2) + real(r8) :: edges(pver+2) - edges(1) = surface_temperature(i_col) - edges(2:pver+1) = temperature_mid(i_col,pver:1:-1) - edges(pver+2) = temperature_mid(i_col,1) ! Use upper mid-point temperature for top edge - call this%profiles_( PROFILE_INDEX_TEMPERATURE )%update( edge_values = edges ) + edges(1) = surface_temperature(i_col) + edges(2:pver+1) = temperature_mid(i_col,pver:1:-1) + edges(pver+2) = temperature_mid(i_col,1) ! Use upper mid-point temperature for top edge + call this%profiles_( PROFILE_INDEX_TEMPERATURE )%update( edge_values = edges ) - end subroutine set_temperatures + end subroutine set_temperatures !================================================================================================ - !----------------------------------------------------------------------- - ! Sets the surface albedo in TUV-x for the given column - ! - ! CAM uses a single value for surface albedo at all wavelengths - !----------------------------------------------------------------------- - subroutine set_surface_albedo( this, i_col, surface_albedo ) + !----------------------------------------------------------------------- + ! Sets the surface albedo in TUV-x for the given column + ! + ! CAM uses a single value for surface albedo at all wavelengths + !----------------------------------------------------------------------- + subroutine set_surface_albedo( this, i_col, surface_albedo ) - use ppgrid, only : pcols ! maximum number of columns + use ppgrid, only : pcols ! maximum number of columns - class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator - integer, intent(in) :: i_col ! column to set conditions for - real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! column to set conditions for + real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) - this%wavelength_values_(:) = surface_albedo( i_col ) - call this%profiles_( PROFILE_INDEX_ALBEDO )%update( & - edge_values = this%wavelength_values_(:) ) + this%wavelength_values_(:) = surface_albedo( i_col ) + call this%profiles_( PROFILE_INDEX_ALBEDO )%update( & + edge_values = this%wavelength_values_(:) ) - end subroutine set_surface_albedo + end subroutine set_surface_albedo !================================================================================================ - !----------------------------------------------------------------------- - ! Sets the extraterrestrial flux in TUV-x for the given column - ! - ! Extraterrestrial flux is read from data files and interpolated to the - ! TUV-x wavelength grid. CAM ET Flux values are multiplied by the - ! width of the wavelength bins to get the TUV-x units of photon cm-2 s-1 - ! - ! NOTE: TUV-x only uses mid-point values for ET Flux - !----------------------------------------------------------------------- - subroutine set_et_flux( this ) - - use mo_util, only : rebin - use solar_irrad_data, only : nbins, & ! number of wavelength bins - we, & ! wavelength bin edges - sol_etf ! extraterrestrial flux - ! (photon cm-2 nm-1 s-1) - - class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator - - real(r8) :: et_flux_orig(nbins) - integer :: n_tuvx_bins, i_bin - - ! =============================================== - ! regrid normalized flux to TUV-x wavelength grid - !================================================ - et_flux_orig(:) = sol_etf(:) - n_tuvx_bins = size(this%wavelength_mid_values_) - call rebin( nbins, n_tuvx_bins, we, this%wavelength_edges_, et_flux_orig, & - this%wavelength_mid_values_ ) - - ! ======================================================== - ! convert normalized flux to flux on TUV-x wavelength grid - ! ======================================================== - this%wavelength_mid_values_(:) = this%wavelength_mid_values_(:) * & - ( this%wavelength_edges_(2:n_tuvx_bins+1) - & - this%wavelength_edges_(1:n_tuvx_bins) ) - - ! ==================================== - ! estimate unused edge values for flux - ! ==================================== - this%wavelength_values_(1) = this%wavelength_mid_values_(1) - & - ( this%wavelength_mid_values_(2) - & - this%wavelength_mid_values_(1) ) * 0.5_r8 - do i_bin = 2, n_tuvx_bins - this%wavelength_values_(i_bin) = this%wavelength_mid_values_(i_bin-1) + & - ( this%wavelength_mid_values_(i_bin) - & - this%wavelength_mid_values_(i_bin-1) ) * 0.5_r8 - end do - this%wavelength_values_(n_tuvx_bins+1) = & - this%wavelength_mid_values_(n_tuvx_bins) + & - ( this%wavelength_mid_values_(n_tuvx_bins) - & - this%wavelength_mid_values_(n_tuvx_bins-1) ) * 0.5_r8 - - ! ============================ - ! update TUV-x ET flux profile - ! ============================ - call this%profiles_( PROFILE_INDEX_ET_FLUX )%update( & - mid_point_values = this%wavelength_mid_values_, & - edge_values = this%wavelength_values_) - - ! ====================================================================== - ! rebin extraterrestrial flux to MS93 grid for use with jno calculations - ! ====================================================================== - call rebin( nbins, NUM_BINS_MS93, we, WAVELENGTH_EDGES_MS93, et_flux_orig, & - this%et_flux_ms93_ ) - - end subroutine set_et_flux + !----------------------------------------------------------------------- + ! Sets the extraterrestrial flux in TUV-x for the given column + ! + ! Extraterrestrial flux is read from data files and interpolated to the + ! TUV-x wavelength grid. CAM ET Flux values are multiplied by the + ! width of the wavelength bins to get the TUV-x units of photon cm-2 s-1 + ! + ! NOTE: TUV-x only uses mid-point values for ET Flux + !----------------------------------------------------------------------- + subroutine set_et_flux( this ) + + use mo_util, only : rebin + use solar_irrad_data, only : nbins, & ! number of wavelength bins + we, & ! wavelength bin edges + sol_etf ! extraterrestrial flux + ! (photon cm-2 nm-1 s-1) + + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + + real(r8) :: et_flux_orig(nbins) + integer :: n_tuvx_bins, i_bin + + ! =============================================== + ! regrid normalized flux to TUV-x wavelength grid + !================================================ + et_flux_orig(:) = sol_etf(:) + n_tuvx_bins = size(this%wavelength_mid_values_) + call rebin( nbins, n_tuvx_bins, we, this%wavelength_edges_, et_flux_orig, & + this%wavelength_mid_values_ ) + + ! ======================================================== + ! convert normalized flux to flux on TUV-x wavelength grid + ! ======================================================== + this%wavelength_mid_values_(:) = this%wavelength_mid_values_(:) * & + ( this%wavelength_edges_(2:n_tuvx_bins+1) - & + this%wavelength_edges_(1:n_tuvx_bins) ) + + ! ==================================== + ! estimate unused edge values for flux + ! ==================================== + this%wavelength_values_(1) = this%wavelength_mid_values_(1) - & + ( this%wavelength_mid_values_(2) - & + this%wavelength_mid_values_(1) ) * 0.5_r8 + do i_bin = 2, n_tuvx_bins + this%wavelength_values_(i_bin) = this%wavelength_mid_values_(i_bin-1) + & + ( this%wavelength_mid_values_(i_bin) - & + this%wavelength_mid_values_(i_bin-1) ) * 0.5_r8 + end do + this%wavelength_values_(n_tuvx_bins+1) = & + this%wavelength_mid_values_(n_tuvx_bins) + & + ( this%wavelength_mid_values_(n_tuvx_bins) - & + this%wavelength_mid_values_(n_tuvx_bins-1) ) * 0.5_r8 + + ! ============================ + ! update TUV-x ET flux profile + ! ============================ + call this%profiles_( PROFILE_INDEX_ET_FLUX )%update( & + mid_point_values = this%wavelength_mid_values_, & + edge_values = this%wavelength_values_) + + ! ====================================================================== + ! rebin extraterrestrial flux to MS93 grid for use with jno calculations + ! ====================================================================== + call rebin( nbins, NUM_BINS_MS93, we, WAVELENGTH_EDGES_MS93, et_flux_orig, & + this%et_flux_ms93_ ) + + end subroutine set_et_flux !================================================================================================ - !----------------------------------------------------------------------- - ! Sets the profiles of optically active atmospheric constituents - ! in TUV-x for the given column - ! - ! See `set_height` for a description of the CAM <-> TUV-x vertical grid - ! mapping. - ! - ! Above layer densities are calculated using a scale height for air - ! and pre-calculated values for O2 and O3 - !----------------------------------------------------------------------- - subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species_vmr, & - exo_column_conc, delta_pressure, cloud_fraction, & - liquid_water_content ) - - use chem_mods, only : gas_pcnst, & ! number of non-fixed species - nfs, & ! number of fixed species - nabscol, & ! number of absorbing species (radiators) - indexm ! index for air density in fixed species array - - class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator - integer, intent(in) :: i_col ! column to set conditions for - integer, intent(in) :: ncol ! number of columns - real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities - ! (molecule cm-3) - real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing - ! ratios (mol mol-1) - real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! above column densities - ! (molecule cm-2) - real(r8), intent(in) :: delta_pressure(ncol,pver) ! pressure delta about midpoints (Pa) - real(r8), intent(in) :: cloud_fraction(ncol,pver) ! cloud fraction (unitless) - real(r8), intent(in) :: liquid_water_content(ncol,pver) ! liquid water content (kg/kg) - - integer :: i_level - real(r8) :: tmp(pver) - real(r8) :: tau(pver+1, size(this%wavelength_mid_values_)) - real(r8) :: edges(pver+2), densities(pver+1) - real(r8) :: exo_val - real(r8), parameter :: rgrav = 1.0_r8 / 9.80616_r8 ! reciprocal of acceleration by gravity (s/m) - real(r8), parameter :: km2cm = 1.0e5_r8 ! conversion from km to cm - - ! =========== - ! air profile - ! =========== - edges(1) = fixed_species_conc(i_col,pver,indexm) - edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,indexm) - edges(pver+2) = fixed_species_conc(i_col,1,indexm) ! use upper mid-point value for top edge - densities(1:pver+1) = this%height_delta_(1:pver+1) * km2cm * & - sqrt(edges(1:pver+1)) * sqrt(edges(2:pver+2)) - call this%profiles_( PROFILE_INDEX_AIR )%update( & - edge_values = edges, layer_densities = densities, & - scale_height = 8.01_r8 ) ! scale height in [km] - - ! ========== - ! O2 profile - ! ========== - if( is_fixed_O2 ) then - edges(1) = fixed_species_conc(i_col,pver,index_O2) - edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,index_O2) - edges(pver+2) = fixed_species_conc(i_col,1,index_O2) - else if( index_O2 > 0 ) then - edges(1) = species_vmr(i_col,pver,index_O2) * & - fixed_species_conc(i_col,pver,indexm) - edges(2:pver+1) = species_vmr(i_col,pver:1:-1,index_O2) * & - fixed_species_conc(i_col,pver:1:-1,indexm) - edges(pver+2) = species_vmr(i_col,1,index_O2) * & - fixed_species_conc(i_col,1,indexm) - else - edges(:) = 0.0_r8 - end if - densities(1:pver+1) = this%height_delta_(1:pver+1) * km2cm * & - sqrt(edges(1:pver+1)) * sqrt(edges(2:pver+2)) - call this%profiles_( PROFILE_INDEX_O2 )%update( & - edge_values = edges, layer_densities = densities, & - scale_height = 7.0_r8 ) - - ! ========== - ! O3 profile - ! ========== - if( is_fixed_O3 ) then - edges(1) = fixed_species_conc(i_col,pver,index_O3) - edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,index_O3) - edges(pver+2) = fixed_species_conc(i_col,1,index_O3) - else if( index_O3 > 0 ) then - edges(1) = species_vmr(i_col,pver,index_O3) * & - fixed_species_conc(i_col,pver,indexm) - edges(2:pver+1) = species_vmr(i_col,pver:1:-1,index_O3) * & - fixed_species_conc(i_col,pver:1:-1,indexm) - edges(pver+2) = species_vmr(i_col,1,index_O3) * & - fixed_species_conc(i_col,1,indexm) - else - edges(:) = 0.0_r8 - end if - if( nabscol >= 1 ) then - densities(1) = 0.5_r8 * exo_column_conc(i_col,pver,1) - densities(2:pver) = 0.5_r8 * ( exo_column_conc(i_col,pver-1:1:-1,1) & - + exo_column_conc(i_col,pver:2:-1,1) ) - densities(pver+1) = exo_column_conc(i_col,0,1) & - + 0.5_r8 * exo_column_conc(i_col,1,1) - call this%profiles_( PROFILE_INDEX_O3 )%update( & - edge_values = edges, layer_densities = densities, & - exo_density = exo_column_conc(i_col,0,1) ) - else + !----------------------------------------------------------------------- + ! Sets the profiles of optically active atmospheric constituents + ! in TUV-x for the given column + ! + ! See `set_height` for a description of the CAM <-> TUV-x vertical grid + ! mapping. + ! + ! Above layer densities are calculated using a scale height for air + ! and pre-calculated values for O2 and O3 + !----------------------------------------------------------------------- + subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species_vmr, & + exo_column_conc, delta_pressure, cloud_fraction, & + liquid_water_content ) + + use chem_mods, only : gas_pcnst, & ! number of non-fixed species + nfs, & ! number of fixed species + nabscol, & ! number of absorbing species (radiators) + indexm ! index for air density in fixed species array + + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + integer, intent(in) :: i_col ! column to set conditions for + integer, intent(in) :: ncol ! number of columns + real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities + ! (molecule cm-3) + real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) + real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! above column densities + ! (molecule cm-2) + real(r8), intent(in) :: delta_pressure(ncol,pver) ! pressure delta about midpoints (Pa) + real(r8), intent(in) :: cloud_fraction(ncol,pver) ! cloud fraction (unitless) + real(r8), intent(in) :: liquid_water_content(ncol,pver) ! liquid water content (kg/kg) + + integer :: i_level + real(r8) :: tmp(pver) + real(r8) :: tau(pver+1, size(this%wavelength_mid_values_)) + real(r8) :: edges(pver+2), densities(pver+1) + real(r8) :: exo_val + real(r8), parameter :: rgrav = 1.0_r8 / 9.80616_r8 ! reciprocal of acceleration by gravity (s/m) + real(r8), parameter :: km2cm = 1.0e5_r8 ! conversion from km to cm + + ! =========== + ! air profile + ! =========== + edges(1) = fixed_species_conc(i_col,pver,indexm) + edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,indexm) + edges(pver+2) = fixed_species_conc(i_col,1,indexm) ! use upper mid-point value for top edge densities(1:pver+1) = this%height_delta_(1:pver+1) * km2cm * & - ( edges(1:pver+1) + edges(2:pver+2) ) * 0.5_r8 - call this%profiles_( PROFILE_INDEX_O3 )%update( & - edge_values = edges, layer_densities = densities, & - scale_height = 7.0_r8 ) - end if - - ! =============== - ! aerosol profile - ! =============== - if( do_aerosol ) then - call this%radiators_( RADIATOR_INDEX_AEROSOL )%update( & - optical_depths = this%optical_depth_(i_col,:,:), & - single_scattering_albedos = this%single_scattering_albedo_(i_col,:,:), & - asymmetry_factors = this%asymmetry_factor_(i_col,:,:) ) - end if - - ! ============= - ! cloud profile - ! ============= - if( do_clouds ) then - ! =================================================== - ! estimate cloud optical depth as: - ! liquid_water_path * 0.155 * cloud_fraction^(1.5) - ! =================================================== - associate( clouds => cloud_fraction(i_col,:) ) - where( clouds(:) /= 0.0_r8 ) - tmp(:) = ( rgrav * liquid_water_content(i_col,:) * delta_pressure(i_col,:) & - * 1.0e3_r8 / clouds(:) ) * 0.155_r8 * clouds(:)**1.5_r8 - elsewhere - tmp(:) = 0.0_r8 - end where - end associate - do i_level = 1, pver - tau(i_level,:) = tmp(pver-i_level+1) - end do - tau(pver+1,:) = 0.0_r8 - call this%radiators_( RADIATOR_INDEX_CLOUDS )%update( optical_depths = tau ) - end if + sqrt(edges(1:pver+1)) * sqrt(edges(2:pver+2)) + call this%profiles_( PROFILE_INDEX_AIR )%update( & + edge_values = edges, layer_densities = densities, & + scale_height = 8.01_r8 ) ! scale height in [km] + + ! ========== + ! O2 profile + ! ========== + if( is_fixed_O2 ) then + edges(1) = fixed_species_conc(i_col,pver,index_O2) + edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,index_O2) + edges(pver+2) = fixed_species_conc(i_col,1,index_O2) + else if( index_O2 > 0 ) then + edges(1) = species_vmr(i_col,pver,index_O2) * & + fixed_species_conc(i_col,pver,indexm) + edges(2:pver+1) = species_vmr(i_col,pver:1:-1,index_O2) * & + fixed_species_conc(i_col,pver:1:-1,indexm) + edges(pver+2) = species_vmr(i_col,1,index_O2) * & + fixed_species_conc(i_col,1,indexm) + else + edges(:) = 0.0_r8 + end if + densities(1:pver+1) = this%height_delta_(1:pver+1) * km2cm * & + sqrt(edges(1:pver+1)) * sqrt(edges(2:pver+2)) + call this%profiles_( PROFILE_INDEX_O2 )%update( & + edge_values = edges, layer_densities = densities, & + scale_height = 7.0_r8 ) + + ! ========== + ! O3 profile + ! ========== + if( is_fixed_O3 ) then + edges(1) = fixed_species_conc(i_col,pver,index_O3) + edges(2:pver+1) = fixed_species_conc(i_col,pver:1:-1,index_O3) + edges(pver+2) = fixed_species_conc(i_col,1,index_O3) + else if( index_O3 > 0 ) then + edges(1) = species_vmr(i_col,pver,index_O3) * & + fixed_species_conc(i_col,pver,indexm) + edges(2:pver+1) = species_vmr(i_col,pver:1:-1,index_O3) * & + fixed_species_conc(i_col,pver:1:-1,indexm) + edges(pver+2) = species_vmr(i_col,1,index_O3) * & + fixed_species_conc(i_col,1,indexm) + else + edges(:) = 0.0_r8 + end if + if( nabscol >= 1 ) then + densities(1) = 0.5_r8 * exo_column_conc(i_col,pver,1) + densities(2:pver) = 0.5_r8 * ( exo_column_conc(i_col,pver-1:1:-1,1) & + + exo_column_conc(i_col,pver:2:-1,1) ) + densities(pver+1) = exo_column_conc(i_col,0,1) & + + 0.5_r8 * exo_column_conc(i_col,1,1) + call this%profiles_( PROFILE_INDEX_O3 )%update( & + edge_values = edges, layer_densities = densities, & + exo_density = exo_column_conc(i_col,0,1) ) + else + densities(1:pver+1) = this%height_delta_(1:pver+1) * km2cm * & + ( edges(1:pver+1) + edges(2:pver+2) ) * 0.5_r8 + call this%profiles_( PROFILE_INDEX_O3 )%update( & + edge_values = edges, layer_densities = densities, & + scale_height = 7.0_r8 ) + end if - end subroutine set_radiator_profiles + ! =============== + ! aerosol profile + ! =============== + if( do_aerosol ) then + call this%radiators_( RADIATOR_INDEX_AEROSOL )%update( & + optical_depths = this%optical_depth_(i_col,:,:), & + single_scattering_albedos = this%single_scattering_albedo_(i_col,:,:), & + asymmetry_factors = this%asymmetry_factor_(i_col,:,:) ) + end if + + ! ============= + ! cloud profile + ! ============= + if( do_clouds ) then + ! =================================================== + ! estimate cloud optical depth as: + ! liquid_water_path * 0.155 * cloud_fraction^(1.5) + ! =================================================== + associate( clouds => cloud_fraction(i_col,:) ) + where( clouds(:) /= 0.0_r8 ) + tmp(:) = ( rgrav * liquid_water_content(i_col,:) * delta_pressure(i_col,:) & + * 1.0e3_r8 / clouds(:) ) * 0.155_r8 * clouds(:)**1.5_r8 + elsewhere + tmp(:) = 0.0_r8 + end where + end associate + do i_level = 1, pver + tau(i_level,:) = tmp(pver-i_level+1) + end do + tau(pver+1,:) = 0.0_r8 + call this%radiators_( RADIATOR_INDEX_CLOUDS )%update( optical_depths = tau ) + end if + + end subroutine set_radiator_profiles !================================================================================================ - !----------------------------------------------------------------------- - ! Updates working arrays of aerosol optical properties for all - ! columns from the aerosol package - !----------------------------------------------------------------------- - subroutine get_aerosol_optical_properties( this, state, pbuf ) - - use aer_rad_props, only : aer_rad_props_sw - use mo_util, only : rebin - use physics_types, only : physics_state - use physics_buffer, only : physics_buffer_desc - use ppgrid, only : pcols ! maximum number of columns - use radconstants, only : nswbands, & ! Number of CAM shortwave radiation bands - get_sw_spectral_boundaries - - class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator - type(physics_state), target, intent(in) :: state - type(physics_buffer_desc), pointer, intent(inout) :: pbuf(:) - - real(r8) :: wavelength_edges(nswbands+1) ! CAM radiation wavelength grid edges [nm] - real(r8) :: aer_tau (pcols,0:pver,nswbands) ! aerosol extinction optical depth - real(r8) :: aer_tau_w (pcols,0:pver,nswbands) ! aerosol single scattering albedo * tau - real(r8) :: aer_tau_w_g(pcols,0:pver,nswbands) ! aerosol assymetry parameter * w * tau - real(r8) :: aer_tau_w_f(pcols,0:pver,nswbands) ! aerosol forward scattered fraction * w * tau - real(r8) :: low_bound(nswbands) ! lower bound of CAM wavenumber bins - real(r8) :: high_bound(nswbands) ! upper bound of CAM wavenumber bins - integer :: n_night ! number of night columns - integer :: idx_night(pcols) ! indices of night columns - integer :: n_tuvx_bins ! number of TUV-x wavelength bins - integer :: i_col, i_level ! column and level indices - - ! ============================================ - ! do nothing if no aerosol module is available - ! ============================================ - if( .not. do_aerosol ) return - - ! TODO just assume all daylight columns for now - ! can adjust later if necessary - n_night = 0 - idx_night(:) = 0 - - ! =========================================================== - ! get aerosol optical properties on native CAM radiation grid - ! =========================================================== - call aer_rad_props_sw( 0, state, pbuf, n_night, idx_night, & - aer_tau, aer_tau_w, aer_tau_w_g, aer_tau_w_f ) - - ! ========================================================================= - ! Convert CAM wavenumber grid to wavelength grid and re-order optics arrays - ! NOTE: CAM wavenumber grid is continuous and increasing, except that the - ! last bin should be moved to the just before the first bin (!?!) - ! ========================================================================= - call get_sw_spectral_boundaries( low_bound, high_bound, 'nm' ) - wavelength_edges(1:nswbands-1) = high_bound(nswbands-1:1:-1) - wavelength_edges(nswbands ) = high_bound(nswbands ) - wavelength_edges(nswbands+1 ) = low_bound( nswbands ) - call reorder_optics_array( aer_tau ) - call reorder_optics_array( aer_tau_w ) - call reorder_optics_array( aer_tau_w_g ) - - ! ============================================================= - ! regrid optical properties to TUV-x wavelength and height grid - ! ============================================================= - ! TODO is this the correct regridding scheme to use? - n_tuvx_bins = size(this%wavelength_mid_values_) - do i_col = 1, pcols - do i_level = 1, pver - call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & - aer_tau(i_col,pver-i_level,:), this%optical_depth_(i_col,i_level,:)) - call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & - aer_tau_w(i_col,pver-i_level,:), this%single_scattering_albedo_(i_col,i_level,:)) - call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & - aer_tau_w_g(i_col,pver-i_level,:), this%asymmetry_factor_(i_col,i_level,:)) + !----------------------------------------------------------------------- + ! Updates working arrays of aerosol optical properties for all + ! columns from the aerosol package + !----------------------------------------------------------------------- + subroutine get_aerosol_optical_properties( this, state, pbuf ) + + use aer_rad_props, only : aer_rad_props_sw + use mo_util, only : rebin + use physics_types, only : physics_state + use physics_buffer, only : physics_buffer_desc + use ppgrid, only : pcols ! maximum number of columns + use radconstants, only : nswbands, & ! Number of CAM shortwave radiation bands + get_sw_spectral_boundaries + + class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator + type(physics_state), target, intent(in) :: state + type(physics_buffer_desc), pointer, intent(inout) :: pbuf(:) + + real(r8) :: wavelength_edges(nswbands+1) ! CAM radiation wavelength grid edges [nm] + real(r8) :: aer_tau (pcols,0:pver,nswbands) ! aerosol extinction optical depth + real(r8) :: aer_tau_w (pcols,0:pver,nswbands) ! aerosol single scattering albedo * tau + real(r8) :: aer_tau_w_g(pcols,0:pver,nswbands) ! aerosol assymetry parameter * w * tau + real(r8) :: aer_tau_w_f(pcols,0:pver,nswbands) ! aerosol forward scattered fraction * w * tau + real(r8) :: low_bound(nswbands) ! lower bound of CAM wavenumber bins + real(r8) :: high_bound(nswbands) ! upper bound of CAM wavenumber bins + integer :: n_night ! number of night columns + integer :: idx_night(pcols) ! indices of night columns + integer :: n_tuvx_bins ! number of TUV-x wavelength bins + integer :: i_col, i_level ! column and level indices + + ! ============================================ + ! do nothing if no aerosol module is available + ! ============================================ + if( .not. do_aerosol ) return + + ! TODO just assume all daylight columns for now + ! can adjust later if necessary + n_night = 0 + idx_night(:) = 0 + + ! =========================================================== + ! get aerosol optical properties on native CAM radiation grid + ! =========================================================== + call aer_rad_props_sw( 0, state, pbuf, n_night, idx_night, & + aer_tau, aer_tau_w, aer_tau_w_g, aer_tau_w_f ) + + ! ========================================================================= + ! Convert CAM wavenumber grid to wavelength grid and re-order optics arrays + ! NOTE: CAM wavenumber grid is continuous and increasing, except that the + ! last bin should be moved to the just before the first bin (!?!) + ! ========================================================================= + call get_sw_spectral_boundaries( low_bound, high_bound, 'nm' ) + wavelength_edges(1:nswbands-1) = high_bound(nswbands-1:1:-1) + wavelength_edges(nswbands ) = high_bound(nswbands ) + wavelength_edges(nswbands+1 ) = low_bound( nswbands ) + call reorder_optics_array( aer_tau ) + call reorder_optics_array( aer_tau_w ) + call reorder_optics_array( aer_tau_w_g ) + + ! ============================================================= + ! regrid optical properties to TUV-x wavelength and height grid + ! ============================================================= + ! TODO is this the correct regridding scheme to use? + n_tuvx_bins = size(this%wavelength_mid_values_) + do i_col = 1, pcols + do i_level = 1, pver + call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & + aer_tau(i_col,pver-i_level,:), this%optical_depth_(i_col,i_level,:)) + call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & + aer_tau_w(i_col,pver-i_level,:), this%single_scattering_albedo_(i_col,i_level,:)) + call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & + aer_tau_w_g(i_col,pver-i_level,:), this%asymmetry_factor_(i_col,i_level,:)) + end do + this%optical_depth_(i_col,pver+1,:) = & + this%optical_depth_(i_col,pver,:) + this%single_scattering_albedo_(i_col,pver+1,:) = & + this%single_scattering_albedo_(i_col,pver,:) + this%asymmetry_factor_(i_col,pver+1,:) = & + this%asymmetry_factor_(i_col,pver,:) end do - this%optical_depth_(i_col,pver+1,:) = & - this%optical_depth_(i_col,pver,:) - this%single_scattering_albedo_(i_col,pver+1,:) = & - this%single_scattering_albedo_(i_col,pver,:) - this%asymmetry_factor_(i_col,pver+1,:) = & - this%asymmetry_factor_(i_col,pver,:) - end do - - ! ================================================================ - ! back-calculate the single scattering albedo and asymmetry factor - ! ================================================================ - associate( tau => this%optical_depth_, & - omega => this%single_scattering_albedo_, & - g => this%asymmetry_factor_ ) - where(omega > 0.0_r8) - g = g / omega - elsewhere - g = 0.0_r8 - end where - where(tau > 0.0_r8) - omega = omega / tau - elsewhere - omega = 0.0_r8 - end where - end associate - - end subroutine get_aerosol_optical_properties + + ! ================================================================ + ! back-calculate the single scattering albedo and asymmetry factor + ! ================================================================ + associate( tau => this%optical_depth_, & + omega => this%single_scattering_albedo_, & + g => this%asymmetry_factor_ ) + where(omega > 0.0_r8) + g = g / omega + elsewhere + g = 0.0_r8 + end where + where(tau > 0.0_r8) + omega = omega / tau + elsewhere + omega = 0.0_r8 + end where + end associate + + end subroutine get_aerosol_optical_properties !================================================================================================ - !----------------------------------------------------------------------- - ! Reorders elements of an optical property array for conversion - ! from wavenumber to wavelength grid and out-of-order final - ! element in CAM wavenumber grid - !----------------------------------------------------------------------- - subroutine reorder_optics_array( optics_array ) + !----------------------------------------------------------------------- + ! Reorders elements of an optical property array for conversion + ! from wavenumber to wavelength grid and out-of-order final + ! element in CAM wavenumber grid + !----------------------------------------------------------------------- + subroutine reorder_optics_array( optics_array ) - use ppgrid, only : pcols ! maximum number of columns - use radconstants, only : nswbands ! Number of CAM shortwave radiation bands + use ppgrid, only : pcols ! maximum number of columns + use radconstants, only : nswbands ! Number of CAM shortwave radiation bands - real(r8), intent(inout) :: optics_array(pcols,0:pver,nswbands) ! optics array to reorder + real(r8), intent(inout) :: optics_array(pcols,0:pver,nswbands) ! optics array to reorder - real(r8) :: working(pcols,0:pver,nswbands) ! working array + real(r8) :: working(pcols,0:pver,nswbands) ! working array - working(:,:,:) = optics_array(:,:,:) - optics_array(:,:,1:nswbands-1) = working(:,:,nswbands-1:1:-1) + working(:,:,:) = optics_array(:,:,:) + optics_array(:,:,1:nswbands-1) = working(:,:,nswbands-1:1:-1) - end subroutine reorder_optics_array + end subroutine reorder_optics_array !================================================================================================ - !----------------------------------------------------------------------- - ! Calculates extreme-UV ionization rates - ! - ! NOTE This never includes an above-column layer - !----------------------------------------------------------------------- - subroutine calculate_euv_rates( solar_zenith_angle, fixed_species_conc, & + !----------------------------------------------------------------------- + ! Calculates extreme-UV ionization rates + ! + ! NOTE This never includes an above-column layer + !----------------------------------------------------------------------- + subroutine calculate_euv_rates( solar_zenith_angle, fixed_species_conc, & species_vmr, height_mid, height_int, euv_rates ) - use chem_mods, only : gas_pcnst, & ! number of non-fixed species - nfs, & ! number of fixed species - indexm ! index for air density in fixed species array - use mo_jeuv, only : jeuv, neuv ! number of extreme-UV rates - use ref_pres, only : ptop_ref ! pressure at the top of the column (Pa) - - real(r8), intent(in) :: solar_zenith_angle ! degrees - real(r8), intent(in) :: fixed_species_conc(pver,max(1,nfs)) ! fixed species densities - ! (molecule cm-3) - real(r8), intent(in) :: species_vmr(pver,max(1,gas_pcnst)) ! species volume mixing - ! ratios (mol mol-1) - real(r8), intent(in) :: height_mid(pver) ! height at mid-points (km) - real(r8), intent(in) :: height_int(pver+1) ! height at interfaces (km) - real(r8), intent(out) :: euv_rates(pver,neuv) ! calculated extreme-UV rates - - real(r8) :: o_dens(pver), o2_dens(pver), n2_dens(pver), height_arg(pver) - - ! ========== - ! N2 density - ! ========== - if( is_fixed_N2 ) then - n2_dens(:) = fixed_species_conc(:pver,index_N2) - else - n2_dens(:) = species_vmr(:pver,index_N2) * fixed_species_conc(:pver,indexm) - end if - - ! ========= - ! O density - ! ========= - if( is_fixed_O ) then - o_dens(:) = fixed_species_conc(:pver,index_O) - else - o_dens(:) = species_vmr(:pver,index_O) * fixed_species_conc(:pver,indexm) - end if - - ! ========== - ! O2 density - ! ========== - if( is_fixed_O2 ) then - o2_dens(:) = fixed_species_conc(:pver,index_O2) - else - o2_dens(:) = species_vmr(:pver,index_O2) * fixed_species_conc(:pver,indexm) - end if - - ! ======================= - ! special height argument - ! ======================= - height_arg(:) = height_mid(:) - - call jeuv( pver, solar_zenith_angle, o_dens, o2_dens, n2_dens, height_arg, euv_rates ) - - end subroutine calculate_euv_rates + use chem_mods, only : gas_pcnst, & ! number of non-fixed species + nfs, & ! number of fixed species + indexm ! index for air density in fixed species array + use mo_jeuv, only : jeuv, neuv ! number of extreme-UV rates + use ref_pres, only : ptop_ref ! pressure at the top of the column (Pa) + + real(r8), intent(in) :: solar_zenith_angle ! degrees + real(r8), intent(in) :: fixed_species_conc(pver,max(1,nfs)) ! fixed species densities + ! (molecule cm-3) + real(r8), intent(in) :: species_vmr(pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) + real(r8), intent(in) :: height_mid(pver) ! height at mid-points (km) + real(r8), intent(in) :: height_int(pver+1) ! height at interfaces (km) + real(r8), intent(out) :: euv_rates(pver,neuv) ! calculated extreme-UV rates + + real(r8) :: o_dens(pver), o2_dens(pver), n2_dens(pver), height_arg(pver) + + ! ========== + ! N2 density + ! ========== + if( is_fixed_N2 ) then + n2_dens(:) = fixed_species_conc(:pver,index_N2) + else + n2_dens(:) = species_vmr(:pver,index_N2) * fixed_species_conc(:pver,indexm) + end if + + ! ========= + ! O density + ! ========= + if( is_fixed_O ) then + o_dens(:) = fixed_species_conc(:pver,index_O) + else + o_dens(:) = species_vmr(:pver,index_O) * fixed_species_conc(:pver,indexm) + end if + + ! ========== + ! O2 density + ! ========== + if( is_fixed_O2 ) then + o2_dens(:) = fixed_species_conc(:pver,index_O2) + else + o2_dens(:) = species_vmr(:pver,index_O2) * fixed_species_conc(:pver,indexm) + end if + + ! ======================= + ! special height argument + ! ======================= + height_arg(:) = height_mid(:) + + call jeuv( pver, solar_zenith_angle, o_dens, o2_dens, n2_dens, height_arg, euv_rates ) + + end subroutine calculate_euv_rates !================================================================================================ - !----------------------------------------------------------------------- - ! Calculates NO photolysis rates - ! - ! NOTE: Always includes an above-column layer - !----------------------------------------------------------------------- - subroutine calculate_jno( solar_zenith_angle, et_flux, fixed_species_conc, species_vmr, & + !----------------------------------------------------------------------- + ! Calculates NO photolysis rates + ! + ! NOTE: Always includes an above-column layer + !----------------------------------------------------------------------- + subroutine calculate_jno( solar_zenith_angle, et_flux, fixed_species_conc, species_vmr, & height_int, jno ) - use chem_mods, only : gas_pcnst, & ! number of non-fixed species - nfs, & ! number of fixed species - indexm ! index for air density in fixed species array - use mo_jshort, only : sphers, slant_col, calc_jno - use ref_pres, only : ptop_ref ! pressure at the top of the column (Pa) - - real(r8), intent(in) :: solar_zenith_angle ! degrees - real(r8), intent(in) :: et_flux(NUM_BINS_MS93) ! extraterrestrial flux MS93 grid - ! (photon cm-2 nm-1 s-1) - real(r8), intent(in) :: fixed_species_conc(pver,max(1,nfs)) ! fixed species densities - ! (molecule cm-3) - real(r8), intent(in) :: species_vmr(pver,max(1,gas_pcnst)) ! species volume mixing - ! ratios (mol mol-1) - real(r8), intent(in) :: height_int(pver+1) ! height at interfaces (km) - real(r8), intent(out) :: jno(pver) ! calculated NO rate - - ! species column densities (molecule cm-3) - real(kind=r8) :: n2_dens(pver+1), o2_dens(pver+1), o3_dens(pver+1), no_dens(pver+1) - ! species slant column densities (molecule cm-2) - real(kind=r8) :: o2_slant(pver+1), o3_slant(pver+1), no_slant(pver+1) - ! working photo rate array - real(kind=r8) :: work_jno(pver+1) - ! parameters needed to calculate slant column densities - ! (see sphers routine description for details) - integer :: nid(pver+1) - real(kind=r8) :: dsdh(0:pver+1,pver+1) - ! layer thickness (cm) - real(kind=r8) :: delz(pver+1) - ! conversion from km to cm - real(kind=r8), parameter :: km2cm = 1.0e5_r8 - - ! ========== - ! N2 density - ! ========== - if( is_fixed_N2 ) then - n2_dens(2:) = fixed_species_conc(:pver,index_N2) - else - n2_dens(2:) = species_vmr(:pver,index_N2) * fixed_species_conc(:pver,indexm) - end if - n2_dens(1) = n2_dens(2) * 0.9_r8 - - ! ========== - ! O2 density - ! ========== - if( is_fixed_O2 ) then - o2_dens(2:) = fixed_species_conc(:pver,index_O2) - else - o2_dens(2:) = species_vmr(:pver,index_O2) * fixed_species_conc(:pver,indexm) - end if - o2_dens(1) = o2_dens(2) * 7.0_r8 / ( height_int(1) - height_int(2) ) - - ! ========== - ! O3 density - ! ========== - if( is_fixed_O3 ) then - o3_dens(2:) = fixed_species_conc(:pver,index_O3) - else - o3_dens(2:) = species_vmr(:pver,index_O3) * fixed_species_conc(:pver,indexm) - end if - o3_dens(1) = o3_dens(2) * 7.0_r8 / ( height_int(1) - height_int(2) ) - - ! ========== - ! NO density - ! ========== - if( is_fixed_NO ) then - no_dens(2:) = fixed_species_conc(:pver,index_NO) - else - no_dens(2:) = species_vmr(:pver,index_NO) * fixed_species_conc(:pver,indexm) - end if - no_dens(1) = no_dens(2) * 0.9_r8 - - ! ================================ - ! calculate slant column densities - ! ================================ - call sphers( pver+1, height_int, solar_zenith_angle, dsdh, nid ) - delz(1:pver) = km2cm * ( height_int(1:pver) - height_int(2:pver+1) ) - call slant_col( pver+1, delz, dsdh, nid, o2_dens, o2_slant ) - call slant_col( pver+1, delz, dsdh, nid, o3_dens, o3_slant ) - call slant_col( pver+1, delz, dsdh, nid, no_dens, no_slant ) - - ! ========================================= - ! calculate the NO photolysis rate constant - ! ========================================= - call calc_jno( pver+1, et_flux, n2_dens, o2_slant, o3_slant, no_slant, work_jno ) - jno(:) = work_jno(:pver) - - end subroutine calculate_jno + use chem_mods, only : gas_pcnst, & ! number of non-fixed species + nfs, & ! number of fixed species + indexm ! index for air density in fixed species array + use mo_jshort, only : sphers, slant_col, calc_jno + use ref_pres, only : ptop_ref ! pressure at the top of the column (Pa) + + real(r8), intent(in) :: solar_zenith_angle ! degrees + real(r8), intent(in) :: et_flux(NUM_BINS_MS93) ! extraterrestrial flux MS93 grid + ! (photon cm-2 nm-1 s-1) + real(r8), intent(in) :: fixed_species_conc(pver,max(1,nfs)) ! fixed species densities + ! (molecule cm-3) + real(r8), intent(in) :: species_vmr(pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) + real(r8), intent(in) :: height_int(pver+1) ! height at interfaces (km) + real(r8), intent(out) :: jno(pver) ! calculated NO rate + + ! species column densities (molecule cm-3) + real(kind=r8) :: n2_dens(pver+1), o2_dens(pver+1), o3_dens(pver+1), no_dens(pver+1) + ! species slant column densities (molecule cm-2) + real(kind=r8) :: o2_slant(pver+1), o3_slant(pver+1), no_slant(pver+1) + ! working photo rate array + real(kind=r8) :: work_jno(pver+1) + ! parameters needed to calculate slant column densities + ! (see sphers routine description for details) + integer :: nid(pver+1) + real(kind=r8) :: dsdh(0:pver+1,pver+1) + ! layer thickness (cm) + real(kind=r8) :: delz(pver+1) + ! conversion from km to cm + real(kind=r8), parameter :: km2cm = 1.0e5_r8 + + ! ========== + ! N2 density + ! ========== + if( is_fixed_N2 ) then + n2_dens(2:) = fixed_species_conc(:pver,index_N2) + else + n2_dens(2:) = species_vmr(:pver,index_N2) * fixed_species_conc(:pver,indexm) + end if + n2_dens(1) = n2_dens(2) * 0.9_r8 + + ! ========== + ! O2 density + ! ========== + if( is_fixed_O2 ) then + o2_dens(2:) = fixed_species_conc(:pver,index_O2) + else + o2_dens(2:) = species_vmr(:pver,index_O2) * fixed_species_conc(:pver,indexm) + end if + o2_dens(1) = o2_dens(2) * 7.0_r8 / ( height_int(1) - height_int(2) ) + + ! ========== + ! O3 density + ! ========== + if( is_fixed_O3 ) then + o3_dens(2:) = fixed_species_conc(:pver,index_O3) + else + o3_dens(2:) = species_vmr(:pver,index_O3) * fixed_species_conc(:pver,indexm) + end if + o3_dens(1) = o3_dens(2) * 7.0_r8 / ( height_int(1) - height_int(2) ) + + ! ========== + ! NO density + ! ========== + if( is_fixed_NO ) then + no_dens(2:) = fixed_species_conc(:pver,index_NO) + else + no_dens(2:) = species_vmr(:pver,index_NO) * fixed_species_conc(:pver,indexm) + end if + no_dens(1) = no_dens(2) * 0.9_r8 + + ! ================================ + ! calculate slant column densities + ! ================================ + call sphers( pver+1, height_int, solar_zenith_angle, dsdh, nid ) + delz(1:pver) = km2cm * ( height_int(1:pver) - height_int(2:pver+1) ) + call slant_col( pver+1, delz, dsdh, nid, o2_dens, o2_slant ) + call slant_col( pver+1, delz, dsdh, nid, o3_dens, o3_slant ) + call slant_col( pver+1, delz, dsdh, nid, no_dens, no_slant ) + + ! ========================================= + ! calculate the NO photolysis rate constant + ! ========================================= + call calc_jno( pver+1, et_flux, n2_dens, o2_slant, o3_slant, no_slant, work_jno ) + jno(:) = work_jno(:pver) + + end subroutine calculate_jno !================================================================================================ From 8a8c1af3ff5ae8ae02a234fd96f552fbcf48a3aa Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Thu, 21 Mar 2024 15:55:35 -0700 Subject: [PATCH 78/84] Develop add heating rates (#9) * update to latest TUV-x * add heating rate terms to config * add heating rate calcs * remove default output of photo rates * remove default TUV-x output * initialize heating rates to zero * fix max sza type * update TUV-x commit --- .gitignore | 3 +- Externals_CAM.cfg | 15 +- cime_config/buildlib | 146 ++++----------- cime_config/buildnml | 4 +- cime_config/config_component.xml | 2 +- cime_config/tuvx_MOZART_TS1.json | 216 ++++++++++++++++++----- src/chemistry/mozart/chemistry.F90 | 5 +- src/chemistry/mozart/mo_chemini.F90 | 2 +- src/chemistry/mozart/mo_tuvx.F90 | 158 +++++++++++++++-- src/chemistry/mozart/mo_waccm_hrates.F90 | 117 ++++++++++-- 10 files changed, 465 insertions(+), 203 deletions(-) diff --git a/.gitignore b/.gitignore index 54898a9808..130f252e2c 100644 --- a/.gitignore +++ b/.gitignore @@ -15,8 +15,7 @@ src/dynamics/fv3/atmos_cubed_sphere libraries/FMS libraries/mct libraries/parallelio -libraries/musica -libraries/json-fortran +libraries/tuv-x src/atmos_phys src/dynamics/mpas/dycore share diff --git a/Externals_CAM.cfg b/Externals_CAM.cfg index e7866ae5af..6ca13f4613 100644 --- a/Externals_CAM.cfg +++ b/Externals_CAM.cfg @@ -71,18 +71,11 @@ sparse = ../.mpas_sparse_checkout hash = b8c33daa required = True -[musica] -local_path = libraries/musica +[TUV-x] +local_path = libraries/tuv-x protocol = git -repo_url = https://github.com/NCAR/musica.git -branch = develop-update-tuvx -required = True - -[json-fortran] -local_path = libraries/json-fortran -protocol = git -repo_url = https://github.com/jacobwilliams/json-fortran.git -tag = 8.2.1 +repo_url = https://github.com/NCAR/tuv-x.git +hash = b771af381777b7689d9da56995fb5b5fdd33be65 required = True [hemco] diff --git a/cime_config/buildlib b/cime_config/buildlib index 9cd718f59f..c07ef02ad9 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -151,96 +151,19 @@ def _cmake_default_args(caseroot): return arg_dict ############################################################################### -def _build_json_fortran(caseroot, libroot, bldroot): +def _build_tuvx(caseroot, libroot, bldroot): ############################################################################### -# Builds the json-fortran 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, "json-fortran") - if not os.path.exists(bldpath): - os.makedirs(bldpath) - srcpath = os.path.abspath(os.path.join(case.get_value("COMP_ROOT_DIR_ATM"), \ - "libraries", "json-fortran", "")) - logger.info("Building json-fortran in {} from source in {}\n".format(bldpath, srcpath)) - - arg_dict = _cmake_default_args(caseroot) - cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["SFC"]) - cmake_args += "-DCMAKE_C_COMPILER_WORKS=1 " - cmake_args += "-DCMAKE_CXX_COMPILER_WORKS=1 " - cmake_args += "-DCMAKE_BUILD_TYPE=Release " - cmake_args += "-DSKIP_DOC_GEN:BOOL=TRUE " - cmake_args += "-DCMAKE_INSTALL_PREFIX='{}' ".format(libroot) - cmake_args += srcpath - - _run_cmd("cmake {}".format(cmake_args), bldpath) - _run_cmd(case.get_value("GMAKE"), bldpath) - _run_cmd("{} install".format(case.get_value("GMAKE")), bldpath) - - # add json-fortran to include paths - incldir = os.environ.get('USER_INCLDIR') - if incldir is None: - incldir = '' - os.environ['USER_INCLDIR'] = incldir + \ - " -I{} ".format(_json_fortran_include_dir(libroot)) - - # create simlink to library in folder CIME expects libraries to be in - dst = os.path.join(libroot, "libjsonfortran.a") - if os.path.isfile(dst): - os.remove(dst) - os.symlink(_json_fortran_lib_path(libroot), dst) - -############################################################################### -def _json_fortran_include_dir(libroot): -############################################################################### -# Returns the path to the json-fortran include directory - - jsoninc = os.path.join(_json_fortran_install_dir(libroot), "lib", "") - expect(os.path.exists(jsoninc), \ - "JSON-Fortran include directory not found at {}".format(jsoninc)) - return jsoninc - -############################################################################### -def _json_fortran_lib_path(libroot): -############################################################################### -# Returns the path to the json-fortran library - - jsonlib = os.path.join(_json_fortran_install_dir(libroot), "lib", "libjsonfortran.a") - expect(os.path.exists(jsonlib), \ - "JSON-Fortran library not found at {}".format(jsonlib)) - return jsonlib - -############################################################################### -def _json_fortran_install_dir(libroot): -############################################################################### -# Returns the path to the json-fortran install directory - - jsonpaths = glob(os.path.join(libroot, "jsonfortran*")) - expect(len(jsonpaths)>0, \ - "JSON-Fortran not found at {}".format(libroot)) - expect(len(jsonpaths)<2, \ - "Multiple JSON-Fortran versions found at {}".format(libroot)) - expect(os.path.exists(jsonpaths[0]), \ - "JSON-Fortran install directory not found at {}".format(jsonpaths[0])) - return jsonpaths[0] - -############################################################################### -def _build_musica(caseroot, libroot, bldroot): -############################################################################### -# Builds the musica library, including TUV-x, musica, and MICM -# and updates the case variables used to set the include paths -# and linked libraries +# Builds the TUV-x library and updates the case variables used to set the +# include paths and linked libraries build = EnvBuild(case_root=caseroot) with Case(caseroot) as case: - bldpath = os.path.join(bldroot, "musica") + bldpath = os.path.join(bldroot, "tuv-x") if not os.path.exists(bldpath): os.makedirs(bldpath) - jsoninc = _json_fortran_include_dir(libroot) - jsonlib = _json_fortran_lib_path(libroot) srcpath = os.path.abspath(os.path.join(case.get_value("COMP_ROOT_DIR_ATM"), \ - "libraries", "musica", "")) - logger.info("Building musica in {} from source in {}\n".format(bldpath, srcpath)) + "libraries", "tuv-x", "")) + logger.info("Building TUV-x in {} from source in {}\n".format(bldpath, srcpath)) arg_dict = _cmake_default_args(caseroot) if build.get_value("MPILIB") == "mpi-serial": @@ -255,12 +178,8 @@ def _build_musica(caseroot, libroot, bldroot): cmake_args += "-DENABLE_UTIL_ONLY=ON " cmake_args += "-DCMAKE_C_COMPILER_WORKS=1 " cmake_args += "-DCMAKE_CXX_COMPILER_WORKS=1 " - cmake_args += "-DENABLE_MICM=OFF " - cmake_args += "-DENABLE_TUVX=ON " cmake_args += "-DENABLE_TESTS=OFF " cmake_args += "-DENABLE_COVERAGE=OFF " - cmake_args += "-DJSON_INCLUDE_DIR={} ".format(jsoninc) - cmake_args += "-DJSON_LIB={} ".format(jsonlib) cmake_args += "-DCMAKE_Fortran_FLAGS='{}' ".format(arg_dict["FFLAGS"]) cmake_args += "-DCMAKE_INSTALL_PREFIX='{}' ".format(libroot) cmake_args += srcpath @@ -269,67 +188,67 @@ def _build_musica(caseroot, libroot, bldroot): _run_cmd(case.get_value("GMAKE"), bldpath) _run_cmd("{} install".format(case.get_value("GMAKE")), bldpath) - # add musica to include paths + # add TUV-x to include paths incldir = os.environ.get('USER_INCLDIR') if incldir is None: incldir = '' os.environ['USER_INCLDIR'] = incldir + \ - " -I{} ".format(_musica_include_dir(libroot)) + " -I{} ".format(_tuvx_include_dir(libroot)) # create simlink to library in folder CIME expects libraries to be in - dst = os.path.join(libroot, "libmusica.a") + dst = os.path.join(libroot, "libtuvx.a") if os.path.isfile(dst): os.remove(dst) - os.symlink(_musica_lib_path(libroot), dst) + os.symlink(_tuvx_lib_path(libroot), dst) ############################################################################### -def _musica_include_dir(libroot): +def _tuvx_include_dir(libroot): ############################################################################### -# Returns the path to the musica include directory +# Returns the path to the TUV-x include directory - coreinc = os.path.join(_musica_install_dir(libroot), "include", "") + coreinc = os.path.join(_tuvx_install_dir(libroot), "include", "") expect(os.path.exists(coreinc), \ - "musica include directory not found at {}".format(coreinc)) + "TUV-x include directory not found at {}".format(coreinc)) return coreinc ############################################################################### -def _musica_lib_path(libroot): +def _tuvx_lib_path(libroot): ############################################################################### -# Returns the path to the musica library +# Returns the path to the TUV-x library - corelib = os.path.join(_musica_install_dir(libroot), "lib64", "libmusica.a") + corelib = os.path.join(_tuvx_install_dir(libroot), "lib64", "libtuvx.a") if not os.path.exists(corelib): - corelib = os.path.join(_musica_install_dir(libroot), "lib", "libmusica.a") + corelib = os.path.join(_tuvx_install_dir(libroot), "lib", "libtuvx.a") expect(os.path.exists(corelib), \ - "musica library not found at {}".format(corelib)) + "TUV-x library not found at {}".format(corelib)) return corelib ############################################################################### -def _musica_install_dir(libroot): +def _tuvx_install_dir(libroot): ############################################################################### -# Returns the path to the musica install directory +# Returns the path to the TUV-x install directory - corepaths = glob(os.path.join(libroot, "musica*")) + corepaths = glob(os.path.join(libroot, "tuvx*")) expect(len(corepaths)>0, \ - "musica not found at {}".format(libroot)) + "TUV-x not found at {}".format(libroot)) expect(len(corepaths)<2, \ - "Multiple musica versions found at {}".format(libroot)) + "Multiple TUV-x versions found at {}".format(libroot)) expect(os.path.exists(corepaths[0]), \ - "musica install directory not found at {}".format(corepaths[0])) + "TUV-x install directory not found at {}".format(corepaths[0])) return corepaths[0] ############################################################################### -def _musica_package_dir(libroot): +def _tuvx_package_dir(libroot): ############################################################################### -# Returns the path to the musica CMake package +# Returns the path to the TUV-x CMake package - paths = glob(os.path.join(libroot, "musica*", "cmake", "musica*" )) + paths = glob(os.path.join(libroot, "tuvx*", "cmake", "tuvx*" )) expect(len(paths)>0, \ - "musica package not found at {}".format(libroot)) + "TUV-x package not found at {}".format(libroot)) expect(len(paths)<2, \ - "Multiple musica versions found at {}".format(libroot)) + "Multiple TUV-x versions found at {}".format(libroot)) expect(os.path.exists(paths[0]), \ - "musica package directory not found at {}".format(paths[0])) + "TUV-x package directory not found at {}".format(paths[0])) return paths[0] ############################################################################### @@ -337,8 +256,7 @@ def _musica_package_dir(libroot): def _main_func(): caseroot, libroot, bldroot = parse_input(sys.argv) - _build_json_fortran(caseroot, libroot, bldroot) - _build_musica(caseroot, libroot, bldroot) + _build_tuvx(caseroot, libroot, bldroot) _build_cam(caseroot, libroot, bldroot) diff --git a/cime_config/buildnml b/cime_config/buildnml index 0e66d457e5..bebcd94426 100755 --- a/cime_config/buildnml +++ b/cime_config/buildnml @@ -215,8 +215,8 @@ def buildnml(case, caseroot, compname): dest_data = os.path.join(rundir, "data") if os.path.exists(dest_data): shutil.rmtree(dest_data) - shutil.copytree(os.path.join(srcroot, "libraries", "musica", "lib", \ - "tuv-x", "data"), dest_data) + shutil.copytree(os.path.join(srcroot, "libraries", "tuv-x", "data"), \ + dest_data) shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_MOZART.json"), \ os.path.join(rundir, "tuvx_MOZART.json")) shutil.copy2(os.path.join(srcroot, "cime_config", "tuvx_MOZART_TS1.json"), \ diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index 1d0cf001ab..80d39d7510 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -206,7 +206,7 @@ char - -lmusica -ljsonfortran + -ltuvx build_component_cam env_build.xml diff --git a/cime_config/tuvx_MOZART_TS1.json b/cime_config/tuvx_MOZART_TS1.json index 5a37185017..82b5d0175c 100644 --- a/cime_config/tuvx_MOZART_TS1.json +++ b/cime_config/tuvx_MOZART_TS1.json @@ -93,12 +93,16 @@ "value": 1.0 } ] + }, + "heating" : { + "energy term": 175.05 } }, { "name": "jo2_b", "__reaction": "O2 + hv -> O + O", "cross section": { + "apply O2 bands": true, "netcdf files": [ { "file path": "data/cross_sections/O2_1.nc", @@ -121,6 +125,9 @@ "value": 0.0 } ] + }, + "heating" : { + "energy term": 242.37 } }, { @@ -137,6 +144,9 @@ }, "quantum yield": { "type": "O3+hv->O2+O(1D)" + }, + "heating" : { + "energy term": 310.32 } }, { @@ -153,6 +163,9 @@ }, "quantum yield": { "type": "O3+hv->O2+O(3P)" + }, + "heating" : { + "energy term": 1179.87 } }, { @@ -185,31 +198,121 @@ "name": "jn2o5_a", "__reaction": "N2O5 + hv -> NO2 + NO3", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/N2O5_1.nc" }, - { "file path": "data/cross_sections/N2O5_2.nc" } - ], - "type": "N2O5+hv->NO2+NO3" - }, - "quantum yield": { - "type": "base", - "netcdf files": [ "data/quantum_yields/N2O5_NO3_NO2.nc" ] - } + "type":"temperature based", + "netcdf file": "data/cross_sections/N2O5_JPL06.nc", + "parameterization": { + "type": "HARWOOD", + "aa": [ -18.27, -18.42, -18.59, -18.72, -18.84, + -18.90, -18.93, -18.87, -18.77, -18.71, + -18.31, -18.14, -18.01, -18.42, -18.59, + -18.13 ], + "bb": [ -91.0, -104.0, -112.0, -135.0, -170.0, + -226.0, -294.0, -388.0, -492.0, -583.0, + -770.0, -885.0, -992.0, -949.0, -966.0, + -1160.0 ], + "base temperature": 0.0, + "base wavelength": 0.0, + "logarithm": "base 10", + "minimum wavelength": 260.0, + "maximum wavelength": 410.0, + "temperature ranges": [ + { + "maximum": 199.999999999999, + "fixed value": 200 + }, + { + "minimum": 200, + "maximum": 295 + }, + { + "minimum": 295.00000000001, + "fixed value": 295.0 + } + ] + }, + "parameterization wavelength grid": { + "name": "custom wavelengths", + "type": "from config file", + "units": "nm", + "values": [ + 255.0, 265.0, 275.0, 285.0, 295.0, 305.0, + 315.0, 325.0, 335.0, 345.0, 355.0, 365.0, + 375.0, 385.0, 395.0, 405.0, 415.0 + ] + } + }, + "quantum yield": { + "type": "Taylor series", + "constant value": 0.0, + "coefficients": [ -2.832441, 0.012809638 ], + "override bands": [ + { + "band": "range", + "minimum wavelength": 300.0, + "value": 1.0 + } + ] + } }, { "name": "jn2o5_b", "__reaction": "N2O5 + hv -> NO + O + NO3", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/N2O5_1.nc" }, - { "file path": "data/cross_sections/N2O5_2.nc" } - ], - "type": "N2O5+hv->NO2+NO3" - }, - "quantum yield": { - "type": "base", - "netcdf files": [ "data/quantum_yields/N2O5_NO3_NO_O.nc" ] - } + "type":"temperature based", + "netcdf file": "data/cross_sections/N2O5_JPL06.nc", + "parameterization": { + "type": "HARWOOD", + "aa": [ -18.27, -18.42, -18.59, -18.72, -18.84, + -18.90, -18.93, -18.87, -18.77, -18.71, + -18.31, -18.14, -18.01, -18.42, -18.59, + -18.13 ], + "bb": [ -91.0, -104.0, -112.0, -135.0, -170.0, + -226.0, -294.0, -388.0, -492.0, -583.0, + -770.0, -885.0, -992.0, -949.0, -966.0, + -1160.0 ], + "base temperature": 0.0, + "base wavelength": 0.0, + "logarithm": "base 10", + "minimum wavelength": 260.0, + "maximum wavelength": 410.0, + "temperature ranges": [ + { + "maximum": 199.999999999999, + "fixed value": 200 + }, + { + "minimum": 200, + "maximum": 295 + }, + { + "minimum": 295.00000000001, + "fixed value": 295.0 + } + ] + }, + "parameterization wavelength grid": { + "name": "custom wavelengths", + "type": "from config file", + "units": "nm", + "values": [ + 255.0, 265.0, 275.0, 285.0, 295.0, 305.0, + 315.0, 325.0, 335.0, 345.0, 355.0, 365.0, + 375.0, 385.0, 395.0, 405.0, 415.0 + ] + } + }, + "quantum yield": { + "type": "Taylor series", + "constant value": 0.0, + "coefficients": [ 3.832441, -0.012809638 ], + "override bands": [ + { + "band": "range", + "minimum wavelength": 300.0, + "value": 0.0 + } + ] + } }, { "name": "jhno3", @@ -375,35 +478,34 @@ "__reaction": "CH3COCH3 + hv -> CH3CO + CH3", "cross section": { "type": "temperature based", - "netcdf file": "data/cross_sections/CH3CL_JPL06.nc", "parameterization": { - "AA": [ -299.80, 5.1047, -3.3630e-2, 9.5805e-5, -1.0135e-7 ], - "BB": [ -7.1727, 1.4837e-1, -1.1463e-3, 3.9188e-6, -4.9994e-9 ], - "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], - "minimum wavelength": 174.1, - "maximum wavelength": 216.0, - "base temperature": 273.0, - "base wavelength": 0.0, - "logarithm": "base 10", + "type": "TAYLOR_SERIES", + "netcdf file": { + "file path": "data/cross_sections/ACETONE_JPL06.nc" + }, + "base temperature": 0.0, "temperature ranges": [ { - "maximum": 209.999999999999, - "fixed value": 210.0 + "maximum": 234.999999999999, + "fixed value": 235.0 }, { - "minimum": 210, - "maximum": 300 + "minimum": 235.0, + "maximum": 298.0 }, { - "minimum": 300.00000000001, - "fixed value": 300.0 + "minimum": 298.00000000001, + "fixed value": 298.0 } ] } }, "quantum yield": { - "type": "base", - "constant value": 1.0 + "type": "CH3COCH3+hv->CH3CO+CH3", + "branch": "CO+CH3CO", + "low wavelength value": 1, + "minimum temperature": 218, + "maximum temperature": 295 } }, { @@ -433,7 +535,7 @@ "constant value": 0.5 } }, - { +{ "name": "jglyoxal", "__reaction": "GLYOXAL + hv -> 2*CO + 2*HO2", "__comments": "TODO the products of this reaction don't exactly match", @@ -859,15 +961,37 @@ "name": "jch3cl", "__reaction": "CH3Cl + hv -> Products", "cross section": { - "netcdf files": [ - { "file path": "data/cross_sections/CH3Cl_1.nc" } - ], - "type": "tint" - }, - "quantum yield": { + "type": "temperature based", + "netcdf file": "data/cross_sections/CH3CL_JPL06.nc", + "parameterization": { + "AA": [ -299.80, 5.1047, -3.3630e-2, 9.5805e-5, -1.0135e-7 ], + "BB": [ -7.1727, 1.4837e-1, -1.1463e-3, 3.9188e-6, -4.9994e-9 ], + "lp": [ 0.0, 1.0, 2.0, 3.0, 4.0 ], + "minimum wavelength": 174.1, + "maximum wavelength": 216.0, + "base temperature": 273.0, + "base wavelength": 0.0, + "logarithm": "base 10", + "temperature ranges": [ + { + "maximum": 209.999999999999, + "fixed value": 210.0 + }, + { + "minimum": 210, + "maximum": 300 + }, + { + "minimum": 300.00000000001, + "fixed value": 300.0 + } + ] + } + }, + "quantum yield": { "type": "base", "constant value": 1.0 - } + } }, { "name": "jchbr3", @@ -1607,6 +1731,8 @@ ] }, "__CAM options": { + "disable clouds": true, + "disable aerosols": true, "aliasing": { "default matching": "backup", "pairs": [ diff --git a/src/chemistry/mozart/chemistry.F90 b/src/chemistry/mozart/chemistry.F90 index a0ff675169..9557907f1f 100644 --- a/src/chemistry/mozart/chemistry.F90 +++ b/src/chemistry/mozart/chemistry.F90 @@ -312,13 +312,12 @@ subroutine chem_register call register_cfc11star() if ( waccmx_is('ionosphere') ) then - if( tuvx_active ) then - call tuvx_register( ) - else + if( .not. tuvx_active ) then call photo_register( ) end if call aurora_register() endif + call tuvx_register( ) ! add fields to pbuf needed by aerosol models call aero_model_register() diff --git a/src/chemistry/mozart/mo_chemini.F90 b/src/chemistry/mozart/mo_chemini.F90 index 2d550a79b1..a13de223b9 100644 --- a/src/chemistry/mozart/mo_chemini.F90 +++ b/src/chemistry/mozart/mo_chemini.F90 @@ -204,7 +204,7 @@ subroutine chemini & ! ... initialize the TUV-x photolysis rate constant calculator !----------------------------------------------------------------------- if( tuvx_active ) then - call tuvx_init( photon_file, electron_file, photo_max_zen ) + call tuvx_init( photon_file, electron_file, photo_max_zen, pbuf2d ) if (masterproc) write(iulog,*) 'chemini: after tuvx_init on node ',iam end if diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 52c45aadf0..c83daffe9c 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -5,7 +5,7 @@ module mo_tuvx use musica_map, only : map_t use musica_string, only : string_t - use ppgrid, only : pver ! number of vertical layers + use ppgrid, only : pver,pverp ! number of vertical layers 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 @@ -50,6 +50,17 @@ module mo_tuvx real(kind=r8), parameter :: WAVELENGTH_EDGES_MS93(NUM_BINS_MS93+1) = & (/ 181.6_r8, 183.1_r8, 184.6_r8, 190.2_r8, 192.5_r8 /) + ! Heating rate indices + integer :: number_of_heating_rates = 0 ! number of heating rates in TUV-x + integer :: index_cpe_jo2_a = -1 ! index for jo2_a in heating rate array + integer :: index_cpe_jo2_b = -1 ! index for jo2_b in heating rate array + integer :: index_cpe_jo3_a = -1 ! index for jo3_a in heating rate array + integer :: index_cpe_jo3_b = -1 ! index for jo3_b in heating rate array + integer :: cpe_jo2_a_pbuf_index = -1 ! index in physics buffer for jo2_a heating rate + integer :: cpe_jo2_b_pbuf_index = -1 ! index in physics buffer for jo2_b heating rate + integer :: cpe_jo3_a_pbuf_index = -1 ! index in physics buffer for jo3_a heating rate + integer :: cpe_jo3_b_pbuf_index = -1 ! index in physics buffer for jo3_b heating rate + ! Information needed to access CAM species state data logical :: is_fixed_N2 = .false. ! indicates whether N2 concentrations are fixed logical :: is_fixed_O = .false. ! indicates whether O concentrations are fixed @@ -81,7 +92,7 @@ module mo_tuvx integer :: jno_index = 0 ! Index in tuvx_ptr::photo_rates_ array for jno ! Cutoff solar zenith angle for doing photolysis rate calculations [degrees] - integer :: max_sza = 0.0_r8 + real(r8) :: max_sza = 0.0_r8 ! TODO how should these paths be set and communicated to this wrapper? character(len=*), parameter :: wavelength_config_path = & @@ -140,10 +151,17 @@ subroutine tuvx_register( ) use physics_buffer, only : pbuf_add_field, dtype_r8 use ppgrid, only : pcols ! maximum number of columns + if( .not. tuvx_active ) return + ! add photo-ionization rates to physics buffer for WACCMX Ionosphere module call pbuf_add_field( 'IonRates', 'physpkg', dtype_r8, (/ pcols, pver, nIonRates /), & ion_rates_pbuf_index ) ! Ionization rates for O+, O2+, N+, N2+, NO+ + call pbuf_add_field( 'CPE_jO2a', 'global', dtype_r8, (/ pcols, pver /), cpe_jo2_a_pbuf_index ) + call pbuf_add_field( 'CPE_jO2b', 'global', dtype_r8, (/ pcols, pver /), cpe_jo2_b_pbuf_index ) + call pbuf_add_field( 'CPE_jO3a', 'global', dtype_r8, (/ pcols, pver /), cpe_jo3_a_pbuf_index ) + call pbuf_add_field( 'CPE_jO3b', 'global', dtype_r8, (/ pcols, pver /), cpe_jo3_b_pbuf_index ) + end subroutine tuvx_register !================================================================================================ @@ -211,12 +229,14 @@ end subroutine tuvx_readnl !----------------------------------------------------------------------- ! Initializes TUV-x for photolysis calculations !----------------------------------------------------------------------- - subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) + subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle, pbuf2d ) #ifdef HAVE_MPI use mpi #endif + use cam_history, only : addfld use cam_logfile, only : iulog ! log file output unit + 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 use musica_assert, only : assert_msg, die_msg @@ -226,6 +246,8 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) 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 shr_const_mod, only : pi => shr_const_pi use solar_irrad_data, only : has_spectrum @@ -236,11 +258,13 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) use tuvx_grid_warehouse, only : grid_warehouse_t use tuvx_profile_warehouse, only : profile_warehouse_t use tuvx_radiator_warehouse, only : radiator_warehouse_t + use time_manager, only : is_first_step 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 character(len=*), parameter :: my_name = "TUV-x wrapper initialization" class(core_t), pointer :: core @@ -249,6 +273,7 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) type(config_t) :: tuvx_config, cam_config, map_config type(map_t) :: map class(grid_t), pointer :: height + class(grid_t), pointer :: wavelength class(grid_warehouse_t), pointer :: cam_grids class(profile_warehouse_t), pointer :: cam_profiles class(radiator_warehouse_t), pointer :: cam_radiators @@ -257,11 +282,25 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) type(string_t) :: required_keys(1), optional_keys(2) logical, save :: is_initialized = .false. + type(string_t), allocatable :: labels(:) + character(len=16) :: label + integer :: i + real(r8) :: nanval + if( .not. tuvx_active ) return if( is_initialized ) return is_initialized = .true. - ! call die_msg( 121631567, "TUV-x is not yet ready for use in CAM" ) + nanval=nan + + call pbuf_set_field( pbuf2d, ion_rates_pbuf_index, nanval ) + + if( is_first_step( ) ) then + call pbuf_set_field( pbuf2d, cpe_jo2_a_pbuf_index, 0.0_r8 ) + call pbuf_set_field( pbuf2d, cpe_jo2_b_pbuf_index, 0.0_r8 ) + call pbuf_set_field( pbuf2d, cpe_jo3_a_pbuf_index, 0.0_r8 ) + call pbuf_set_field( pbuf2d, cpe_jo3_b_pbuf_index, 0.0_r8 ) + end if if( is_main_task ) write(iulog,*) "Beginning TUV-x Initialization" @@ -419,7 +458,43 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle ) ! ============================================ call initialize_diagnostics( tuvx_ptrs( 1 ) ) - if( is_main_task ) call log_initialization( ) + ! =============================== + ! set up map to CAM heating rates + ! =============================== + labels = tuvx_ptrs(1)%core_%heating_rate_labels( ) + do i = 1, size( labels ) + label = trim( labels( i )%to_char( ) ) + select case( label ) + case( 'jo2_a' ) + index_cpe_jo2_a = i + number_of_heating_rates = number_of_heating_rates + 1 + call addfld('CPE_jO2a',(/ 'lev' /), 'A', 'joules sec-1', & + trim(label)//' chemical potential energy') + case( 'jo2_b' ) + index_cpe_jo2_b = i + number_of_heating_rates = number_of_heating_rates + 1 + call addfld('CPE_jO2b',(/ 'lev' /), 'A', 'joules sec-1', & + trim(label)//' chemical potential energy') + case( 'jo3_a' ) + index_cpe_jo3_a = i + number_of_heating_rates = number_of_heating_rates + 1 + call addfld('CPE_jO3a',(/ 'lev' /), 'A', 'joules sec-1', & + trim(label)//' chemical potential energy') + case( 'jo3_b' ) + index_cpe_jo3_b = i + number_of_heating_rates = number_of_heating_rates + 1 + call addfld('CPE_jO3b',(/ 'lev' /), 'A', 'joules sec-1', & + trim(label)//' chemical potential energy') + end select + end do + call assert_msg( 398372957, & + number_of_heating_rates == size( labels ), & + "TUV-x heating rate mismatch. Expected "// & + trim( to_char( size( labels ) )// & + " rates, but only matched "// & + trim( to_char( number_of_heating_rates ) )//"." ) ) + + if( is_main_task ) call log_initialization( labels ) end subroutine tuvx_init @@ -453,6 +528,7 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & earth_sun_distance, pressure_delta, cloud_fraction, liquid_water_content, & photolysis_rates ) + use cam_history, only : outfld use cam_logfile, only : iulog ! log info output unit use chem_mods, only : phtcnt, & ! number of photolysis reactions gas_pcnst, & ! number of non-fixed species @@ -460,6 +536,7 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & nabscol ! number of absorbing species (radiators) use physics_types, only : physics_state use physics_buffer, only : physics_buffer_desc + use physics_buffer, only : pbuf_get_field use ppgrid, only : pcols ! maximum number of columns use shr_const_mod, only : pi => shr_const_pi use spmd_utils, only : main_task => masterprocid, & @@ -492,9 +569,25 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & integer :: i_col ! column index integer :: i_level ! vertical level index real(r8) :: sza ! solar zenith angle [degrees] + real(r8) :: cpe_rates(ncol,pverp+1,number_of_heating_rates) ! heating rates from TUV-x + real(r8), pointer :: cpe_jo2_a(:,:) ! heating rate for jo2_a in physics buffer + real(r8), pointer :: cpe_jo2_b(:,:) ! heating rate for jo2_b in physics buffer + real(r8), pointer :: cpe_jo3_a(:,:) ! heating rate for jo3_a in physics buffer + real(r8), pointer :: cpe_jo3_b(:,:) ! heating rate for jo3_b in physics buffer if( .not. tuvx_active ) return + call pbuf_get_field(pbuf, cpe_jo2_a_pbuf_index, cpe_jo2_a) + call pbuf_get_field(pbuf, cpe_jo2_b_pbuf_index, cpe_jo2_b) + call pbuf_get_field(pbuf, cpe_jo3_a_pbuf_index, cpe_jo3_a) + call pbuf_get_field(pbuf, cpe_jo3_b_pbuf_index, cpe_jo3_b) + + cpe_rates(:,:,:) = 0.0_r8 + cpe_jo2_a(:,:) = 0.0_r8 + cpe_jo2_b(:,:) = 0.0_r8 + cpe_jo3_a(:,:) = 0.0_r8 + cpe_jo3_b(:,:) = 0.0_r8 + associate( tuvx => tuvx_ptrs( thread_id( ) ) ) tuvx%photo_rates_(:,:,:) = 0.0_r8 @@ -533,7 +626,8 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & call tuvx%core_%run( solar_zenith_angle = sza, & earth_sun_distance = earth_sun_distance, & photolysis_rate_constants = & - tuvx%photo_rates_(i_col,:,1:tuvx%n_photo_rates_) ) + tuvx%photo_rates_(i_col,:,1:tuvx%n_photo_rates_), & + heating_rates = cpe_rates(i_col,:,:) ) ! ============================== ! Calculate the extreme-UV rates @@ -582,6 +676,40 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & end associate + if (index_cpe_jo2_a>0) then + do i_level = 1, pver + cpe_jo2_a(:ncol,i_level) = & + 0.5_r8 * ( cpe_rates(:ncol,pver-i_level+2,index_cpe_jo2_a) & + + cpe_rates(:ncol,pver-i_level+1,index_cpe_jo2_a) ) + end do + call outfld('CPE_jO2a', cpe_jo2_a(:ncol,:), ncol, lchnk ) + end if + if (index_cpe_jo2_b>0) then + do i_level = 1, pver + cpe_jo2_b(:ncol,i_level) = & + 0.5_r8 * ( cpe_rates(:ncol,pver-i_level+2,index_cpe_jo2_b) & + + cpe_rates(:ncol,pver-i_level+1,index_cpe_jo2_b) ) + end do + call outfld('CPE_jO2b', cpe_jo2_b(:ncol,:), ncol, lchnk ) + end if + + if (index_cpe_jo3_a>0) then + do i_level = 1, pver + cpe_jo3_a(:ncol,i_level) = & + 0.5_r8 * ( cpe_rates(:ncol,pver-i_level+2,index_cpe_jo3_a) & + + cpe_rates(:ncol,pver-i_level+1,index_cpe_jo3_a) ) + end do + call outfld('CPE_jO3a', cpe_jo3_a(:ncol,:), ncol, lchnk ) + end if + if (index_cpe_jo3_b>0) then + do i_level = 1, pver + cpe_jo3_b(:ncol,i_level) = & + 0.5_r8 * ( cpe_rates(:ncol,pver-i_level+2,index_cpe_jo3_b) & + + cpe_rates(:ncol,pver-i_level+1,index_cpe_jo3_b) ) + end do + call outfld('CPE_jO3b', cpe_jo3_b(:ncol,:), ncol, lchnk ) + end if + end subroutine tuvx_get_photo_rates !================================================================================================ @@ -652,13 +780,17 @@ end function max_threads !----------------------------------------------------------------------- ! Prints initialization conditions to the log file !----------------------------------------------------------------------- - subroutine log_initialization( ) + subroutine log_initialization( heating_rate_labels ) use cam_logfile, only : iulog ! log info output unit use musica_string, only : to_char use spmd_utils, only : main_task => masterprocid, & is_main_task => masterproc + type(string_t), intent(in) :: heating_rate_labels(:) ! heating rate labels + + integer :: i + if( is_main_task ) then write(iulog,*) "Initialized TUV-x" #ifdef HAVE_MPI @@ -692,6 +824,12 @@ subroutine log_initialization( ) if( do_euv ) write(iulog,*) " - doing Extreme-UV calculations" if( do_jno ) write(iulog,*) " - including special jno rate calculation" write(iulog,*) " - max solar zenith angle [degrees]:", max_sza + if( size( heating_rate_labels ) > 0 ) then + write(iulog,*) " - with heating rates:" + do i = 1, size( heating_rate_labels ) + write(iulog,*) " - "//trim( heating_rate_labels( i )%to_char( ) ) + end do + end if end if end subroutine log_initialization @@ -1171,7 +1309,6 @@ subroutine create_updaters( this, grids, profiles, radiators, disable_aerosols, call rad_cnst_get_info( 0, nmodes = n_modes ) if( n_modes > 0 .and. .not. do_aerosol .and. .not. disable_aerosols ) then do_aerosol = .true. - do_aerosol = .false. ! temporarily disable aerosols ! TODO update to use new aerosol_optics class ! call modal_aer_opt_init( ) else @@ -1705,7 +1842,8 @@ subroutine calculate_euv_rates( solar_zenith_angle, fixed_species_conc, & ! ======================= height_arg(:) = height_mid(:) - call jeuv( pver, solar_zenith_angle, o_dens, o2_dens, n2_dens, height_arg, euv_rates ) + call jeuv( pver, solar_zenith_angle, o_dens, o2_dens, n2_dens, height_arg, & + euv_rates(pver:1:-1,:) ) end subroutine calculate_euv_rates @@ -1803,7 +1941,7 @@ subroutine calculate_jno( solar_zenith_angle, et_flux, fixed_species_conc, speci ! calculate the NO photolysis rate constant ! ========================================= call calc_jno( pver+1, et_flux, n2_dens, o2_slant, o3_slant, no_slant, work_jno ) - jno(:) = work_jno(:pver) + jno(:) = work_jno(pver:1:-1) end subroutine calculate_jno diff --git a/src/chemistry/mozart/mo_waccm_hrates.F90 b/src/chemistry/mozart/mo_waccm_hrates.F90 index 4f368c749f..322360d94c 100644 --- a/src/chemistry/mozart/mo_waccm_hrates.F90 +++ b/src/chemistry/mozart/mo_waccm_hrates.F90 @@ -25,6 +25,11 @@ module mo_waccm_hrates logical :: has_hrates integer :: ele_temp_ndx, ion_temp_ndx + integer :: cpe_jo2a_ndx = -1 + integer :: cpe_jo2b_ndx = -1 + integer :: cpe_jo3a_ndx = -1 + integer :: cpe_jo3b_ndx = -1 + contains subroutine init_hrates( ) @@ -81,9 +86,34 @@ subroutine init_hrates( ) attr = 'total jo2 euv photolysis rate' call addfld( 'JO2_EUV', (/ 'lev' /), 'I', '/s', trim(attr) ) + attr = 'O2 + hv -> O1D + O3P tuvx photo-chem heating rate' + call addfld( 'QRS_O2A_tuvx', (/ 'lev' /), 'I', 'K/s', trim(attr) ) + attr = 'O2 + hv -> O3P + O3P tuvx photo-chem heating rate' + call addfld( 'QRS_O2B_tuvx', (/ 'lev' /), 'I', 'K/s', trim(attr) ) + attr = 'O3 + hv -> O1D + O2_1S tuvx photo-chem heating rate' + call addfld( 'QRS_O3A_tuvx', (/ 'lev' /), 'I', 'K/s', trim(attr) ) + attr = 'O3 + hv -> O3P + O2 tuvx photo-chem heating rate' + call addfld( 'QRS_O3B_tuvx', (/ 'lev' /), 'I', 'K/s', trim(attr) ) + + + attr = 'O2 + hv -> O1D + O3P table photo-chem heating rate' + call addfld( 'QRS_O2A_tabl', (/ 'lev' /), 'I', 'K/s', trim(attr) ) + attr = 'O2 + hv -> O3P + O3P table photo-chem heating rate' + call addfld( 'QRS_O2B_tabl', (/ 'lev' /), 'I', 'K/s', trim(attr) ) + attr = 'O3 + hv -> O1D + O2_1S table photo-chem heating rate' + call addfld( 'QRS_O3A_tabl', (/ 'lev' /), 'I', 'K/s', trim(attr) ) + attr = 'O3 + hv -> O3P + O2 table photo-chem heating rate' + call addfld( 'QRS_O3B_tabl', (/ 'lev' /), 'I', 'K/s', trim(attr) ) + + ele_temp_ndx = pbuf_get_index('TElec',errcode=err)! electron temperature index ion_temp_ndx = pbuf_get_index('TIon',errcode=err) ! ion temperature index + cpe_jo2a_ndx = pbuf_get_index('CPE_jO2a',errcode=err) + cpe_jo2b_ndx = pbuf_get_index('CPE_jO2b',errcode=err) + cpe_jo3a_ndx = pbuf_get_index('CPE_jO3a',errcode=err) + cpe_jo3b_ndx = pbuf_get_index('CPE_jO3b',errcode=err) + end subroutine init_hrates subroutine waccm_hrates(ncol, state, asdir, bot_mlt_lev, qrs_tot, pbuf ) @@ -124,6 +154,9 @@ subroutine waccm_hrates(ncol, state, asdir, bot_mlt_lev, qrs_tot, pbuf ) use phys_control, only : waccmx_is use orbit, only : zenith + use physconst, only : avogad + use mo_tuvx, only : tuvx_active + !----------------------------------------------------------------------- ! ... dummy arguments !----------------------------------------------------------------------- @@ -161,6 +194,14 @@ subroutine waccm_hrates(ncol, state, asdir, bot_mlt_lev, qrs_tot, pbuf ) real(r8) :: qrl_col(pver,4) ! column thermal heating > 200nm real(r8) :: qrs(ncol,pver,4) ! chunk thermal heating < 200nm real(r8) :: qrl(ncol,pver,4) ! chunk thermal heating > 200nm + + real(r8) :: qr_jo2a_tuvx(ncol,pver) ! heating rates (K/s) + real(r8) :: qr_jo2b_tuvx(ncol,pver) ! heating rates (K/s) + real(r8) :: qr_jo3a_tuvx(ncol,pver) ! heating rates (K/s) + real(r8) :: qr_jo3b_tuvx(ncol,pver) ! heating rates (K/s) + + real(r8) :: hfactor(pver) + real(r8) :: euv_hrate_col(pver) ! column euv thermal heating rate real(r8) :: co2_hrate_col(pver) ! column co2 nir heating rate real(r8) :: euv_hrate(ncol,pver) ! chunk euv thermal heating rate @@ -196,6 +237,11 @@ subroutine waccm_hrates(ncol, state, asdir, bot_mlt_lev, qrs_tot, pbuf ) real(r8), pointer :: ele_temp_fld(:,:) ! electron temperature pointer real(r8), pointer :: ion_temp_fld(:,:) ! ion temperature pointer + real(r8), pointer :: cpe_jo2a(:,:) ! chemical potential energy + real(r8), pointer :: cpe_jo2b(:,:) ! chemical potential energy + real(r8), pointer :: cpe_jo3a(:,:) ! chemical potential energy + real(r8), pointer :: cpe_jo3b(:,:) ! chemical potential energy + if ( ele_temp_ndx>0 .and. ion_temp_ndx>0 ) then call pbuf_get_field(pbuf, ele_temp_ndx, ele_temp_fld) call pbuf_get_field(pbuf, ion_temp_ndx, ion_temp_fld) @@ -204,6 +250,11 @@ subroutine waccm_hrates(ncol, state, asdir, bot_mlt_lev, qrs_tot, pbuf ) ion_temp_fld => state%t endif + if (cpe_jo2a_ndx > 0) call pbuf_get_field(pbuf, cpe_jo2a_ndx, cpe_jo2a) + if (cpe_jo2b_ndx > 0) call pbuf_get_field(pbuf, cpe_jo2b_ndx, cpe_jo2b) + if (cpe_jo3a_ndx > 0) call pbuf_get_field(pbuf, cpe_jo3a_ndx, cpe_jo3a) + if (cpe_jo3b_ndx > 0) call pbuf_get_field(pbuf, cpe_jo3b_ndx, cpe_jo3b) + qrs_tot(:ncol,:) = 0._r8 if (.not. has_hrates) return @@ -343,6 +394,12 @@ subroutine waccm_hrates(ncol, state, asdir, bot_mlt_lev, qrs_tot, pbuf ) qrl(:,k,m) = 0._r8 end do end do + + qr_jo2a_tuvx = 0._r8 + qr_jo2b_tuvx = 0._r8 + qr_jo3a_tuvx = 0._r8 + qr_jo3b_tuvx = 0._r8 + do k = 1,pver euv_hrate(:,k) = 0._r8 co2_hrate(:,k) = 0._r8 @@ -372,18 +429,29 @@ subroutine waccm_hrates(ncol, state, asdir, bot_mlt_lev, qrs_tot, pbuf ) mw(:) = mbar(i,:) cparg(:) = cpair(i,:) do_diag = .false. + call jshort( pver, sza, o2_line, o3_line, o2cc, & o3cc, tline, zarg, mw, qrs_col, & cparg, lchnk, i, co2cc, scco2, do_diag ) - call jlong( pver, sza, eff_alb, parg, tline, & - mw, o2_line, o3_line, colo3, qrl_col, & - cparg, kbot_hrates ) - do m = 1,4 - qrs(i,pver:1:-1,m) = qrs_col(:,m) * esfact - end do - do m = 2,4 - qrl(i,:,m) = qrl_col(:,m) * esfact - end do + + if (tuvx_active) then + hfactor(:) = avogad/(cparg(:)*mw(:)) + qr_jo2a_tuvx(i,:) = cpe_jo2a(i,:) * hfactor(:) * o2_line(:) * esfact + qr_jo2b_tuvx(i,:) = cpe_jo2b(i,:) * hfactor(:) * o2_line(:) * esfact + qr_jo3a_tuvx(i,:kbot_hrates) = cpe_jo3a(i,:kbot_hrates) * hfactor(:kbot_hrates) * o3_line(:kbot_hrates) * esfact + qr_jo3b_tuvx(i,:kbot_hrates) = cpe_jo3b(i,:kbot_hrates) * hfactor(:kbot_hrates) * o3_line(:kbot_hrates) * esfact + else + call jlong( pver, sza, eff_alb, parg, tline, & + mw, o2_line, o3_line, colo3, qrl_col, & + cparg, kbot_hrates ) + do m = 1,4 + qrs(i,pver:1:-1,m) = qrs_col(:,m) * esfact + end do + do m = 2,4 + qrl(i,:,m) = qrl_col(:,m) * esfact + end do + end if + call heuv( pver, sza, occ, o2cc, n2cc, & o_line, o2_line, n2_line, cparg, mw, & zarg, euv_hrate_col, kbot_hrates ) @@ -403,6 +471,8 @@ subroutine waccm_hrates(ncol, state, asdir, bot_mlt_lev, qrs_tot, pbuf ) write(iulog,*) '===================================' #endif co2_hrate(i,:kbot_hrates) = co2_hrate_col(:kbot_hrates) * esfact * daypsec + + end if end do column_loop @@ -414,10 +484,23 @@ subroutine waccm_hrates(ncol, state, asdir, bot_mlt_lev, qrs_tot, pbuf ) call outfld( 'QRS_LO2B', qrl(:,:,2), ncol, lchnk ) call outfld( 'QRS_LO3A', qrl(:,:,3), ncol, lchnk ) call outfld( 'QRS_LO3B', qrl(:,:,4), ncol, lchnk ) + call outfld( 'QRS_LO3', qrl(:,:,3)+qrl(:,:,4), ncol, lchnk ) call outfld( 'QRS_EUV', euv_hrate(:,:), ncol, lchnk ) call outfld( 'QRS_CO2NIR', co2_hrate(:,:), ncol, lchnk ) + if (tuvx_active) then + call outfld( 'QRS_O2A_tuvx', qr_jo2a_tuvx(:ncol,:), ncol, lchnk ) + call outfld( 'QRS_O2B_tuvx', qr_jo2b_tuvx(:ncol,:), ncol, lchnk ) + call outfld( 'QRS_O3A_tuvx', qr_jo3a_tuvx(:ncol,:), ncol, lchnk ) + call outfld( 'QRS_O3B_tuvx', qr_jo3b_tuvx(:ncol,:), ncol, lchnk ) + else + call outfld( 'QRS_O2A_tabl', qrs(:,:,1)+qrl(:,:,1), ncol, lchnk ) + call outfld( 'QRS_O2B_tabl', qrs(:,:,2)+qrl(:,:,2), ncol, lchnk ) + call outfld( 'QRS_O3A_tabl', qrs(:,:,3)+qrl(:,:,3), ncol, lchnk ) + call outfld( 'QRS_O3B_tabl', qrs(:,:,4)+qrl(:,:,4), ncol, lchnk ) + endif + !----------------------------------------------------------------------- ! ... chemical pot heating rate !----------------------------------------------------------------------- @@ -436,7 +519,7 @@ subroutine waccm_hrates(ncol, state, asdir, bot_mlt_lev, qrs_tot, pbuf ) call outfld( 'QRS_AUR', aur_hrate(:,:), ncol, lchnk ) !----------------------------------------------------------------------- -! ... airglow heating rate +! ... airglow heating rate -- for diagnostic output only within airglow !----------------------------------------------------------------------- call airglow( aghrate, vmr(1,1,id_o2_1s), vmr(1,1,id_o2_1d), vmr(1,1,id_o1d), reaction_rates, cpair, & ncol, lchnk ) @@ -444,11 +527,17 @@ subroutine waccm_hrates(ncol, state, asdir, bot_mlt_lev, qrs_tot, pbuf ) !----------------------------------------------------------------------- ! ... form total heating rate !----------------------------------------------------------------------- - do k = 1,kbot_hrates - qrs_tot(:ncol,k) = qrs(:,k,1) + qrs(:,k,2) + qrs(:,k,3) + qrs(:,k,4) & - + qrl(:,k,1) + qrl(:,k,2) + qrl(:,k,3) + qrl(:,k,4) - end do + if (tuvx_active) then + qrs_tot(:ncol,:kbot_hrates) = qr_jo2a_tuvx(:ncol,:kbot_hrates) + qr_jo2b_tuvx(:ncol,:kbot_hrates) & + + qr_jo3a_tuvx(:ncol,:kbot_hrates) + qr_jo3b_tuvx(:ncol,:kbot_hrates) + else + do k = 1,kbot_hrates + qrs_tot(:ncol,k) = qrs(:,k,1) + qrs(:,k,2) + qrs(:,k,3) + qrs(:,k,4) & + + qrl(:,k,1) + qrl(:,k,2) + qrl(:,k,3) + qrl(:,k,4) + end do + end if call outfld( 'QTHERMAL', qrs_tot, pcols, lchnk ) + do k = 1,kbot_hrates qrs_tot(:ncol,k) = qrs_tot(:ncol,k) & + cphrate(:,k) + euv_hrate(:,k) + aur_hrate(:,k) + co2_hrate(:,k) From 3e4c77bed8dcf9ceedda91bbd8b08e63e74da2e5 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 22 Mar 2024 12:51:11 -0600 Subject: [PATCH 79/84] remove working arrays from tuvx wrapper class --- src/chemistry/mozart/mo_tuvx.F90 | 204 +++++++++++++++---------------- 1 file changed, 102 insertions(+), 102 deletions(-) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index c83daffe9c..6cf2c027c9 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -5,7 +5,8 @@ module mo_tuvx use musica_map, only : map_t use musica_string, only : string_t - use ppgrid, only : pver,pverp ! number of vertical layers + 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 @@ -75,17 +76,17 @@ module mo_tuvx ! Information needed to access aerosol and cloud optical properties logical :: do_aerosol = .false. ! indicates whether aerosol optical properties - ! are available and should be used in radiative - ! transfer calculations + ! are available and should be used in radiative + ! transfer calculations logical :: do_clouds = .false. ! indicates whether cloud optical properties - ! should be calculated and used in radiative - ! transfer calculations + ! should be calculated and used in radiative + ! transfer calculations ! Information needed to set extended-UV photo rates - logical :: do_euv = .false. ! Indicates whether to calculate - ! extended-UV photo rates - integer :: ion_rates_pbuf_index = 0 ! Index in physics buffer for - ! ionization rates + logical :: do_euv = .false. ! Indicates whether to calculate + ! extended-UV photo rates + integer :: ion_rates_pbuf_index = 0 ! Index in physics buffer for + ! ionization rates ! Information needed to do special NO photolysis rate calculation logical :: do_jno = .false. ! Indicates whether to calculate jno @@ -105,8 +106,7 @@ module mo_tuvx integer :: n_photo_rates_ = 0 ! number of photo reactions in TUV-x integer :: n_euv_rates_ = 0 ! number of extreme-UV rates integer :: n_special_rates_ = 0 ! number of special photo rates - real(r8), allocatable :: photo_rates_(:,:,:) ! photolysis rate constants - ! (column, vertical level, reaction) [s-1] + integer :: n_wavelength_bins_ = 0 ! number of wavelength bins in TUV-x type(map_t) :: photo_rate_map_ ! map between TUV-x and CAM ! photo rate constant arrays type(grid_updater_t) :: grids_(NUM_GRIDS) ! grid updaters @@ -115,13 +115,7 @@ module mo_tuvx real(r8) :: height_delta_(pver+1) ! change in height in each ! vertical layer (km) real(r8), allocatable :: wavelength_edges_(:) ! TUV-x wavelength bin edges (nm) - real(r8), allocatable :: wavelength_values_(:) ! Working array for interface values ! on the TUV-x wavelength grid - real(r8), allocatable :: wavelength_mid_values_(:) ! Working array for mid-point values - ! on the TUV-x wavelength grid - real(r8), allocatable :: optical_depth_(:,:,:) ! (column, vertical level, wavelength) [unitless] - real(r8), allocatable :: single_scattering_albedo_(:,:,:) ! (column, vertical level, wavelength) [unitless] - real(r8), allocatable :: asymmetry_factor_(:,:,:) ! (column, vertical level, wavelength) [unitless] real(r8) :: et_flux_ms93_(NUM_BINS_MS93) ! extraterrestrial flux on the MS93 grid ! [photon cm-2 nm-1 s-1] end type tuvx_ptr @@ -410,17 +404,12 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle, pbuf2d call create_updaters( tuvx, cam_grids, cam_profiles, cam_radiators, & disable_aerosols, disable_clouds ) - ! =============================================================== - ! Create a working array for calculated photolysis rate constants - ! =============================================================== + ! ============================================ + ! Save the number of photolysis rate constants + ! ============================================ tuvx%n_photo_rates_ = tuvx%core_%number_of_photolysis_reactions( ) if( do_euv ) tuvx%n_euv_rates_ = neuv if( do_jno ) tuvx%n_special_rates_ = tuvx%n_special_rates_ + 1 - height => tuvx%core_%get_grid( "height", "km" ) - allocate( tuvx%photo_rates_( pcols, height%ncells_ + 1, & - tuvx%n_photo_rates_ + tuvx%n_euv_rates_ + & - tuvx%n_special_rates_ ) ) - deallocate( height ) end associate end do @@ -575,6 +564,12 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & real(r8), pointer :: cpe_jo3_a(:,:) ! heating rate for jo3_a in physics buffer real(r8), pointer :: cpe_jo3_b(:,:) ! heating rate for jo3_b in physics buffer + ! working arrays + real(r8), allocatable :: photo_rates(:,:,:) ! calculated photo rate constants (column, level, reaction) [s-1] + real(r8), allocatable :: optical_depth(:,:,:) ! aerosol optical depth (column, level, wavelength) [unitless] + real(r8), allocatable :: single_scattering_albedo(:,:,:) ! aerosol single scattering albedo (column, level, wavelength) [unitless] + real(r8), allocatable :: asymmetry_factor(:,:,:) ! aerosol asymmetry factor (column, level, wavelength) [unitless] + if( .not. tuvx_active ) return call pbuf_get_field(pbuf, cpe_jo2_a_pbuf_index, cpe_jo2_a) @@ -590,12 +585,18 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & associate( tuvx => tuvx_ptrs( thread_id( ) ) ) - tuvx%photo_rates_(:,:,:) = 0.0_r8 + allocate( photo_rates( pcols, pver+2, tuvx%n_photo_rates_ + tuvx%n_euv_rates_ & + + tuvx%n_special_rates_ ) ) + allocate( optical_depth( pcols, pver+1, tuvx%n_wavelength_bins_ ) ) + allocate( single_scattering_albedo( pcols, pver+1, tuvx%n_wavelength_bins_ ) ) + allocate( asymmetry_factor( pcols, pver+1, tuvx%n_wavelength_bins_ ) ) + photo_rates(:,:,:) = 0.0_r8 ! ============================================== ! set aerosol optical properties for all columns ! ============================================== - call get_aerosol_optical_properties( tuvx, state, pbuf ) + call get_aerosol_optical_properties( tuvx, state, pbuf, optical_depth, & + single_scattering_albedo, asymmetry_factor ) do i_col = 1, ncol @@ -618,7 +619,8 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & call set_radiator_profiles( tuvx, i_col, ncol, fixed_species_conc, & species_vmr, exo_column_conc, & pressure_delta(1:ncol,:), cloud_fraction, & - liquid_water_content ) + liquid_water_content, optical_depth, & + single_scattering_albedo, asymmetry_factor) ! =================================================== ! Calculate photolysis rate constants for this column @@ -626,7 +628,7 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & call tuvx%core_%run( solar_zenith_angle = sza, & earth_sun_distance = earth_sun_distance, & photolysis_rate_constants = & - tuvx%photo_rates_(i_col,:,1:tuvx%n_photo_rates_), & + photo_rates(i_col,:,1:tuvx%n_photo_rates_), & heating_rates = cpe_rates(i_col,:,:) ) ! ============================== @@ -640,7 +642,7 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & species_vmr(i_col,:,:), & height_mid(i_col,:), & height_int(i_col,:), & - tuvx%photo_rates_(i_col,2:pver+1,euv_begin:euv_end) ) + photo_rates(i_col,2:pver+1,euv_begin:euv_end) ) end associate end if @@ -653,26 +655,26 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & fixed_species_conc(i_col,:,:), & species_vmr(i_col,:,:), & height_int(i_col,:), & - tuvx%photo_rates_(i_col,2:pver+1,jno_index) ) + photo_rates(i_col,2:pver+1,jno_index) ) end if end do ! ===================== ! Filter negative rates ! ===================== - tuvx%photo_rates_(:,:,:) = max( 0.0_r8, tuvx%photo_rates_(:,:,:) ) + photo_rates(:,:,:) = max( 0.0_r8, photo_rates(:,:,:) ) ! ============================================ ! Return the photolysis rates on the CAM grids ! ============================================ do i_col = 1, ncol do i_level = 1, pver - call tuvx%photo_rate_map_%apply( tuvx%photo_rates_(i_col,pver-i_level+2,:), & + call tuvx%photo_rate_map_%apply( photo_rates(i_col,pver-i_level+2,:), & photolysis_rates(i_col,i_level,:) ) end do end do - call output_diagnostics( tuvx, ncol, lchnk ) + call output_diagnostics( tuvx, ncol, lchnk, photo_rates ) end associate @@ -1016,13 +1018,15 @@ end subroutine set_photo_rate_map !----------------------------------------------------------------------- ! Outputs diagnostic information for the current time step !----------------------------------------------------------------------- - subroutine output_diagnostics( this, ncol, lchnk ) + subroutine output_diagnostics( this, ncol, lchnk, photo_rates ) use cam_history, only : outfld type(tuvx_ptr), intent(in) :: this integer, intent(in) :: ncol ! number of active columns on this thread integer, intent(in) :: lchnk ! identifier for this thread + real(r8), intent(in) :: photo_rates(:,:,:) ! photo rate constants + ! (column, level, reaction) [s-1] integer :: i_diag @@ -1030,7 +1034,7 @@ subroutine output_diagnostics( this, ncol, lchnk ) do i_diag = 1, size( diagnostics ) associate( diag => diagnostics( i_diag ) ) - call outfld( "tuvx_"//diag%name_, this%photo_rates_(:ncol,pver+1:2:-1,diag%index_), & + call outfld( "tuvx_"//diag%name_, photo_rates(:ncol,pver+1:2:-1,diag%index_), & ncol, lchnk ) end associate end do @@ -1249,18 +1253,10 @@ subroutine create_updaters( this, grids, profiles, radiators, disable_aerosols, ! wavelength grid cannot be updated at runtime ! ============================================ wavelength => grids%get_grid( "wavelength", "nm" ) - allocate( this%wavelength_edges_( wavelength%size( ) + 1 ) ) - allocate( this%wavelength_values_( wavelength%size( ) + 1 ) ) - allocate( this%wavelength_mid_values_( wavelength%size( ) ) ) + this%n_wavelength_bins_ = wavelength%size( ) + allocate( this%wavelength_edges_( this%n_wavelength_bins_ + 1 ) ) this%wavelength_edges_(:) = wavelength%edge_(:) - ! ============================== - ! optical property working array - ! ============================== - allocate( this%optical_depth_( pcols, height%size( ), wavelength%size( ) ) ) - allocate( this%single_scattering_albedo_( pcols, height%size( ), wavelength%size( ) ) ) - allocate( this%asymmetry_factor_( pcols, height%size( ), wavelength%size( ) ) ) - deallocate( height ) deallocate( wavelength ) @@ -1311,12 +1307,6 @@ subroutine create_updaters( this, grids, profiles, radiators, disable_aerosols, do_aerosol = .true. ! TODO update to use new aerosol_optics class ! call modal_aer_opt_init( ) - else - ! TODO are there default aerosol optical properties that should be used - ! when an aerosol module is not available? - this%optical_depth_(:,:,:) = 0.0_r8 - this%single_scattering_albedo_(:,:,:) = 0.0_r8 - this%asymmetry_factor_(:,:,:) = 0.0_r8 end if host_radiator => radiators%get_radiator( "aerosol" ) this%radiators_( RADIATOR_INDEX_AEROSOL ) = & @@ -1437,9 +1427,11 @@ subroutine set_surface_albedo( this, i_col, surface_albedo ) integer, intent(in) :: i_col ! column to set conditions for real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) - this%wavelength_values_(:) = surface_albedo( i_col ) + real(r8) :: albedos(this%n_wavelength_bins_ + 1) + + albedos(:) = surface_albedo( i_col ) call this%profiles_( PROFILE_INDEX_ALBEDO )%update( & - edge_values = this%wavelength_values_(:) ) + edge_values = albedos(:) ) end subroutine set_surface_albedo @@ -1458,52 +1450,50 @@ subroutine set_et_flux( this ) use mo_util, only : rebin use solar_irrad_data, only : nbins, & ! number of wavelength bins - we, & ! wavelength bin edges - sol_etf ! extraterrestrial flux - ! (photon cm-2 nm-1 s-1) + we, & ! wavelength bin edges + sol_etf ! extraterrestrial flux + ! (photon cm-2 nm-1 s-1) class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator - real(r8) :: et_flux_orig(nbins) - integer :: n_tuvx_bins, i_bin + real(r8) :: et_flux_orig(nbins), tuv_mid_values(this%n_wavelength_bins_), & + tuv_edge_values(this%n_wavelength_bins_ + 1) + integer :: i_bin ! =============================================== ! regrid normalized flux to TUV-x wavelength grid !================================================ et_flux_orig(:) = sol_etf(:) - n_tuvx_bins = size(this%wavelength_mid_values_) - call rebin( nbins, n_tuvx_bins, we, this%wavelength_edges_, et_flux_orig, & - this%wavelength_mid_values_ ) + call rebin( nbins, this%n_wavelength_bins_, we, this%wavelength_edges_, & + et_flux_orig, tuv_mid_values ) ! ======================================================== ! convert normalized flux to flux on TUV-x wavelength grid ! ======================================================== - this%wavelength_mid_values_(:) = this%wavelength_mid_values_(:) * & - ( this%wavelength_edges_(2:n_tuvx_bins+1) - & - this%wavelength_edges_(1:n_tuvx_bins) ) + tuv_mid_values(:) = tuv_mid_values(:) * & + ( this%wavelength_edges_(2:this%n_wavelength_bins_+1) - & + this%wavelength_edges_(1:this%n_wavelength_bins_) ) ! ==================================== ! estimate unused edge values for flux ! ==================================== - this%wavelength_values_(1) = this%wavelength_mid_values_(1) - & - ( this%wavelength_mid_values_(2) - & - this%wavelength_mid_values_(1) ) * 0.5_r8 - do i_bin = 2, n_tuvx_bins - this%wavelength_values_(i_bin) = this%wavelength_mid_values_(i_bin-1) + & - ( this%wavelength_mid_values_(i_bin) - & - this%wavelength_mid_values_(i_bin-1) ) * 0.5_r8 + tuv_edge_values(1) = tuv_mid_values(1) - & + ( tuv_mid_values(2) - tuv_mid_values(1) ) * 0.5_r8 + do i_bin = 2, this%n_wavelength_bins_ + tuv_edge_values(i_bin) = tuv_mid_values(i_bin-1) + & + ( tuv_mid_values(i_bin) - tuv_mid_values(i_bin-1) ) * 0.5_r8 end do - this%wavelength_values_(n_tuvx_bins+1) = & - this%wavelength_mid_values_(n_tuvx_bins) + & - ( this%wavelength_mid_values_(n_tuvx_bins) - & - this%wavelength_mid_values_(n_tuvx_bins-1) ) * 0.5_r8 + tuv_edge_values(this%n_wavelength_bins_+1) = & + tuv_mid_values(this%n_wavelength_bins_) + & + ( tuv_mid_values(this%n_wavelength_bins_) - & + tuv_mid_values(this%n_wavelength_bins_-1) ) * 0.5_r8 ! ============================ ! update TUV-x ET flux profile ! ============================ call this%profiles_( PROFILE_INDEX_ET_FLUX )%update( & - mid_point_values = this%wavelength_mid_values_, & - edge_values = this%wavelength_values_) + mid_point_values = tuv_mid_values, & + edge_values = tuv_edge_values) ! ====================================================================== ! rebin extraterrestrial flux to MS93 grid for use with jno calculations @@ -1526,13 +1516,14 @@ end subroutine set_et_flux ! and pre-calculated values for O2 and O3 !----------------------------------------------------------------------- subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species_vmr, & - exo_column_conc, delta_pressure, cloud_fraction, & - liquid_water_content ) + exo_column_conc, delta_pressure, cloud_fraction, liquid_water_content, & + optical_depth, single_scattering_albedo, asymmetry_factor ) use chem_mods, only : gas_pcnst, & ! number of non-fixed species nfs, & ! number of fixed species nabscol, & ! number of absorbing species (radiators) indexm ! index for air density in fixed species array + use ppgrid, only : pcols ! maximum number of columns class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator integer, intent(in) :: i_col ! column to set conditions for @@ -1546,10 +1537,13 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species real(r8), intent(in) :: delta_pressure(ncol,pver) ! pressure delta about midpoints (Pa) real(r8), intent(in) :: cloud_fraction(ncol,pver) ! cloud fraction (unitless) real(r8), intent(in) :: liquid_water_content(ncol,pver) ! liquid water content (kg/kg) + real(r8), intent(in) :: optical_depth(pcols, pver+1, this%n_wavelength_bins_) ! aerosol optical depth [unitless] + real(r8), intent(in) :: single_scattering_albedo(pcols, pver+1, this%n_wavelength_bins_) ! single scattering albedo [unitless] + real(r8), intent(in) :: asymmetry_factor(pcols, pver+1, this%n_wavelength_bins_) ! asymmetry factor [unitless] integer :: i_level real(r8) :: tmp(pver) - real(r8) :: tau(pver+1, size(this%wavelength_mid_values_)) + real(r8) :: tau(pver+1, this%n_wavelength_bins_) real(r8) :: edges(pver+2), densities(pver+1) real(r8) :: exo_val real(r8), parameter :: rgrav = 1.0_r8 / 9.80616_r8 ! reciprocal of acceleration by gravity (s/m) @@ -1629,9 +1623,9 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species ! =============== if( do_aerosol ) then call this%radiators_( RADIATOR_INDEX_AEROSOL )%update( & - optical_depths = this%optical_depth_(i_col,:,:), & - single_scattering_albedos = this%single_scattering_albedo_(i_col,:,:), & - asymmetry_factors = this%asymmetry_factor_(i_col,:,:) ) + optical_depths = optical_depth(i_col,:,:), & + single_scattering_albedos = single_scattering_albedo(i_col,:,:), & + asymmetry_factors = asymmetry_factor(i_col,:,:) ) end if ! ============= @@ -1665,7 +1659,8 @@ end subroutine set_radiator_profiles ! Updates working arrays of aerosol optical properties for all ! columns from the aerosol package !----------------------------------------------------------------------- - subroutine get_aerosol_optical_properties( this, state, pbuf ) + subroutine get_aerosol_optical_properties( this, state, pbuf, optical_depth, & + single_scattering_albedo, asymmetry_factor ) use aer_rad_props, only : aer_rad_props_sw use mo_util, only : rebin @@ -1678,6 +1673,9 @@ subroutine get_aerosol_optical_properties( this, state, pbuf ) class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator type(physics_state), target, intent(in) :: state type(physics_buffer_desc), pointer, intent(inout) :: pbuf(:) + real(r8), intent(out) :: optical_depth(pcols,pver+1,this%n_wavelength_bins_) ! aerosol optical depth [unitless] + real(r8), intent(out) :: single_scattering_albedo(pcols,pver+1,this%n_wavelength_bins_) ! aerosol single scattering albedo [unitless] + real(r8), intent(out) :: asymmetry_factor(pcols,pver+1,this%n_wavelength_bins_) ! aerosol asymmetry factor [unitless] real(r8) :: wavelength_edges(nswbands+1) ! CAM radiation wavelength grid edges [nm] real(r8) :: aer_tau (pcols,0:pver,nswbands) ! aerosol extinction optical depth @@ -1691,10 +1689,15 @@ subroutine get_aerosol_optical_properties( this, state, pbuf ) integer :: n_tuvx_bins ! number of TUV-x wavelength bins integer :: i_col, i_level ! column and level indices - ! ============================================ - ! do nothing if no aerosol module is available - ! ============================================ - if( .not. do_aerosol ) return + ! =================================================================== + ! return default optical properties if no aerosol module is available + ! =================================================================== + if( .not. do_aerosol ) then + optical_depth(:,:,:) = 0.0_r8 + single_scattering_albedo(:,:,:) = 0.0_r8 + asymmetry_factor(:,:,:) = 0.0_r8 + return + end if ! TODO just assume all daylight columns for now ! can adjust later if necessary @@ -1724,30 +1727,27 @@ subroutine get_aerosol_optical_properties( this, state, pbuf ) ! regrid optical properties to TUV-x wavelength and height grid ! ============================================================= ! TODO is this the correct regridding scheme to use? - n_tuvx_bins = size(this%wavelength_mid_values_) + n_tuvx_bins = this%n_wavelength_bins_ do i_col = 1, pcols do i_level = 1, pver call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & - aer_tau(i_col,pver-i_level,:), this%optical_depth_(i_col,i_level,:)) + aer_tau(i_col,pver-i_level,:), optical_depth(i_col,i_level,:)) call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & - aer_tau_w(i_col,pver-i_level,:), this%single_scattering_albedo_(i_col,i_level,:)) + aer_tau_w(i_col,pver-i_level,:), single_scattering_albedo(i_col,i_level,:)) call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & - aer_tau_w_g(i_col,pver-i_level,:), this%asymmetry_factor_(i_col,i_level,:)) + aer_tau_w_g(i_col,pver-i_level,:), asymmetry_factor(i_col,i_level,:)) end do - this%optical_depth_(i_col,pver+1,:) = & - this%optical_depth_(i_col,pver,:) - this%single_scattering_albedo_(i_col,pver+1,:) = & - this%single_scattering_albedo_(i_col,pver,:) - this%asymmetry_factor_(i_col,pver+1,:) = & - this%asymmetry_factor_(i_col,pver,:) + optical_depth(i_col,pver+1,:) = optical_depth(i_col,pver,:) + single_scattering_albedo(i_col,pver+1,:) = single_scattering_albedo(i_col,pver,:) + asymmetry_factor(i_col,pver+1,:) = asymmetry_factor(i_col,pver,:) end do ! ================================================================ ! back-calculate the single scattering albedo and asymmetry factor ! ================================================================ - associate( tau => this%optical_depth_, & - omega => this%single_scattering_albedo_, & - g => this%asymmetry_factor_ ) + associate( tau => optical_depth, & + omega => single_scattering_albedo, & + g => asymmetry_factor ) where(omega > 0.0_r8) g = g / omega elsewhere From a3669a516fb10eeabed586b1b6aa354e36f4a0bd Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 22 Mar 2024 13:47:04 -0600 Subject: [PATCH 80/84] clean up some comments --- src/chemistry/mozart/mo_tuvx.F90 | 96 ++++++++++++++++---------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index 6cf2c027c9..b5689e88aa 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -108,16 +108,16 @@ module mo_tuvx integer :: n_special_rates_ = 0 ! number of special photo rates integer :: n_wavelength_bins_ = 0 ! number of wavelength bins in TUV-x type(map_t) :: photo_rate_map_ ! map between TUV-x and CAM - ! photo rate constant arrays + ! photo rate constant arrays type(grid_updater_t) :: grids_(NUM_GRIDS) ! grid updaters type(profile_updater_t) :: profiles_(NUM_PROFILES) ! profile updaters type(radiator_updater_t) :: radiators_(NUM_RADIATORS) ! radiator updaters real(r8) :: height_delta_(pver+1) ! change in height in each - ! vertical layer (km) + ! vertical layer (km) real(r8), allocatable :: wavelength_edges_(:) ! TUV-x wavelength bin edges (nm) - ! on the TUV-x wavelength grid - real(r8) :: et_flux_ms93_(NUM_BINS_MS93) ! extraterrestrial flux on the MS93 grid - ! [photon cm-2 nm-1 s-1] + ! on the TUV-x wavelength grid + real(r8) :: et_flux_ms93_(NUM_BINS_MS93) ! extraterrestrial flux on the MS93 grid + ! [photon cm-2 nm-1 s-1] end type tuvx_ptr type(tuvx_ptr), allocatable :: tuvx_ptrs(:) @@ -172,7 +172,7 @@ subroutine tuvx_readnl(nlfile) use cam_logfile, only : iulog ! log file output unit use namelist_utils, only : find_group_name use spmd_utils, only : mpicom, is_main_task => masterproc, & - main_task => masterprocid + main_task => masterprocid character(len=*), intent(in) :: nlfile ! filepath for file containing namelist input @@ -236,9 +236,9 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle, pbuf2d use musica_assert, only : assert_msg, die_msg use musica_config, only : config_t use musica_mpi, only : musica_mpi_rank, & - musica_mpi_pack_size, & - musica_mpi_pack, & - musica_mpi_unpack + musica_mpi_pack_size, & + 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 @@ -246,8 +246,8 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle, pbuf2d use shr_const_mod, only : pi => shr_const_pi use solar_irrad_data, only : has_spectrum use spmd_utils, only : main_task => masterprocid, & - is_main_task => masterproc, & - mpicom + is_main_task => masterproc, & + mpicom use tuvx_grid, only : grid_t use tuvx_grid_warehouse, only : grid_warehouse_t use tuvx_profile_warehouse, only : profile_warehouse_t @@ -257,7 +257,7 @@ subroutine tuvx_init( photon_file, electron_file, max_solar_zenith_angle, pbuf2d 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] + ! photo rate calculations [degrees] type(physics_buffer_desc), pointer :: pbuf2d(:,:) ! Physics buffer character(len=*), parameter :: my_name = "TUV-x wrapper initialization" @@ -520,9 +520,9 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & use cam_history, only : outfld use cam_logfile, only : iulog ! log info output unit use chem_mods, only : phtcnt, & ! number of photolysis reactions - gas_pcnst, & ! number of non-fixed species - nfs, & ! number of fixed species - nabscol ! number of absorbing species (radiators) + gas_pcnst, & ! number of non-fixed species + nfs, & ! number of fixed species + nabscol ! number of absorbing species (radiators) use physics_types, only : physics_state use physics_buffer, only : physics_buffer_desc use physics_buffer, only : pbuf_get_field @@ -541,11 +541,11 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & real(r8), intent(in) :: temperature_mid(pcols,pver) ! midpoint temperature (K) real(r8), intent(in) :: surface_temperature(pcols) ! surface temperature (K) real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities - ! (molecule cm-3) + ! (molecule cm-3) real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing - ! ratios (mol mol-1) + ! ratios (mol mol-1) real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! layer column densities - ! (molecule cm-2) + ! (molecule cm-2) real(r8), intent(in) :: surface_albedo(pcols) ! surface albedo (unitless) real(r8), intent(in) :: solar_zenith_angle(ncol) ! solar zenith angle (radians) real(r8), intent(in) :: earth_sun_distance ! Earth-Sun distance (AU) @@ -553,7 +553,7 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & real(r8), intent(in) :: cloud_fraction(ncol,pver) ! cloud fraction (unitless) 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) + ! constants (1/s) integer :: i_col ! column index integer :: i_level ! vertical level index @@ -565,10 +565,10 @@ subroutine tuvx_get_photo_rates( state, pbuf, ncol, lchnk, height_mid, & real(r8), pointer :: cpe_jo3_b(:,:) ! heating rate for jo3_b in physics buffer ! working arrays - real(r8), allocatable :: photo_rates(:,:,:) ! calculated photo rate constants (column, level, reaction) [s-1] - real(r8), allocatable :: optical_depth(:,:,:) ! aerosol optical depth (column, level, wavelength) [unitless] + real(r8), allocatable :: photo_rates(:,:,:) ! calculated photo rate constants (column, level, reaction) [s-1] + real(r8), allocatable :: optical_depth(:,:,:) ! aerosol optical depth (column, level, wavelength) [unitless] real(r8), allocatable :: single_scattering_albedo(:,:,:) ! aerosol single scattering albedo (column, level, wavelength) [unitless] - real(r8), allocatable :: asymmetry_factor(:,:,:) ! aerosol asymmetry factor (column, level, wavelength) [unitless] + real(r8), allocatable :: asymmetry_factor(:,:,:) ! aerosol asymmetry factor (column, level, wavelength) [unitless] if( .not. tuvx_active ) return @@ -847,11 +847,11 @@ subroutine initialize_euv( photon_file, electron_file, do_euv ) use mo_jeuv, only : jeuv_init ! extreme-UV initialization character(len=*), intent(in) :: photon_file ! photon file used in extended-UV module - ! setup + ! setup character(len=*), intent(in) :: electron_file ! electron file used in extended-UV - ! module setup + ! module setup logical, intent(out) :: do_euv ! indicates whether extreme-UV - ! calculations are needed + ! calculations are needed integer, allocatable :: euv_index_map(:) @@ -914,9 +914,9 @@ subroutine set_photo_rate_map( core, config, do_euv, do_jno, jno_index, map ) use cam_logfile, only : iulog ! log info output unit use chem_mods, only : phtcnt, & ! number of photolysis reactions - rxt_tag_lst ! labels for all chemical reactions - ! NOTE photolysis reactions are - ! expected to appear first + rxt_tag_lst ! labels for all chemical reactions + ! NOTE photolysis reactions are + ! expected to appear first use mo_jeuv, only : neuv ! number of extreme-UV rates use musica_config, only : config_t use musica_string, only : string_t @@ -924,11 +924,11 @@ subroutine set_photo_rate_map( core, config, do_euv, do_jno, jno_index, map ) type(core_t), intent(in) :: core ! TUV-x core type(config_t), intent(inout) :: config ! CAM<->TUV-x map configuration logical, intent(in) :: do_euv ! indicates whether to include - ! extreme-UV rates in the mapping + ! extreme-UV rates in the mapping logical, intent(out) :: do_jno ! indicates whether jno should be - ! calculated + ! calculated integer, intent(out) :: jno_index ! index for jno in source photo - ! rate array + ! rate array type(map_t), intent(out) :: map integer :: i_label, i_start, i_end @@ -1520,20 +1520,20 @@ subroutine set_radiator_profiles( this, i_col, ncol, fixed_species_conc, species optical_depth, single_scattering_albedo, asymmetry_factor ) use chem_mods, only : gas_pcnst, & ! number of non-fixed species - nfs, & ! number of fixed species - nabscol, & ! number of absorbing species (radiators) - indexm ! index for air density in fixed species array + nfs, & ! number of fixed species + nabscol, & ! number of absorbing species (radiators) + indexm ! index for air density in fixed species array use ppgrid, only : pcols ! maximum number of columns class(tuvx_ptr), intent(inout) :: this ! TUV-x calculator integer, intent(in) :: i_col ! column to set conditions for integer, intent(in) :: ncol ! number of columns real(r8), intent(in) :: fixed_species_conc(ncol,pver,max(1,nfs)) ! fixed species densities - ! (molecule cm-3) + ! (molecule cm-3) real(r8), intent(in) :: species_vmr(ncol,pver,max(1,gas_pcnst)) ! species volume mixing - ! ratios (mol mol-1) + ! ratios (mol mol-1) real(r8), intent(in) :: exo_column_conc(ncol,0:pver,max(1,nabscol)) ! above column densities - ! (molecule cm-2) + ! (molecule cm-2) real(r8), intent(in) :: delta_pressure(ncol,pver) ! pressure delta about midpoints (Pa) real(r8), intent(in) :: cloud_fraction(ncol,pver) ! cloud fraction (unitless) real(r8), intent(in) :: liquid_water_content(ncol,pver) ! liquid water content (kg/kg) @@ -1794,16 +1794,16 @@ subroutine calculate_euv_rates( solar_zenith_angle, fixed_species_conc, & species_vmr, height_mid, height_int, euv_rates ) use chem_mods, only : gas_pcnst, & ! number of non-fixed species - nfs, & ! number of fixed species - indexm ! index for air density in fixed species array + nfs, & ! number of fixed species + indexm ! index for air density in fixed species array use mo_jeuv, only : jeuv, neuv ! number of extreme-UV rates use ref_pres, only : ptop_ref ! pressure at the top of the column (Pa) real(r8), intent(in) :: solar_zenith_angle ! degrees - real(r8), intent(in) :: fixed_species_conc(pver,max(1,nfs)) ! fixed species densities - ! (molecule cm-3) - real(r8), intent(in) :: species_vmr(pver,max(1,gas_pcnst)) ! species volume mixing - ! ratios (mol mol-1) + real(r8), intent(in) :: fixed_species_conc(pver,max(1,nfs)) ! fixed species densities + ! (molecule cm-3) + real(r8), intent(in) :: species_vmr(pver,max(1,gas_pcnst)) ! species volume mixing + ! ratios (mol mol-1) real(r8), intent(in) :: height_mid(pver) ! height at mid-points (km) real(r8), intent(in) :: height_int(pver+1) ! height at interfaces (km) real(r8), intent(out) :: euv_rates(pver,neuv) ! calculated extreme-UV rates @@ -1858,18 +1858,18 @@ subroutine calculate_jno( solar_zenith_angle, et_flux, fixed_species_conc, speci height_int, jno ) use chem_mods, only : gas_pcnst, & ! number of non-fixed species - nfs, & ! number of fixed species - indexm ! index for air density in fixed species array + nfs, & ! number of fixed species + indexm ! index for air density in fixed species array use mo_jshort, only : sphers, slant_col, calc_jno use ref_pres, only : ptop_ref ! pressure at the top of the column (Pa) real(r8), intent(in) :: solar_zenith_angle ! degrees real(r8), intent(in) :: et_flux(NUM_BINS_MS93) ! extraterrestrial flux MS93 grid - ! (photon cm-2 nm-1 s-1) + ! (photon cm-2 nm-1 s-1) real(r8), intent(in) :: fixed_species_conc(pver,max(1,nfs)) ! fixed species densities - ! (molecule cm-3) + ! (molecule cm-3) real(r8), intent(in) :: species_vmr(pver,max(1,gas_pcnst)) ! species volume mixing - ! ratios (mol mol-1) + ! ratios (mol mol-1) real(r8), intent(in) :: height_int(pver+1) ! height at interfaces (km) real(r8), intent(out) :: jno(pver) ! calculated NO rate From c12d1e46e0fdc1dccb0a651a6c9fefd6bb80b2ba Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 5 Apr 2024 15:42:52 -0700 Subject: [PATCH 81/84] Address issues with the Intel compiler and aerosol wavelength grid indexing (#10) * link to cpp std lib * fix CMake flag, update TUV-x, and fix aerosol wavelength grid use --- Externals_CAM.cfg | 2 +- cime_config/buildlib | 7 +++---- cime_config/config_component.xml | 2 +- src/chemistry/mozart/mo_tuvx.F90 | 21 +++++++++------------ 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/Externals_CAM.cfg b/Externals_CAM.cfg index 6ca13f4613..f22eec8b71 100644 --- a/Externals_CAM.cfg +++ b/Externals_CAM.cfg @@ -75,7 +75,7 @@ required = True local_path = libraries/tuv-x protocol = git repo_url = https://github.com/NCAR/tuv-x.git -hash = b771af381777b7689d9da56995fb5b5fdd33be65 +hash = d60e6c037ee0c481d629d5b9bf977f89a29d1b35 required = True [hemco] diff --git a/cime_config/buildlib b/cime_config/buildlib index c07ef02ad9..48767f2b43 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -170,16 +170,15 @@ def _build_tuvx(caseroot, libroot, bldroot): cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["SFC"]) else: cmake_args = "-DCMAKE_Fortran_COMPILER={} ".format(arg_dict["MPIFC"]) - cmake_args += "-DENABLE_MPI:BOOL=TRUE " + cmake_args += "-DTUVX_ENABLE_MPI:BOOL=TRUE " if case.get_value("DEBUG"): cmake_args += "-DCMAKE_BUILD_TYPE=Debug " else: cmake_args += "-DCMAKE_BUILD_TYPE=Release " - cmake_args += "-DENABLE_UTIL_ONLY=ON " cmake_args += "-DCMAKE_C_COMPILER_WORKS=1 " cmake_args += "-DCMAKE_CXX_COMPILER_WORKS=1 " - cmake_args += "-DENABLE_TESTS=OFF " - cmake_args += "-DENABLE_COVERAGE=OFF " + cmake_args += "-DTUVX_ENABLE_TESTS=OFF " + cmake_args += "-DTUVX_ENABLE_COVERAGE=OFF " cmake_args += "-DCMAKE_Fortran_FLAGS='{}' ".format(arg_dict["FFLAGS"]) cmake_args += "-DCMAKE_INSTALL_PREFIX='{}' ".format(libroot) cmake_args += srcpath diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index 80d39d7510..fc1ca74b8b 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -206,7 +206,7 @@ char - -ltuvx + -ltuvx -lstdc++ build_component_cam env_build.xml diff --git a/src/chemistry/mozart/mo_tuvx.F90 b/src/chemistry/mozart/mo_tuvx.F90 index b5689e88aa..8791053431 100644 --- a/src/chemistry/mozart/mo_tuvx.F90 +++ b/src/chemistry/mozart/mo_tuvx.F90 @@ -1716,9 +1716,9 @@ subroutine get_aerosol_optical_properties( this, state, pbuf, optical_depth, & ! last bin should be moved to the just before the first bin (!?!) ! ========================================================================= call get_sw_spectral_boundaries( low_bound, high_bound, 'nm' ) - wavelength_edges(1:nswbands-1) = high_bound(nswbands-1:1:-1) - wavelength_edges(nswbands ) = high_bound(nswbands ) - wavelength_edges(nswbands+1 ) = low_bound( nswbands ) + wavelength_edges(1:nswbands-1) = low_bound( nswbands-1:1:-1) + wavelength_edges(nswbands ) = low_bound( nswbands ) + wavelength_edges(nswbands+1 ) = high_bound(nswbands ) call reorder_optics_array( aer_tau ) call reorder_optics_array( aer_tau_w ) call reorder_optics_array( aer_tau_w_g ) @@ -1729,17 +1729,14 @@ subroutine get_aerosol_optical_properties( this, state, pbuf, optical_depth, & ! TODO is this the correct regridding scheme to use? n_tuvx_bins = this%n_wavelength_bins_ do i_col = 1, pcols - do i_level = 1, pver + do i_level = 1, pver + 1 call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & - aer_tau(i_col,pver-i_level,:), optical_depth(i_col,i_level,:)) + aer_tau(i_col,pver+1-i_level,:), optical_depth(i_col,i_level,:)) call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & - aer_tau_w(i_col,pver-i_level,:), single_scattering_albedo(i_col,i_level,:)) + aer_tau_w(i_col,pver+1-i_level,:), single_scattering_albedo(i_col,i_level,:)) call rebin(nswbands, n_tuvx_bins, wavelength_edges, this%wavelength_edges_, & - aer_tau_w_g(i_col,pver-i_level,:), asymmetry_factor(i_col,i_level,:)) + aer_tau_w_g(i_col,pver+1-i_level,:), asymmetry_factor(i_col,i_level,:)) end do - optical_depth(i_col,pver+1,:) = optical_depth(i_col,pver,:) - single_scattering_albedo(i_col,pver+1,:) = single_scattering_albedo(i_col,pver,:) - asymmetry_factor(i_col,pver+1,:) = asymmetry_factor(i_col,pver,:) end do ! ================================================================ @@ -1748,12 +1745,12 @@ subroutine get_aerosol_optical_properties( this, state, pbuf, optical_depth, & associate( tau => optical_depth, & omega => single_scattering_albedo, & g => asymmetry_factor ) - where(omega > 0.0_r8) + where(omega > 0.0_r8 .and. g > 0.0_r8) g = g / omega elsewhere g = 0.0_r8 end where - where(tau > 0.0_r8) + where(tau > 0.0_r8 .and. omega > 0.0_r8) omega = omega / tau elsewhere omega = 0.0_r8 From 15ba87b0012daf9a5f2d4739353f72572c506baf Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Wed, 11 Sep 2024 13:06:12 -0600 Subject: [PATCH 82/84] update tuvx build --- cime_config/buildlib | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cime_config/buildlib b/cime_config/buildlib index 48767f2b43..3a090c9688 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -181,6 +181,8 @@ def _build_tuvx(caseroot, libroot, bldroot): cmake_args += "-DTUVX_ENABLE_COVERAGE=OFF " cmake_args += "-DCMAKE_Fortran_FLAGS='{}' ".format(arg_dict["FFLAGS"]) cmake_args += "-DCMAKE_INSTALL_PREFIX='{}' ".format(libroot) + cmake_args += "-DTUVX_INSTALL_INCLUDE_DIR='{}' ".format(_tuvx_include_dir(libroot)) + cmake_args += "-DTUVX_INSTALL_MOD_DIR='{}' ".format(_tuvx_include_dir(libroot)) cmake_args += srcpath _run_cmd("cmake {}".format(cmake_args), bldpath) @@ -205,7 +207,7 @@ def _tuvx_include_dir(libroot): ############################################################################### # Returns the path to the TUV-x include directory - coreinc = os.path.join(_tuvx_install_dir(libroot), "include", "") + coreinc = os.path.join(libroot, "include", "") expect(os.path.exists(coreinc), \ "TUV-x include directory not found at {}".format(coreinc)) return coreinc From ace84498d7872bfac7b42999900e729d72bca4eb Mon Sep 17 00:00:00 2001 From: Matthew Dawson Date: Wed, 11 Sep 2024 13:06:55 -0600 Subject: [PATCH 83/84] update tuv-x commit --- Externals_CAM.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Externals_CAM.cfg b/Externals_CAM.cfg index f22eec8b71..8de90f77a0 100644 --- a/Externals_CAM.cfg +++ b/Externals_CAM.cfg @@ -75,7 +75,7 @@ required = True local_path = libraries/tuv-x protocol = git repo_url = https://github.com/NCAR/tuv-x.git -hash = d60e6c037ee0c481d629d5b9bf977f89a29d1b35 +hash = ccda76c0553064a9b7b0eba73162ddeee6d8eaff required = True [hemco] From 402be6eb614ab0ba9710c619dd9e46cef50984f4 Mon Sep 17 00:00:00 2001 From: Francis Vitt Date: Fri, 13 Sep 2024 16:58:23 -0600 Subject: [PATCH 84/84] change for DEBUG compiler flags --- cime_config/buildlib | 1 + 1 file changed, 1 insertion(+) diff --git a/cime_config/buildlib b/cime_config/buildlib index 3a090c9688..0b125e032b 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -138,6 +138,7 @@ def _cmake_default_args(caseroot): args += "-DCMAKE_C_COMPILER_WORKS=1 " args += "-DCMAKE_Fortran_COMPILER_WORKS=1 " args += "-DCMAKE_CXX_COMPILER_WORKS=1 " + args += "-DDEBUG={} ".format(build.get_value("DEBUG")) cmd = "cmake {} .".format(args) rc, out, err = run_cmd(cmd, combine_output=True, from_dir=macro_path) expect(rc == 0, "Command {} failed with rc={} out={} err={}".format(cmd, rc, out, err))