diff --git a/.gitignore b/.gitignore index 5aaa6e46e98..e21d4ad68e6 100644 --- a/.gitignore +++ b/.gitignore @@ -213,3 +213,9 @@ versions/run.ver # jcb checkout and symlinks ush/python/jcb + + +specs/ +.specify/ +.github/agents/ +.github/prompts/ \ No newline at end of file diff --git a/changelog.md b/changelog.md new file mode 100644 index 00000000000..10a593bc744 --- /dev/null +++ b/changelog.md @@ -0,0 +1,69 @@ +# Changelog + +All notable changes to global-workflow will be documented in this file. + +This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- CATChem interactive atmospheric chemistry for GCAFS via a new `APP=ATMC` + option (atmosphere + CATChem), mirroring the existing `ATMA` + (atmosphere + GOCART) forecast-only workflow: + - `dev/workflow/setup_expt.py` accepts `--app ATMC` for GCAFS. + - `dev/parm/config/gcafs/config.base.j2` recognizes `ATMC`, exports a + `CHEM_MODEL` selector (`none`/`gocart`/`catchem`), and widens the EnKF + APP suffix strip to `+([WAC])`. + - `dev/parm/config/gcafs/config.aero.j2` adds `CATCHEM_CONFIG` (default + `gcafs_aero`) and `CATCHEM_CONFIG_DIR`, mirroring `NEXUS_CONFIG`. + - `dev/parm/config/gcafs/config.fcst.j2` passes `--catchem` to `config.ufs` + and selects `FCSTEXEC="ufs_model_gcafs_catchem.x"` when + `CHEM_MODEL="catchem"`. + - `dev/parm/config/gcafs/config.ufs` parses `--catchem`, setting + `cplchm=".true."`, `cplcat=".true."`, and `chm_model="catchem"` while + reusing the `ufs.configure.atmaero` template. + - `ush/parsing_ufs_configure.sh` renders `chm_model`/`cplcat` into the UFS + configure file; `ush/parsing_namelists_fv3.sh` renders `cplcat` into + `global_control.nml`. + - `ush/forecast_predet.sh` and `ush/forecast_postdet.sh` gain + `CATCHEM_predet`, `CATCHEM_rc`, `CATCHEM_postdet`, and `CATCHEM_out`: + the CATChem runtime YAML set (`CATChem_config/species/emissions/ + field_mapping_${CATCHEM_CONFIG}.yaml`) is copied and renamed into `DATA` + under the fixed names the CATChem NUOPC cap expects + (`CATChem_new_config.yml`, `CATChem_species.yml`, `CATChem_emission.yml`, + `CATChem_field_mapping.yml`), `ExtData` is linked from + `AERO_INPUTS_DIR`, and `catchem*` diagnostics are copied to + `$COM/model/chem/history`. + - `dev/scripts/exglobal_forecast.sh` branches the chemistry steps onto the + CATChem functions when `cplcat=".true."`. + - `parm/archive/chem.yaml.j2` archives `{{ CHEM_MODEL }}*` history and + `parm/archive/master_gcafs.yaml.j2` includes the `chem` tarball for + `CHEM_MODEL == "catchem"` (GCAFS ATMA archiving is unchanged). + - `dev/scripts/exgfs_aero_init_aerosol.py` uses + `parm/chem/catchem/catchem_tracer.list` when `CHEM_MODEL == "catchem"`. + - `dev/scripts/exglobal_prep_emissions.py` validates the selected CATChem + emissions configuration set when `CHEM_MODEL == "catchem"`. + - Build support: `sorc/build_opts.yaml` adds the `gcafs_catchem_model` + entry (`./build_ufs.sh -a CATCHEM -e gcafs_catchem_model.x`) to the GCAFS + system; `sorc/build_ufs.sh` registers the `COMPILE_ID`; + `sorc/link_workflow.sh` links `exec/ufs_model_gcafs_catchem.x`. + - Docs: `docs/source/user_guide/setup.rst`, `gcafs.rst`, and `clone.rst` + describe the `ATMC` option. + - CI: `dev/ci/cases/pr/C96_gcafs_catchem_cycled.yaml` adds an `APP=ATMC` + cycled GCAFS case (modeled on `C96_gcafs_cycled_noDA.yaml`), registered + in the hera/gaeac6/ursa/derecho matrices of `dev/ci/gitlab-ci-hosts.yml`. + +### Changed + +- `sorc/ufs_model.fd` pinned to ufs-weather-model + `6dc36481b6d1347f3c5332905ad48e7e9da8675e`, the first revision exposing the + `CATCHEM` application (`-DAPP=CATCHEM`) and the `cplcat` entry in + `global_control.nml.IN`. + +### Compatibility + +- No behavior change for existing `APP=ATMA`/`ATM`/`S2S*` configurations: the + GOCART code paths, GCAFS archive contents, and NEXUS/fire emissions + preprocessing are untouched; all new logic is gated on `APP=ATMC` / + `CHEM_MODEL=="catchem"` / `cplcat==".true."`. diff --git a/dev/ci/cases/gcafsv1/C96_gcafs_cycled_noDA_dev_catchem.yaml b/dev/ci/cases/gcafsv1/C96_gcafs_cycled_noDA_dev_catchem.yaml new file mode 100644 index 00000000000..e6240427cd3 --- /dev/null +++ b/dev/ci/cases/gcafsv1/C96_gcafs_cycled_noDA_dev_catchem.yaml @@ -0,0 +1,23 @@ +experiment: + net: gcafs + mode: cycled + pslot: {{ 'pslot' | getenv }} + app: ATMC + resdetatmos: 96 + comroot: {{ 'RUNTESTS' | getenv }}/COMROOT + expdir: {{ 'RUNTESTS' | getenv }}/EXPDIR + icsdir: {{ BASE_IC }}/C96C48/20250808 + idate: 2021122012 + edate: 2021122200 + nens: 0 + interval: 0 + start: cold + yaml: {{ HOMEglobal }}/dev/ci/cases/yamls/gcafs_cycled_noDA_defaults_dev.yaml + +workflow: + engine: rocoto + rocoto: + maxtries: 2 + cyclethrottle: 3 + taskthrottle: 25 + verbosity: 2 diff --git a/dev/ci/cases/pr/C96_gcafs_catchem_cycled.yaml b/dev/ci/cases/pr/C96_gcafs_catchem_cycled.yaml new file mode 100644 index 00000000000..f375a6e4ade --- /dev/null +++ b/dev/ci/cases/pr/C96_gcafs_catchem_cycled.yaml @@ -0,0 +1,28 @@ +experiment: + net: gcafs + mode: cycled + pslot: {{ 'pslot' | getenv }} + app: ATMC + resdetatmos: 96 + comroot: {{ 'RUNTESTS' | getenv }}/COMROOT + expdir: {{ 'RUNTESTS' | getenv }}/EXPDIR + icsdir: {{ BASE_IC }}/C96C48/20250808 + idate: 2021122012 + edate: 2021122100 + nens: 0 + interval: 12 + start: cold + yaml: {{ HOMEglobal }}/dev/ci/cases/yamls/gcafs_cycled_noDA_defaults_ci.yaml + +skip_ci_on_hosts: + - orion + - hercules + - awsepicglobalworkflow + +workflow: + engine: rocoto + rocoto: + maxtries: 2 + cyclethrottle: 3 + taskthrottle: 25 + verbosity: 2 diff --git a/dev/parm/config/gcafs/config.base.j2 b/dev/parm/config/gcafs/config.base.j2 index 25b6257f407..2ba58cdfb46 100644 --- a/dev/parm/config/gcafs/config.base.j2 +++ b/dev/parm/config/gcafs/config.base.j2 @@ -164,7 +164,7 @@ shopt -s extglob # If a component (WAVES, etc) needs to be turned off by RUN, set it here case "${RUN}" in enkf*) # Turn off aerosols and waves - APP="${APP/%+([WA])}" + APP="${APP/%+([WAC])}" ;; *) # Keep app unchanged ;; @@ -234,6 +234,9 @@ case "${APP}" in ATMA) DO_AERO="YES" ;; + ATMC) # Atmosphere + CATChem interactive chemistry + DO_AERO="YES" + ;; ATMW) export DO_COUPLED="YES" export DO_WAVE="YES" @@ -262,6 +265,19 @@ case "${APP}" in ;; esac +# Chemistry model selector: none (default), gocart (ATM*A apps), catchem (ATMC) +case "${APP}" in + ATMC) + export CHEM_MODEL="catchem" + ;; + *A) + export CHEM_MODEL="gocart" + ;; + *) + export CHEM_MODEL="none" + ;; +esac + # Aerosol forecasts and analyses may be RUN-dependent if [[ "${DO_AERO}" == "YES" ]]; then if [[ ${aero_anl_runs} =~ ${RUN} ]]; then diff --git a/dev/parm/config/gcafs/config.fcst.j2 b/dev/parm/config/gcafs/config.fcst.j2 index e0e7805c538..90067d17ef5 100644 --- a/dev/parm/config/gcafs/config.fcst.j2 +++ b/dev/parm/config/gcafs/config.fcst.j2 @@ -25,7 +25,11 @@ if [[ "${DO_WAVE}" == "YES" ]]; then string="${string} --ww3 ${waveGRD}" fi if [[ "${DO_AERO_FCST}" == "YES" ]]; then - string="${string} --gocart" + if [[ "${CHEM_MODEL}" == "catchem" ]]; then + string="${string} --catchem" + else + string="${string} --gocart" + fi fi # We are counting on $string being multiple arguments # shellcheck disable=SC2086 @@ -78,7 +82,11 @@ export esmf_logkind="ESMF_LOGKIND_MULTI_ON_ERROR" #Options: ESMF_LOGKIND_MULTI_O export FORECASTSH="${SCRglobal}/exglobal_forecast.sh" #export FORECASTSH="${SCRglobal}/exglobal_forecast.py" # Temp. while this is worked on -export FCSTEXEC="ufs_model_gcafs.x" +if [[ "${CHEM_MODEL}" == "catchem" ]]; then + export FCSTEXEC="ufs_model_gcafs_catchem.x" +else + export FCSTEXEC="ufs_model_gcafs.x" +fi ####################################################################### # Model configuration diff --git a/dev/parm/config/gfs/config.aero.j2 b/dev/parm/config/gfs/config.aero.j2 index e4f958ae44f..23fd5b08800 100644 --- a/dev/parm/config/gfs/config.aero.j2 +++ b/dev/parm/config/gfs/config.aero.j2 @@ -228,5 +228,21 @@ export NEXUS_DO_CAMS=.false. # Use CAMS global emissions # Enables time-varying scaling when CAMS is active. export NEXUS_DO_CAMSTEMPO=.true. # Use CAMS temporal emissions -#================================================================================ -echo "END: config.aero" +#================================================================================# 4. CATChem settings +#=============================================================================== +# CATChem is an interactive atmospheric chemistry model coupled to FV3 through +# its own NUOPC cap (chm_model='catchem'). Runtime configuration is provided as +# a set of four YAML files (config, species, emissions, field mapping) that are +# staged into the DATA directory under fixed names before the forecast runs. + +# CATChem configuration set (selects the *_.yaml bundle under +# parm/chem/catchem). Options: gcafs_aero (default) +#--------------------------------------------------------------------------------------------------- +export CATCHEM_CONFIG="{{ CATCHEM_CONFIG | default('gcafs_aero') }}" + +# Directory containing the CATChem runtime YAML sets. The selected config set is +# resolved by filename suffix, e.g. CATChem_config_${CATCHEM_CONFIG}.yaml. +#--------------------------------------------------------------------------------------------------- +export CATCHEM_CONFIG_DIR="${PARMglobal}/chem/catchem" + +#===============================================================================echo "END: config.aero" diff --git a/dev/parm/config/gfs/config.ufs b/dev/parm/config/gfs/config.ufs index 43c1e6ed4ba..8164a69c801 100644 --- a/dev/parm/config/gfs/config.ufs +++ b/dev/parm/config/gfs/config.ufs @@ -17,6 +17,7 @@ if (( $# <= 1 )); then echo "--cice6 500|100|025" echo "--ww3 gwes_30m|glo_025|glo_100|glo_200|glo_500|mx025|uglo_100km" echo "--gocart" + echo "--catchem" exit 1 @@ -27,6 +28,7 @@ skip_mom6=true skip_cice6=true skip_ww3=true skip_gocart=true +skip_catchem=true skip_mediator=true # Loop through named arguments @@ -55,6 +57,9 @@ while (( $# > 0 )); do "--gocart") skip_gocart=false ;; + "--catchem") + skip_catchem=false + ;; *) # unknown option echo "FATAL ERROR: Unknown option: ${key}, ABORT!" exit 1 @@ -648,6 +653,18 @@ if [[ "${skip_gocart}" == "false" ]]; then model_list="${model_list}.aero" fi +# CATChem specific settings +# CATChem couples through its own NUOPC cap (chm_model='catchem') but reuses the +# atmaero configure template: cplcat=true implies cplchm=true (the chemistry PETs +# are the same as the GOCART/aerosol PETs), so the model_list suffix stays .aero. +if [[ "${skip_catchem}" == "false" ]]; then + source "${EXPDIR}/config.aero" + export cplchm=".true." + export cplcat=".true." + export chm_model="catchem" + model_list="${model_list}.aero" +fi + # Set the name of the UFS (previously nems) configure template to use # Default ufs.configure templates for supported model configurations # WW3 restart field variable is different for slow vs fast loop. Add WW3_RSTFLDS="ice" for slow loop variables based on coupling scheme. diff --git a/dev/scripts/exgfs_aero_init_aerosol.py b/dev/scripts/exgfs_aero_init_aerosol.py index c4a887b2997..c92cc1678f5 100755 --- a/dev/scripts/exgfs_aero_init_aerosol.py +++ b/dev/scripts/exgfs_aero_init_aerosol.py @@ -57,8 +57,9 @@ tracer_file_pattern = "{file_base}/{timestamp}fv_tracer.res.{tile}.nc" # Name of restart dycore file (time when restart is valid) dycore_file_pattern = "{file_base}/{timestamp}fv_core.res.nc" -# Text list of tracer names to copy +# Text list of tracer names to copy (selected from CHEM_MODEL at runtime) tracer_list_file_pattern = "{parm_gfs}/ufs/gocart/gocart_tracer.list" +catchem_tracer_list_file_pattern = "{parm_gfs}/chem/catchem/catchem_tracer.list" merge_script_pattern = "{ush_gfs}/merge_fv3_aerosol_tile.py" n_tiles = 6 # Maximum number of past cycles to look for for tracer data @@ -84,11 +85,15 @@ def main() -> None: rot_dir = get_env_var("ROTDIR") ush_gfs = get_env_var("USHglobal") parm_gfs = get_env_var("PARMglobal") + chem_model = get_env_var("CHEM_MODEL", fail_on_missing=False) or "none" # os.chdir(data) merge_script = merge_script_pattern.format(ush_gfs=ush_gfs) - tracer_list_file = tracer_list_file_pattern.format(parm_gfs=parm_gfs) + if chem_model == "catchem": + tracer_list_file = catchem_tracer_list_file_pattern.format(parm_gfs=parm_gfs) + else: + tracer_list_file = tracer_list_file_pattern.format(parm_gfs=parm_gfs) time = datetime.strptime(f"{pdy}{cyc}", "%Y%m%d%H") atm_source_path = time.strftime(atm_base_pattern.format(**locals())) diff --git a/dev/scripts/exglobal_forecast.sh b/dev/scripts/exglobal_forecast.sh index b00ac6d9fe0..a9f5b5eff84 100755 --- a/dev/scripts/exglobal_forecast.sh +++ b/dev/scripts/exglobal_forecast.sh @@ -95,6 +95,7 @@ cpl=${cpl:-.false.} cplflx=${cplflx:-.false.} # default off,import from outside source cplwav=${cplwav:-.false.} # ? how to control 1-way/2-way? cplchm=${cplchm:-.false.} # Chemistry model +cplcat=${cplcat:-.false.} # CATChem interactive chemistry model cplice=${cplice:-.false.} # ICE model OCNTIM=${OCNTIM:-1800} @@ -122,7 +123,11 @@ if [[ "${cplice}" == ".true." ]]; then CICE_predet fi if [[ "${cplchm}" == ".true." ]]; then - GOCART_predet + if [[ "${cplcat}" == ".true." ]]; then + CATCHEM_predet + else + GOCART_predet + fi fi echo "MAIN: Variables before determination of run type loaded" @@ -145,7 +150,11 @@ if [[ "${cplice}" == ".true." ]]; then CICE_postdet fi if [[ "${cplchm}" == ".true." ]]; then - GOCART_postdet + if [[ "${cplcat}" == ".true." ]]; then + CATCHEM_postdet + else + GOCART_postdet + fi fi echo "MAIN: Post-determination set up of run type finished" @@ -161,7 +170,11 @@ if [[ "${cplice}" == ".true." ]]; then CICE_nml fi if [[ "${cplchm}" == ".true." ]]; then - GOCART_rc + if [[ "${cplcat}" == ".true." ]]; then + CATCHEM_rc + else + GOCART_rc + fi fi UFS_configure echo "MAIN: Name lists and model configuration written" @@ -202,7 +215,11 @@ if [[ "${cplice}" == ".true." ]]; then CICE_out fi if [[ "${cplchm}" == ".true." ]]; then - GOCART_out + if [[ "${cplcat}" == ".true." ]]; then + CATCHEM_out + else + GOCART_out + fi fi if [[ "${esmf_profile:-}" == ".true." ]]; then CPL_out diff --git a/dev/scripts/exglobal_prep_emissions.py b/dev/scripts/exglobal_prep_emissions.py index abd773e0bfa..e0de8a6a4aa 100755 --- a/dev/scripts/exglobal_prep_emissions.py +++ b/dev/scripts/exglobal_prep_emissions.py @@ -4,7 +4,7 @@ # which perform the pre-processing for aerosol emissions import os -from wxflow import Logger, cast_strdict_as_dtypedict +from wxflow import Logger, cast_strdict_as_dtypedict, WorkflowException from pygfs import ChemFireEmissions, NEXUSEmissions @@ -13,10 +13,61 @@ level=os.environ.get("LOGGING_LEVEL", "DEBUG"), colored_log=True) +def _check_catchem_emissions(config) -> None: + """Validate the CATChem emissions configuration set. + + CATChem reads the anthropogenic and fire emission fields from ChemInput + (the NEXUS_DIAG.* and FIRE_EMIS_*.nc netCDF files produced below) and maps + them onto model tracers through its own runtime emissions YAML, staged as + CATChem_emission.yml by CATCHEM_rc(). Confirm the selected emissions set + exists so that an unknown CATCHEM_CONFIG fails loudly here rather than at + forecast time. + + Parameters + ---------- + config : dict + Task configuration from the environment. + + Returns + ------- + None + + Raises + ------ + WorkflowException + If the CATChem emissions YAML for the selected config is missing. + """ + catchem_config = config.get('CATCHEM_CONFIG', None) + catchem_config_dir = config.get('CATCHEM_CONFIG_DIR', None) + if catchem_config is None or catchem_config_dir is None: + raise WorkflowException( + "CHEM_MODEL is 'catchem' but CATCHEM_CONFIG/CATCHEM_CONFIG_DIR " + "are not set; check config.aero sourcing") + + emis_yaml = os.path.join( + catchem_config_dir, f"CATChem_emissions_{catchem_config}.yaml") + if not os.path.isfile(emis_yaml): + raise WorkflowException( + f"CATChem emissions configuration not found: {emis_yaml} " + f"(CATCHEM_CONFIG='{catchem_config}')") + + logger.info( + f"CATChem emissions: species-to-tracer mapping delegated to {emis_yaml}; " + "NEXUS/fire preprocessing still produces the ChemInput netCDF fields") + + if __name__ == '__main__': # Take configuration from environment and cast it as python dictionary config = cast_strdict_as_dtypedict(os.environ) + + # When running with the CATChem chemistry model, the emission species-to- + # tracer mapping lives in the staged CATChem emissions YAML rather than the + # NEXUS/HEMCO .rc set. Validate that set up front; the underlying NEXUS and + # fire emissions still produce the ChemInput fields CATChem reads. + if config.get('CHEM_MODEL', 'none') == 'catchem': + _check_catchem_emissions(config) + nxsemis = NEXUSEmissions(config.copy()) # Instantiate the emissions pre-processing task fireemis = ChemFireEmissions(config.copy()) diff --git a/dev/workflow/applications/applications.py b/dev/workflow/applications/applications.py index 3d0ebc6e36c..f8ea6fa81a5 100644 --- a/dev/workflow/applications/applications.py +++ b/dev/workflow/applications/applications.py @@ -197,6 +197,7 @@ def _get_run_options(self, conf: Configuration) -> Dict[str, Any]: run_options[run]['do_aero_anl'] = run_base.get('DO_AERO_ANL', False) run_options[run]['use_aero_anl'] = run_base.get('USE_AERO_ANL', False) run_options[run]['do_aero_fcst'] = run_base.get('DO_AERO_FCST', False) + run_options[run]['chem_model'] = run_base.get('CHEM_MODEL', 'none') if run_base.get('GEFSTYPE', "") == "gefs-real-time": run_options[run]['do_gefs_real_time'] = True diff --git a/dev/workflow/setup_expt.py b/dev/workflow/setup_expt.py index 03e15e79449..d4fdbdd8866 100755 --- a/dev/workflow/setup_expt.py +++ b/dev/workflow/setup_expt.py @@ -177,7 +177,7 @@ def input_args(*argv): If start type is invalid """ - ufs_apps = ['ATM', 'ATMA', 'ATMW', 'S2S', 'S2SA', 'S2SW', 'S2SWA'] + ufs_apps = ['ATM', 'ATMA', 'ATMC', 'ATMW', 'S2S', 'S2SA', 'S2SW', 'S2SWA'] def _validate_interval(interval_str): """ diff --git a/docs/source/user_guide/clone.rst b/docs/source/user_guide/clone.rst index 8e33dc140fa..1b6c80fb69c 100644 --- a/docs/source/user_guide/clone.rst +++ b/docs/source/user_guide/clone.rst @@ -189,6 +189,9 @@ Build Options +------------+-----------------------------------------------------------+ | GCAFS | Non-hydrostatic FV3/GOCART | +------------+-----------------------------------------------------------+ +| GCAFS | Non-hydrostatic FV3/CATChem (APP=ATMC; build with | +| CATChem | ./build_ufs.sh -a CATCHEM -e gcafs_catchem_model.x) | ++------------+-----------------------------------------------------------+ ^^^^^^^^^^^^^^^ Link components diff --git a/docs/source/user_guide/gcafs.rst b/docs/source/user_guide/gcafs.rst index 166af1b77ac..84598f22fc4 100644 --- a/docs/source/user_guide/gcafs.rst +++ b/docs/source/user_guide/gcafs.rst @@ -34,6 +34,9 @@ GCAFS can be run using the global-workflow framework. To set up a free-forecast --idate 2023010100 --edate 2023010100 \ --resdetatmos 384 --comroot /path/to/com --expdir /path/to/exp +Use ``--app ATMC`` instead of ``--app ATMA`` to run with the CATChem +interactive chemistry model (see :ref:`gcafs_catchem` below). + Configuration is managed through the standard global-workflow configuration files. GCAFS-specific settings are documented in :ref:`gcafs_config`. @@ -109,6 +112,43 @@ The primary configuration file for aerosol settings, containing: These settings are processed as Jinja2 templates, allowing for experiment-specific customization through template variables like ``{{ NEXUS_CONFIG | default('gocart') }}``. +.. _gcafs_catchem: + +------------------- +CATChem (APP=ATMC) +------------------- + +Selecting ``--app ATMC`` replaces the GOCART chemistry model with CATChem, an +interactive atmospheric chemistry model coupled to FV3 through its own NUOPC +cap (``chm_model='catchem'``, ``cplcat=.true.``). The forecast executable is +switched to ``ufs_model_gcafs_catchem.x`` (built via +``./build_ufs.sh -a CATCHEM -e gcafs_catchem_model.x``) and the same aerosol +forecast tasks (``prep_emissions``, ``aerosol_init``, ``fcst``) are used. + +**CATChem settings** (in ``config.aero.j2``): + +.. code-block:: bash + + export CATCHEM_CONFIG="gcafs_aero" # CATChem configuration set + export CATCHEM_CONFIG_DIR="${PARMglobal}/chem/catchem" # Directory holding the YAML sets + +The selected configuration set resolves the four runtime YAML files staged +into the forecast ``DATA`` directory under the fixed names expected by the +CATChem cap: + +.. code-block:: text + + CATChem_config_${CATCHEM_CONFIG}.yaml -> CATChem_new_config.yml + CATChem_species_${CATCHEM_CONFIG}.yaml -> CATChem_species.yml + CATChem_emissions_${CATCHEM_CONFIG}.yaml -> CATChem_emission.yml + CATChem_field_mapping_${CATCHEM_CONFIG}.yaml -> CATChem_field_mapping.yml + +The staged ``CATChem_config.yml`` references ``./CATChem_species.yml`` and +``./CATChem_emission.yml`` relative to ``DATA``, and reads Mie optics tables +from ``./ExtData/monochromatic/`` (linked from ``AERO_INPUTS_DIR``). +CATChem diagnostics (``catchem_diag*.nc``) are copied to +``$COM/model/chem/history`` and archived into ``chem.tar``. + ======================= Emissions Preprocessing ======================= diff --git a/docs/source/user_guide/setup.rst b/docs/source/user_guide/setup.rst index 835be173b78..8c914dab848 100644 --- a/docs/source/user_guide/setup.rst +++ b/docs/source/user_guide/setup.rst @@ -74,6 +74,7 @@ where: - ATM: atmosphere-only [default] - ATMA: atm-aerosols + - ATMC: atm-CATChem interactive chemistry (GCAFS) - ATMW: atm-wave (currently non-functional) - S2S: atm-ocean-ice - S2SA: atm-ocean-ice-aerosols diff --git a/parm/archive/chem.yaml.j2 b/parm/archive/chem.yaml.j2 index 953f9d4f40e..4a6d2df25ab 100644 --- a/parm/archive/chem.yaml.j2 +++ b/parm/archive/chem.yaml.j2 @@ -1,5 +1,5 @@ chem: - {% set head = "gocart" %} + {% set head = CHEM_MODEL | default('gocart') %} name: "CHEM" target: "{{ ATARDIR }}/{{ cycle_YMDH }}/chem.tar" required: diff --git a/parm/archive/master_gcafs.yaml.j2 b/parm/archive/master_gcafs.yaml.j2 index a6fd47520af..04ebdb6a11d 100644 --- a/parm/archive/master_gcafs.yaml.j2 +++ b/parm/archive/master_gcafs.yaml.j2 @@ -35,6 +35,14 @@ datasets: - "{{ COMIN_ATMOS_GRIB_1p00 }}/{{ head }}pres_a.1p00.f{{ '%03d' % fhr }}.grib2" - "{{ COMIN_ATMOS_GRIB_1p00 }}/{{ head }}pres_a.1p00.f{{ '%03d' % fhr }}.grib2.idx" {% endfor %} + +# Archive CATChem output (APP=ATMC). GCAFS ATMA/gocart chem history is not +# archived here to preserve existing behavior; only the CATChem tarball is added. +{% if CHEM_MODEL is defined and CHEM_MODEL == 'catchem' %} +{% filter indent(width=4) %} +{% include "chem.yaml.j2" %} +{% endfilter %} +{% endif %} # Archive the EXPDIR if requested {% if archive_expdir %} {% filter indent(width=4) %} diff --git a/parm/chem/catchem/CATChem_config_gcafs_aero.yaml b/parm/chem/catchem/CATChem_config_gcafs_aero.yaml new file mode 100644 index 00000000000..767136f727b --- /dev/null +++ b/parm/chem/catchem/CATChem_config_gcafs_aero.yaml @@ -0,0 +1,448 @@ +### CATChem_config.yml: CATChem Runtime configuration options. +### +### NOTE: Add quotes around nitrogen oxide ('NO'), because YAML +### parsers will confuse this with a negative "no" value. + +#============================================================================ +# Simulation settings +#============================================================================ +simulation: + name: test + start_date: 20240501 0000 + end_date: 20240501 0100 + #species_filename: ./Configs/Default/CATChem_species.yml + #emission_filename: ./Configs/Default/CATChem_emission.yml + species_filename: ./CATChem_species.yml + emission_filename: ./CATChem_emission.yml + verbose: + activate: false +#============================================================================ +# Grid settings +#============================================================================ +grid: + number_of_levels: 127 + number_of_soil_layers: 4 +#============================================================================ +# Timesteps settings +#============================================================================ +timesteps: + transport_timestep_in_s: 10 + chemistry_timestep_in_s: 60 +#============================================================================ +# Diagnostic settings +#============================================================================ +diagnostics: + output: + enabled: true + directory: "./output" + prefix: "catchem_diag" + frequency: 3600 #output frequency in seconds; e.g., 3600 for hourly output + format: "netcdf" #only format supported now + compress_lev: 0 #compress NC file; 0 for no compression, 1-9 for increasing levels of compression (and run time) + latlon_output: true #stitch cubed-sphere tiles into a single lat/lon file (only applies when tile count > 1) + diag_list: [so2, so4, seas1, seas2, seas3, seas4, seas5, bc1, bc2, oc1, oc2, dust1, dust2, dust3, dust4, dust5] #list of species to output; empty list for all species + collection: + enabled: true + buffer_size: 1000 +#============================================================================ +# Optics files for Mie +#============================================================================ +mie: + directory: "./ExtData/monochromatic/" #in running directory after copying from the above directories + files: + SS: optics_SS.v3_5.nc + DU: optics_DU.v15_5.nc + BC: optics_BC.v1_5.nc + OC: optics_OC.v1_5.nc + NI: optics_NI.v2_5.nc + SU: optics_SU.v1_5.nc +#============================================================================ +# RunPhase settings: define process running order +#============================================================================ +run_phases: + test1: + description: "Emission and chemistry processes" + processes: + - seasalt + - dust + - carbchem + - settling + - drydep + - so4chem + - wetdep + +#============================================================================ +# Settings for CATChem processes +#============================================================================ +processes: + extemis: + activate: true + diagnostics: true + global_factor: 1.0 + anthro1: + activate: true + scale_factor: 1.0 + source_file: "ChemInput/NEXUS_DIAG.%y4%m2%d2.nc" # %y4=year, %m2=month, %d2=day + format: "netcdf" + gridded: true + regrid_method: conserve + lat_name: "lat" + lon_name: "lon" + vertical_dist: "P100" #options: none, P100, P500, Ppbl, aviation, aviation_lto, aviation_cds, aviation_crs + frequency: "hourly" + diagnostics: true + diag_list: [SO2, OC, BC] + anthro2: + activate: true + scale_factor: 1.0 + source_file: "ChemInput/NEXUS_DIAG.%y4%m2%d2.nc" # %y4=year, %m2=month, %d2=day + format: "netcdf" + gridded: true + regrid_method: conserve + lat_name: "lat" + lon_name: "lon" + vertical_dist: "P500" #options: none, P100, P500, Ppbl, aviation, aviation_lto, aviation_cds, aviation_crs + frequency: "hourly" + diagnostics: true + diag_list: [SO2_elev, OC_elev, BC_elev] + ship: + activate: true + scale_factor: 1.0 + source_file: "ChemInput/NEXUS_DIAG.%y4%m2%d2.nc" # %y4=year, %m2=month, %d2=day + format: "netcdf" + gridded: true + regrid_method: conserve + lat_name: "lat" + lon_name: "lon" + vertical_dist: "P100" #options: none, P100, P500, Ppbl, aviation, aviation_lto, aviation_cds, aviation_crs + frequency: "hourly" + diagnostics: true + diag_list: [SO2_ship, OC_ship, BC_ship] + bio: + activate: true + scale_factor: 1.0 + source_file: "ExtData/nexus/MEGAN_OFFLINE_BVOC/v2021-12/MEGAN_OFFLINE_CLIMO_2000_2022_%m2.nc" # %y4=year, %m2=month, %d2=day + format: "netcdf" + gridded: true + regrid_method: conserve + time_interpolation: linear + lat_name: "lat" + lon_name: "lon" + vertical_dist: "none" #biogenic is only at the surface. + frequency: "monthly" #note it is monthly climatology from offline megan for now + diagnostics: true + diag_list: [isoprene, mtpa] + so2_aviation_lto: + activate: true + scale_factor: 1.0 + source_file: "ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_so2.aviation_lto.x3600_y1800_t12.2010.nc4" + format: "netcdf" + gridded: true + regrid_method: conserve + time_interpolation: linear + lat_name: "lat" + lon_name: "lon" + vertical_dist: "aviation_lto" #options: none, P100, P500, Ppbl, aviation, aviation_lto, aviation_cds, aviation_crs + frequency: "monthly" + diagnostics: true + diag_list: [so2_aviation] + so2_aviation_cds: + activate: true + scale_factor: 1.0 + source_file: "ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_so2.aviation_cds.x3600_y1800_t12.2010.nc4" + format: "netcdf" + gridded: true + regrid_method: conserve + time_interpolation: linear + lat_name: "lat" + lon_name: "lon" + vertical_dist: "aviation_cds" #options: none, P100, P500, Ppbl, aviation, aviation_lto, aviation_cds, aviation_crs + frequency: "monthly" + diagnostics: true + diag_list: [so2_aviation] + so2_aviation_crs: + activate: true + scale_factor: 1.0 + source_file: "ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_so2.aviation_crs.x3600_y1800_t12.2010.nc4" + format: "netcdf" + gridded: true + regrid_method: conserve + time_interpolation: linear + lat_name: "lat" + lon_name: "lon" + vertical_dist: "aviation_crs" #options: none, P100, P500, Ppbl, aviation, aviation_lto, aviation_cds, aviation_crs + frequency: "monthly" + diagnostics: true + diag_list: [so2_aviation] + oc_aviation_lto: + activate: true + scale_factor: 1.0 + source_file: "ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_oc.aviation_lto.x3600_y1800_t12.2010.nc4" + format: "netcdf" + gridded: true + regrid_method: conserve + time_interpolation: linear + lat_name: "lat" + lon_name: "lon" + vertical_dist: "aviation_lto" #options: none, P100, P500, Ppbl, aviation, aviation_lto, aviation_cds, aviation_crs + frequency: "monthly" + diagnostics: true + diag_list: [oc_aviation] + oc_aviation_cds: + activate: true + scale_factor: 1.0 + source_file: "ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_oc.aviation_cds.x3600_y1800_t12.2010.nc4" + format: "netcdf" + gridded: true + regrid_method: conserve + time_interpolation: linear + lat_name: "lat" + lon_name: "lon" + vertical_dist: "aviation_cds" #options: none, P100, P500, Ppbl, aviation, aviation_lto, aviation_cds, aviation_crs + frequency: "monthly" + diagnostics: true + diag_list: [oc_aviation] + oc_aviation_crs: + activate: true + scale_factor: 1.0 + source_file: "ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_oc.aviation_crs.x3600_y1800_t12.2010.nc4" + format: "netcdf" + gridded: true + regrid_method: conserve + time_interpolation: linear + lat_name: "lat" + lon_name: "lon" + vertical_dist: "aviation_crs" #options: none, P100, P500, Ppbl, aviation, aviation_lto, aviation_cds, aviation_crs + frequency: "monthly" + diagnostics: true + diag_list: [oc_aviation] + bc_aviation_lto: + activate: true + scale_factor: 1.0 + source_file: "ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_bc.aviation_lto.x3600_y1800_t12.2010.nc4" + format: "netcdf" + gridded: true + regrid_method: conserve + time_interpolation: linear + lat_name: "lat" + lon_name: "lon" + vertical_dist: "aviation_lto" #options: none, P100, P500, Ppbl, aviation, aviation_lto, aviation_cds, aviation_crs + frequency: "monthly" + diagnostics: true + diag_list: [bc_aviation] + bc_aviation_cds: + activate: true + scale_factor: 1.0 + source_file: "ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_bc.aviation_cds.x3600_y1800_t12.2010.nc4" + format: "netcdf" + gridded: true + regrid_method: conserve + time_interpolation: linear + lat_name: "lat" + lon_name: "lon" + vertical_dist: "aviation_cds" #options: none, P100, P500, Ppbl, aviation, aviation_lto, aviation_cds, aviation_crs + frequency: "monthly" + diagnostics: true + diag_list: [bc_aviation] + bc_aviation_crs: + activate: true + scale_factor: 1.0 + source_file: "ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_bc.aviation_crs.x3600_y1800_t12.2010.nc4" + format: "netcdf" + gridded: true + regrid_method: conserve + time_interpolation: linear + lat_name: "lat" + lon_name: "lon" + vertical_dist: "aviation_crs" #options: none, P100, P500, Ppbl, aviation, aviation_lto, aviation_cds, aviation_crs + frequency: "monthly" + diagnostics: true + diag_list: [bc_aviation] + point: #not used for now + activate: true + scale_factor: 1.0 + topfraction: -1.0 + source_file: "INPUT/PT.nc" + format: "netcdf" + gridded: false + frequency: "hourly" + lat_name: "LATITUDE" + lon_name: "LONGITUDE" + plume_rise: "sofiev" + stack_diameter: STKDM + stack_height: STKHT + stack_temperature: STKTK + stack_velocity: STKVE + diagnostics: true + diag_list: [NO, SO2, POC, PSO4] + gmi: + activate: true + scale_factor: 1.0 + source_file: "ExtData/PIESA/L127/A2_ACCMIP_gmic_MERRA_oh_h2o2_no3.x144_y91_z127_t14.%y4.nc" + format: "netcdf" + gridded: true + is_2d: false + apply_method: "replace" + reverse_vertical: true + regrid_method: conserve + time_interpolation: linear + lat_name: "lat" + lon_name: "lon" + vertical_dist: "none" + frequency: "monthly" # 14 months (Dec+Jan padding); time-coord matching selects the right slice + diagnostics: true + diag_list: [oh, h2o2] + dms: + activate: true + scale_factor: 1.0 + source_file: "ExtData/MERRA2/sfc/DMSclim_sfcconcentration.x360_y181_t12.Lana2011.nc4" + format: "netcdf" + gridded: true + is_2d: true + apply_method: "replace" + regrid_method: conserve + time_interpolation: linear + lat_name: "latitude" + lon_name: "longitude" + vertical_dist: "none" + frequency: "monthly" + diagnostics: true + diag_list: [conc] + volcano: + activate: true + scale_factor: 1.0 + source_file: "ExtData/nexus/VOLCANO/v2021-09/so2_volcanic_emissions_CARN_v202005.degassing_only.rc" + format: "volcano" # triggers the point-source reader + 3D injector + gridded: false + frequency: "static" # "daily" if seperated by day + diagnostics: false #current will all be zeros + diag_list: [SO2] + fire: + activate: true + scale_factor: 0.7778 + topfraction: -1.0 + source_file: "ChemInput/FIRE_EMIS_%y4%m2%d2.nc" # %y4=year, %m2=month, %d2=day + format: "netcdf" + gridded: true + is_2d: true + regrid_method: conserve + time_interpolation: linear + use_oc_fbb: true + diurnal_bb: true + lat_name: "lat" + lon_name: "lon" + vertical_dist: "Ppbl" + frequency: "daily" + plume_rise: "sofiev" + diagnostics: true + diag_list: [SO2,OC,BC] + dust: + activate: true + scale_factor: 1.0 + source_file: "ExtData/FENGSHA/FENGSHA_2022_NESDIS_inputs_10km_v3.2.nc" + format: "netcdf" + gridded: true + regrid_method: neareststod + time_interpolation: linear + lat_name: "lat" + lon_name: "lon" + is_2d: true + apply_method: "replace" + frequency: "static" + diagnostics: true + diag_list: [clayfrac,sandfrac,uthres,sep] + fengsha: + activate: true + scale_factor: 1.0 + source_file: "ExtData/FENGSHA/FENGSHA_2022_NESDIS_inputs_10km_v4.nc" + format: "netcdf" + gridded: true + regrid_method: neareststod + time_interpolation: linear + lat_name: "lat" + lon_name: "lon" + is_2d: true + apply_method: "replace" + frequency: "monthly" + diagnostics: true + diag_list: [albedo_drag] + seasalt: + activate: true + diagnostics: true + diag_species: [] + scheme: 'geos12' + gong97: + scale_factor: 1.0 + weibull_flag: false + gong03: + scale_factor: 1.0 + weibull_flag: false + geos12: + scale_factor: 1.0 + weibull_flag: false + dust: + activate: true + diagnostics: true + diag_species: [] + scheme: 'fengsha' + fengsha: + alpha: 0.20 + gamma: 1.0 + drylimit_factor: 1.2 + moist_correction_factor: 1.0 + kvhmax: 0.0002 + drag_option: 1 + horizflux_option: 1 + moist_option: 1 + distribution_option: 1 + ginoux: + Ch_DU: [0.07, 0.07, 0.07, 0.07, 0.07] + drydep: + activate: true + diagnostics: true + diag_species: [so2, so4, seas1, seas3, seas5, oc1,oc2,bc1,bc2,dust1, dust3, dust5] + gas_scheme: 'wesely' + aero_scheme: 'gocart' + wesely: + scale_factor: 1.0 + gocart: + scale_factor: 1.0 + resuspension: false + dust_resuspension_only: true # default + zhang: + scale_factor: 1.0 + wetdep: + activate: true + diagnostics: true + diag_species: [so2, so4, seas1, seas3, seas5, oc1,oc2,bc1,bc2,dust1, dust3, dust5] + scheme: 'jacob' + jacob: + scale_factor: 1.0 #washout scale for all species + radius_threshold: 0.05 + so4_gocart_resusp: true #sulfate-only GOCART-style resuspension + so4_washout_eff: 0.3 #sulfate-only below-cloud washout efficiency (1.0=unchanged); reduce to cut excess SO4 washout + settling: + activate: true + diagnostics: true + diag_species: [so4, seas1, seas3, seas5, oc1,oc2,bc1,bc2,dust1, dust3, dust5] + scheme: 'gocart' + gocart: + scale_factor: 1.0 #not doing anything for now since we cannot edit gocart2g module + simple_scheme: true #read in mie data for wet particles if true; otherwise calculate particles wet swelling internally + swelling_method: 2 # 1 for Fitzgerald 1975; 2 for Gerber 1985 if simple_scheme is false + correction_maring: false #correction settling velocity following Maring et al, 2003 for both simple and non-simple schemes + maring_dust_only: true #apply Maring correction to dust only (true) or all species (false) + so4chem: + activate: true + diagnostics: true + diag_species: [so2, so4] #only msa, so2 and so4 for gocart now + scheme: 'gocart' + gocart: + update_so2: true #whether to update SO2 concentration based on chemical production/loss + carbchem: + activate: true + diagnostics: true + diag_species: [oc1,oc2,bc1,bc2] #no brown carbon for now + scheme: 'gocart' + gocart: + time_days_hydrophobic_to_hydrophilic: 2.5 # Rate of conversion of hydrophobic to hydrophilic [days] \ No newline at end of file diff --git a/parm/chem/catchem/CATChem_emissions_gcafs_aero.yaml b/parm/chem/catchem/CATChem_emissions_gcafs_aero.yaml new file mode 100644 index 00000000000..bc3b26626a7 --- /dev/null +++ b/parm/chem/catchem/CATChem_emissions_gcafs_aero.yaml @@ -0,0 +1,283 @@ +anthro1: + #NO: + # long_name: "Nitrogen Oxide" + # units: "kg/m2/s" + # scale: [1.0] + # map: ["NO"] #double check upper or lower case + SO2: + long_name: "Sulfur Dioxide" + units: "kg/m2/s" + scale: [0.97, 0.045] #1-0.03; 0.03*96/64 + map: ["so2", "so4"] + OC: + long_name: "Organic Carbon" + units: "kg/m2/s" + scale: [0.9, 0.9] #0.5 x 1.8; 0.5 is the fraction of OC that is hydrophobic/hydrophilic; 1.8 is the scaling factor from OC to POM + map: ["oc1", "oc2"] + BC: + long_name: "Black Carbon" + units: "kg/m2/s" + scale: [0.8, 0.2] #There is no 1.8 factor for BC + map: ["bc1", "bc2"] +anthro2: + SO2_elev: + long_name: "Sulfur Dioxide at Elevated Levels" + units: "kg/m2/s" + scale: [0.97, 0.045] #1-0.03; 0.03*96/64 + map: ["so2", "so4"] + OC_elev: + long_name: "Organic Carbon at Elevated Levels" + units: "kg/m2/s" + scale: [0.9, 0.9] #0.5 x 1.8; 0.5 is the fraction of OC that is hydrophobic/hydrophilic; 1.8 is the scaling factor from OC to POM + map: ["oc1", "oc2"] + BC_elev: + long_name: "Black Carbon at Elevated Levels" + units: "kg/m2/s" + scale: [0.8, 0.2] #There is no 1.8 factor for BC + map: ["bc1", "bc2"] +ship: + SO2_ship: + long_name: "Sulfur Dioxide from Ships" + units: "kg/m2/s" + scale: [1.0] + map: ["so2"] + SO4_ship: + long_name: "Sulfate from Ships" + units: "kg/m2/s" + scale: [1.0] + map: ["so4"] + OC_ship: + long_name: "Organic Carbon from Ships" + units: "kg/m2/s" + scale: [0.9, 0.9] #0.5 x 1.8; 0.5 is the fraction of OC that is hydrophobic/hydrophilic; 1.8 is the scaling factor from OC to POM + map: ["oc1", "oc2"] + BC_ship: + long_name: "Black Carbon from Ships" + units: "kg/m2/s" + scale: [0.8, 0.2] #There is no 1.8 factor for BC + map: ["bc1", "bc2"] +fire: + SO2: + long_name: "Sulfur Dioxide from Biomass Burning" + units: "kg/m2/s" + scale: [1.0] + map: ["so2"] + OC: + long_name: "Organic Carbon from Biomass Burning" + units: "kg/m2/s" + scale: [0.9, 0.9] #0.5 x 1.8; 0.5 is the fraction of OC that is hydrophobic/hydrophilic; 1.8 is the scaling factor from OC to POM + map: ["oc1", "oc2"] + BC: + long_name: "Black Carbon from Biomass Burning" + units: "kg/m2/s" + scale: [0.8, 0.2] #There is no 1.8 factor for BC + map: ["bc1", "bc2"] +so2_aviation_lto: + so2_aviation: + long_name: "Sulfur Dioxide from Aviation LTO" + units: "kg/m2/s" + scale: [1.0] + map: ["so2"] +oc_aviation_lto: + oc_aviation: + long_name: "Organic Carbon from Aviation LTO" + units: "kg/m2/s" + scale: [0.9, 0.9] #0.5 x 1.8; 0.5 is the fraction of OC that is hydrophobic/hydrophilic; 1.8 is the scaling factor from OC to POM + map: ["oc1", "oc2"] +bc_aviation_lto: + bc_aviation: + long_name: "Black Carbon from Aviation LTO" + units: "kg/m2/s" + scale: [0.8, 0.2] #There is no 1.8 factor for BC + map: ["bc1", "bc2"] +so2_aviation_cds: + so2_aviation: + long_name: "Sulfur Dioxide from Aviation CDS" + units: "kg/m2/s" + scale: [1.0] + map: ["so2"] +oc_aviation_cds: + oc_aviation: + long_name: "Organic Carbon from Aviation CDS" + units: "kg/m2/s" + scale: [0.9, 0.9] #0.5 x 1.8; 0.5 is the fraction of OC that is hydrophobic/hydrophilic; 1.8 is the scaling factor from OC to POM + map: ["oc1", "oc2"] +bc_aviation_cds: + bc_aviation: + long_name: "Black Carbon from Aviation CDS" + units: "kg/m2/s" + scale: [0.8, 0.2] #There is no 1.8 factor for BC + map: ["bc1", "bc2"] +so2_aviation_crs: + so2_aviation: + long_name: "Sulfur Dioxide from Aviation CRS" + units: "kg/m2/s" + scale: [1.0] + map: ["so2"] +oc_aviation_crs: + oc_aviation: + long_name: "Organic Carbon from Aviation CRS" + units: "kg/m2/s" + scale: [0.9, 0.9] #0.5 x 1.8; 0.5 is the fraction of OC that is hydrophobic/hydrophilic; 1.8 is the scaling factor from OC to POM + map: ["oc1", "oc2"] +bc_aviation_crs: + bc_aviation: + long_name: "Black Carbon from Aviation CRS" + units: "kg/m2/s" + scale: [0.8, 0.2] #There is no 1.8 factor for BC + map: ["bc1", "bc2"] +bio: + limo: + long_name: "Limonene" + units: "kg/m2/s" + scale: [0.09] + map: ["oc2"] #0.05 x 1.8; Note: biogenic terpenes are assumed to be emitted as hydrophilic OC with 5% conversion rate + isoprene: + long_name: "Isoprene" + units: "kg/m2/s" + scale: [0.054] + map: ["oc2"] #0.03 x 1.8; Note: biogenic isoprene is assumed to be emitted as hydrophilic OC with 3% conversion rate + mtpa: + long_name: "Monoterpene alpha-Pinene" + units: "kg/m2/s" + scale: [0.09] + map: ["oc2"] #0.05 x 1.8; Note: biogenic terpenes are assumed to be emitted as hydrophilic OC with 5% conversion rate + mtpo: + long_name: "Monoterpene other" + units: "kg/m2/s" + scale: [0.09] + map: ["oc2"] #0.05 x 1.8; Note: biogenic terpenes are assumed to be emitted as hydrophilic OC with 5% conversion rate + #PEC: + # long_name: "Primary Elemental Carbon" + # units: "kg/m2/s" + # scale: [0.8, 0.2] # 0.8/0.2 is the fraction of hydrophobic/hydrophilic; 1.8 is not used for BC + # map: ["bc1", "bc2"] + #PSO4: + # long_name: "Primary Sulfate" + # units: "kg/m2/s" + # scale: [1.0] + # map: ["so4"] #will be changed to SO4 later +gmi: + oh: + long_name: "Hydroxyl Radical" + units: "mol/mol" + scale: [1.0] + map: ["oh"] + h2o2: + long_name: "Hydrogen Peroxide" + units: "mol/mol" + scale: [1.0] + map: ["h2o2"] + no3: + long_name: "Nitrate Radical" + units: "mol/mol" + scale: [1.0] + map: ["no3"] +dms: + conc: + long_name: "Dimethyl Sulfide" + units: "nmol/l" + scale: [1.0] + map: ["dms_in"] +volcano: + SO2: + long_name: "Volcanic Sulfur Dioxide (degassing)" + units: "kg/m2/s" + scale: [2.0] # file rate is kg S/s; 2.0 = fMassSO2/fMassSulfur (64/32) -> kg SO2/s + map: ["so2"] +dust: + clayfrac: + long_name: "Clay Fraction" + units: "1" + scale: [1.0] + map: ["MET_CLAYFRAC"] + sandfrac: + long_name: "Sand Fraction" + units: "1" + scale: [1.0] + map: ["MET_SANDFRAC"] + sep: + long_name: "Sediment Supply Map" + units: "1" + scale: [1.0] + map: ["MET_SSM"] + uthres: + long_name: "Friction Velocity Threshold" + units: "m/s" + scale: [1.0] + map: ["MET_USTAR_THRESHOLD"] +fengsha: + albedo_drag: + long_name: "Drag Partition" + units: "1" + scale: [1.0] + map: ["MET_RDRAG"] +# point: +# NO: +# long_name: "Nitrogen Oxide" +# units: "kg/m2/s" +# scale: [1.0] +# map: ["NO"] +# fire: +# NO: +# long_name: "Nitrogen Oxide" +# units: "kg/m2/s" +# scale: [1.0] +# map: ["NO"] +# FRP: +# long_name: "Fire Radiative Power" +# units: "1" +# scale: [1.0] +# map: [] +# dust: +# DU1: +# long_name: "Dust Species Fine Mode" +# units: "kg/m2/s" +# scale: [1.0] +# map: ["dust1"] +# DU2: +# long_name: "Dust Species Fine Mode" +# units: "kg/m2/s" +# scale: [1.0] +# map: ["dust2"] +# DU3: +# long_name: "Dust Species Fine Mode" +# units: "kg/m2/s" +# scale: [1.0] +# map: ["dust3"] +# DU4: +# long_name: "Dust Species Fine Mode" +# units: "kg/m2/s" +# scale: [1.0] +# map: ["dust4"] +# DU5: +# long_name: "Dust Species Coarse Mode" +# units: "kg/m2/s" +# scale: [1.0] +# map: ["dust5"] +# seasalt: +# SeaS1: +# long_name: "Seasalt Species Fine Mode" +# units: "kg/m2/s" +# scale: [1.0] +# map: ["seas1"] ##dummy mapping here for test only +# SeaS2: +# long_name: "Seasalt Species Fine Mode" +# units: "kg/m2/s" +# scale: [1.0] +# map: ["seas2"] +# SeaS3: +# long_name: "Seasalt Species Fine Mode" +# units: "kg/m2/s" +# scale: [1.0] +# map: ["seas3"] +# SeaS4: +# long_name: "Seasalt Species Fine Mode" +# units: "kg/m2/s" +# scale: [1.0] +# map: ["seas4"] +# SeaS5: +# long_name: "Seasalt Species Coarse Mode" +# units: "kg/m2/s" +# scale: [1.0] +# map: ["seas5"] \ No newline at end of file diff --git a/parm/chem/catchem/CATChem_field_mapping_gcafs_aero.yaml b/parm/chem/catchem/CATChem_field_mapping_gcafs_aero.yaml new file mode 100644 index 00000000000..2fa2a4ce6c4 --- /dev/null +++ b/parm/chem/catchem/CATChem_field_mapping_gcafs_aero.yaml @@ -0,0 +1,269 @@ +import_fields: + - standard_name: "inst_temp_levels" + catchem_var: "T" + dimensions: 3 + units: "K" + optional: false + # RH is intentionally NOT imported. Upstream/develop derives RH internally + # from T, QV, PMID (met_utilities::relative_humidity), and the C++ core uses + # the identical formula. Importing a host RH here would override the + # derivation and diverge hydrophilic swelling (settling/wetdep) from the + # legacy baseline. Keep RH derived to preserve numerical parity. + - standard_name: "inst_geop_interface" + catchem_var: "Z" + dimensions: 3 + vertical_axis: "interface" + units: "m" + optional: false + - standard_name: "inst_geop_levels" + catchem_var: "ZMID" + dimensions: 3 + units: "m" + optional: false + - standard_name: "inst_pres_interface" + catchem_var: "PEDGE" + dimensions: 3 + vertical_axis: "interface" + units: "Pa" + optional: false + - standard_name: "inst_pres_levels" + catchem_var: "PMID" + dimensions: 3 + units: "Pa" + optional: false + - standard_name: "inst_zonal_wind_levels" + catchem_var: "U" + dimensions: 3 + units: "m/s" + optional: false + - standard_name: "inst_merid_wind_levels" + catchem_var: "V" + dimensions: 3 + units: "m/s" + optional: false + - standard_name: "inst_cloud_frac_levels" + catchem_var: "CLDF" + dimensions: 3 + units: "1" + optional: false + - standard_name: "inst_rainfall_amount" + catchem_var: "PRECLSC" + dimensions: 2 + units: "m" + optional: false + - standard_name: "inst_convective_rainfall_amount" + catchem_var: "PRECCON" + dimensions: 2 + units: "m" + optional: false + - standard_name: "inst_zonal_wind_height10m" + catchem_var: "U10M" + dimensions: 2 + units: "m/s" + optional: false + - standard_name: "inst_merid_wind_height10m" + catchem_var: "V10M" + dimensions: 2 + units: "m/s" + optional: false + - standard_name: "inst_friction_velocity" + catchem_var: "USTAR" + dimensions: 2 + units: "m/s" + optional: false + - standard_name: "inst_land_sea_mask" + catchem_var: "LWI" + dimensions: 2 + units: "1" + optional: false + - standard_name: "inst_temp_height_surface" + catchem_var: "TS" + dimensions: 2 + units: "K" + optional: false + - standard_name: "inst_pres_height_surface" + catchem_var: "PS" + dimensions: 2 + units: "Pa" + optional: false + - standard_name: "inst_temp_height2m" + catchem_var: "T2M" + dimensions: 2 + units: "K" + optional: false + - standard_name: "inst_spec_humid_height2m" + catchem_var: "QV2M" + dimensions: 2 + units: "kg/kg" + optional: false + - standard_name: "inst_sensi_heat_flx" + catchem_var: "HFLUX" + dimensions: 2 + units: "w/m2" + optional: false + - standard_name: "inst_laten_heat_flx" + catchem_var: "EFLUX" + dimensions: 2 + units: "w/m2" + optional: false + - standard_name: "inst_net_sw_flx" + catchem_var: "SWGDN" + dimensions: 2 + units: "w/m2" + optional: false + - standard_name: "inst_aerodynamic_conductance" + catchem_var: "CMM" + dimensions: 2 + units: "s/m" + optional: false + - standard_name: "inst_surface_roughness" + catchem_var: "Z0" + dimensions: 2 + units: "m" + optional: false + - standard_name: "inst_pbl_height" + catchem_var: "PBLH" + dimensions: 2 + units: "m" + optional: false + - standard_name: "height" + catchem_var: "ORO" + dimensions: 2 + units: "m" + optional: true + advertise: true + - standard_name: "canopy_moisture_storage" + catchem_var: "WCA" + dimensions: 2 + units: "m3" + optional: true + advertise: true + - standard_name: "inst_canopy_resistance" + catchem_var: "RCA" + dimensions: 2 + units: "s/m" + optional: true + advertise: true + - standard_name: "surface_cell_area" + catchem_var: "AREA_M2" + dimensions: 2 + units: "m2" + optional: false + - standard_name: "surface_snow_area_fraction" + catchem_var: "FRSNO" + dimensions: 2 + units: "frac" + optional: false + - standard_name: "ice_fraction_in_atm" + catchem_var: "FRSEAICE" + dimensions: 2 + units: "frac" + optional: false + - standard_name: "leaf_area_index" + catchem_var: "LAI" + dimensions: 2 + units: "m2/m2" + optional: false + # FV3 exposes green vegetation fraction as ``vfrac``. Keep the host + # exchange name here and translate it to the mechanism-independent CATChem + # met-state key below; the NUOPC cap uses this YAML mapping at runtime. + - standard_name: "inst_vegetation_area_frac" + catchem_var: "GVF" + dimensions: 2 + units: "frac" + optional: false + - standard_name: "ocean_fraction" + catchem_var: "FROCEAN" + dimensions: 2 + units: "frac" + optional: false + - standard_name: "vegetation_type" + catchem_var: "DLUSE" + dimensions: 2 + units: "1" + optional: false + - standard_name: "lake_fraction" + catchem_var: "FRLAKE" + dimensions: 2 + units: "1" + optional: false + - standard_name: "inst_surface_soil_wetness" + catchem_var: "GWETTOP" + dimensions: 2 + units: "1" + optional: false + - standard_name: "inst_ice_nonconv_tendency_levels" + catchem_var: "PFILSAN" + dimensions: 3 + # UFS supplies nlev levels. Preserve the upstream CATChem cap's + # nlev+1 interface layout by copying those values into entries 1:nlev + # and repeating the final host value at entry nlev+1. + vertical_axis: "level_to_interface" + units: "kg/m2/s" + optional: false + - standard_name: "inst_liq_nonconv_tendency_levels" + catchem_var: "PFLLSAN" + dimensions: 3 + vertical_axis: "level_to_interface" + units: "kg/m2/s" + optional: false + - standard_name: "soil_type" + catchem_var: "DSOILTYPE" + dimensions: 2 + units: "1" + optional: false + - standard_name: "inst_soil_moisture_content" + catchem_var: "SOILM" + dimensions: 3 + vertical_axis: "soil_layer" + units: "m3/m3" + optional: false + - standard_name: "temperature_of_soil_layer" + catchem_var: "SOILT" + dimensions: 3 + vertical_axis: "soil_layer" + units: "K" + optional: true + advertise: true + - standard_name: "inst_up_sensi_heat_flx" + catchem_var: "HFLUX_UP" + dimensions: 2 + units: "w/m2" + optional: true + advertise: true + - standard_name: "inst_tracer_mass_frac" + catchem_var: "chem" + dimensions: 4 + # Match upstream: specific humidity is supplied by the host-owned sphum + # tracer, rather than through a competing rank-3 meteorology import. + host_tracer_name: "sphum" + host_tracer_var: "QV" + units: "1" + optional: false + +export_fields: + - standard_name: "inst_tracer_mass_frac" + catchem_var: "chem" + dimensions: 4 + units: "1" + optional: false + - standard_name: "inst_tracer_diag_aod" + catchem_var: "AOD" + dimensions: 2 + units: "1" + optional: false + - standard_name: "inst_tracer_diag_coszens" + catchem_var: "coszens" + dimensions: 2 + units: "1" + optional: false + - standard_name: "inst_tracer_diag_jo3o1d" + catchem_var: "jo3o1d" + dimensions: 2 + units: "1" + optional: false + - standard_name: "inst_tracer_diag_jno2" + catchem_var: "jno2" + dimensions: 2 + units: "1" + optional: false \ No newline at end of file diff --git a/parm/chem/catchem/CATChem_species_gcafs_aero.yaml b/parm/chem/catchem/CATChem_species_gcafs_aero.yaml new file mode 100644 index 00000000000..88467a8e682 --- /dev/null +++ b/parm/chem/catchem/CATChem_species_gcafs_aero.yaml @@ -0,0 +1,327 @@ +### CATChem_species.yml: CATChem Runtime Species Configuration +### +### NOTE: Add quotes around nitrogen oxide ('NO'), because YAML +### parsers will confuse this with a negative "no" value. + +# species (gas dey dep set to false to test GOCART only for now): +- name: so2 + __description: Sulfur dioxide + __is_gas: true + __is_drydep: true + __is_wetdep: true + __dd_f0: 0.0 + __dd_hstar: 1.0e+5 + molecular weight [kg mol-1]: 64.04e-3 + __dd_DvzAerSnow: 0.03 + __dd_DvzMinVal_snow: 0.2 + __dd_DvzMinVal_land: 0.3 + __viscosity: 0.0 #dummy value + #This is Luo's scheme for SO2 but adopted here + __henry_k0: 1.22 + __henry_cr: 3100.0 + __wd_retfactor: 5.0e-2 + __wd_LiqAndGas: true + __wd_convfacI2G: 6.17395e-1 + __wd_rainouteff: [1.0, 0.0, 1.0] + __wd_reevap_frac: 1.0 #GOCART resuspends full re-evaporated fraction (GEOS-Chem/Luo default 0.5) +- name: h2o2 + __description: Hydrogen peroxide + __is_gas: true + __is_drydep: false #turn off for now due to climatology reading from GOCART inputs. + __is_wetdep: true + __is_advected: false + __dd_f0: 1.0 + __dd_hstar: 5.0e+7 + molecular weight [kg mol-1]: 34.02e-3 + __viscosity: 0.0 #dummy value + __henry_k0: 8.3e+4 + __henry_cr: 7400.0 + __wd_retfactor: 5.0e-2 + __wd_LiqAndGas: true + __wd_convfacI2G: 4.36564e-1 +- name: oh + __description: Hydroxyl radical + __is_gas: true + __is_advected: false + molecular weight [kg mol-1]: 17.01e-3 +- name: no3 + __description: Nitrate radical + __is_gas: true + __is_photolysis: true + __is_advected: false + molecular weight [kg mol-1]: 62.01e-3 +- name: so4 #This is to match CMAQ name to use the restart file. so4 is used in GEOS-Chem. + __description: sulfate + __is_aerosol: true + __is_drydep: true + __is_wetdep: true + __dd_f0: 0.0 + __dd_hstar: 0.0 + molecular weight [kg mol-1]: 96.06e-3 + __radius: 0.35 + __density: 1700.0 + __dd_DvzAerSnow: 0.03 + __dd_DvzMinVal_snow: 0.01 + __dd_DvzMinVal_land: 0.01 + __lower_radius: 0.1 + __upper_radius: 1.0 + __wd_rainouteff: [1.0, 0.0, 1.0] + __wd_reevap_frac: 1.0 #GOCART resuspends full re-evaporated fraction (GEOS-Chem/Luo default 0.5) + __mie_name: SU +- name: dms + __description: Dimethyl sulfide + molecular weight [kg mol-1]: 62.13e-3 + __radius: 0.0 + __is_gas: true #gocart treat it as a gas without dry and wet deposition + __is_dust: false + __is_aerosol: false + __is_drydep: false + __henry_k0: 0.48 + __henry_cr: 3100.0 + __mie_name: SU +- name: dms_in + __description: Dimethyl sulfide in ocean water (climatology for emissions calculation) + __is_advected: false + molecular weight [kg mol-1]: 62.13e-3 + __radius: 8.7e-2 +- name: msa + __description: methanesulphonic acid + __is_aerosol: true #gocart treat it as a gas, but we treat it as aerosol + __is_drydep: true + __is_wetdep: true + __radius: 0.35 + __density: 1700.0 + __lower_radius: 0.1 + __upper_radius: 1.0 + __dd_f0: 0.0 + __dd_hstar: 0.0 + __dd_DvzAerSnow: 0.03 + __dd_DvzMinVal_snow: 0.01 + __dd_DvzMinVal_land: 0.01 + molecular weight [kg mol-1]: 96.10e-3 + __wd_rainouteff: [1.0, 0.0, 1.0] + __mie_name: SU +- name: bc1 + __description: BC1 + __is_aerosol: true + __is_drydep: true + __is_wetdep: true + __dd_f0: 0.0 + __dd_hstar: 0.0 + molecular weight [kg mol-1]: 12.01e-3 #gocart is 180 g/mol but it seems not used in gocart processes. So we go with GC + __radius: 0.35 #gocart is 0.35; GC is 0.087 + __density: 1800.0 + __dd_DvzAerSnow: 0.03 + __lower_radius: 0.1 + __upper_radius: 1.0 + __wd_rainouteff: [0.0, 0.0, 0.0] + __t_chem_loss: -1.0 #Rate of chemical destruction of carbon species [days]; Note -1 (any negative value) means carbon loss is turned off by default. + __mie_name: BC +- name: bc2 + __description: BC2 + __is_aerosol: true + __is_drydep: true + __is_wetdep: true + __dd_f0: 0.0 + __dd_hstar: 0.0 + molecular weight [kg mol-1]: 12.01e-3 #gocart is 180 g/mol but it seems not used in gocart processes. So we go with GC + __radius: 0.35 #gocart is 0.35; GC is 0.087 + __density: 1800.0 + __dd_DvzAerSnow: 0.03 + __lower_radius: 0.1 + __upper_radius: 1.0 + __wd_rainouteff: [1.0, 1.0, 1.0] + __t_chem_loss: -1.0 #Rate of chemical destruction of carbon species [days]; Note -1 (any negative value) means carbon loss is turned off by default. + __mie_name: BC +- name: oc1 + __description: OC1 + __is_aerosol: true + __is_drydep: true + __is_wetdep: true + __dd_f0: 0.0 + __dd_hstar: 0.0 + molecular weight [kg mol-1]: 12.01e-3 #gocart is 180 g/mol but it seems not used in gocart processes. So we go with GC + __radius: 0.35 #gocart is 0.35; GC is 0.087 + __density: 1800.0 + __dd_DvzAerSnow: 0.03 + __lower_radius: 0.1 + __upper_radius: 1.0 + __wd_rainouteff: [0.0, 0.0, 0.0] + __t_chem_loss: -1.0 #Rate of chemical destruction of carbon species [days]; Note -1 (any negative value) means carbon loss is turned off by default. + __mie_name: OC +- name: oc2 + __description: OC2 + __is_aerosol: true + __is_drydep: true + __is_wetdep: true + __dd_f0: 0.0 + __dd_hstar: 0.0 + molecular weight [kg mol-1]: 12.01e-3 #gocart is 180 g/mol but it seems not used in gocart processes. So we go with GC + __radius: 0.35 #gocart is 0.35; GC is 0.087 + __density: 1800.0 + __dd_DvzAerSnow: 0.03 + __lower_radius: 0.1 + __upper_radius: 1.0 + __wd_rainouteff: [1.0, 1.0, 1.0] + __t_chem_loss: -1.0 #Rate of chemical destruction of carbon species [days]; Note -1 (any negative value) means carbon loss is turned off by default. + __mie_name: OC +- name: dust1 + __long_name: dust1 + __description: dust1 + __lower_radius: 0.1 + __upper_radius: 1.0 + __dd_f0: 0.0 + __dd_hstar: 0.0 + molecular weight [kg mol-1]: 29.00e-3 + __radius: 0.73 + __density: 2500. + __is_dust: true + __is_aerosol: true + __is_drydep: true + __is_wetdep: true + __wd_rainouteff: [0.8, 0.8, 0.8] + __mie_name: DU +- name: dust2 + __long_name: dust2 + __description: dust2 + __lower_radius: 1.0 + __upper_radius: 1.8 + __dd_f0: 0.0 + __dd_hstar: 0.0 + molecular weight [kg mol-1]: 29.00e-3 + __radius: 1.4 + __density: 2650. + __is_dust: true + __is_aerosol: true + __is_drydep: true + __is_wetdep: true + __wd_rainouteff: [0.8, 0.8, 0.8] + __mie_name: DU +- name: dust3 + __long_name: dust3 + __description: dust3 + __lower_radius: 1.8 + __upper_radius: 3.0 + __dd_f0: 0.0 + __dd_hstar: 0.0 + molecular weight [kg mol-1]: 29.00e-3 + __radius: 2.4 + __density: 2650. + __is_dust: true + __is_aerosol: true + __is_drydep: true + __is_wetdep: true + __wd_rainouteff: [0.8, 0.8, 0.8] + __mie_name: DU +- name: dust4 + __long_name: dust4 + __description: dust4 + __lower_radius: 3.0 + __upper_radius: 6.0 + __dd_f0: 0.0 + __dd_hstar: 0.0 + molecular weight [kg mol-1]: 29.00e-3 + __radius: 4.5 + __density: 2650. + __is_dust: true + __is_aerosol: true + __is_drydep: true + __is_wetdep: true + __wd_rainouteff: [1.0, 1.0, 1.0] + __mie_name: DU +- name: dust5 + __long_name: dust5 + __description: dust5 + __lower_radius: 6.0 + __upper_radius: 10.0 + __dd_f0: 0.0 + __dd_hstar: 0.0 + molecular weight [kg mol-1]: 29.00e-3 + __radius: 8.0 + __density: 2650. + __is_dust: true + __is_aerosol: true + __is_drydep: true + __is_wetdep: true + __wd_rainouteff: [1.0, 1.0, 1.0] + __mie_name: DU +- name: seas1 + __long_name: seas1 + __description: seas1 + __lower_radius: 0.03 + __upper_radius: 0.1 + __dd_f0: 0.0 + __dd_hstar: 0.0 + molecular weight [kg mol-1]: 31.40e-3 #GEOS-Chem is 31.40; gocart is 58 but it seems not used in gocart processes. So we go with GC + __radius: 0.079 + __density: 2200. + __is_seasalt: true + __is_aerosol: true + __is_drydep: true + __is_wetdep: true + __wd_rainouteff: [1.0, 1.0, 1.0] + __mie_name: SS +- name: seas2 + __long_name: seas2 + __description: seas2 + __lower_radius: 0.1 + __upper_radius: 0.5 + __dd_f0: 0.0 + __dd_hstar: 0.0 + molecular weight [kg mol-1]: 31.40e-3 #GEOS-Chem is 31.40; gocart is 58 + __radius: 0.316 + __density: 2200. + __is_seasalt: true + __is_aerosol: true + __is_drydep: true + __is_wetdep: true + __wd_rainouteff: [1.0, 1.0, 1.0] + __mie_name: SS +- name: seas3 + __long_name: seas3 + __description: seas3 + __lower_radius: 0.5 + __upper_radius: 1.5 + __dd_f0: 0.0 + __dd_hstar: 0.0 + molecular weight [kg mol-1]: 31.40e-3 #GEOS-Chem is 31.40; gocart is 58 + __radius: 1.119 + __density: 2200. + __is_seasalt: true + __is_aerosol: true + __is_drydep: true + __is_wetdep: true + __wd_rainouteff: [1.0, 1.0, 1.0] + __mie_name: SS +- name: seas4 + __long_name: seas4 + __description: seas4 + __lower_radius: 1.5 + __upper_radius: 5.0 + __dd_f0: 0.0 + __dd_hstar: 0.0 + molecular weight [kg mol-1]: 31.40e-3 #GEOS-Chem is 31.40; gocart is 58 + __radius: 2.818 + __density: 2200. + __is_seasalt: true + __is_aerosol: true + __is_drydep: true + __is_wetdep: true + __wd_rainouteff: [1.0, 1.0, 1.0] + __mie_name: SS +- name: seas5 + __long_name: seas5 + __description: seas5 + __lower_radius: 5.0 + __upper_radius: 10.0 + __dd_f0: 0.0 + __dd_hstar: 0.0 + molecular weight [kg mol-1]: 31.40e-3 #GEOS-Chem is 31.40; gocart is 58 + __radius: 7.772 + __density: 2200. + __is_seasalt: true + __is_aerosol: true + __is_drydep: true + __is_wetdep: true + __wd_rainouteff: [1.0, 1.0, 1.0] + __mie_name: SS \ No newline at end of file diff --git a/parm/chem/catchem/catchem_tracer.list b/parm/chem/catchem/catchem_tracer.list new file mode 100644 index 00000000000..dfb29d31263 --- /dev/null +++ b/parm/chem/catchem/catchem_tracer.list @@ -0,0 +1,18 @@ +so2 +so4 +dms +msa +bc1 +bc2 +oc1 +oc2 +dust1 +dust2 +dust3 +dust4 +dust5 +seas1 +seas2 +seas3 +seas4 +seas5 diff --git a/parm/fetch/gcafs_ATMC_gdas-anl.yaml.j2 b/parm/fetch/gcafs_ATMC_gdas-anl.yaml.j2 new file mode 120000 index 00000000000..606412bb892 --- /dev/null +++ b/parm/fetch/gcafs_ATMC_gdas-anl.yaml.j2 @@ -0,0 +1 @@ +gcafs_ATMA_gdas-anl.yaml.j2 \ No newline at end of file diff --git a/sorc/build_opts.yaml b/sorc/build_opts.yaml index e0a08d9b723..edc8d22500d 100644 --- a/sorc/build_opts.yaml +++ b/sorc/build_opts.yaml @@ -34,6 +34,7 @@ systems: - "gefs_ww3_prepost" gcafs: - "gcafs_model" + - "gcafs_catchem_model" - "nexus" - "gsi_utils" build: @@ -71,6 +72,12 @@ build: walltime: "00:40:00" memory: "40G" + gcafs_catchem_model: + command: "./build_ufs.sh -a CATCHEM -e gcafs_catchem_model.x" + cores: 12 + walltime: "00:40:00" + memory: "40G" + upp: command: "./build_upp.sh" cores: 8 diff --git a/sorc/build_ufs.sh b/sorc/build_ufs.sh index 835de9ae801..43a41e8b884 100755 --- a/sorc/build_ufs.sh +++ b/sorc/build_ufs.sh @@ -58,6 +58,7 @@ case "${EXEC_NAME}" in "gefs_model.x") COMPILE_ID=2 ;; "sfs_model.x") COMPILE_ID=3 ;; "gcafs_model.x") COMPILE_ID=4 ;; + "gcafs_catchem_model.x") COMPILE_ID=5 ;; *) echo "Unsupported executable name: ${EXEC_NAME}" exit 1 diff --git a/sorc/link_workflow.sh b/sorc/link_workflow.sh index b696b4fb3f7..4616c12953f 100755 --- a/sorc/link_workflow.sh +++ b/sorc/link_workflow.sh @@ -415,6 +415,14 @@ for sys in "${model_systems[@]}"; do fi done +# CATChem GCAFS executable (built via "./build_ufs.sh -a CATCHEM -e gcafs_catchem_model.x") +if [[ -s "ufs_model_gcafs_catchem.x" ]]; then + rm -f "ufs_model_gcafs_catchem.x" +fi +if [[ -f "${HOMEglobal}/sorc/ufs_model.fd/tests/gcafs_catchem_model.x" ]]; then + safe_link_or_copy "${HOMEglobal}/sorc/ufs_model.fd/tests/gcafs_catchem_model.x" "ufs_model_gcafs_catchem.x" +fi + # WW3 pre/post executables declare -a ww3_exes=("ww3_grid" "ww3_prnc" "ww3_outp" "ww3_gint" "ww3_grib") declare -A wave_systems diff --git a/sorc/ufs_model.fd b/sorc/ufs_model.fd index f6ca8234186..6dc36481b6d 160000 --- a/sorc/ufs_model.fd +++ b/sorc/ufs_model.fd @@ -1 +1 @@ -Subproject commit f6ca8234186f07594b7621e960c3e20d4def8952 +Subproject commit 6dc36481b6d1347f3c5332905ad48e7e9da8675e diff --git a/ush/forecast_postdet.sh b/ush/forecast_postdet.sh index ff52ef84d37..9cd731ae5df 100755 --- a/ush/forecast_postdet.sh +++ b/ush/forecast_postdet.sh @@ -1030,6 +1030,94 @@ GOCART_out() { fi } +CATCHEM_rc() { + echo "SUB ${FUNCNAME[0]}: Staging CATChem runtime configuration files" + + # Link directory containing the chemistry input dataset (ExtData), if provided. + # CATChem reads Mie optics tables from ./ExtData/monochromatic/ (see the + # 'mie.directory' key in CATChem_config.yml). + if [[ -n "${AERO_INPUTS_DIR}" ]]; then + ${NLN} "${AERO_INPUTS_DIR}" "${DATA}/ExtData" + status=$? + if [[ ${status} -ne 0 ]]; then + exit "${status}" + fi + fi + + # Stage the selected CATChem configuration set under the fixed names expected + # by the CATChem NUOPC cap and by CATChem_config.yml itself: + # - the cap hardcodes 'CATChem_new_config.yml' and 'CATChem_field_mapping.yml' + # - the config file references './CATChem_species.yml' and './CATChem_emission.yml' + local -a catchem_src=( + "CATChem_config_${CATCHEM_CONFIG}.yaml" + "CATChem_species_${CATCHEM_CONFIG}.yaml" + "CATChem_emissions_${CATCHEM_CONFIG}.yaml" + "CATChem_field_mapping_${CATCHEM_CONFIG}.yaml" + ) + local -a catchem_dst=( + "CATChem_new_config.yml" + "CATChem_species.yml" + "CATChem_emission.yml" + "CATChem_field_mapping.yml" + ) + + local idx + for idx in "${!catchem_src[@]}"; do + if [[ ! -r "${CATCHEM_CONFIG_DIR}/${catchem_src[idx]}" ]]; then + err_exit "FATAL ERROR: CATChem configuration file '${CATCHEM_CONFIG_DIR}/${catchem_src[idx]}' not found (CATCHEM_CONFIG='${CATCHEM_CONFIG}'), ABORT!" + fi + cpreq "${CATCHEM_CONFIG_DIR}/${catchem_src[idx]}" "${DATA}/${catchem_dst[idx]}" + done +} + +CATCHEM_postdet() { + echo "SUB ${FUNCNAME[0]}: Pre-cleaning CATChem output files" + + # CATChem diagnostics (prefix 'catchem_diag' per CATChem_config.yml) cannot + # overwrite existing files, so remove any pre-existing output in COM. + local outfile + for outfile in "${COMOUT_CHEM_HISTORY}"/catchem*; do + if [[ -e "${outfile}" ]]; then + rm -f "${outfile}" + fi + done +} + +CATCHEM_out() { + echo "SUB ${FUNCNAME[0]}: Copying output data for CATChem" + + # CATChem writes diagnostics into the output directory configured in + # CATChem_config.yml ('directory: "./output"', relative to DATA). + local diag_dir="${DATA}/output" + if [[ ! -d "${diag_dir}" ]]; then + diag_dir="${DATA}" + fi + + # Build MPMD cmdfile to copy CATChem output files in parallel + local cmdfile="${DATA}/cmdfile_catchem_out" + rm -f "${cmdfile}" + + local outfile + for outfile in "${diag_dir}"/catchem*; do + if [[ -e "${outfile}" ]]; then + echo "cpfs ${outfile} ${COMOUT_CHEM_HISTORY}/$(basename "${outfile}")" >> "${cmdfile}" + fi + done + + if [[ -s "${cmdfile}" ]]; then + if [[ ! -d "${COMOUT_CHEM_HISTORY}" ]]; then + echo "INFO: Directory ${COMOUT_CHEM_HISTORY} does not exist, creating..." + mkdir -p "${COMOUT_CHEM_HISTORY}" + fi + + "${USHglobal}/run_mpmd.sh" "${cmdfile}" && true + export err=$? + if [[ ${err} -ne 0 ]]; then + err_exit "run_mpmd.sh failed to copy CATChem output files!" + fi + fi +} + # shellcheck disable=SC2178 CMEPS_postdet() { echo "SUB ${FUNCNAME[0]}: Linking output data for CMEPS mediator" diff --git a/ush/forecast_predet.sh b/ush/forecast_predet.sh index 7d31a3ec2f2..20643baa44c 100755 --- a/ush/forecast_predet.sh +++ b/ush/forecast_predet.sh @@ -769,3 +769,16 @@ GOCART_predet() { # a subsequent PR to fix this properly. ${NLN} "${COMIN_CHEM_INPUT}" "${DATA}/ChemInput" } + +# shellcheck disable=SC2034 +CATCHEM_predet() { + echo "SUB ${FUNCNAME[0]}: CATChem before run type determination" + + # FHMAX gets modified when IAU is on, so keep original value for CATChem output + GOCART_MAX=${FHMAX} + + # Stage the prepared chemistry input (emissions) when available + if [[ -d "${COMIN_CHEM_INPUT}" ]]; then + ${NLN} "${COMIN_CHEM_INPUT}" "${DATA}/ChemInput" + fi +} diff --git a/ush/parsing_namelists_fv3.sh b/ush/parsing_namelists_fv3.sh index ff17b0c485b..afaa0191cc0 100755 --- a/ush/parsing_namelists_fv3.sh +++ b/ush/parsing_namelists_fv3.sh @@ -291,6 +291,7 @@ FV3_namelists() { local DO_SKEB=${do_skeb:-".false."} local FRAC_GRID=${FRAC_GRID:-".true."} local CPLCHM=${cplchm:-".false."} + local CPLCAT=${cplcat:-".false."} local CPLFLX=${cplflx:-".false."} local CPLICE=${cplice:-".false."} local CPLWAV=${cplwav:-".false."} diff --git a/ush/parsing_ufs_configure.sh b/ush/parsing_ufs_configure.sh index c8bb0643060..9a93e9e38d7 100755 --- a/ush/parsing_ufs_configure.sh +++ b/ush/parsing_ufs_configure.sh @@ -101,7 +101,8 @@ UFS_configure() { if [[ "${cplchm}" = ".true." ]]; then - local chm_model="gocart" + local chm_model="${chm_model:-gocart}" + local cplcat="${cplcat:-.false.}" local chm_petlist_bounds="0 $((CHMPETS - 1))" local chm_omp_num_threads="${CHMTHREADS}" local coupling_interval_sec="${CPL_FAST}"