diff --git a/.gitignore b/.gitignore index ae916806c6..0c9f941a97 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ src/physics/carma/base src/physics/clubb src/physics/cosp2/src src/physics/silhs +src/chemistry/geoschem/geoschem_src src/physics/pumas src/physics/pumas-frozen src/dynamics/fv3/atmos_cubed_sphere diff --git a/Externals_CAM.cfg b/Externals_CAM.cfg index 03698dc3f6..3b9952f6e9 100644 --- a/Externals_CAM.cfg +++ b/Externals_CAM.cfg @@ -71,6 +71,13 @@ sparse = ../.mpas_sparse_checkout hash = b8c33daa required = True +[geoschem] +local_path = src/chemistry/geoschem/geoschem_src +protocol = git +repo_url = https://github.com/geoschem/geos-chem.git +tag = 14.1.2 +required = True + [hemco] local_path = src/hemco tag = hemco-cesm1_2_1_hemco3_6_3_cesm diff --git a/bld/build-namelist b/bld/build-namelist index 97bcbcd991..5ebe186a76 100755 --- a/bld/build-namelist +++ b/bld/build-namelist @@ -540,7 +540,7 @@ my $rad_prog_bcarb = (($prog_species =~ "BC" or $aero_chem) and !($chem_rad_pa my $rad_prog_sulf = (($prog_species =~ "SO4" or $aero_chem) and !($chem_rad_passive)); my $rad_prog_dust = (($prog_species =~ "DST" or $aero_chem) and !($chem_rad_passive)); my $rad_prog_sslt = (($prog_species =~ "SSLT" or $aero_chem) and !($chem_rad_passive)); -my $rad_prog_ozone = (($chem =~ "mozart" or $chem =~ "waccm_ma" or $chem =~ "tsmlt" or $chem =~ "trop_strat") and !($chem_rad_passive)); +my $rad_prog_ozone = (($chem =~ "mozart" or $chem =~ "waccm_ma" or $chem =~ "tsmlt" or $chem =~ "trop_strat" or $chem =~ /geoschem/) and !($chem_rad_passive)); # Check for eruptive volcano emissions. These will be radiatively active by default, but # only if using BAM and the camrt radiation package @@ -576,12 +576,13 @@ if ( ($chem ne 'none') or ( $prog_species ) ){ my ( $gas_wetdep_list, $aer_wetdep_list, $aer_sol_facti, $aer_sol_factb, $aer_scav_coef, $aer_drydep_list, $gas_drydep_list ) = - set_dep_lists( $cfgdir, $chem_proc_src, $chem_src_dir, $nl, $print ); + set_dep_lists( $chem, $cfgdir, $chem_proc_src, $chem_src_dir, $nl, $print ); if (length($gas_wetdep_list)>2){ add_default($nl, 'gas_wetdep_method' ); add_default($nl, 'gas_wetdep_list', 'val'=>$gas_wetdep_list ); } + if (length($aer_wetdep_list)>2){ # determine if prescribed aerosols are not needed ... if ($aer_wetdep_list =~ /so4/i && @@ -862,7 +863,7 @@ my $radval = "'A:Q:H2O'"; if (($chem =~ /waccm_ma/ or $chem =~ /waccm_tsmlt/) and !$chem_rad_passive) { $radval .= ",'A:O2:O2','A:CO2:CO2'"; } -elsif ($chem =~ /trop_strat/ and !$chem_rad_passive) { +elsif (($chem =~ /trop_strat/ or $chem =~ /geoschem/) and !$chem_rad_passive) { $radval .= ",'N:O2:O2','A:CO2:CO2'"; } elsif (($co2_cycle and !$co2_cycle_rad_passive) or ($chem =~ /ghg_mam4/)) { @@ -897,6 +898,8 @@ if ((($chem =~ /ghg_mam4/) or ($chem =~ /waccm_ma/) or ($chem =~ /waccm_sc_mam/) $radval .= ",'A:N2O:N2O','A:CH4:CH4','A:CFC11:CFC11','A:CFC12:CFC12'"; } elsif ($prog_ghg1 and !$prog_ghg2 and !$chem_rad_passive ) { $radval .= ",'A:N2O:N2O','A:CH4:CH4','N:CFC11:CFC11','N:CFC12:CFC12'"; +} elsif ($chem =~ /geoschem/) { + $radval .= ",'A:N2O:N2O','A:CH4:CH4','A:CFC11:CFC11','A:CFC12:CFC12'"; } else { $radval .= ",'N:N2O:N2O','N:CH4:CH4','N:CFC11:CFC11','N:CFC12:CFC12'"; } @@ -2018,6 +2021,34 @@ if (($chem =~ /trop_mozart/ or $chem =~ /trop_strat/ or $chem =~ /waccm_tsmlt/) } } +if ($chem =~ /geoschem/) { + + my $val; + + # Species with fixed lower boundary + $val = "'CCL4','CH4','N2O','CO2','CFC11','CFC12','CH3BR','CH3CCL3','CH3CL'" + .",'HCFC22','CFC114','CFC115','HCFC141B','HCFC142B','CH2BR2','CHBR3','H2402'"; + + if ($chem_has_ocs) { + $val .= ",'OCS'"; + } + if (chem_has_species($cfg, 'SF6')) { + $val .= ",'SF6'"; + } + add_default($nl, 'flbc_list', 'val'=>$val); + unless (defined $nl->get_value('flbc_type')) { + add_default($nl, 'flbc_type', 'val'=>'CYCLICAL'); + add_default($nl, 'flbc_cycle_yr', 'val'=>'2000'); + } + + my @files; + # Datasets + @files = ( 'soil_erod_file', 'flbc_file' ); + foreach my $file (@files) { + add_default($nl, $file); + } +} + if ($chem =~ /trop_mozart/ or $chem =~ /trop_strat/ or $chem =~ /waccm_tsmlt/) { my $val; @@ -2364,10 +2395,12 @@ if (($chem =~ /_mam4/ or $chem =~ /_mam5/) and ($phys =~ /cam6/ or $phys =~ /cam } } } - add_default($nl, 'srf_emis_specifier', 'val'=>$val); - unless (defined $nl->get_value('srf_emis_type')) { - add_default($nl, 'srf_emis_type', 'val'=>'CYCLICAL'); - add_default($nl, 'srf_emis_cycle_yr', 'val'=>2000); + if ($chem !~ /geoschem/) { + add_default($nl, 'srf_emis_specifier', 'val'=>$val); + unless (defined $nl->get_value('srf_emis_type')) { + add_default($nl, 'srf_emis_type', 'val'=>'CYCLICAL'); + add_default($nl, 'srf_emis_cycle_yr', 'val'=>2000); + } } # Vertical emission datasets: @@ -2433,10 +2466,12 @@ if (($chem =~ /_mam4/ or $chem =~ /_mam5/) and ($phys =~ /cam6/ or $phys =~ /cam $first = 0; } } - add_default($nl, 'ext_frc_specifier', 'val'=>$val); - unless (defined $nl->get_value('ext_frc_type')) { - add_default($nl, 'ext_frc_type', 'val'=>"'CYCLICAL'"); - add_default($nl, 'ext_frc_cycle_yr', 'val'=>2000); + if ($chem !~ /geoschem/) { + add_default($nl, 'ext_frc_specifier', 'val'=>$val); + unless (defined $nl->get_value('ext_frc_type')) { + add_default($nl, 'ext_frc_type', 'val'=>"'CYCLICAL'"); + add_default($nl, 'ext_frc_cycle_yr', 'val'=>2000); + } } # MEGAN emissions @@ -2482,6 +2517,30 @@ if (($chem =~ /_mam4/ or $chem =~ /_mam5/) and ($phys =~ /cam6/ or $phys =~ /cam add_default($nl, 'megan_factors_file'); add_default($nl, 'megan_mapped_emisfctrs', 'val'=>'.false.'); } + if ($chem =~ /geoschem/) { + my $val = "'ISOP = isoprene'," + . "'MOH = methanol'," + . "'EOH = ethanol'," + . "'CH2O = formaldehyde'," + . "'ALD2 = acetaldehyde'," + . "'ACTA = acetic_acid'," + . "'ACET = acetone'," + . "'HCOOH = formic_acid'," + . "'HCN = hydrogen_cyanide'," + . "'CO = carbon_monoxide'," + . "'C2H6 = ethane'," + . "'C2H4 = ethene'," + . "'C3H8 = propane'," + . "'ALK4 = pentane + hexane + heptane + tricyclene'," + . "'PRPE = propene + butene'," + . "'TOLU = toluene'," + . "'LIMO = limonene'," + . "'MTPA = pinene_a + pinene_b + sabinene + carene_3'," + . "'MTPO = terpinene_g + terpinene_a + terpinolene + myrcene + ocimene_al + ocimene_t_b + ocimene_c_b + thujene_a + 2met_styrene + cymene_p + cymene_o + bornene + fenchene_a + camphene + phellandrene_a + phellandrene_b'"; + add_default($nl, 'megan_specifier', 'val'=>$val); + add_default($nl, 'megan_factors_file'); + add_default($nl, 'megan_mapped_emisfctrs', 'val'=>'.false.'); + } if ($chem =~ /trop_strat_mam4_vbs/ or $chem =~ /trop_strat_mam5_vbs/) { my $val = "'ISOP = isoprene'," . "'MTERP = carene_3 + pinene_a + thujene_a + bornene + terpineol_4 + terpineol_a + terpinyl_ACT_a " @@ -2897,6 +2956,29 @@ if ($nl->get_value('use_hemco') =~ m/$TRUE/io) { # ignored at runtime when HEMCO is used. $nl->delete_variable('chem_inparm', 'ext_frc_specifier'); $nl->delete_variable('chem_inparm', 'srf_emis_specifier'); + + if ($chem =~ /geoschem/) { + + # For now, HEMCO config and diagnostic configuration files are always used from + # the case directory. Exit if user has specified other paths in the user namelist + # because it will not work. + if ($nl->get_value('hemco_config_file') ne "'" . $inputdata_rootdir . "/HEMCO_Config.rc'") { + die "CAM Namelist ERROR: When running with GEOS-Chem chemistry, hemco_config_file\n". + "must not be manually set in the namelist. Instead, modify (or symlink from) the HEMCO_Config.rc\n". + "in the case directory, which will be copied to the run directory when submitting.\n". + "Then remove the hemco_config_file option from the user namelist.\n"; + } + + if ($nl->get_value('hemco_diagn_file') ne "'" . $inputdata_rootdir . "/HEMCO_Diagn.rc'") { + die "CAM Namelist ERROR: When running with GEOS-Chem chemistry, hemco_diagn_file\n". + "must not be manually set in the namelist. Instead, modify (or symlink from) the HEMCO_Diagn.rc\n". + "in the case directory, which will be copied to the run directory when submitting.\n". + "Then remove the hemco_diagn_file option from the user namelist.\n"; + } + + $nl->set_variable_value('hemco_nl', 'hemco_config_file', "'HEMCO_Config.rc'"); + $nl->set_variable_value('hemco_nl', 'hemco_diagn_file', "'HEMCO_Diagn.rc'"); + } } # Physics options @@ -3025,7 +3107,7 @@ if (!$simple_phys) { } # tropopause level used in gas-phase / aerosol processes -if (($chem ne 'none') and ($chem ne 'terminator')) { +if (($chem ne 'none') and ($chem ne 'terminator') and !($chem =~ /geoschem/)) { add_default($nl, 'chem_use_chemtrop'); } @@ -3562,7 +3644,7 @@ if ( length($nl->get_value('soil_erod_file'))>0 ) { add_default($nl, 'dust_emis_fact', 'tms'=>'1'); } else { - if ($chem =~ /trop_strat/ or $chem =~ /waccm_ma/ or $chem =~ /waccm_tsmlt/ or $chem =~ /trop_mozart/) { + if ($chem =~ /trop_strat/ or $chem =~ /geoschem/ or $chem =~ /waccm_ma/ or $chem =~ /waccm_tsmlt/ or $chem =~ /trop_mozart/) { add_default($nl, 'dust_emis_fact', 'ver'=>'chem'); } else { diff --git a/bld/config_files/definition.xml b/bld/config_files/definition.xml index 37f43e918b..7f3ed23873 100644 --- a/bld/config_files/definition.xml +++ b/bld/config_files/definition.xml @@ -103,8 +103,8 @@ meteor_smoke (Meteor Smoke), mixed_sulfate (Meteor Smoke and Sulfate), pmc (Pola sulfate (Sulfate Aerosols), tholin (early earth haze), test_detrain (Detrainment), test_growth (Particle Growth), test_passive (Passive Dust), test_radiative (Radiatively Active Dust), test_swelling (Sea Salt), test_tracers (Asian Monsoon), test_tracers2 (Guam). - - Chemistry package: none,ghg_mam4,terminator,trop_mam3,trop_mam4,trop_mam7,trop_mozart,trop_strat_mam4_ts2,trop_strat_mam4_vbs,trop_strat_mam4_vbsext,trop_strat_mam5_ts2,trop_strat_mam5_vbs,trop_strat_mam5_vbsext,waccm_ma,waccm_mad,waccm_ma_sulfur,waccm_sc,waccm_sc_mam4,waccm_mad_mam4,waccm_ma_mam4,waccm_tsmlt_mam4,waccm_tsmlt_mam4_vbsext,waccm_mad_mam5,waccm_ma_mam5,waccm_tsmlt_mam5,waccm_tsmlt_mam5_vbsext + + Chemistry package: none,ghg_mam4,terminator,trop_mam3,trop_mam4,trop_mam7,trop_mozart,trop_strat_mam4_ts2,trop_strat_mam4_vbs,trop_strat_mam4_vbsext,trop_strat_mam5_ts2,trop_strat_mam5_vbs,trop_strat_mam5_vbsext,waccm_ma,waccm_mad,waccm_ma_sulfur,waccm_sc,waccm_sc_mam4,waccm_mad_mam4,waccm_ma_mam4,waccm_tsmlt_mam4,waccm_tsmlt_mam4_vbsext,waccm_mad_mam5,waccm_ma_mam5,waccm_tsmlt_mam5,waccm_tsmlt_mam5_vbsext,geoschem_mam4 Prognostic mozart species packages: list of any subset of the following: DST,SSLT,SO4,GHG,OC,BC,CARBON16 diff --git a/bld/configure b/bld/configure index fc9151315b..7ec8b13833 100755 --- a/bld/configure +++ b/bld/configure @@ -67,7 +67,7 @@ OPTIONS trop_strat_mam4_vbs | trop_strat_mam4_vbsext | trop_strat_mam5_ts2 | trop_strat_mam5_vbs | trop_strat_mam5_vbsext | waccm_ma | waccm_mad | waccm_ma_sulfur | waccm_sc | waccm_sc_mam4 | waccm_mad_mam4 | waccm_ma_mam4 | waccm_tsmlt_mam4 | waccm_tsmlt_mam4_vbsext | waccm_mad_mam5 | - waccm_ma_mam5 | waccm_tsmlt_mam5 | waccm_tsmlt_mam5_vbsext ]. + waccm_ma_mam5 | waccm_tsmlt_mam5 | waccm_tsmlt_mam5_vbsext | geoschem_mam4 ]. Default: trop_mam4 for cam6 and trop_mam3 for cam5. -[no]clubb_sgs Switch on [off] CLUBB_SGS. Default: on for cam6, otherwise off. -clubb_opts Comma separated list of CLUBB options to turn on/off. By default they are all off. @@ -592,7 +592,6 @@ if (defined $opts{'chem'}) { # If the user has specified a simple physics package... if ($simple_phys) { - # the only valid chemistry options are 'none' and 'terminator' if (($chem_pkg ne 'none') and ($chem_pkg ne 'terminator')) { die "configure ERROR: -phys=$phys_pkg -chem=$chem_pkg\n". " -chem can only be set to 'none' or 'terminator'.\n"; @@ -1360,7 +1359,11 @@ my $chem_cppdefs = ''; my $chem_src_dir = ''; if (!$prog_species) { - $chem_src_dir = "$cam_dir/src/chemistry/pp_$chem_pkg"; + if ($chem_pkg =~ 'geoschem') { + $chem_src_dir = "$cam_dir/src/chemistry/geoschem"; + } else { + $chem_src_dir = "$cam_dir/src/chemistry/pp_$chem_pkg"; + } $cfg_ref->set('chem_src_dir', $chem_src_dir); } @@ -1387,10 +1390,10 @@ if ($customize) { } if ($print>=2) { print "Chem preprocessor compiler: $chemproc_fc $eol"; } ($chem_nadv) = chem_preprocess($cfg_ref,$print,$chemproc_fc); -} elsif ($chem_pkg ne 'none') { +} elsif ($chem_pkg ne 'none' and $chem_pkg !~ 'geoschem') { # copy over chem docs - copy("$chem_src_dir/chem_mech.doc",$cam_bld) or die "copy failed $! \n"; - copy("$chem_src_dir/chem_mech.in" ,$cam_bld) or die "copy failed $! \n"; + copy("$chem_src_dir/chem_mech.doc",$cam_bld) or die "copy of chem_mec.doc failed $! \n"; + copy("$chem_src_dir/chem_mech.in" ,$cam_bld) or die "copy of chem_mech.in failed $! \n"; ($chem_nadv) = chem_number_adv($chem_src_dir); } @@ -1404,6 +1407,13 @@ if ($chem_pkg =~ '_mam3') { $chem_cppdefs = ' -DMODAL_AERO -DMODAL_AERO_7MODE '; } +# Customize GEOS-Chem advected species and chemistry CPP definitions +if ($chem_pkg =~ 'geoschem') { + $chem_cppdefs .= ' -DEXTERNAL_GRID -DEXTERNAL_FORCING '; + if ($chem_pkg =~ '_mam4') { + $chem_nadv = 267; # includes GC advected species (233), CO2 (1), and MAM aerosols (33) + } +} # CARMA sectional microphysics # # New CARMA models need to define the number of advected constituents. @@ -1565,8 +1575,6 @@ else { $nadv = $cfg_ref->get('nadv'); if ($print>=2) { print "Total advected constituents: $nadv$eol"; } -#----------------------------------------------------------------------------------------------- - #----------------------------------------------------------------------------------------------- # Makefile configuration ####################################################################### #----------------------------------------------------------------------------------------------- @@ -1675,7 +1683,7 @@ elsif ($fc =~ /nvfor/) { $fc_type = 'nvhpc'; } # User override for Fortran compiler type if (defined $opts{'fc_type'}) { $fc_type = $opts{'fc_type'}; } -if ($fc_type == "oneapi") {$fc_type = 'intel'; } +if ($fc_type eq "oneapi") {$fc_type = 'intel'; } if ($fc_type) { $cfg_ref->set('fc_type', $fc_type); if ($print>=2) { print "Fortran compiler type: $fc_type$eol"; } @@ -1941,6 +1949,12 @@ if ($unicon) { $cfg_cppdefs .= ' -DUSE_UNICON'; } # HEMCO_CESM - indicates CESM model environment. Deprecated, will be removed soon. $cfg_cppdefs .= ' -DMODEL_ -DMODEL_CESM -DHEMCO_CESM -DUSE_REAL8 '; +# Compiler CPP definitions for GEOS-Chem +if ($chem_pkg =~ 'geoschem') { + if ($fc_type eq 'intel') { $cfg_cppdefs .= ' -DLINUX_IFORT'; } + elsif ($fc_type eq 'gnu') { $cfg_cppdefs .= ' -DLINUX_GFORTRAN'; } +} + #----------------------------------------------------------------------------------------------- # CPP defines to put on Makefile @@ -2161,10 +2175,21 @@ sub write_filepath if ($chem_src_dir) { print $fh "$chem_src_dir\n"; } + + # GEOS-Chem must be prior to Mozart + if ($chem_pkg =~ 'geoschem') { + print $fh "$chem_src_dir/geoschem_src/GeosCore\n"; + print $fh "$chem_src_dir/geoschem_src/GeosUtil\n"; + print $fh "$chem_src_dir/geoschem_src/Headers\n"; + print $fh "$chem_src_dir/geoschem_src/ISORROPIA\n"; + print $fh "$chem_src_dir/geoschem_src/KPP/fullchem\n"; + print $fh "$camsrcdir/src/chemistry/pp_none\n"; + } + if ($chem =~ /_mam/) { - print $fh "$camsrcdir/src/chemistry/modal_aero\n"; + print $fh "$camsrcdir/src/chemistry/modal_aero\n"; } else { - print $fh "$camsrcdir/src/chemistry/bulk_aero\n"; + print $fh "$camsrcdir/src/chemistry/bulk_aero\n"; } print $fh "$camsrcdir/src/chemistry/aerosol\n"; diff --git a/bld/namelist_files/geoschem_master_aer_drydep_list.xml b/bld/namelist_files/geoschem_master_aer_drydep_list.xml new file mode 100644 index 0000000000..a31d3ff31c --- /dev/null +++ b/bld/namelist_files/geoschem_master_aer_drydep_list.xml @@ -0,0 +1,91 @@ + + + + + + + dst_a1 + so4_a1 + nh4_a1 + pom_a1 + pomff1_a1 + pombb1_a1 + soa_a1 + bc_a1 + ncl_a1 + num_a1 + so4_a2 + nh4_a2 + soa_a2 + ncl_a2 + dst_a2 + num_a2 + dst_a3 + ncl_a3 + so4_a3 + pom_a3 + bc_a3 + num_a3 + ncl_a4 + so4_a4 + pom_a4 + pomff1_a4 + pombb1_a4 + bc_a4 + nh4_a4 + num_a4 + dst_a5 + so4_a5 + nh4_a5 + num_a5 + ncl_a6 + so4_a6 + nh4_a6 + num_a6 + dst_a7 + so4_a7 + nh4_a7 + num_a7 + soa1_a1 + soa1_a2 + soa2_a1 + soa2_a2 + soa3_a1 + soa3_a2 + soa4_a1 + soa4_a2 + soa5_a1 + soa5_a2 + soaff1_a1 + soaff2_a1 + soaff3_a1 + soaff4_a1 + soaff5_a1 + soabb1_a1 + soabb2_a1 + soabb3_a1 + soabb4_a1 + soabb5_a1 + soabg1_a1 + soabg2_a1 + soabg3_a1 + soabg4_a1 + soabg5_a1 + soaff1_a2 + soaff2_a2 + soaff3_a2 + soaff4_a2 + soaff5_a2 + soabb1_a2 + soabb2_a2 + soabb3_a2 + soabb4_a2 + soabb5_a2 + soabg1_a2 + soabg2_a2 + soabg3_a2 + soabg4_a2 + soabg5_a2 + + + diff --git a/bld/namelist_files/geoschem_master_aer_wetdep_list.xml b/bld/namelist_files/geoschem_master_aer_wetdep_list.xml new file mode 100644 index 0000000000..16391485fe --- /dev/null +++ b/bld/namelist_files/geoschem_master_aer_wetdep_list.xml @@ -0,0 +1,89 @@ + + + + + dst_a1 + so4_a1 + nh4_a1 + pom_a1 + pomff1_a1 + pombb1_a1 + soa_a1 + bc_a1 + ncl_a1 + num_a1 + so4_a2 + nh4_a2 + soa_a2 + ncl_a2 + dst_a2 + num_a2 + dst_a3 + ncl_a3 + so4_a3 + pom_a3 + bc_a3 + num_a3 + ncl_a4 + so4_a4 + pom_a4 + pomff1_a4 + pombb1_a4 + bc_a4 + nh4_a4 + num_a4 + dst_a5 + so4_a5 + nh4_a5 + num_a5 + ncl_a6 + so4_a6 + nh4_a6 + num_a6 + dst_a7 + so4_a7 + nh4_a7 + num_a7 + soa1_a1 + soa1_a2 + soa2_a1 + soa2_a2 + soa3_a1 + soa3_a2 + soa4_a1 + soa4_a2 + soa5_a1 + soa5_a2 + soaff1_a1 + soaff2_a1 + soaff3_a1 + soaff4_a1 + soaff5_a1 + soabb1_a1 + soabb2_a1 + soabb3_a1 + soabb4_a1 + soabb5_a1 + soabg1_a1 + soabg2_a1 + soabg3_a1 + soabg4_a1 + soabg5_a1 + soaff1_a2 + soaff2_a2 + soaff3_a2 + soaff4_a2 + soaff5_a2 + soabb1_a2 + soabb2_a2 + soabb3_a2 + soabb4_a2 + soabb5_a2 + soabg1_a2 + soabg2_a2 + soabg3_a2 + soabg4_a2 + soabg5_a2 + + + diff --git a/bld/namelist_files/geoschem_master_gas_drydep_list.xml b/bld/namelist_files/geoschem_master_gas_drydep_list.xml new file mode 100644 index 0000000000..eebafa33a7 --- /dev/null +++ b/bld/namelist_files/geoschem_master_gas_drydep_list.xml @@ -0,0 +1,170 @@ + + + + + + + ACET + ACTA + ALD2 + AROMP4 + AROMP5 + ATOOH + BALD + BENZP + BR2 + BRCL + BRNO3 + BZCO3H + BZPAN + CH2O + CL2 + CLNO2 + CLNO3 + CLO + CLOO + CSL + EOH + ETHLN + ETHN + ETHP + ETNO3 + ETP + GLYC + GLYX + H2O2 + HAC + HBR + HC5A + HCL + HCOOH + HI + HMHP + HMML + HNO3 + HOBR + HOCL + HOI + HONIT + HPALD1 + HPALD2 + HPALD3 + HPALD4 + HPETHNL + I2 + I2O2 + I2O3 + I2O4 + IBR + ICHE + ICL + ICN + ICPDH + IDC + IDCHP + IDHDP + IDHPE + IDN + IEPOXA + IEPOXB + IEPOXD + IHN1 + IHN2 + IHN3 + IHN4 + INPB + INPD + IONO + IONO2 + IPRNO3 + ITCN + ITHN + LIMO + LVOC + LVOCOA + MACR + MACR1OOH + MAP + MCRDH + MCRENOL + MCRHN + MCRHNB + MCRHP + MCT + MENO3 + MGLY + MOH + MONITS + MONITU + MPAN + MTPA + MTPO + MVK + MVKDH + MVKHC + MVKHCB + MVKHP + MVKN + MVKPC + N2O5 + NH3 + NO2 + NPHEN + NPRNO3 + O3 + PAN + PHEN + PP + PPN + PROPNN + PRPN + PYAC + R4N2 + R4P + RA3P + RB3P + RIPA + RIPB + RIPC + RIPD + RP + SO2 + AERI + AONITA + ASOA1 + ASOA2 + ASOA3 + ASOAN + ASOG1 + ASOG2 + ASOG3 + BRSALA + BRSALC + INDIOL + IONITA + ISALA + ISALC + MONITA + MSA + NH4 + NIT + NITS + SALAAL + SALACL + SALCAL + SALCCL + SO4S + SOAGX + SOAIE + TSOA0 + TSOA1 + TSOA2 + TSOA3 + TSOG0 + TSOG1 + TSOG2 + TSOG3 + PFE + + + diff --git a/bld/namelist_files/geoschem_master_gas_wetdep_list.xml b/bld/namelist_files/geoschem_master_gas_wetdep_list.xml new file mode 100644 index 0000000000..419f518c32 --- /dev/null +++ b/bld/namelist_files/geoschem_master_gas_wetdep_list.xml @@ -0,0 +1,152 @@ + + + + + ACTA + ALD2 + AROMP4 + AROMP5 + ATOOH + BALD + BENZP + BR2 + BRCL + BZCO3H + BZPAN + CH2O + CSL + EOH + ETHLN + ETHN + ETHP + ETP + GLYC + GLYX + H2O2 + HAC + HBR + HC5A + HCL + HCOOH + HI + HMHP + HMML + HNO3 + HOBR + HOCL + HOI + HONIT + HPETHNL + I2 + I2O2 + I2O3 + I2O4 + IBR + ICHE + ICL + ICN + ICPDH + IDCHP + IDHDP + IDHPE + IDN + IEPOXA + IEPOXB + IEPOXD + IHN1 + IHN2 + IHN3 + IHN4 + INPB + INPD + IONO + IONO2 + ITCN + ITHN + LIMO + LVOC + LVOCOA + MACR1OOH + MAP + MCRDH + MCRENOL + MCRHN + MCRHNB + MCRHP + MCT + MEK + MGLY + MOH + MONITS + MONITU + MP + MPAN + MPN + MTPA + MTPO + MVK + MVKDH + MVKHC + MVKHCB + MVKHP + MVKN + MVKPC + NH3 + NPHEN + PAN + PHEN + PP + PPN + PROPNN + PRPE + PRPN + PYAC + R4N2 + R4P + RA3P + RB3P + RIPA + RIPB + RIPC + RIPD + RP + SO2 + AERI + AONITA + ASOA1 + ASOA2 + ASOA3 + ASOAN + ASOG1 + ASOG2 + ASOG3 + BRSALA + BRSALC + INDIOL + IONITA + ISALA + ISALC + MONITA + MSA + NH4 + NIT + NITS + SALAAL + SALACL + SALCAL + SALCCL + SO4S + SOAGX + SOAIE + TSOA0 + TSOA1 + TSOA2 + TSOA3 + TSOG0 + TSOG1 + TSOG2 + TSOG3 + PFE + + + diff --git a/bld/namelist_files/master_aer_drydep_list.xml b/bld/namelist_files/mozart_master_aer_drydep_list.xml similarity index 100% rename from bld/namelist_files/master_aer_drydep_list.xml rename to bld/namelist_files/mozart_master_aer_drydep_list.xml diff --git a/bld/namelist_files/master_aer_wetdep_list.xml b/bld/namelist_files/mozart_master_aer_wetdep_list.xml similarity index 100% rename from bld/namelist_files/master_aer_wetdep_list.xml rename to bld/namelist_files/mozart_master_aer_wetdep_list.xml diff --git a/bld/namelist_files/master_gas_drydep_list.xml b/bld/namelist_files/mozart_master_gas_drydep_list.xml similarity index 100% rename from bld/namelist_files/master_gas_drydep_list.xml rename to bld/namelist_files/mozart_master_gas_drydep_list.xml diff --git a/bld/namelist_files/master_gas_wetdep_list.xml b/bld/namelist_files/mozart_master_gas_wetdep_list.xml similarity index 100% rename from bld/namelist_files/master_gas_wetdep_list.xml rename to bld/namelist_files/mozart_master_gas_wetdep_list.xml diff --git a/bld/namelist_files/namelist_defaults_cam.xml b/bld/namelist_files/namelist_defaults_cam.xml index 16cf8e67db..d2e028a388 100644 --- a/bld/namelist_files/namelist_defaults_cam.xml +++ b/bld/namelist_files/namelist_defaults_cam.xml @@ -740,6 +740,7 @@ 0.5D0 0.5D0 0.7D0 +0.5D0 0.5D0 0.5D0 0.5D0 @@ -763,29 +764,34 @@ atm/waccm/gw/mfspectra_shallow_c140530.nc 0.25d0 0.5d0 +0.5d0 0.5d0 0.5d0 0.5d0 0.5d0 1.d0 2.d0 +2.d0 2.d0 2.d0 2.d0 2.d0 .true. .false. +.false. .false. .false. .false. .false. .false. .true. +.true. .true. .true. .true. .true. .true. +.false. .false. .false. .false. @@ -1934,6 +1940,7 @@ atm/cam/chem/trop_mozart/dvel/dep_data_c20221208.nc +atm/cam/chem/geoschem/dvel/dep_data_file_geoschem_c230417.nc atm/waccm/phot/effxstex.txt @@ -3402,4 +3409,5 @@ 144 91 + diff --git a/bld/namelist_files/namelist_definition.xml b/bld/namelist_files/namelist_definition.xml index 286bf4f953..14da5403a3 100644 --- a/bld/namelist_files/namelist_definition.xml +++ b/bld/namelist_files/namelist_definition.xml @@ -4967,7 +4967,7 @@ Default: set by build-namelist trop_strat_mam4_vbs,trop_strat_mam4_vbsext,trop_strat_mam5_ts2,trop_strat_mam5_vbs, trop_strat_mam5_vbsext,waccm_ma,waccm_mad,waccm_ma_sulfur,waccm_sc,waccm_sc_mam4, waccm_mad_mam4,waccm_ma_mam4,waccm_tsmlt_mam4,waccm_tsmlt_mam4_vbsext,waccm_mad_mam5, - waccm_ma_mam5,waccm_tsmlt_mam5,waccm_tsmlt_mam5_vbsext"> + waccm_ma_mam5,waccm_tsmlt_mam5,waccm_tsmlt_mam5_vbsext,geoschem"> Name of the CAM chemistry package. N.B. this variable may not be set by the user. It is set by build-namelist via information in the configure cache file to be consistent with how CAM was built. @@ -5123,6 +5123,7 @@ Default: NONE. + Full pathname of HEMCO data root for use in reading HEMCO input files. @@ -5160,6 +5161,14 @@ Force emission year for HEMCO clock if positive. This will force cycling of data Default: set by build-namelist for climo cases, otherwise -1 to use model clock. + + + +Full pathname to GEOS-Chem chemistry inputs directory +Default: set by build-namelist. + + diff --git a/bld/namelist_files/use_cases/2000_geoschem.xml b/bld/namelist_files/use_cases/2000_geoschem.xml new file mode 100644 index 0000000000..7463a49361 --- /dev/null +++ b/bld/namelist_files/use_cases/2000_geoschem.xml @@ -0,0 +1,174 @@ + + + + + + + + +atm/cam/geoschem/ExtData/CHEM_INPUTS/ + +atm/cam/geoschem/initial_conditions/f.e20.FC2010.f09_f09.144.GC_vbsext.001.cam.i.0007-01-01-00000.nc + +atm/cam/geoschem/initial_conditions/f.e20.FC2010.f19_f19.144.GC_vbsext.001.cam.i.0007-01-01-00000.nc + +HEMCO_Config.rc +HEMCO_Diagn.rc + + + +00010101 + + +atm/cam/solar/SolarForcing1995-2005avg_c160929.nc +20000101 +FIXED + + +.true. +.true. +.false. +0.25D0 + + +CYCLICAL +2000 +atm/waccm/lb/LBC_2000climo_CMIP6_0p5degLat_c180227.nc + + + + + + + + + + 1,30,365,240,240,480,365,73,30 + 0,-24,-24,-3,-1,1,-24,-120,-240 +'A','A','A','A','A','A','A','A','I' + +.true. +.false. +.false. +.false. +.false. +.false. +.false. +.false. +.false. + + +'AREA', +'HEIGHT', +'T', +'U', +'V', +'Q', +'PS', +'CLOUD', +'TROPP_P', +'TROPP_T', +'TROPP_Z', +'DF_CO', +'DF_O3', +'DF_NO2', +'DF_SO4', +'DF_NIT', +'CT_O3', +'CT_OH', +'OHwgtByAirMassColumnFull', +'Chem_SO3AQ', +'Jval_Cl2O2', +'Jval_H2O2', +'Jval_NO2', +'Jval_PAN', +'JvalO3O3P', +'JvalO3O1D', +'LNO_COL_PROD', +'Prod_Ox', +'Prod_SO4', +'Prod_CO', +'Prod_H2O2', +'ProdCOfromCH4', +'ProdCOfromNMVOC', +'Loss_Ox', +'Loss_CH4', +'Loss_CO', +'LossOHbyCH4columnTrop', +'LossOHbyMCFcolumnTrop', +'LossHNO3onSeaSalt', +'ACET', +'ALD2', +'ALK4', +'BR', +'BRCL', +'BRNO3', +'BRO', +'BROX', +'BROY', +'C3H8', +'CH2O', +'CH3CL', +'CH4', +'CL', +'CLNO3', +'CLO', +'CLOX', +'CLOY', +'CO', +'DMS', +'EOH', +'H2O', +'H2O2', +'H2SO4', +'HO2', +'HOX', +'HBR', +'HCL', +'HOBR', +'HOCL', +'HNO3', +'HNO4', +'ISOP', +'MACR', +'MAP', +'MEK', +'MOH', +'MVK', +'N2O', +'N2O5', +'NHX', +'NIT', +'NO', +'NO2', +'NO3', +'NOX', +'NOY', +'O3', +'OH', +'PAN', +'PM25', +'RCHO', +'SALA', +'SALC', +'SO2', +'SO4', +'SOX', +'TOLU', +'bc_a1', +'bc_a4', +'dst_a1', +'dst_a2', +'dst_a3', +'num_a1', +'num_a2', +'num_a3', +'num_a4', +'pom_a1', +'pom_a4', +'so4_a1', +'so4_a2', +'so4_a3', + + + diff --git a/bld/namelist_files/use_cases/2010_geoschem.xml b/bld/namelist_files/use_cases/2010_geoschem.xml new file mode 100644 index 0000000000..2d3ee5db95 --- /dev/null +++ b/bld/namelist_files/use_cases/2010_geoschem.xml @@ -0,0 +1,171 @@ + + + + + + +atm/cam/geoschem/ExtData/CHEM_INPUTS/ + +atm/cam/geoschem/initial_conditions/f.e20.FC2010.f09_f09.144.GC_vbsext.001.cam.i.0007-01-01-00000.nc + +atm/cam/geoschem/initial_conditions/f.e20.FC2010.f19_f19.144.GC_vbsext.001.cam.i.0007-01-01-00000.nc + +HEMCO_Config.rc +HEMCO_Diagn.rc + + + + +00010101 + + +atm/cam/solar/SolarForcing2006-2014avg_c180917.nc +20100101 +FIXED + + +.true. +.true. +.false. +0.25D0 + + +CYCLICAL +2010 +atm/waccm/lb/LBC_2010climo_CMIP6_0p5degLat_c180227.nc + + + + + + + + 1,30,365,240,240,480,365,73,30 + 0,-24,-24,-3,-1,1,-24,-120,-240 +'A','A','A','A','A','A','A','A','I' + +.true. +.false. +.false. +.false. +.false. +.false. +.false. +.false. +.false. + + +'AREA', +'HEIGHT', +'T', +'U', +'V', +'Q', +'PS', +'CLOUD', +'TROPP_P', +'TROPP_T', +'TROPP_Z', +'DF_CO', +'DF_O3', +'DF_NO2', +'DF_SO4', +'DF_NIT', +'CT_O3', +'CT_OH', +'OHwgtByAirMassColumnFull', +'Chem_SO3AQ', +'Jval_Cl2O2', +'Jval_H2O2', +'Jval_NO2', +'Jval_PAN', +'JvalO3O3P', +'JvalO3O1D', +'LNO_COL_PROD', +'Prod_Ox', +'Prod_SO4', +'Prod_CO', +'Prod_H2O2', +'ProdCOfromCH4', +'ProdCOfromNMVOC', +'Loss_Ox', +'Loss_CH4', +'Loss_CO', +'LossOHbyCH4columnTrop', +'LossOHbyMCFcolumnTrop', +'LossHNO3onSeaSalt', +'ACET', +'ALD2', +'ALK4', +'BR', +'BRCL', +'BRNO3', +'BRO', +'BROX', +'BROY', +'C3H8', +'CH2O', +'CH3CL', +'CH4', +'CL', +'CLNO3', +'CLO', +'CLOX', +'CLOY', +'CO', +'DMS', +'EOH', +'H2O', +'H2O2', +'H2SO4', +'HO2', +'HOX', +'HBR', +'HCL', +'HOBR', +'HOCL', +'HNO3', +'HNO4', +'ISOP', +'MACR', +'MAP', +'MEK', +'MOH', +'MVK', +'N2O', +'N2O5', +'NHX', +'NIT', +'NO', +'NO2', +'NO3', +'NOX', +'NOY', +'O3', +'OH', +'PAN', +'PM25', +'RCHO', +'SALA', +'SALC', +'SO2', +'SO4', +'SOX', +'TOLU', +'bc_a1', +'bc_a4', +'dst_a1', +'dst_a2', +'dst_a3', +'num_a1', +'num_a2', +'num_a3', +'num_a4', +'pom_a1', +'pom_a4', +'so4_a1', +'so4_a2', +'so4_a3', + + + diff --git a/bld/namelist_files/use_cases/hist_geoschem.xml b/bld/namelist_files/use_cases/hist_geoschem.xml new file mode 100644 index 0000000000..78b681e572 --- /dev/null +++ b/bld/namelist_files/use_cases/hist_geoschem.xml @@ -0,0 +1,168 @@ + + + + + + + + +atm/cam/geoschem/ExtData/CHEM_INPUTS/ + +atm/cam/geoschem/initial_conditions/f.e20.FC2010.f09_f09.144.GC_vbsext.001.cam.i.0007-01-01-00000.nc + +atm/cam/geoschem/initial_conditions/f.e20.FC2010.f19_f19.144.GC_vbsext.001.cam.i.0007-01-01-00000.nc + +HEMCO_Config.rc +HEMCO_Diagn.rc + + + +00010101 + + +atm/cam/solar/SolarForcingCMIP6_18491230-23000102_c20200615.nc + + +.true. +.true. +.false. +0.25D0 + +SERIAL +atm/waccm/lb/LBC_1750-2014_CMIP6_0p5degLat_c170126.nc + +SERIAL + +'noy', 'nhx' + + + + 1,30,365,240,240,480,365,73,30 + 0,-24,-24,-3,-1,1,-24,-120,-240 +'A','A','A','A','A','A','A','A','I' + +.true. +.false. +.false. +.false. +.false. +.false. +.false. +.false. +.false. + + +'AREA', +'HEIGHT', +'T', +'U', +'V', +'Q', +'PS', +'CLOUD', +'TROPP_P', +'TROPP_T', +'TROPP_Z', +'DF_CO', +'DF_O3', +'DF_NO2', +'DF_SO4', +'DF_NIT', +'CT_O3', +'CT_OH', +'OHwgtByAirMassColumnFull', +'Chem_SO3AQ', +'Jval_Cl2O2', +'Jval_H2O2', +'Jval_NO2', +'Jval_PAN', +'JvalO3O3P', +'JvalO3O1D', +'LNO_COL_PROD', +'Prod_Ox', +'Prod_SO4', +'Prod_CO', +'Prod_H2O2', +'ProdCOfromCH4', +'ProdCOfromNMVOC', +'Loss_Ox', +'Loss_CH4', +'Loss_CO', +'LossOHbyCH4columnTrop', +'LossOHbyMCFcolumnTrop', +'LossHNO3onSeaSalt', +'ACET', +'ALD2', +'ALK4', +'BR', +'BRCL', +'BRNO3', +'BRO', +'BROX', +'BROY', +'C3H8', +'CH2O', +'CH3CL', +'CH4', +'CL', +'CLNO3', +'CLO', +'CLOX', +'CLOY', +'CO', +'DMS', +'EOH', +'H2O', +'H2O2', +'H2SO4', +'HO2', +'HOX', +'HBR', +'HCL', +'HOBR', +'HOCL', +'HNO3', +'HNO4', +'ISOP', +'MACR', +'MAP', +'MEK', +'MOH', +'MVK', +'N2O', +'N2O5', +'NHX', +'NIT', +'NO', +'NO2', +'NO3', +'NOX', +'NOY', +'O3', +'OH', +'PAN', +'PM25', +'RCHO', +'SALA', +'SALC', +'SO2', +'SO4', +'SOX', +'TOLU', +'bc_a1', +'bc_a4', +'dst_a1', +'dst_a2', +'dst_a3', +'num_a1', +'num_a2', +'num_a3', +'num_a4', +'pom_a1', +'pom_a4', +'so4_a1', +'so4_a2', +'so4_a3', + + + diff --git a/bld/namelist_files/use_cases/hist_geoschem_nudged.xml b/bld/namelist_files/use_cases/hist_geoschem_nudged.xml new file mode 100644 index 0000000000..0550880d80 --- /dev/null +++ b/bld/namelist_files/use_cases/hist_geoschem_nudged.xml @@ -0,0 +1,223 @@ + + + + + + + + +atm/cam/geoschem/ExtData/CHEM_INPUTS/ + +atm/cam/geoschem/initial_conditions/f.e20.FC2010.f09_f09.144.GC_vbsext.001.cam.i.0007-01-01-00000.nc + +atm/cam/geoschem/initial_conditions//f.e20.FC2010.f19_f19.144.GC_vbsext.001.cam.i.0007-01-01-00000.nc + +HEMCO_Config.rc +HEMCO_Diagn.rc + + + +00010101 + + +atm/cam/solar/SolarForcingCMIP6_18491230-23000102_c20200615.nc + + +.true. +.true. +.false. +0.25D0 + +SERIAL +atm/waccm/lb/LBC_1750-2014_CMIP6_0p5degLat_c170126.nc + +SERIAL + +'noy', 'nhx' + + + +.true. +'atm/cam/met/nudging/MERRA2_fv09_32L/' +'atm/cam/met/nudging/MERRA2_ne30_32L/' +'atm/cam/met/nudging/MERRA2_ne30pg3_32L/' +'atm/cam/met/nudging/MERRA2_ne0CONUS30x8_L32/' +'%y/MERRA2_fv09.cam2.i.%y-%m-%d-%s.nc' +'%y/MERRA2_ne30np4_L32.cam2.i.%y-%m-%d-%s.nc' +'%y/MERRA2_ne30pg3_L32.cam2.i.%y-%m-%d-%s.nc' +'%y/MERRA2_ne0CONUS30x8_L32.cam2.i.%y-%m-%d-%s.nc' +0 +0 +4 +8 + +48 +384 +1 +0.06 +1 +0.06 +1 +0.06 +0 +0.00 +0 +0.00 +2010 +2013 +1 +1 +2020 +12 +31 +0.0 +37. +9999. +56. +1. +5. +180. +264. +9999. +94. +1. +5. +.false. +.true. +33. +0.001 +0. +0.1 +.false. + + + + 1,30,365,240,240,480,365,73,30 + 0,-24,-24,-3,-1,1,-24,-120,-240 +'A','A','A','A','A','A','A','A','I' + +.true. +.false. +.false. +.false. +.false. +.false. +.false. +.false. +.false. + + +'AREA', +'HEIGHT', +'T', +'U', +'V', +'Q', +'PS', +'CLOUD', +'TROPP_P', +'TROPP_T', +'TROPP_Z', +'DF_CO', +'DF_O3', +'DF_NO2', +'DF_SO4', +'DF_NIT', +'CT_O3', +'CT_OH', +'OHwgtByAirMassColumnFull', +'Chem_SO3AQ', +'Jval_Cl2O2', +'Jval_H2O2', +'Jval_NO2', +'Jval_PAN', +'JvalO3O3P', +'JvalO3O1D', +'LNO_COL_PROD', +'Prod_Ox', +'Prod_SO4', +'Prod_CO', +'Prod_H2O2', +'ProdCOfromCH4', +'ProdCOfromNMVOC', +'Loss_Ox', +'Loss_CH4', +'Loss_CO', +'LossOHbyCH4columnTrop', +'LossOHbyMCFcolumnTrop', +'LossHNO3onSeaSalt', +'ACET', +'ALD2', +'ALK4', +'BR', +'BRCL', +'BRNO3', +'BRO', +'BROX', +'BROY', +'C3H8', +'CH2O', +'CH3CL', +'CH4', +'CL', +'CLNO3', +'CLO', +'CLOX', +'CLOY', +'CO', +'DMS', +'EOH', +'H2O', +'H2O2', +'H2SO4', +'HO2', +'HOX', +'HBR', +'HCL', +'HOBR', +'HOCL', +'HNO3', +'HNO4', +'ISOP', +'MACR', +'MAP', +'MEK', +'MOH', +'MVK', +'N2O', +'N2O5', +'NHX', +'NIT', +'NO', +'NO2', +'NO3', +'NOX', +'NOY', +'O3', +'OH', +'PAN', +'PM25', +'RCHO', +'SALA', +'SALC', +'SO2', +'SO4', +'SOX', +'TOLU', +'bc_a1', +'bc_a4', +'dst_a1', +'dst_a2', +'dst_a3', +'num_a1', +'num_a2', +'num_a3', +'num_a4', +'pom_a1', +'pom_a4', +'so4_a1', +'so4_a2', +'so4_a3', + + + diff --git a/bld/perl5lib/Build/ChemNamelist.pm b/bld/perl5lib/Build/ChemNamelist.pm index e415e4a299..7d4f5a6103 100644 --- a/bld/perl5lib/Build/ChemNamelist.pm +++ b/bld/perl5lib/Build/ChemNamelist.pm @@ -1,5 +1,7 @@ package Build::ChemNamelist; +no if $] >= 5.017011, warnings => 'experimental::smartmatch'; + #------------------------------------------------------------------------------------- # generates species lists for chemistry namelist settings #------------------------------------------------------------------------------------- @@ -42,7 +44,7 @@ sub chem_has_species #------------------------------------------------------------------------------- sub set_dep_lists { - my ( $cfgdir, $chem_proc_src, $chem_src_dir, $nl, $print_lvl ) = @_; + my ( $chem, $cfgdir, $chem_proc_src, $chem_src_dir, $nl, $print_lvl ) = @_; my ( $gas_wetdep_list, $aer_wetdep_list, $aer_drydep_list, $aer_sol_facti, $aer_sol_factb, $aer_scav_coef, $gas_drydep_list ) ; @@ -69,13 +71,17 @@ sub set_dep_lists if ($print_lvl>=2) {print "Chemistry species : @species_list \n" ;} if ($print_lvl>=2) {print "Not transported species : @nottransported_list \n" ;} - $gas_wetdep_list = get_gas_wetdep_list( $cfgdir, $print_lvl, \@species_list, \@nottransported_list ); + $gas_wetdep_list = get_gas_wetdep_list( $chem, $cfgdir, $print_lvl, \@species_list, \@nottransported_list ); + if ($print_lvl>=2) {print " gas wet dep list : $gas_wetdep_list \n" ;} - $aer_wetdep_list = get_aer_wetdep_list( $cfgdir, $print_lvl, \@species_list, \@nottransported_list ); + $aer_wetdep_list = get_aer_wetdep_list( $chem, $cfgdir, $print_lvl, \@species_list, \@nottransported_list ); + if ($print_lvl>=2) {print " aer wet dep list : $aer_wetdep_list \n" ;} - $gas_drydep_list = get_gas_drydep_list( $cfgdir, $print_lvl, \@species_list, \@nottransported_list ); + $gas_drydep_list = get_gas_drydep_list( $chem, $cfgdir, $print_lvl, \@species_list, \@nottransported_list ); + if ($print_lvl>=2) {print " dry dep list : $gas_drydep_list \n" ;} - $aer_drydep_list = get_aer_drydep_list( $cfgdir, $print_lvl, \@species_list, \@nottransported_list ); + $aer_drydep_list = get_aer_drydep_list( $chem, $cfgdir, $print_lvl, \@species_list, \@nottransported_list ); + if ($print_lvl>=2) {print " aer dry dep list : $aer_drydep_list \n" ;} # set solubility factors for aerosols if (length($aer_wetdep_list)>2){ @@ -203,9 +209,14 @@ sub print_modal_info #------------------------------------------------------------------------------- sub get_gas_drydep_list { - my ($cfg_dir,$print_lvl,$species_list,$nottransported_list) = @_; + my ($chem,$cfg_dir,$print_lvl,$species_list,$nottransported_list) = @_; - my $master_file = "$cfg_dir/namelist_files/master_gas_drydep_list.xml"; + my $master_file = ''; + if ($chem =~ /geoschem/) { + $master_file = "$cfg_dir/namelist_files/geoschem_master_gas_drydep_list.xml"; + } else { + $master_file = "$cfg_dir/namelist_files/mozart_master_gas_drydep_list.xml"; + } my $list = get_dep_list($master_file,$print_lvl,$species_list,$nottransported_list); @@ -218,9 +229,14 @@ sub get_gas_drydep_list #------------------------------------------------------------------------------- sub get_aer_drydep_list { - my ($cfg_dir,$print_lvl,$species_list,$nottransported_list) = @_; + my ($chem,$cfg_dir,$print_lvl,$species_list,$nottransported_list) = @_; - my $master_file = "$cfg_dir/namelist_files/master_aer_drydep_list.xml"; + my $master_file = ''; + if ($chem =~ /geoschem/) { + $master_file = "$cfg_dir/namelist_files/geoschem_master_aer_drydep_list.xml"; + } else { + $master_file = "$cfg_dir/namelist_files/mozart_master_aer_drydep_list.xml"; + } my $list = get_dep_list($master_file,$print_lvl,$species_list,$nottransported_list); @@ -231,10 +247,15 @@ sub get_aer_drydep_list #------------------------------------------------------------------------------- sub get_aer_wetdep_list { - my ($cfg_dir,$print_lvl,$species_list,$nottransported_list) = @_; - - my $master_file = "$cfg_dir/namelist_files/master_aer_wetdep_list.xml"; + my ($chem,$cfg_dir,$print_lvl,$species_list,$nottransported_list) = @_; + my $master_file = ''; + if ($chem =~ /geoschem/) { + $master_file = "$cfg_dir/namelist_files/geoschem_master_aer_wetdep_list.xml"; + } else { + $master_file = "$cfg_dir/namelist_files/mozart_master_aer_wetdep_list.xml"; + } + my $list = get_dep_list($master_file,$print_lvl,$species_list,$nottransported_list); if ($print_lvl>=2) {print " aer wet dep list : $list \n" ;} @@ -244,9 +265,14 @@ sub get_aer_wetdep_list #------------------------------------------------------------------------------- sub get_gas_wetdep_list { - my ($cfg_dir,$print_lvl,$species_list,$nottransported_list) = @_; + my ($chem,$cfg_dir,$print_lvl,$species_list,$nottransported_list) = @_; - my $master_file = "$cfg_dir/namelist_files/master_gas_wetdep_list.xml"; + my $master_file = ''; + if ($chem =~ /geoschem/) { + $master_file = "$cfg_dir/namelist_files/geoschem_master_gas_wetdep_list.xml"; + } else { + $master_file = "$cfg_dir/namelist_files/mozart_master_gas_wetdep_list.xml"; + } my $list = get_dep_list($master_file,$print_lvl,$species_list,$nottransported_list); @@ -284,7 +310,6 @@ sub get_dep_list return ($list); } - #------------------------------------------------------------------------------- sub read_master_list_file { diff --git a/cime_config/buildnml b/cime_config/buildnml index 28e3e8198c..0af683719a 100755 --- a/cime_config/buildnml +++ b/cime_config/buildnml @@ -36,6 +36,7 @@ def buildnml(case, caseroot, compname): din_loc_root = case.get_value("DIN_LOC_ROOT") atm_ncpl = case.get_value("ATM_NCPL") CAM_NAMELIST_OPTS = case.get_value("CAM_NAMELIST_OPTS") + CAM_CONFIG_OPTS = case.get_value("CAM_CONFIG_OPTS") CAM_NML_USE_CASE = case.get_value("CAM_NML_USE_CASE") DEBUG = case.get_value("DEBUG") NINST_ATM = case.get_value("NINST_ATM") @@ -211,6 +212,18 @@ def buildnml(case, caseroot, compname): if (os.path.isfile(file1)) and (not os.path.isfile(file2)): shutil.copy(file1,file2) + # ----------------------------------------------------- + # copy geos-chem config files to rundir if using geos-chem chemistry + # ----------------------------------------------------- + + if os.path.isdir(rundir) and '-chem geoschem' in CAM_CONFIG_OPTS: + for fname in ['species_database.yml', 'geoschem_config.yml', + 'HISTORY.rc', 'HEMCO_Config.rc', 'HEMCO_Diagn.rc']: + file1 = os.path.join(caseroot, fname) + file2 = os.path.join(rundir, fname) + logger.info("GEOS-Chem config file copy: file1 %s file2 %s ", file1, file2) + shutil.copy(file1,file2) + ############################################################################### def _main_func(): diff --git a/cime_config/cam.case_setup.py b/cime_config/cam.case_setup.py new file mode 100755 index 0000000000..e8cb17c5a6 --- /dev/null +++ b/cime_config/cam.case_setup.py @@ -0,0 +1,69 @@ +#! /usr/bin/env python3 + +"""Copy GEOS-Chem configuration files from source to the case directory. +This script is run from CIME when calling case.setup""" + +import logging +import os +import shutil +import sys + +_CIMEROOT = os.environ.get("CIMEROOT") +if _CIMEROOT is None: + raise SystemExit("ERROR: must set CIMEROOT environment variable") +# end if +_LIBDIR = os.path.join(_CIMEROOT, "CIME", "Tools") +sys.path.append(_LIBDIR) +sys.path.insert(0, _CIMEROOT) + +#pylint: disable=wrong-import-position +from CIME.case import Case + +logger = logging.getLogger(__name__) + +if len(sys.argv) != 3: + raise SystemExit(f"Incorrect call to {sys.argv[0]}, need CAM root and case root") +# end if +cam_root = sys.argv[1] +case_root = sys.argv[2] + +with Case(case_root) as case: + cam_config = case.get_value('CAM_CONFIG_OPTS') + # Gather case information (from _build_usernl_files in case_setup.py) + comp_interface = case.get_value("COMP_INTERFACE") + + if comp_interface == "nuopc": + ninst = case.get_value("NINST") + elif ninst == 1: + ninst = case.get_value("NINST_CAM") + # end if +# end with + +# GEOS-Chem only: copy config files to case +if '-chem geoschem' in cam_config: + geoschem_config_src = os.path.join(cam_root, 'src', 'chemistry', + 'geoschem', 'geoschem_src', 'run', 'CESM') + if not os.path.isdir(geoschem_config_src): + raise SystemExit(f"ERROR: Did not find path to GEOS-Chem config files at {geoschem_config_src}") + # end if + for fileName in ['species_database.yml', 'geoschem_config.yml', 'HISTORY.rc', + 'HEMCO_Config.rc', 'HEMCO_Diagn.rc']: + source_file = os.path.join(cam_root, geoschem_config_src, fileName) + if not os.path.exists(source_file): + raise SystemExit(f"ERROR: Did not find source file, {source_file}") + # end if + spaths = os.path.splitext(source_file) + for inst_num in range(ninst): + if ninst > 1: + target_file = f"{spaths[0]}_{inst_num+1:04d}{spaths[1]}" + else: + target_file = os.path.join(case_root, fileName) + # end if + if not os.path.exists(target_file): + logger.info("CAM namelist one-time copy of GEOS-Chem run directory files: source_file %s target_file %s ", + source_file, target_file) + shutil.copy(source_file, target_file) + # end if + # end for + # end for +# end if diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index 0dc9a4d10b..21d3ec6d4b 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -8,9 +8,9 @@ CAM =============== --> - CAM cam6 physics: - CAM cam5 physics: - CAM cam4 physics: + CAM cam6 physics: + CAM cam5 physics: + CAM cam4 physics: CAM cam3 physics: CAM simplified and non-versioned physics : CAM7 development physics: @@ -38,6 +38,7 @@ --> CAM-Chem troposphere/stratosphere chemistry with simplified VBS-SOA: CAM-Chem troposphere/stratosphere chemistry with simplified VBS-SOA and expanded isoprene and terpene oxidation: + GEOS-Chem troposphere/stratosphere chemistry : CAM-Chem troposphere/stratosphere chem with simplified volatility basis set SOA scheme and fire emissons : CAM CLUBB - turned on by default in CAM60: CAM-Chem troposphere/stratosphere chem with extended volatility basis set SOA scheme and modal aersols : @@ -138,6 +139,7 @@ -phys cam_dev -chem ghg_mam4 -chem trop_strat_mam5_vbs + -chem geoschem_mam4 -chem trop_mam7 -chem trop_strat_mam5_vbsext @@ -228,6 +230,7 @@ waccm_ma_2000_cam6 waccm_sc_2000_cam6 2000_trop_strat_vbs_cam6 + 2000_geoschem waccmx_ma_2000_cam6 aquaplanet_cam3 @@ -244,6 +247,7 @@ 2010_trop_strat_vbs_cam6 waccm_tsmlt_2010_cam6 waccm_sc_2010_cam6 + 2010_geoschem 1850-2005_cam5 1850-2005_cam4 @@ -264,6 +268,8 @@ hist_trop_strat_nudged_cam6 hist_trop_strat_vbsext_cam6 hist_trop_strat_vbsfire_cam6 + hist_geoschem + hist_geoschem_nudged waccmx_ma_hist_cam6 1850-PD_cam5 diff --git a/cime_config/config_compsets.xml b/cime_config/config_compsets.xml index eedcf65e38..d2aec47d2e 100644 --- a/cime_config/config_compsets.xml +++ b/cime_config/config_compsets.xml @@ -555,6 +555,26 @@ HIST_CAM60%WXIED%SDYN_CLM50%SP_CICE%PRES_DOCN%DOM_MOSART_SGLC_SWAV + + FC2000climo_GC + 2000_CAM60%GEOSCHEM%HEMCO_CLM50%SP_CICE%PRES_DOCN%DOM_MOSART_CISM2%NOEVOLVE_SWAV + + + + FC2010climo_GC + 2010_CAM60%GEOSCHEM%HEMCO_CLM50%SP_CICE%PRES_DOCN%DOM_MOSART_CISM2%NOEVOLVE_SWAV + + + + FCHIST_GC + HIST_CAM60%GEOSCHEM%HEMCO_CLM50%SP_CICE%PRES_DOCN%DOM_MOSART_CISM2%NOEVOLVE_SWAV + + + + FCnudged_GC + HIST_CAM60%GEOSCHEM%HEMCO%NUDG_CLM50%SP_CICE%PRES_DOCN%DOM_MOSART_SGLC_SWAV + + @@ -568,6 +588,7 @@ 1980-01-01 1850-01-01 2010-01-01 + 2015-01-01 2013-01-01 1995-01-01 1995-01-01 @@ -576,6 +597,8 @@ 2010-01-01 1980-01-01 2000-01-01 + 2000-01-01 + 2010-01-01 2004-01-01 1950-01-01 diff --git a/cime_config/testdefs/testlist_cam.xml b/cime_config/testdefs/testlist_cam.xml index 384387edc2..4b6647697d 100644 --- a/cime_config/testdefs/testlist_cam.xml +++ b/cime_config/testdefs/testlist_cam.xml @@ -17,6 +17,14 @@ + + + + + + + + @@ -34,6 +42,14 @@ + + + + + + + + @@ -1689,6 +1705,16 @@ + + + + + + + + + + @@ -1896,7 +1922,17 @@ - + + + + + + + + + + + diff --git a/doc/ChangeLog b/doc/ChangeLog index a219f92580..9d40f7ba63 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,5 +1,287 @@ =============================================================== +Tag name: cam6_3_147 +Originator(s): lizziel, jimmielin, fritzt +Date: 2 Feb 2024 +One-line Summary: Add GEOS-Chem chemistry as new chemistry option in CAM +Github PR URL: https://github.com/ESCOMP/CAM/pull/484 + +Purpose of changes (include the issue number and title text for each relevant GitHub issue): + - Include GEOS-Chem 14.1.2 chemistry as alternative to CAM-chem + (issue #424 -- Implementing GEOS-Chem chemistry in CESM (CESM-GC)) + +Describe any changes made to build system: N/A + +Describe any changes made to the namelist: N/A + +List any changes to the defaults for the boundary datasets: N/A + +Describe any substantial timing or memory changes: N/A + +Code reviewed by: fvitt, brian-eaton, cacraigucar, gold2718, jedwards4b + +List all files that are renamed and why: +R100 bld/namelist_files/master_aer_drydep_list.xml +R100 bld/namelist_files/master_aer_wetdep_list.xml +R100 bld/namelist_files/master_gas_drydep_list.xml +R100 bld/namelist_files/master_gas_wetdep_list.xml + - Renamed with prefix mozart_ to distinguish from GEOS-Chem lists + +R099 src/chemistry/aerosol/drydep_mod.F90 + - Renamed to aer_drydep_mod.F90 to avoid module conflict name in GEOS-Chem + - Changed module name in file from drydep_mod to aer_drydep_mod + +List all files added and what they do: +A bld/namelist_files/geoschem_master_aer_drydep_list.xml +A bld/namelist_files/geoschem_master_aer_wetdep_list.xml +A bld/namelist_files/geoschem_master_gas_drydep_list.xml +A bld/namelist_files/geoschem_master_gas_wetdep_list.xml + - New deposition lists for use in GEOS-Chem only + +A bld/namelist_files/use_cases/2000_geoschem.xml +A bld/namelist_files/use_cases/2010_geoschem.xml +A bld/namelist_files/use_cases/hist_geoschem.xml +A bld/namelist_files/use_cases/hist_geoschem_nudged.xml + - Use case files for four GEOS-Chem chemistry compsets + +A cime_config/cam.case_setup.py + - Script called during CESM case setup for CAM-specific commands + - Copies GEOS-Chem config files from source to case directory if using GEOS-Chem + +A src/chemistry/geoschem/.exclude + - List of GEOS-Chem source files to skip during build + +A src/chemistry/geoschem/chem_mods.F90 + - GEOS-Chem version of chem_mods.F90 equivalent to Mozart pre-processed files + +A src/chemistry/geoschem/chemistry.F90 + - GEOS-Chem implementation of chemistry module used in CAM + +A src/chemistry/geoschem/geoschem_diagnostics_mod.F90 + - GEOS-Chem diagnostics module + +A src/chemistry/geoschem/geoschem_emissions_mod.F90 + - GEOS-Chem emissions module + +A src/chemistry/geoschem/geoschem_history_mod.F90 + - Interface file to connect GEOS-Chem state arrays to CAM history + +A src/chemistry/geoschem/m_spc_id.F90 + - GEOS-Chem version of m_spc_id.F90 equivalent to Mozart pre-processed files + +A src/chemistry/geoschem/mo_sim_dat.F90 + - GEOS-Chem version of m_spc_id.F90 equivalent to Mozart pre-processed files + +List all existing files that have been modified, and describe the changes: +M .gitignore + - Added GEOS-Chem directory which is its own git repository + +M Externals_CAM.cfg + - Added GEOS-Chem repository, tag 14.1.2 + +M bld/build-namelist + - Updates for GEOS-Chem namelists + +M bld/config_files/definition.xml + - Added geoschem_mam4 to list of chemistry packages + +M bld/configure + - Updates to build GEOS-Chem + +M bld/namelist_files/namelist_defaults_cam.xml + - Set GEOS-Chem default wave params and path to Henry's coeff file for deposition + +M bld/namelist_files/namelist_definition.xml + - Added GEOS-Chem input data path as new entry + - Added geoschem_mam4 to chem package list + +M bld/perl5lib/Build/ChemNamelist.pm + - Added log prints of all deposition species lists + - Updates to use different deposition lists based on chemistry selection + +M cime_config/buildnml + - Copy GEOS-Chem config files from case to run directory if using GEOS-Chem + +M cime_config/config_component.xml + - Added GEOS-Chem as chemistry option + - Set GEOS-Chem compset aliases + +M cime_config/config_compsets.xml + - Defined four GEOS-Chem compsets + +M cime_config/testdefs/testlist_cam.xml + - Added tests for all four GEOS-Chem compsets as category geoschem + - Included FCHIST_GC in aux_cam tests and FCnudged_GC in prealpha tests + +M doc/ChangeLog +M src/chemistry/bulk_aero/aero_model.F90 + - Renamed drydep_mod to aer_drydep_mod in use statements + +M src/chemistry/modal_aero/aero_model.F90 + - Renamed drydep_mod to aer_drydep_mod in use statements + +M src/chemistry/modal_aero/modal_aero_data.F90 + - Distinguish between SOAG and SOAGX since SOAGX a species in GEOS-Chem + +M src/chemistry/modal_aero/modal_aero_gasaerexch.F90 + - Skip MSA tendency if using GEOS-Chem + +M src/chemistry/modal_aero/sox_cldaero_mod.F90 + - Exit prior to in-cloud sulfur oxidation if using GEOS-Chem to avoid double-counting + +M src/chemistry/mozart/chemistry.F90 + - Add call to new subroutine short_lived_species_final + +M src/chemistry/mozart/mo_chem_utls.F90 + - Add optional argument in get_spc_ndx to ignore case in string compariosn + +M src/chemistry/mozart/mo_neu_wetdep.F90 + - Skip aerosol mapping if using GEOS-Chem; assume all species in dep_data_file + +M src/chemistry/mozart/short_lived_species.F90 + - Added array slvd_ref_mmr to store short-lived species reference values + - Initialized short-lived species not found to ref values if available + - Initialized GEOS-Chem short-lived species from slvd_lst not solsym + - Added set/get subroutines for GEOS-Chem short-lived species + - Added new subroutine short_lived_species_final to deallocate new array + +M src/cpl/nuopc/atm_import_export.F90 + - Added So_ustar to atm imports for use in GEOS-Chem dry dep over ocean + +M src/physics/cam/constituents.F90 + - Improved existing error handling messages for clarity + +If there were any failures reported from running test_driver.sh on any test +platform, and checkin with these failures has been OK'd by the gatekeeper, +then copy the lines from the td.*.status files for the failed tests to the +appropriate machine below. All failed tests must be justified. + +derecho/intel/aux_cam: + PEND ERP_Ln9_Vnuopc.C96_C96_mg17.F2000climo.derecho_intel.cam-outfrq9s_mg3 + FAIL ERP_Ln9_Vnuopc.f09_f09_mg17.FCSD_HCO.derecho_intel.cam-outfrq9s + - pre-existing failures + + FAIL SMS_Ld1_Vnuopc.f09_f09_mg17.FCHIST_GC.derecho_intel.cam-outfrq1d + - expected failure due to goeschem config file copy issue + + DIFF ERC_D_Ln9_P144x1_Vnuopc.ne16pg3_ne16pg3_mg17.QPC6HIST.derecho_intel.cam-outfrq3s_ttrac_usecase + DIFF ERC_D_Ln9_Vnuopc.f19_f19_mg17.QPC6.derecho_intel.cam-outfrq3s_cosp + DIFF ERC_D_Ln9_Vnuopc.f19_f19_mg17.QPMOZ.derecho_intel.cam-outfrq3s + DIFF ERC_D_Ln9_Vnuopc.f19_f19_mg17.QPX2000.derecho_intel.cam-outfrq3s + DIFF ERC_D_Ln9_Vnuopc.ne16_ne16_mg17.QPC5HIST.derecho_intel.cam-outfrq3s_usecase + DIFF ERI_D_Ln18_Vnuopc.f45_f45_mg37.QPC41850.derecho_intel.cam-co2rmp_usecase + DIFF ERP_D_Ln9.ne30pg3_ne30pg3_mg17.FLTHIST.derecho_intel.cam-outfrq9s + DIFF ERP_D_Ln9_Vnuopc.f09_f09_mg17.QSC6.derecho_intel.cam-outfrq9s + DIFF ERP_D_Ln9_Vnuopc.f19_f19_mg17.QPC6.derecho_intel.cam-outfrq9s + DIFF ERP_D_Ln9_Vnuopc.ne30pg3_ne30pg3_mg17.F2000dev.derecho_intel.cam-outfrq9s + DIFF ERP_Ld3_Vnuopc.f09_f09_mg17.FWHIST.derecho_intel.cam-reduced_hist1d + DIFF ERP_Lh12_Vnuopc.f19_f19_mg17.FW4madSD.derecho_intel.cam-outfrq3h + DIFF ERP_Ln9_P24x3_Vnuopc.f45_f45_mg37.QPWmaC6.derecho_intel.cam-outfrq9s_mee_fluxes + DIFF ERP_Ln9_Vnuopc.f09_f09_mg17.F1850.derecho_intel.cam-outfrq9s + DIFF ERP_Ln9_Vnuopc.f09_f09_mg17.F2000climo.derecho_intel.cam-outfrq9s + DIFF ERP_Ln9_Vnuopc.f09_f09_mg17.F2000dev.derecho_intel.cam-outfrq9s_mg3 + DIFF ERP_Ln9_Vnuopc.f09_f09_mg17.F2010climo.derecho_intel.cam-outfrq9s + DIFF ERP_Ln9_Vnuopc.f09_f09_mg17.FHIST_BDRD.derecho_intel.cam-outfrq9s + DIFF ERP_Ln9_Vnuopc.f19_f19_mg17.FWsc1850.derecho_intel.cam-outfrq9s + DIFF ERP_Ln9_Vnuopc.ne30_ne30_mg17.FCnudged.derecho_intel.cam-outfrq9s + DIFF ERP_Ln9_Vnuopc.ne30pg3_ne30pg3_mg17.FW2000climo.derecho_intel.cam-outfrq9s + DIFF ERS_Ld3_Vnuopc.f10_f10_mg37.F1850.derecho_intel.cam-outfrq1d_14dec_ghg_cam_dev + DIFF ERS_Ln9_P288x1_Vnuopc.mpasa120_mpasa120.F2000climo.derecho_intel.cam-outfrq9s_mpasa120 + DIFF ERS_Ln9_P36x1_Vnuopc.mpasa480_mpasa480.F2000climo.derecho_intel.cam-outfrq9s_mpasa480 + DIFF ERS_Ln9_Vnuopc.f09_f09_mg17.FX2000.derecho_intel.cam-outfrq9s + DIFF ERS_Ln9_Vnuopc.f19_f19_mg17.FSPCAMS.derecho_intel.cam-outfrq9s + DIFF ERS_Ln9_Vnuopc.f19_f19_mg17.FXSD.derecho_intel.cam-outfrq9s + DIFF SCT_D_Ln7_Vnuopc.T42_T42_mg17.QPC5.derecho_intel.cam-scm_prep + DIFF SMS_D_Ld2_Vnuopc.f19_f19_mg17.QPC5HIST.derecho_intel.cam-volc_usecase + DIFF SMS_D_Ln9.ne30pg3_ne30pg3_mg17.FMTHIST.derecho_intel.cam-outfrq9s + DIFF SMS_D_Ln9_Vnuopc.f09_f09_mg17.FCts2nudged.derecho_intel.cam-outfrq9s_leapday + DIFF SMS_D_Ln9_Vnuopc.f09_f09_mg17.FCvbsxHIST.derecho_intel.cam-outfrq9s + DIFF SMS_D_Ln9_Vnuopc.f09_f09_mg17.FSD.derecho_intel.cam-outfrq9s + DIFF SMS_D_Ln9_Vnuopc.f19_f19_mg17.FWma2000climo.derecho_intel.cam-outfrq9s + DIFF SMS_D_Ln9_Vnuopc.f19_f19_mg17.FWma2000climo.derecho_intel.cam-outfrq9s_waccm_ma_mam4 + DIFF SMS_D_Ln9_Vnuopc.f19_f19_mg17.FXHIST.derecho_intel.cam-outfrq9s_amie + DIFF SMS_D_Ln9_Vnuopc.f19_f19_mg17.QPC2000climo.derecho_intel.cam-outfrq3s_usecase + DIFF SMS_D_Ln9_Vnuopc.f19_f19_mg17.QPC5M7.derecho_intel.cam-outfrq9s + DIFF SMS_D_Ln9_Vnuopc.ne16_ne16_mg17.FX2000.derecho_intel.cam-outfrq9s + DIFF SMS_D_Ln9_Vnuopc.ne16_ne16_mg17.QPX2000.derecho_intel.cam-outfrq9s + DIFF SMS_D_Ln9_Vnuopc_P1280x1.ne0ARCTICne30x4_ne0ARCTICne30x4_mt12.FHIST.derecho_intel.cam-outfrq9s + DIFF SMS_D_Ln9_Vnuopc_P1280x1.ne0CONUSne30x8_ne0CONUSne30x8_mt12.FCHIST.derecho_intel.cam-outfrq9s + DIFF SMS_D_Ln9_Vnuopc_P1280x1.ne30pg3_ne30pg3_mg17.FCLTHIST.derecho_intel.cam-outfrq9s + DIFF SMS_D_Ln9_Vnuopc.T42_T42.FSCAM.derecho_intel.cam-outfrq9s + DIFF SMS_Ld1_Vnuopc.f09_f09_mg17.FW2000climo.derecho_intel.cam-outfrq1d + DIFF SMS_Ld1_Vnuopc.f19_f19.F2000dev.derecho_intel.cam-outfrq1d + DIFF SMS_Ld1_Vnuopc.ne30pg3_ne30pg3_mg17.FC2010climo.derecho_intel.cam-outfrq1d + DIFF SMS_Lh12_Vnuopc.f09_f09_mg17.FCSD_HCO.derecho_intel.cam-outfrq3h + DIFF SMS_Lm13_Vnuopc.f10_f10_mg37.F2000climo.derecho_intel.cam-outfrq1m + DIFF SMS_Ln9_Vnuopc.f09_f09_mg17.F2010climo.derecho_intel.cam-nudging + DIFF SMS_Ln9_Vnuopc.f09_f09_mg17.FW1850.derecho_intel.cam-reduced_hist3s + DIFF SMS_Ln9_Vnuopc.f19_f19.F2000climo.derecho_intel.cam-silhs + DIFF SMS_Ln9_Vnuopc.f19_f19_mg17.FHIST.derecho_intel.cam-outfrq9s_nochem + - differences are only in Med_aoflux_atm_So_ustar mediator field resulting from adding + So_ustar to atm imports, otherwise all other fields bit-for-bit + +izumi/nag/aux_cam: + FAIL DAE_Vnuopc.f45_f45_mg37.FHS94.izumi_nag.cam-dae + - pre-existing failure + + DIFF ERC_D_Ln9_Vnuopc.f10_f10_mg37.QPC5.izumi_nag.cam-carma_sea_salt + DIFF ERC_D_Ln9_Vnuopc.f10_f10_mg37.QPC5.izumi_nag.cam-outfrq3s_cosp + DIFF ERC_D_Ln9_Vnuopc.f10_f10_mg37.QPC5.izumi_nag.cam-outfrq3s_subcol + DIFF ERC_D_Ln9_Vnuopc.f10_f10_mg37.QPC6.izumi_nag.cam-outfrq3s_am + DIFF ERC_D_Ln9_Vnuopc.f10_f10_mg37.QPC6.izumi_nag.cam-outfrq3s_cospsathist + DIFF ERC_D_Ln9_Vnuopc.f10_f10_mg37.QPC6.izumi_nag.cam-outfrq3s + DIFF ERC_D_Ln9_Vnuopc.f10_f10_mg37.QPWmaC6.izumi_nag.cam-outfrq3s + DIFF ERC_D_Ln9_Vnuopc.f10_f10_mg37.QSPCAMS.izumi_nag.cam-outfrq3s + DIFF ERC_D_Ln9_Vnuopc.ne16_ne16_mg17.QPC4.izumi_nag.cam-outfrq3s_usecase + DIFF ERC_D_Ln9_Vnuopc.ne16pg3_ne16pg3_mg17.QPC4.izumi_nag.cam-outfrq3s_usecase + DIFF ERC_D_Ln9_Vnuopc.ne5_ne5_mg37.QPC5.izumi_nag.cam-outfrq3s_ttrac + DIFF ERC_D_Ln9_Vnuopc.T5_T5_mg37.QPC4.izumi_nag.cam-outfrq3s_usecase + DIFF ERI_D_Ln18_Vnuopc.f10_f10_mg37.QPC5.izumi_nag.cam-outfrq3s_eoyttrac + DIFF ERI_D_Ln18_Vnuopc.f19_f19_mg17.QPC6.izumi_nag.cam-ghgrmp_e8 + DIFF ERP_Ln9_Vnuopc.ne5pg3_ne5pg3_mg37.QPC6.izumi_nag.cam-outfrq9s_clubbmf + DIFF PLB_D_Ln9_Vnuopc.f10_f10_mg37.QPC5.izumi_nag.cam-ttrac_loadbal0 + DIFF PLB_D_Ln9_Vnuopc.f10_f10_mg37.QPC5.izumi_nag.cam-ttrac_loadbal1 + DIFF PLB_D_Ln9_Vnuopc.f10_f10_mg37.QPC5.izumi_nag.cam-ttrac_loadbal3 + DIFF PLB_D_Ln9_Vnuopc.ne5_ne5_mg37.QPC5.izumi_nag.cam-ttrac_loadbal0 + DIFF PLB_D_Ln9_Vnuopc.ne5_ne5_mg37.QPC5.izumi_nag.cam-ttrac_loadbal1 + DIFF PLB_D_Ln9_Vnuopc.ne5_ne5_mg37.QPC5.izumi_nag.cam-ttrac_loadbal3 + DIFF SMS_D_Ln3_Vnuopc.ne5pg3_ne5pg3_mg37.QPX2000.izumi_nag.cam-outfrq3s + DIFF SMS_D_Ln6_Vnuopc.ne5_ne5_mg37.QPWmaC4.izumi_nag.cam-outfrq3s_physgrid_tem + DIFF SMS_D_Ln7_Vnuopc.T42_T42_mg17.QPSCAMC5.izumi_nag.cam-scmarm + DIFF SMS_D_Ln9_Vnuopc.f10_f10_mg37.QPC5.izumi_nag.cam-rad_diag_mam + DIFF SMS_D_Ln9_Vnuopc.f10_f10_mg37.QPC6.izumi_nag.cam-outfrq3s_ba + DIFF SMS_P48x1_D_Ln3_Vnuopc.f09_f09_mg17.QPC6HIST.izumi_nag.cam-outfrq3s_co2cycle_usecase + DIFF SUB_D_Ln9_Vnuopc.f10_f10_mg37.QPC5.izumi_nag.cam-outfrq3s + - differences are only in Med_aoflux_atm_So_ustar mediator field resulting from adding + So_ustar to atm imports, otherwise all other fields bit-for-bit + +izumi/gnu/aux_cam: + DIFF ERC_D_Ln9_Vnuopc.f10_f10_mg37.QPC4.izumi_gnu.cam-outfrq3s_diags + DIFF ERC_D_Ln9_Vnuopc.f10_f10_mg37.QPC5.izumi_gnu.cam-outfrq3s_unicon + DIFF ERC_D_Ln9_Vnuopc.f10_f10_mg37.QPC5.izumi_gnu.cam-rad_diag + DIFF ERC_D_Ln9_Vnuopc.f10_f10_mg37.QPSPCAMM.izumi_gnu.cam-outfrq3s + DIFF ERC_D_Ln9_Vnuopc.ne5_ne5_mg37.QPC4.izumi_gnu.cam-outfrq3s_nudging_ne5_L26 + DIFF ERC_D_Ln9_Vnuopc.ne5_ne5_mg37.QPC5.izumi_gnu.cam-outfrq3s_ba + DIFF ERI_D_Ln18_Vnuopc.T5_T5_mg37.QPC4.izumi_gnu.cam-co2rmp + DIFF ERP_Ln9_Vnuopc.ne5_ne5_mg37.QPC5.izumi_gnu.cam-outfrq9s + DIFF PLB_D_Ln9_Vnuopc.ne5pg3_ne5pg3_mg37.QPC5.izumi_gnu.cam-ttrac_loadbal0 + DIFF PLB_D_Ln9_Vnuopc.ne5pg3_ne5pg3_mg37.QPC5.izumi_gnu.cam-ttrac_loadbal1 + DIFF PLB_D_Ln9_Vnuopc.ne5pg3_ne5pg3_mg37.QPC5.izumi_gnu.cam-ttrac_loadbal3 + DIFF SCT_D_Ln7_Vnuopc.T42_T42_mg17.QPC4.izumi_gnu.cam-scm_prep + DIFF SCT_D_Ln7_Vnuopc.T42_T42_mg17.QPC6.izumi_gnu.cam-scm_prep_c6 + DIFF SMS_D_Ln3_Vnuopc.f10_f10_mg37.QPMOZ.izumi_gnu.cam-outfrq3s_chemproc + DIFF SMS_D_Ln9.f10_f10_mg37.2000_CAM%DEV%GHGMAM4_CLM50%SP_CICE%PRES_DOCN%DOM_MOSART_SGLC_SWAV_SESP.izumi_gnu.cam-outfrq9s + DIFF SMS_D_Ln9_Vnuopc.f10_f10_mg37.QPWmaC4.izumi_gnu.cam-outfrq9s_apmee + DIFF SMS_D_Ln9_Vnuopc.ne5pg3_ne5pg3_mg37.QPC5.izumi_gnu.cam-outfrq3s_ttrac + DIFF SMS_P48x1_D_Ln9_Vnuopc.f19_f19_mg17.FW4madSD.izumi_gnu.cam-outfrq9s + - differences are only in Med_aoflux_atm_So_ustar mediator field resulting from adding + So_ustar to atm imports, otherwise all other fields bit-for-bit + +Summarize any changes to answers: bit-for-bit unchanged + +=============================================================== +=============================================================== + Tag name: cam6_3_146 Originator(s): cacraig Date: Jan 23, 2024 diff --git a/src/chemistry/aerosol/drydep_mod.F90 b/src/chemistry/aerosol/aer_drydep_mod.F90 similarity index 99% rename from src/chemistry/aerosol/drydep_mod.F90 rename to src/chemistry/aerosol/aer_drydep_mod.F90 index 1e83641d71..512a8bdc5b 100644 --- a/src/chemistry/aerosol/drydep_mod.F90 +++ b/src/chemistry/aerosol/aer_drydep_mod.F90 @@ -1,4 +1,4 @@ -module drydep_mod +module aer_drydep_mod use shr_kind_mod, only: r8 => shr_kind_r8 use ppgrid @@ -265,4 +265,4 @@ end subroutine calcram !############################################################################## -end module drydep_mod +end module aer_drydep_mod diff --git a/src/chemistry/bulk_aero/aero_model.F90 b/src/chemistry/bulk_aero/aero_model.F90 index 4c3d0ab80e..c5c25abc74 100644 --- a/src/chemistry/bulk_aero/aero_model.F90 +++ b/src/chemistry/bulk_aero/aero_model.F90 @@ -124,16 +124,16 @@ end subroutine aero_model_register !============================================================================= subroutine aero_model_init( pbuf2d ) - use mo_chem_utls, only: get_inv_ndx, get_spc_ndx - use cam_history, only: addfld, add_default, horiz_only - use phys_control, only: phys_getopts - use mo_aerosols, only: aerosols_inti - use mo_setsoa, only: soa_inti - use dust_model, only: dust_init - use seasalt_model, only: seasalt_init - use drydep_mod, only: inidrydep - use wetdep, only: wetdep_init - use mo_setsox, only: has_sox + use mo_chem_utls, only: get_inv_ndx, get_spc_ndx + use cam_history, only: addfld, add_default, horiz_only + use phys_control, only: phys_getopts + use mo_aerosols, only: aerosols_inti + use mo_setsoa, only: soa_inti + use dust_model, only: dust_init + use seasalt_model, only: seasalt_init + use aer_drydep_mod, only: inidrydep + use wetdep, only: wetdep_init + use mo_setsox, only: has_sox ! args type(physics_buffer_desc), pointer :: pbuf2d(:,:) @@ -384,7 +384,7 @@ end subroutine aero_model_init subroutine aero_model_drydep ( state, pbuf, obklen, ustar, cam_in, dt, cam_out, ptend ) use dust_sediment_mod, only: dust_sediment_tend - use drydep_mod, only: d3ddflux, calcram + use aer_drydep_mod, only: d3ddflux, calcram use dust_model, only: dust_depvel, dust_nbin, dust_names use seasalt_model, only: sslt_depvel=>seasalt_depvel, sslt_nbin=>seasalt_nbin, sslt_names=>seasalt_names diff --git a/src/chemistry/geoschem/.exclude b/src/chemistry/geoschem/.exclude new file mode 100644 index 0000000000..3629106b6e --- /dev/null +++ b/src/chemistry/geoschem/.exclude @@ -0,0 +1,20 @@ +geoschem_src/GeosCore/regrid_a2a_mod.F90 +geoschem_src/GeosCore/transport_mod.F90 +geoschem_src/GeosCore/tpcore_window_mod.F90 +geoschem_src/GeosCore/tpcore_fvdas_mod.F90 +geoschem_src/GeosCore/flexgrid_read_mod.F90 +geoschem_src/GeosCore/get_met_mod.F90 +geoschem_src/GeosCore/planeflight_mod.F90 +geoschem_src/GeosCore/diag1.F90 +geoschem_src/GeosCore/diag03_mod.F90 +geoschem_src/GeosCore/diag3.F90 +geoschem_src/GeosCore/diag51_mod.F90 +geoschem_src/GeosCore/diag51b_mod.F90 +geoschem_src/GeosCore/diag53_mod.F90 +geoschem_src/GeosCore/emissions_mod.F90 +geoschem_src/GeosCore/gamap_mod.F90 +geoschem_src/GeosCore/gosat_ch4_mod.F90 +geoschem_src/GeosCore/tccon_ch4_mod.F90 +geoschem_src/GeosCore/initialize.F90 +geoschem_src/GeosCore/cleanup.F90 +geoschem_src/Interfaces/GC-Classic/main.F90 diff --git a/src/chemistry/geoschem/chem_mods.F90 b/src/chemistry/geoschem/chem_mods.F90 new file mode 100644 index 0000000000..2d8a500253 --- /dev/null +++ b/src/chemistry/geoschem/chem_mods.F90 @@ -0,0 +1,110 @@ + module chem_mods +!-------------------------------------------------------------- +! ... Basic chemistry parameters and arrays +!-------------------------------------------------------------- + use shr_kind_mod, only : r8 => shr_kind_r8, shr_kind_cl + use constituents, only : pcnst + implicit none + save + + INTEGER, PARAMETER :: nTracersMax = 267 ! Must be equal to chem_nadv + INTEGER :: nTracers + REAL(r8) :: ref_MMR(pcnst) + + CHARACTER(LEN=shr_kind_cl) :: tracerNames(nTracersMax) + CHARACTER(LEN=shr_kind_cl) :: tracerLongNames(nTracersMax) + + ! Index of first constituent + INTEGER :: iFirstCnst + + ! Short-lived species (i.e. not advected) + INTEGER, PARAMETER :: nSlsMax = 500 ! UNadvected species only + INTEGER :: nSls + + CHARACTER(LEN=shr_kind_cl) :: slsNames(nSlsMax) + CHARACTER(LEN=shr_kind_cl) :: slsLongnames(nSlsMax) + + ! Mapping between constituents and GEOS-Chem tracers + INTEGER :: map2GC(pcnst) + INTEGER :: map2GCinv(nTracersMax) + INTEGER :: map2GC_Sls(nSlsMax) + + ! Mapping constituent onto chemical species (as listed in solsym) + INTEGER :: mapCnst(pcnst) + + ! Aerosols + INTEGER, PARAMETER :: nAerMax = 35 + INTEGER :: nAer + CHARACTER(LEN=16) :: aerNames(nAerMax) + REAL(r8) :: aerAdvMass(nAerMax) + + !----------------------------- + ! Aerosol index mapping + !----------------------------- + ! map2MAM4 maps aerNames onto the GEOS-Chem Species array such + ! that + ! State_Chm%Species(1,:,:,map2MAM4(:,:)) = state%q(:,:,MAM4_Indices) + INTEGER, ALLOCATABLE :: map2MAM4(:,:) + + !----------------------------- + ! Dry deposition index mapping + !----------------------------- + ! drySpc_ndx maps drydep_list onto tracerNames such that + ! tracerNames(drySpc_ndx(:)) = drydep_list(:) + INTEGER, ALLOCATABLE :: drySpc_ndx(:) + + ! map2GC_dryDep maps drydep_list onto the GEOS-Chem dry deposition + ! velocity arrays such that + ! State_Chm%DryDepVel(1,:,map2GC_dryDep(:)) = cam_in%depVel(:,:) + INTEGER, ALLOCATABLE :: map2GC_dryDep(:) + + INTEGER, PARAMETER :: phtcnt = 40, & ! number of photolysis reactions + rxntot = 212, & ! number of total reactions + gascnt = 172, & ! number of gas phase reactions + nabscol = 2, & ! number of absorbing column densities + gas_pcnst = 269, & ! number of "gas phase" species (same as solsym length) + ! Includes GC advected species (233), MAM aerosols (33), + ! and CO2 (1), as well as any non-advected species added + ! to solsym and mo_sim_dat.F90. + nfs = 6, & ! number of "fixed" species + relcnt = 0, & ! number of relationship species + grpcnt = 0, & ! number of group members + nzcnt = 824, & ! number of non-zero matrix entries + extcnt = 34, & ! number of species with external forcing, aka 3-D emissions + clscnt1 = 8, & ! number of species in explicit class + clscnt2 = 0, & ! number of species in hov class + clscnt3 = 0, & ! number of species in ebi class + clscnt4 = 95, & ! number of species in implicit class + clscnt5 = 0, & ! number of species in rodas class + indexm = 1, & ! index of total atm density in invariant array + indexh2o = 4, & ! index of water vapor density + clsze = 1, & ! loop length for implicit chemistry + rxt_tag_cnt = 0, & ! number of tagged reactions (unused in GEOS-Chem) + enthalpy_cnt = 0, & + nslvd = 86 ! number of short-lived (non-advected) species + integer :: clscnt(5) = 0 + integer :: cls_rxt_cnt(4,5) = 0 + integer :: clsmap(gas_pcnst,5) = 0 + integer :: permute(gas_pcnst,5) = 0 + integer :: diag_map(clscnt4) = 0 + real(r8) :: adv_mass(gas_pcnst) = 0._r8 + real(r8) :: crb_mass(gas_pcnst) = 0._r8 + real(r8) :: fix_mass(max(1,nfs)) + real(r8), allocatable :: cph_enthalpy(:) + integer, allocatable :: cph_rid(:) + integer, allocatable :: num_rnts(:) + integer, allocatable :: rxt_tag_map(:) + real(r8), allocatable :: pht_alias_mult(:,:) + character(len=16), allocatable :: rxt_tag_lst(:) + character(len=16), allocatable :: pht_alias_lst(:,:) + character(len=16) :: inv_lst(max(1,nfs)) + character(len=16) :: extfrc_lst(max(1,extcnt)) + logical :: frc_from_dataset(max(1,extcnt)) + logical :: is_vector + logical :: is_scalar + character(len=shr_kind_cl), allocatable :: slvd_lst(:) + + ! Mapping between chemical species and GEOS-Chem species/other tracers + INTEGER :: map2chm(gas_pcnst) + + end module chem_mods diff --git a/src/chemistry/geoschem/chemistry.F90 b/src/chemistry/geoschem/chemistry.F90 new file mode 100644 index 0000000000..ab56200cba --- /dev/null +++ b/src/chemistry/geoschem/chemistry.F90 @@ -0,0 +1,4439 @@ +module chemistry + + ! CAM modules + use cam_abortutils, only : endrun + use cam_logfile, only : iulog + use chem_mods, only : nTracersMax, nTracers, tracerNames + use chem_mods, only : gas_pcnst, adv_mass, ref_MMR, iFirstCnst + use chem_mods, only : nSlsMax, nSls, slsNames, nSlvd, slvd_Lst + use chem_mods, only : nAerMax, nAer, aerNames, aerAdvMass + use chem_mods, only : map2GC, map2GCinv, map2GC_Sls + use chem_mods, only : mapCnst, map2chm, map2MAM4 + use constituents, only : pcnst, cnst_add, cnst_get_ind, cnst_name + use mo_tracname, only : solsym + use physics_buffer, only : physics_buffer_desc + use physics_types, only : physics_state, physics_ptend, physics_ptend_init + use ppgrid, only : begchunk, endchunk, pcols, pver, pverp + use shr_const_mod, only : molw_dryair=>SHR_CONST_MWDAIR + use shr_drydep_mod, only : nddvels => n_drydep, drydep_list + use shr_kind_mod, only : r8 => shr_kind_r8, shr_kind_cl + use spmd_utils, only : MasterProc, myCPU=>Iam, nCPUs=>npes + use string_utils, only : to_upper +#if defined( MODAL_AERO ) + use modal_aero_data, only : ntot_amode +#endif + + ! GEOS-Chem derived types + USE DiagList_Mod, ONLY : DgnList ! Diagnostics list object + use GeosChem_History_Mod, ONLY : HistoryConfigObj ! History diagnostic object + USE Input_Opt_Mod, ONLY : OptInput ! Input Options + USE Species_Mod, ONLY : Species ! Species object + USE State_Chm_Mod, ONLY : ChmState ! Chemistry State object + USE State_Diag_Mod, ONLY : DgnState ! Diagnostics State object + USE State_Grid_Mod, ONLY : GrdState ! Grid State object + USE State_Met_Mod, ONLY : MetState ! Meteorology State object + USE TaggedDiagList_Mod, ONLY : TaggedDgnList ! Ragged diagnostics list + + ! GEOS-Chem utilities + USE ErrCode_Mod, ONLY : GC_SUCCESS, GC_FAILURE + USE ErrCode_Mod, ONLY : GC_Error, GC_CheckVar, GC_Warning + USE Error_Mod, ONLY : Error_Stop + USE Precision_Mod, ONLY : fp, f4 ! Flexible precision + + IMPLICIT NONE + PRIVATE + SAVE + + ! Public interfaces + public :: chem_is ! identify which chemistry is being used + public :: chem_register ! register consituents + public :: chem_is_active ! returns true if this package is active (ghg_chem=.true.) + public :: chem_implements_cnst ! returns true if consituent is implemented by this package + public :: chem_init_cnst ! initialize mixing ratios if not read from initial file + public :: chem_init ! initialize (history) variables + public :: chem_timestep_tend ! interface to tendency computation + public :: chem_final + public :: chem_write_restart + public :: chem_read_restart + public :: chem_init_restart + public :: chem_readnl ! read chem namelist + public :: chem_emissions + public :: chem_timestep_init + + ! Location of valid geoschem_config.yml and species_database.yml + ! Use local files in run folder + CHARACTER(LEN=500) :: gcConfig = 'geoschem_config.yml' + CHARACTER(LEN=500) :: speciesDB = 'species_database.yml' + + ! Location of chemistry input + CHARACTER(LEN=shr_kind_cl) :: geoschem_cheminputs + + ! Debugging + LOGICAL :: debug = .TRUE. + + ! Derived type objects + TYPE(OptInput) :: Input_Opt ! Input Options object + TYPE(ChmState),ALLOCATABLE :: State_Chm(:) ! Chemistry State object + TYPE(DgnState),ALLOCATABLE :: State_Diag(:) ! Diagnostics State object + TYPE(GrdState),ALLOCATABLE :: State_Grid(:) ! Grid State object + TYPE(MetState),ALLOCATABLE :: State_Met(:) ! Meteorology State object + TYPE(DgnList ) :: Diag_List ! Diagnostics list object + TYPE(TaggedDgnList ) :: TaggedDiag_List ! Tagged diagnostics list object + TYPE(HistoryConfigObj), POINTER :: HistoryConfig ! HistoryConfig object for History diagn. + type(physics_buffer_desc), POINTER :: hco_pbuf2d(:,:) ! Pointer to 2D pbuf + + ! Mimic code in sfcvmr_mod.F90 + TYPE :: SfcMrObj + CHARACTER(LEN=63) :: FldName ! Field name + INTEGER :: SpcID ! ID in species database + TYPE(SfcMrObj), POINTER :: Next ! Next element in list + END TYPE SfcMrObj + + ! Heat of linked list with SfcMrObj objects + TYPE(SfcMrObj), POINTER :: SfcMrHead => NULL() + + ! Field prefix + CHARACTER(LEN=63), PARAMETER :: Prefix_SfcVMR = 'VMR_' + + ! Indices of critical species in GEOS-Chem + INTEGER :: iH2O, iO3, iCO2, iSO4 + INTEGER :: iO, iH, iO2 + REAL(r8) :: MWO3 + ! Indices of critical species in the constituent list + INTEGER :: cQ, cH2O, cH2SO4 + ! Indices of critical species in the solsym list + INTEGER :: l_H2SO4, l_SO4 +#if defined( MODAL_AERO ) + INTEGER, ALLOCATABLE :: iSulf(:) +#endif + + ! Indices in the physics buffer + INTEGER :: NDX_PBLH ! PBL height [m] + INTEGER :: NDX_FSDS ! Downward shortwave flux at surface [W/m2] + INTEGER :: NDX_CLDTOP ! Cloud top height [index] + INTEGER :: NDX_CLDFRC ! Cloud fraction [-] + INTEGER :: NDX_PRAIN ! Rain production rate [kg/kg/s] + INTEGER :: NDX_NEVAPR ! Total rate of precipitation evaporation [kg/kg/s] + INTEGER :: NDX_LSFLXPRC ! Large-scale precip. at interface (liq + snw) [kg/m2/s] + INTEGER :: NDX_LSFLXSNW ! Large-scale precip. at interface (snow only) [kg/m2/s] + INTEGER :: NDX_CMFDQR ! Convective total precip. production rate [kg/kg/s] + + ! Get constituent indices + INTEGER :: ixCldLiq ! Cloud liquid water + INTEGER :: ixCldIce ! Cloud ice + INTEGER :: ixNDrop ! Cloud droplet number index + + ! ghg + LOGICAL :: ghg_chem = .false. ! .true. => use ghg chem package + CHARACTER(len=shr_kind_cl) :: bndtvg = ' ' ! pathname for greenhouse gas loss rate + CHARACTER(len=shr_kind_cl) :: h2orates = ' ' ! pathname for greenhouse gas (lyman-alpha H2O loss) + + ! Strings + CHARACTER(LEN=shr_kind_cl) :: ThisLoc + CHARACTER(LEN=shr_kind_cl) :: ErrMsg + + ! For dry deposition + character(len=shr_kind_cl) :: depvel_lnd_file = 'depvel_lnd_file' + + +contains + + !================================================================================================ + ! function chem_is + !================================================================================================ + function chem_is (name) result (chem_name_is) + + ! CAM modules + use string_utils, only : to_lower + + character(len=*), intent(in) :: name + logical :: chem_name_is + + chem_name_is = (( to_lower(name) == 'geoschem' ) .or. & + ( to_lower(name) == 'geos-chem' )) + + end function chem_is + + !================================================================================================ + ! subroutine chem_register + !================================================================================================ + subroutine chem_register + + ! CAM modules + use chem_mods, only : drySpc_ndx + use mo_chem_utls, only : get_spc_ndx + use physconst, only : MWDry + use physics_buffer, only : pbuf_add_field, dtype_r8 + use short_lived_species, only : Register_Short_Lived_Species +#if defined( MODAL_AERO ) + use aero_model, only : aero_model_register + use modal_aero_data, only : nspec_max + use modal_aero_data, only : ntot_amode, nspec_amode + use rad_constituents, only : rad_cnst_get_info +#endif + + ! GEOS-Chem interface modules in CAM + use mo_sim_dat, only : set_sim_dat + + ! GEOS-Chem modules + use GC_Environment_Mod, ONLY : GC_Init_Grid + use Input_Opt_Mod, only : Set_Input_Opt, Cleanup_Input_Opt + use State_Chm_Mod, only : Init_State_Chm, Cleanup_State_Chm, Ind_ + use State_Grid_Mod, only : Init_State_Grid, Cleanup_State_Grid + + !----------------------------------------------------------------------- + ! + ! Purpose: register advected constituents for chemistry + ! + !----------------------------------------------------------------------- + ! Need to generate a temporary species database + TYPE(ChmState) :: SC + TYPE(GrdState) :: SG + TYPE(OptInput) :: IO + TYPE(Species), POINTER :: ThisSpc + + INTEGER :: I, N, M, L + INTEGER :: nIgnored + INTEGER :: tmpIdx + REAL(r8) :: cptmp + REAL(r8) :: MWTmp + REAL(r8) :: qmin + REAL(r8) :: refmmr, refvmr + REAL(r8), ALLOCATABLE :: slvd_refmmr(:) + CHARACTER(LEN=128) :: mixtype + CHARACTER(LEN=128) :: molectype + CHARACTER(LEN=128) :: lngName + CHARACTER(LEN=64) :: cnstName + CHARACTER(LEN=64) :: trueName + CHARACTER(LEN=64) :: aerName + LOGICAL :: camout + LOGICAL :: ic_from_cam2 + LOGICAL :: has_fixed_ubc + LOGICAL :: has_fixed_ubflx + + INTEGER :: RC, IERR + + ! Assume a successful return until otherwise + RC = GC_SUCCESS + + ! For error trapping + ErrMsg = '' + ThisLoc = ' -> at GEOS-Chem (in chemistry/geoschem/chemistry.F90)' + + ! Initialize pointer + ThisSpc => NULL() + + if (debug .and. masterproc) write(iulog,'(a)') 'chem_register: registering advected constituents for GEOS-Chem chemistry' + + ! SDE 2018-05-02: This seems to get called before anything else + ! that includes CHEM_INIT + ! At this point, mozart calls SET_SIM_DAT, which is specified by each + ! mechanism separately (ie mozart/chemistry.F90 calls the subroutine + ! set_sim_dat which is in pp_[mechanism]/mo_sim_dat.F90. That sets a lot of + ! data in other places, notably in "chem_mods" + + ! hplin 2020-05-16: Call set_sim_dat to populate chemistry constituent information + ! from mo_sim_dat.F90 in other places. This is needed for HEMCO_CESM. + CALL Set_sim_dat() + + ! Prevent Reporting + IO%amIRoot = .False. + IO%thisCpu = MyCPU + + CALL Set_Input_Opt( am_I_Root = MasterProc, & + Input_Opt = IO, & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Could not generate reference input options object!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! Options needed by Init_State_Chm + IO%ITS_A_FULLCHEM_SIM = .True. + IO%LLinoz = .True. + IO%LPRT = .False. + IO%N_Advect = nTracers + DO I = 1, nTracers + IO%AdvectSpc_Name(I) = TRIM(tracerNames(I)) + ENDDO + IO%SALA_rEdge_um(1) = 0.01e+0_fp + IO%SALA_rEdge_um(2) = 0.50e+0_fp + IO%SALC_rEdge_um(1) = 0.50e+0_fp + IO%SALC_rEdge_um(2) = 8.00e+0_fp + + IO%SpcDatabaseFile = TRIM(speciesDB) + + CALL Init_State_Grid( Input_Opt = IO, & + State_Grid = SG, & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered within call to "Init_State_Grid"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + SG%NX = 1 + SG%NY = 1 + SG%NZ = 1 + + CALL GC_Init_Grid( Input_Opt = IO, & + State_Grid = SG, & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error in GC_Init_Grid"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered within call to "Init_CMN_SIZE"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + CALL Init_State_Chm( Input_Opt = IO, & + State_Chm = SC, & + State_Grid = SG, & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered within call to "Init_State_Chm"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + iFirstCnst = -1 + mapCnst = -1 + map2GC = -1 + map2GCinv = -1 + map2chm = -1 + ref_MMR(:) = 0.0e+0_r8 + + ! nTracersMax must be # advected species in geoschem_config.yml (nTracers) plus + ! # aerosols (nAer) plus 1 (for CO2). It is set in chem_mods.F90. + DO I = 1, nTracersMax + IF ( I .LE. nTracers ) THEN + cnstName = to_upper(TRIM(tracerNames(I))) + trueName = cnstName + N = Ind_(cnstName) + ThisSpc => SC%SpcData(N)%Info + lngName = TRIM(ThisSpc%FullName) + MWTmp = REAL(ThisSpc%MW_g,r8) + refvmr = REAL(ThisSpc%BackgroundVV,r8) + refmmr = refvmr / (MWDry / MWTmp) + ! Make sure that solsym is following the list of tracers as listed + ! geoschem_config.yml + IF ( to_upper(TRIM(tracerNames(I))) /= to_upper(TRIM(solsym(I))) ) THEN + Write(iulog,*) "tracerNames (", TRIM(tracerNames(I)), ") /= solsym (", & + TRIM(solsym(I)), ")" + CALL ENDRUN('Solsym must be following GEOS-Chem tracer. Check geoschem/mo_sim.dat') + ENDIF + ! Nullify pointer + ThisSpc => NULL() + ELSEIF ( I .LE. (nTracers + nAer) ) THEN + ! Add MAM4 aerosols + cnstName = TRIM(aerNames(I - nTracers)) + trueName = cnstName + lngName = cnstName + MWTmp = aerAdvMass(I - nTracers) + refmmr = 1.0e-38_r8 + ELSEIF ( I .EQ. (nTracers + nAer + 1) ) THEN + ! Add CO2 (which is not a GEOS-Chem tracer) + cnstName = 'CO2' + trueName = cnstName + lngName = cnstName + MWTmp = 44.009800_r8 + refmmr = 1.0e-38_r8 + ELSE + cnstName = TRIM(tracerNames(I)) + trueName = cnstName + lngName = cnstName + MWTmp = 1000.0e+0_r8 * (0.001e+0_r8) + refmmr = 1.0e-38_r8 + ENDIF + + ! dummy value for specific heat of constant pressure (Cp) + cptmp = 666._r8 + ! minimum mixing ratio + qmin = 1.e-38_r8 + ! mixing ratio type + mixtype = 'dry' + ! Used for ionospheric WACCM (WACCM-X) + molectype = 'minor' + ! Is an output field (?) + camout = .false. + ! Not true for O2(1-delta) or O2(1-sigma) + ic_from_cam2 = .true. + ! Use a fixed value at the upper boundary + has_fixed_ubc = .false. + ! Use a fixed flux condition at the upper boundary + has_fixed_ubflx = .false. + + ! TMMF - 8/20/2020 + ! Note: I had to modify the IC file to rename variables such as + ! CH3COCH3 into ACET. Using that new IC file, we can thus remove + ! the unnecessary special handlings. + ! Another option would have been to modify cnst_add and read_inidat + ! to use a load_name the first time IC are read. Constituent names + ! would be stored in cnst_name, while read_inidat would load from + ! load_name. load_name would be an optional argument to cnst_add, such + ! that, by default, load_name = cnst_name. + ! However, this would be tricky to handle with restart files that + ! would save cnst_name rather than load_name. + + ! Special handlings + IF ( cnstName == 'HCHO' ) THEN + cnstName = 'CH2O' + !ELSEIF ( cnstName == 'HNO4' ) THEN + ! cnstName = 'HO2NO2' + !ELSEIF ( cnstName == 'HNO2' ) THEN + ! cnstName = 'HONO' + !ELSEIF ( cnstName == 'ACET' ) THEN + ! cnstName = 'CH3COCH3' + !ELSEIF ( cnstName == 'ALD2' ) THEN + ! cnstName = 'CH3CHO' + !ELSEIF ( cnstName == 'PRPE' ) THEN + ! cnstName = 'C3H6' + !ELSEIF ( cnstName == 'MP' ) THEN + ! cnstName = 'CH3OOH' + !ELSEIF ( cnstName == 'HAC' ) THEN + ! cnstName = 'HYAC' + !ELSEIF ( cnstName == 'GLYC' ) THEN + ! cnstName = 'GLYALD' + !ELSEIF ( cnstName == 'MAP' ) THEN + ! cnstName = 'CH3COOOH' + !ELSEIF ( cnstName == 'EOH' ) THEN + ! cnstName = 'C2H5OH' + !ELSEIF ( cnstName == 'MGLY' ) THEN + ! cnstName = 'CH3COCHO' + !ELSEIF ( cnstName == 'GLYX' ) THEN + ! cnstName = 'GLYOXAL' + !ELSEIF ( cnstName == 'ACTA' ) THEN + ! cnstName = 'CH3COOH' + !ELSEIF ( cnstName == 'TOLU' ) THEN + ! cnstName = 'TOLUENE' + ENDIF + + CALL cnst_add( cnstName, MWtmp, cptmp, qmin, N, & + readiv=ic_from_cam2, mixtype=mixtype, & + cam_outfld=camout, molectype=molectype, & + fixed_ubc=has_fixed_ubc, & + fixed_ubflx=has_fixed_ubflx, & + longname=TRIM(lngName) ) + + IF ( iFirstCnst < 0 ) iFirstCnst = N + + ref_MMR(N) = refmmr + + ! Add to GC mapping. When starting a timestep, we will want to update the + ! concentration of State_Chm(x)%Species(m)%Conc(1,iCol,iLev) with data from + ! constituent n + M = Ind_(TRIM(trueName)) + IF ( M > 0 ) THEN + ! Map constituent onto GEOS-Chem tracer as indexed in State_Chm(LCHNK)%Species + map2GC(N) = M + ! Map GEOS-Chem tracer onto constituent + map2GCinv(M) = N + ENDIF + ! Map constituent onto chemically-active species (aka as indexed in solsym) + M = get_spc_ndx(TRIM(trueName), ignore_case=.true.) + IF ( M > 0 ) THEN + mapCnst(N) = M + ENDIF + ENDDO + + ! Now unadvected species + map2GC_Sls = 0 + ALLOCATE(slvd_refmmr(nslvd), STAT=IERR) + slvd_refmmr(:) = 0.0e+0_r8 + IF ( IERR .NE. 0 ) CALL ENDRUN('Failed to allocate map2MAM4') + DO I = 1, nSlvd + N = Ind_(slsNames(I)) + IF ( N .GT. 0 ) THEN + ThisSpc => SC%SpcData(N)%Info + MWTmp = REAL(ThisSpc%MW_g,r8) + refvmr = REAL(ThisSpc%BackgroundVV,r8) + lngName = TRIM(ThisSpc%FullName) + slvd_refmmr(I) = refvmr / (MWDry / MWTmp) + map2GC_Sls(I) = N + ThisSpc => NULL() + ENDIF + ENDDO + CALL Register_Short_Lived_Species(slvd_refmmr) + DEALLOCATE(slvd_refmmr) + ! More information: + ! http://www.cesm.ucar.edu/models/atm-cam/docs/phys-interface/node5.html + + if (debug .and. masterproc) write(iulog,'(a,i4,a)') 'chem_register: looping over gas_pcnst (length', gas_pcnst, ') to map solsym onto GEOS-Chem species' + + DO N = 1, gas_pcnst + ! Map solsym onto GEOS-Chem species + map2chm(N) = Ind_(TRIM(solsym(N))) + IF ( map2chm(N) < 0 ) THEN + ! This is not a GEOS-Chem species and we thus map to constituents list. + ! Most likely, these will be MAM aerosols + ! We store the index as the opposite to not confuse with GEOS-Chem + ! indices. + CALL cnst_get_ind(TRIM(solsym(N)), I, abort=.True.) + map2chm(N) = -I + if (debug .and. masterproc) write(iulog,'(a,a,a,I4,a,I4)') ' -> solsym species ', trim(solsym(N)), ' (index ', N, ') is not a GEOS-Chem species. Mapping to negative constituent index: ', map2chm(N) + ELSE + if (debug .and. masterproc) write(iulog,'(a,a,a,I4,a,I4)') ' -> solsym species ', trim(solsym(N)), ' (index ', N, ') mapped to GEOS-Chem species ', map2chm(N) + ENDIF + ENDDO + ! Get constituent index of specific humidity + CALL cnst_get_ind('Q', cQ, abort=.True.) + CALL cnst_get_ind('H2O', cH2O, abort=.True.) + CALL cnst_get_ind('H2SO4', cH2SO4, abort=.True.) + + !------------------------------------------------------------ + ! Get mapping between dry deposition species and species set + !------------------------------------------------------------ + + nIgnored = 0 + + if (debug .and. masterproc) write(iulog,'(a,i4,a)') 'chem_register: looping over gas dry deposition list with ', nddvels, ' species' + + DO N = 1, nddvels + + ! The species names need to be convert to upper case as, + ! for instance, BR2 != Br2 + drySpc_ndx(N) = get_spc_ndx( to_upper(drydep_list(N)), ignore_case=.true. ) + + if (debug .and. masterproc) write(iulog,'(a,a,a,i4,a,i4)') ' -> species ', trim(drydep_list(N)), ' in dry deposition list at index ', N, ' maps to species in solsym at index ', drySpc_ndx(N) + + IF ( MasterProc .and. ( drySpc_ndx(N) < 0 ) ) THEN + Write(iulog,'(a,a)') ' ## Ignoring dry deposition of ', & + TRIM(drydep_list(N)) + nIgnored = nIgnored + 1 + ENDIF + ENDDO + + IF ( MasterProc .AND. ( nIgnored > 0 ) ) THEN + Write(iulog,'(a,a)') ' The species listed above have dry', & + ' deposition turned off for one of the following reasons:' + Write(iulog,'(a)') ' - They are not present in the GEOS-Chem tracer list.' + Write(iulog,'(a)') ' - They have a synonym (e.g. CH2O and HCHO).' + ENDIF + +#if defined( MODAL_AERO_4MODE ) + ! add fields to pbuf needed by aerosol models + CALL aero_model_register() + + ! Mode | \sigma_g | Dry diameter (micrometers) + ! -----------------------|----------|-------------------------- + ! a2 - Aitken mode | 1.6 | 0.015 - 0.053 + ! a1 - Accumulation mode | 1.8 | 0.058 - 0.27 + ! a3 - Coarse mode | 1.8 | 0.80 - 3.65 + ! a4 - Primary carbon | 1.6 | 0.039 - 0.13 + ! -----------------------|----------|-------------------------- + ! Ref: Liu, Xiaohong, et al. "Toward a minimal representation of aerosols in + ! climate models: Description and evaluation in the Community Atmosphere + ! Model CAM5." Geoscientific Model Development 5.3 (2012): 709. + + ALLOCATE(map2MAM4(nspec_max,ntot_amode), STAT=IERR) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failed to allocate map2MAM4') + + ALLOCATE(iSulf(ntot_amode), STAT=IERR) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failed to allocate iSulf') + + ! Initialize indices + map2MAM4(:,:) = -1 + iSulf(:) = -1 + + ! ewl notes: xname_massptr returns a name. The select case subsets characters? e.g. 1:3, 4:5, 5:6. + ! so want to get a name give an L and M. Need anything else??? + + DO M = 1, ntot_amode + DO L = 1, nspec_amode(M) + call rad_cnst_get_info(0,M,L,spec_name=aername) + SELECT CASE ( to_upper(aername(:3)) ) + CASE ( 'BC_' ) + SELECT CASE ( to_upper(aername(4:5)) ) + CASE ( 'A1' ) + CALL cnst_get_ind( 'BCPI', map2MAM4(L,M) ) + CASE ( 'A4' ) + CALL cnst_get_ind( 'BCPO', map2MAM4(L,M) ) + END SELECT + CASE ( 'DST' ) + SELECT CASE ( to_upper(aername(5:6)) ) + ! DST1 - Dust aerosol, Reff = 0.7 micrometers + ! DST2 - Dust aerosol, Reff = 1.4 micrometers + ! DST3 - Dust aerosol, Reff = 2.4 micrometers + ! DST4 - Dust aerosol, Reff = 4.5 micrometers + CASE ( 'A1' ) + CALL cnst_get_ind( 'DST1', map2MAM4(L,M) ) + CASE ( 'A2' ) + CALL cnst_get_ind( 'DST1', map2MAM4(L,M) ) + CASE ( 'A3' ) + CALL cnst_get_ind( 'DST4', map2MAM4(L,M) ) + END SELECT + !CASE ( 'SOA' ) + ! CALL cnst_get_ind( 'SOAS', map2MAM4(L,M) ) + CASE ( 'SO4' ) + CALL cnst_get_ind( 'SO4', map2MAM4(L,M) ) + iSulf(M) = L + CASE ( 'NCL' ) + SELECT CASE ( to_upper(aername(5:6)) ) + ! SALA - Fine (0.01-0.05 micros) sea salt aerosol + ! SALC - Coarse (0.5-8 micros) sea salt aerosol + CASE ( 'A1' ) + CALL cnst_get_ind( 'SALA', map2MAM4(L,M) ) + CASE ( 'A2' ) + CALL cnst_get_ind( 'SALA', map2MAM4(L,M) ) + CASE ( 'A3' ) + CALL cnst_get_ind( 'SALC', map2MAM4(L,M) ) + END SELECT + CASE ( 'POM' ) + SELECT CASE ( to_upper(aername(5:6)) ) + CASE ( 'A1' ) + CALL cnst_get_ind( 'OCPI', map2MAM4(L,M) ) + CASE ( 'A4' ) + CALL cnst_get_ind( 'OCPO', map2MAM4(L,M) ) + END SELECT + END SELECT + ENDDO + ENDDO + +#endif + + ! Print summary + IF ( MasterProc ) THEN + Write(iulog,'(/, a)') '### Summary of GEOS-Chem species (end of chem_register): ' + Write(iulog,'( a)') REPEAT( '-', 50 ) + Write(iulog,'( a)') '+ List of advected species: ' + Write(iulog,100) 'ID', 'Tracer', ''!'Dry deposition (T/F)' + DO N = 1, nTracers + Write(iulog,120) N, TRIM(tracerNames(N))!, ANY(drySpc_ndx .eq. N) + ENDDO + IF ( nAer > 0 ) THEN + Write(iulog,'(/, a)') '+ List of aerosols: ' + Write(iulog,110) 'ID', 'MAM4 Aerosol' + DO N = 1, nAer + Write(iulog,130) N, TRIM(aerNames(N)) + ENDDO + ENDIF + Write(iulog,'(/, a)') '+ List of short-lived species: ' + DO N = 1, nSls + Write(iulog,130) N, TRIM(slsNames(N)) + ENDDO + ENDIF + +100 FORMAT( 1x, A3, 3x, A10, 1x, A25 ) +110 FORMAT( 1x, A3, 3x, A15 ) +!120 FORMAT( 1x, I3, 3x, A10, 1x, L15 ) +120 FORMAT( 1x, I3, 3x, A10 ) +130 FORMAT( 1x, I3, 3x, A10 ) + + ! Clean up + Call Cleanup_State_Chm ( SC, RC ) + Call Cleanup_State_Grid( SG, RC ) + Call Cleanup_Input_Opt ( IO, RC ) + + ! Add data for HEMCO extensions to buffers + call pbuf_add_field('HCO_IN_JNO2', 'global', dtype_r8, (/pcols/), tmpIdx) + call pbuf_add_field('HCO_IN_JOH', 'global', dtype_r8, (/pcols/), tmpIdx) + + if (debug .and. masterproc) write(iulog,'(a)') 'chem_register: advected constituent registration for GEOS-Chem chemistry complete ' + + end subroutine chem_register + + !================================================================================================ + ! subroutine chem_readnl + !================================================================================================ + subroutine chem_readnl(nlfile) + + ! CAM modules + use cam_abortutils, only : endrun + use chem_mods, only : drySpc_ndx + use gas_wetdep_opts, only : gas_wetdep_readnl + use gckpp_Model, only : nSpec, Spc_Names + use namelist_utils, only : find_group_name + use mo_lightning, only : lightning_readnl + use spmd_utils, only : mpicom, masterprocid, mpi_success + use spmd_utils, only : mpi_character, mpi_integer, mpi_logical + use units, only : getunit, freeunit +#if defined( MODAL_AERO ) + use aero_model, only : aero_model_readnl + use dust_model, only : dust_readnl +#endif + ! For dry deposition on unstructured grids + use mo_drydep, only : drydep_srf_file + + ! args + CHARACTER(LEN=*), INTENT(IN) :: nlfile ! filepath for file containing namelist input + + ! Local variables + INTEGER :: I, N + INTEGER :: UNITN, IERR, RC + CHARACTER(LEN=500) :: line + CHARACTER(LEN=63) :: substrs(2) + CHARACTER(LEN=*), PARAMETER :: subname = 'chem_readnl' + LOGICAL :: validSLS, v_bool + + namelist /chem_inparm/ depvel_lnd_file + namelist /chem_inparm/ drydep_srf_file + + ! ghg chem + namelist /chem_inparm/ bndtvg, h2orates, ghg_chem + + if (debug .and. masterproc) write(iulog,'(a)') 'chem_readnl: reading namelists for GEOS-Chem chemistry' + + ! Assume a successful return until otherwise + RC = GC_SUCCESS + + ALLOCATE(drySpc_ndx(nddvels), STAT=IERR) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failed to allocate drySpc_ndx') + +#if defined( MODAL_AERO_4MODE ) + ! Get names and molar weights of aerosols in MAM4 + + nAer = 33 + + aerNames(:nAer) = (/ 'bc_a1 ','bc_a4 ','dst_a1 ', & + 'dst_a2 ','dst_a3 ','ncl_a1 ', & + 'ncl_a2 ','ncl_a3 ','num_a1 ', & + 'num_a2 ','num_a3 ','num_a4 ', & + 'pom_a1 ','pom_a4 ','so4_a1 ', & + 'so4_a2 ','so4_a3 ','soa1_a1 ', & + 'soa1_a2 ','soa2_a1 ','soa2_a2 ', & + 'soa3_a1 ','soa3_a2 ','soa4_a1 ', & + 'soa4_a2 ','soa5_a1 ','soa5_a2 ', & + 'H2SO4 ','SOAG0 ','SOAG1 ', & + 'SOAG2 ','SOAG3 ','SOAG4 ' /) + + aerAdvMass(:nAer) = (/ 12.011000_r8, 12.011000_r8, 135.064039_r8, & + 135.064039_r8, 135.064039_r8, 58.442468_r8, & + 58.442468_r8, 58.442468_r8, 1.007400_r8, & + 1.007400_r8, 1.007400_r8, 1.007400_r8, & + 12.011000_r8, 12.011000_r8, 115.107340_r8, & + 115.107340_r8, 115.107340_r8, 250.445000_r8, & + 250.445000_r8, 250.445000_r8, 250.445000_r8, & + 250.445000_r8, 250.445000_r8, 250.445000_r8, & + 250.445000_r8, 250.445000_r8, 250.445000_r8, & + 98.078400_r8, 250.445000_r8, 250.445000_r8, & + 250.445000_r8, 250.445000_r8, 250.445000_r8 /) + + CALL aero_model_readnl(nlfile) + CALL dust_readnl(nlfile) +#endif + + DO I = (nAer+1), nAerMax + aerNames(I) = 'EMPTY_AER ' + aerAdvMass(I) = -1.00_r8 + ENDDO + + CALL gas_wetdep_readnl(nlfile) + + CALL lightning_readnl(nlfile) + + CALL geoschem_readnl(nlfile) + + IF ( MasterProc ) THEN + + Write(iulog,'(/,a)') REPEAT( '=', 50 ) + Write(iulog,'(a)') REPEAT( '=', 50 ) + Write(iulog,'(a)') 'This is the GEOS-CHEM / CESM interface' + Write(iulog,'(a)') REPEAT( '=', 50 ) + Write(iulog,'(a)') ' + Routines written by Thibaud M. Fritz' + Write(iulog,'(a)') ' + Laboratory for Aviation and the Environment,' + Write(iulog,'(a)') ' + Department of Aeronautics and Astronautics,' + Write(iulog,'(a)') ' + Massachusetts Institute of Technology' + Write(iulog,'(a)') REPEAT( '=', 50 ) + + Write(iulog,'(/,a,/)') 'Now defining GEOS-Chem tracers and dry deposition mapping...' + + !---------------------------------------------------------- + ! Read GEOS-Chem advected species from geoschem_config.yml + !---------------------------------------------------------- + + unitn = getunit() + + OPEN( unitn, FILE=TRIM(gcConfig), STATUS='OLD', IOSTAT=IERR ) + IF (IERR .NE. 0) THEN + CALL ENDRUN('chem_readnl: ERROR opening geoschem_config.yml') + ENDIF + + ! Find the transported species section + DO + READ( unitn, '(a)', IOSTAT=IERR ) line + IF ( IERR .NE. 0 ) CALL ENDRUN('chem_readnl: error finding adv spc list') + LINE = ADJUSTL( ADJUSTR( LINE ) ) + IF ( INDEX( LINE, 'transported_species' ) > 0 ) EXIT + ENDDO + + if (debug) write(iulog,'(a)') 'chem_readnl: reading advected species list from geoschem_config.yml' + + ! Read in all advected species names and add them to tracer names list + nTracers = 0 + DO WHILE ( LEN_TRIM( line ) > 0 ) + READ(unitn,'(a)', IOSTAT=IERR) line + IF ( IERR .NE. 0 ) CALL ENDRUN('chem_readnl: error setting adv spc list') + line = ADJUSTL( ADJUSTR( line ) ) + IF ( INDEX( line, 'passive_species' ) > 0 ) EXIT + IF ( INDEX( LINE, '-' ) > 0 ) THEN + substrs(1) = LINE(3:) + substrs(1) = ADJUSTL( ADJUSTR( substrs(1) ) ) + + ! Remove quotes (i.e. 'NO' -> NO) + I = INDEX( substrs(1), "'" ) + IF ( I > 0 ) THEN + substrs(1) = substrs(1)(I+1:) + I = INDEX( substrs(1), "'" ) + IF ( I > 0 ) substrs(1) = substrs(1)(1:I-1) + ENDIF + + nTracers = nTracers + 1 + tracerNames(nTracers) = TRIM(substrs(1)) + + write(iulog,'(a,i4,a,a)') ' ', nTracers, ' ', TRIM(substrs(1)) + ENDIF + ENDDO + CLOSE(unitn) + CALL freeunit(unitn) + + ! Assign remaining tracers dummy names + DO I = (nTracers+1), nTracersMax + WRITE(tracerNames(I),'(a,I0.4)') 'GCTRC_', I + ENDDO + + !---------------------------------------------------------- + ! Now go through the KPP mechanism and add any species not + ! implemented by the tracer list in geoschem_config.yml + !---------------------------------------------------------- + + IF ( nSpec > nSlsMax ) THEN + CALL ENDRUN('chem_readnl: too many species - increase nSlsmax') + ENDIF + + if (debug .and. masterproc) write(iulog,'(a)') 'chem_readnl: getting non-advected (short-lived) species list from KPP' + if (debug .and. masterproc) write(iulog,'(a)') 'NOTE: does not include CO2 even if CO2 is not advected' + + nSls = 0 + DO I = 1, nSpec + ! Get the name of the species from KPP + line = ADJUSTL(TRIM(Spc_Names(I))) + ! Only add short-lived KPP species, except from CO2 + validSLS = (( .NOT. ANY(TRIM(line) .EQ. tracerNames) ) & + .AND. TRIM(line) /= 'CO2' ) + IF ( validSLS ) THEN + ! Genuine new short-lived species + nSls = nSls + 1 + slsNames(nSls) = TRIM(line) + write(iulog,'(a,i4,a,a)') ' ', nSls, ' ', TRIM(slsNames(nSls)) + ENDIF + ENDDO + + unitn = getunit() + OPEN( unitn, FILE=TRIM(nlfile), STATUS='old', IOSTAT=IERR ) + IF (IERR .NE. 0) THEN + CALL ENDRUN('chem_readnl: ERROR opening '//TRIM(nlfile)) + ENDIF + + CALL find_group_name(unitn, 'chem_inparm', STATUS=IERR) + IF (IERR == 0) THEN + READ(unitn, chem_inparm, IOSTAT=IERR) + IF (IERR /= 0) THEN + CALL endrun('chem_readnl: ERROR reading namelist chem_inparm') + ENDIF + ENDIF + CLOSE(unitn) + CALL freeunit(unitn) + + ENDIF + + !---------------------------------------------------------- + ! Broadcast to all processors + !---------------------------------------------------------- + CALL mpi_bcast(nTracers, 1, mpi_integer, masterprocid, mpicom, ierr) + IF ( ierr /= mpi_success ) then + CALL endrun(subname//': MPI_BCAST ERROR: nTracers') + ENDIF + CALL mpi_bcast(tracerNames, LEN(tracerNames(1))*nTracersMax, mpi_character, masterprocid, mpicom, ierr) + IF ( ierr /= mpi_success ) then + CALL endrun(subname//': MPI_BCAST ERROR: tracerNames') + ENDIF + CALL mpi_bcast(nSls, 1, mpi_integer, masterprocid, mpicom, ierr) + IF ( ierr /= mpi_success ) then + CALL endrun(subname//': MPI_BCAST ERROR: nSls') + ENDIF + CALL mpi_bcast(slsNames, LEN(slsNames(1))*nSlsMax, mpi_character, masterprocid, mpicom, ierr) + IF ( ierr /= mpi_success ) then + CALL endrun(subname//': MPI_BCAST ERROR: slsNames') + ENDIF + + ! Broadcast namelist variables + CALL mpi_bcast(depvel_lnd_file, LEN(depvel_lnd_file), mpi_character, masterprocid, mpicom, ierr) + IF ( ierr /= mpi_success ) then + CALL endrun(subname//': MPI_BCAST ERROR: depvel_lnd_file') + ENDIF + CALL mpi_bcast(drydep_srf_file, LEN(drydep_srf_file), mpi_character, masterprocid, mpicom, ierr) + IF ( ierr /= mpi_success ) then + CALL endrun(subname//': MPI_BCAST ERROR: drydep_srf_file') + ENDIF + CALL mpi_bcast(ghg_chem, 1, mpi_logical, masterprocid, mpicom, ierr) + IF ( ierr /= mpi_success ) then + CALL endrun(subname//': MPI_BCAST ERROR: ghg_chem') + ENDIF + CALL mpi_bcast(bndtvg, LEN(bndtvg), mpi_character, masterprocid, mpicom, ierr) + IF ( ierr /= mpi_success ) then + CALL endrun(subname//': MPI_BCAST ERROR: bndtvg') + ENDIF + CALL mpi_bcast(h2orates, LEN(h2orates), mpi_character, masterprocid, mpicom, ierr) + IF ( ierr /= mpi_success ) then + CALL endrun(subname//': MPI_BCAST ERROR: h2orates') + ENDIF + + IF ( nSls .NE. nSlvd ) THEN + write(iulog,'(a,i4)') 'nSlvd in geoschem/chem_mods.F90 does not match # non-advected KPP species. Set nSlvd to ', nSls + CALL ENDRUN('Failure while allocating slvd_Lst') + ENDIF + + ALLOCATE(slvd_Lst(nSlvd), STAT=IERR) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating slvd_Lst') + DO I = 1, nSls + slvd_Lst(I) = TRIM(slsNames(I)) + ENDDO + + if (debug .and. masterproc) write(iulog,'(a)') 'chem_readnl: reading GEOS-Chem chemistry namelists complete' + + end subroutine chem_readnl + + !================================================================================================ + ! function chem_is_active + !================================================================================================ + function chem_is_active() + + logical :: chem_is_active + + chem_is_active = .true. + + end function chem_is_active + + !================================================================================================ + ! function chem_implements_cnst + !================================================================================================ + function chem_implements_cnst(name) + ! Purpose: return true if specified constituent is implemented by this package + ! Author: B. Eaton + + IMPLICIT NONE + + CHARACTER(LEN=*), INTENT(IN) :: name ! constituent name + LOGICAL :: chem_implements_cnst ! return value + INTEGER :: M + + chem_implements_cnst = .false. + DO M = 1, gas_pcnst + IF (TRIM(solsym(M)) .eq. TRIM(name)) THEN + chem_implements_cnst = .true. + EXIT + ENDIF + ENDDO + + end function chem_implements_cnst + + !================================================================================================ + ! subroutine chem_init + !================================================================================================ + subroutine chem_init(phys_state, pbuf2d) + !----------------------------------------------------------------------- + ! + ! Purpose: initialize GEOS-Chem parts (state objects, mainly) + ! (and declare history variables) + ! + !----------------------------------------------------------------------- + + ! CAM modules + use cam_abortutils, only : endrun + use chem_mods, only : map2GC_dryDep, drySpc_ndx + use gas_wetdep_opts, only : gas_wetdep_method + use hycoef, only : ps0, hyai, hybi, hyam + use mo_chem_utls, only : get_spc_ndx + use mo_ghg_chem, only : ghg_chem_init + use mo_mean_mass, only : init_mean_mass + use mo_neu_wetdep, only : neu_wetdep_init + use mo_setinv, only : setinv_inti + use Phys_Grid, only : get_Area_All_p + use physics_buffer, only : physics_buffer_desc, pbuf_get_index + use spmd_utils, only : mpicom, masterprocid, mpi_real8, mpi_success + use tracer_cnst, only : tracer_cnst_init + use tracer_srcs, only : tracer_srcs_init +#if defined( MODAL_AERO ) + use aero_model, only : aero_model_init + use mo_setsox, only : sox_inti + use mo_drydep, only : drydep_inti + use modal_aero_data, only : ntot_amode, nspec_amode +#endif + + ! GEOS-Chem interface modules in CAM + use geoschem_diagnostics_mod, only : GC_Diagnostics_Init + use geoschem_emissions_mod, only : GC_Emissions_Init + use geoschem_history_mod, only : HistoryExports_SetServices + + ! GEOS-Chem modules + use Chemistry_Mod, only : Init_Chemistry + use DiagList_Mod, only : Init_DiagList, Print_DiagList + use Drydep_Mod, only : depName, Ndvzind + use Error_Mod, only : Init_Error + use GC_Environment_Mod, only : GC_Init_Grid, GC_Init_StateObj + use GC_Environment_Mod, only : GC_Init_Extra, GC_Allocate_All + use GC_Grid_Mod, only : SetGridFromCtrEdges + use Input_Mod, only : Read_Input_File, Validate_Directories + use Input_Opt_Mod, only : Set_Input_Opt + use isorropiaII_Mod, only : Init_IsorropiaII + use Linear_Chem_Mod, only : Init_Linear_Chem + use Linoz_Mod, only : Linoz_Read + use PhysConstants, only : PI, PI_180, Re + use Pressure_Mod, only : Accept_External_ApBp + use State_Chm_Mod, only : Ind_ + use State_Grid_Mod, only : Init_State_Grid, Cleanup_State_Grid + use TaggedDiagList_Mod, only : Init_TaggedDiagList, Print_TaggedDiagList + use Time_Mod, only : Accept_External_Date_Time + use Ucx_Mod, only : Init_Ucx + use Vdiff_Mod, only : Max_PblHt_For_Vdiff + + TYPE(physics_state), INTENT(IN ) :: phys_state(BEGCHUNK:ENDCHUNK) + TYPE(physics_buffer_desc), POINTER, INTENT(INOUT) :: pbuf2d(:,:) + + ! Local variables + + !---------------------------- + ! Scalars + !---------------------------- + + ! Integers + INTEGER :: LCHNK(BEGCHUNK:ENDCHUNK), NCOL(BEGCHUNK:ENDCHUNK) + INTEGER :: IWAIT, IERR + INTEGER :: nX, nY, nZ + INTEGER :: nStrat, nTrop + INTEGER :: I, J, L, N, M + INTEGER :: RC + INTEGER :: nLinoz + + ! Logicals + LOGICAL :: prtDebug + LOGICAL :: Found + + ! Strings + CHARACTER(LEN=shr_kind_cl) :: historyConfigFile + CHARACTER(LEN=shr_kind_cl) :: SpcName + CHARACTER(LEN=*), PARAMETER :: subname = 'chem_init' + + ! Objects + TYPE(Species), POINTER :: SpcInfo + + ! Grid setup + REAL(fp) :: lonVal, latVal + REAL(fp) :: dLonFix, dLatFix + REAL(f4), ALLOCATABLE :: lonMidArr(:,:), latMidArr(:,:) + REAL(f4), ALLOCATABLE :: lonEdgeArr(:,:), latEdgeArr(:,:) + REAL(r8), ALLOCATABLE :: linozData(:,:,:,:) + + ! Grid with largest number of columns + TYPE(GrdState) :: maxGrid ! Grid State object + + REAL(r8), ALLOCATABLE :: Col_Area(:) + REAL(fp), ALLOCATABLE :: Ap_CAM_Flip(:), Bp_CAM_Flip(:) + + !REAL(r8), POINTER :: SlsPtr(:,:,:) + + ! Assume a successful return until otherwise + RC = GC_SUCCESS + + ! For error trapping + ErrMsg = '' + ThisLoc = ' -> at GEOS-Chem (in chemistry/geoschem/chemistry.F90)' + + ! Initialize pointers + SpcInfo => NULL() + + if (debug .and. masterproc) write(iulog,'(a)') 'chem_init: initializing GEOS-Chem chemistry' + + ! LCHNK: which chunks we have on this process + LCHNK = phys_state%LCHNK + ! NCOL: number of atmospheric columns for each chunk + NCOL = phys_state%NCOL + + ! The GEOS-Chem grids on every "chunk" will all be the same size, to avoid + ! the possibility of having differently-sized chunks + nX = 1 + !nY = MAXVAL(NCOL) + nY = PCOLS + nZ = PVER + + !! Add short lived speies to buffers + !CALL Pbuf_add_field(Trim(SLSBuffer),'global',dtype_r8,(/PCOLS,PVER,nSls/),Sls_Pbf_Idx) + !! Initialize + !ALLOCATE(SlsPtr(PCOLS,PVER,BEGCHUNK:ENDCHUNK), STAT=IERR) + !IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating SlsPtr') + !SlsPtr(:,:,:) = 0.0e+0_r8 + !DO I=1,nSls + ! SlsPtr(:,:,:) = sls_ref_MMR(I) + ! CALL pbuf_set_field(pbuf2d,Sls_Pbf_Idx,SlsPtr,start=(/1,1,i/),kount=(/PCOLS,PVER,1/)) + !ENDDO + !DEALLOCATE(SlsPtr) + + ! This ensures that each process allocates everything needed for its chunks + ALLOCATE(State_Chm(BEGCHUNK:ENDCHUNK) , STAT=IERR) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating State_Chm') + ALLOCATE(State_Diag(BEGCHUNK:ENDCHUNK) , STAT=IERR) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating State_Diag') + ALLOCATE(State_Grid(BEGCHUNK:ENDCHUNK), STAT=IERR) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating State_Grid') + ALLOCATE(State_Met(BEGCHUNK:ENDCHUNK) , STAT=IERR) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating State_Met') + + ! Initialize fields of the Input Options object + CALL Set_Input_Opt( am_I_Root = MasterProc, & + Input_Opt = Input_Opt, & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered within call to "Set_Input_Opt"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! Find maximum tropopause level, set at 40 hPa (based on GEOS-Chem 72 and 47 + ! layer grids) + nTrop = nZ + DO WHILE ( hyam(nZ+1-nTrop) * ps0 < 4000.0 ) + nTrop = nTrop-1 + ENDDO + ! Find stratopause level, defined at 1 hPa + nStrat = nZ + DO WHILE ( hyam(nZ+1-nStrat) * ps0 < 100.0 ) + nStrat = nStrat-1 + ENDDO + + ! Initialize grid with largest number of columns + ! This is required as State_Grid(LCHNK) can have different + ! number of columns, but GEOS-Chem arrays are defined based + ! on State_Grid(BEGCHUNK). + ! To go around this, we define all of GEOS-Chem arrays with + ! size PCOLS x PVER, which is the largest possible number of + ! grid cells. + CALL Init_State_Grid( Input_Opt = Input_Opt, & + State_Grid = maxGrid, & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered within call to "Init_State_Grid"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + maxGrid%NX = nX + maxGrid%NY = nY + maxGrid%NZ = nZ + + Input_Opt%thisCPU = myCPU + Input_Opt%amIRoot = MasterProc + + CALL Read_Input_File( Input_Opt = Input_Opt, & + State_Grid = maxGrid, & + RC = RC ) + + ! First setup directories + Input_Opt%Chem_Inputs_Dir = TRIM(geoschem_cheminputs) + Input_Opt%SpcDatabaseFile = TRIM(speciesDB) + Input_Opt%FAST_JX_DIR = TRIM(geoschem_cheminputs)//'FAST_JX/v2020-02/' + + !---------------------------------------------------------- + ! CESM-specific input flags + !---------------------------------------------------------- + + ! onlineAlbedo -> True (use CLM albedo) + ! -> False (read monthly-mean albedo from HEMCO) + Input_Opt%onlineAlbedo = .true. + + ! applyQtend: apply tendencies of water vapor to specific humidity + Input_Opt%applyQtend = .False. + + ! correctConvUTLS: Apply photolytic correction for convective scavenging of soluble tracers? + Input_Opt%correctConvUTLS = .true. + + IF ( .NOT. Input_Opt%LSOA ) THEN + CALL ENDRUN('CESM2-GC requires the complex SOA option to be on!') + ENDIF + + CALL Validate_Directories( Input_Opt, RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Validation_Directories"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! Initialize GEOS-Chem horizontal grid structure + CALL GC_Init_Grid( Input_Opt = Input_Opt, & + State_Grid = maxGrid, & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered within call to "GC_Init_Grid"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! Define more variables for maxGrid + maxGrid%MaxTropLev = nTrop + maxGrid%MaxStratLev = nStrat + maxGrid%MaxChemLev = maxGrid%MaxStratLev + + DO I = BEGCHUNK, ENDCHUNK + + ! Initialize fields of the Grid State object + CALL Init_State_Grid( Input_Opt = Input_Opt, & + State_Grid = State_Grid(I), & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered within call to "Init_State_Grid"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + State_Grid(I)%NX = nX + State_Grid(I)%NY = NCOL(I) + State_Grid(I)%NZ = nZ + + ! Initialize GEOS-Chem horizontal grid structure + CALL GC_Init_Grid( Input_Opt = Input_Opt, & + State_Grid = State_Grid(I), & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered within call to "GC_Init_Grid"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! Define more variables for State_Grid + State_Grid(I)%MaxTropLev = nTrop + State_Grid(I)%MaxStratLev = nStrat + + ! Set maximum number of levels in the chemistry grid + State_Grid(I)%MaxChemLev = State_Grid(I)%MaxStratLev + + ENDDO + + ! Note - this is called AFTER chem_readnl, after X, and after + ! every constituent has had its initial conditions read. Any + ! constituent which is not found in the CAM restart file will + ! then have already had a call to chem_implements_cnst, and will + ! have then had a call to chem_init_cnst to set a default VMR + ! Call the routine GC_Allocate_All (located in module file + ! GeosCore/gc_environment_mod.F90) to allocate all lat/lon + ! allocatable arrays used by GEOS-Chem. + CALL GC_Allocate_All ( Input_Opt = Input_Opt, & + State_Grid = maxGrid, & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "GC_Allocate_All"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! Read in data for Linoz. All CPUs allocate one array to hold the data. Only + ! the root CPU reads in the data; then we copy it out to a temporary array, + ! broadcast to all other CPUs, and finally duplicate the data into every + ! copy of Input_Opt + IF ( Input_Opt%LLinoz ) THEN + ! Allocate array for broadcast + nLinoz = Input_Opt%Linoz_NLevels * & + Input_Opt%Linoz_NLat * & + Input_Opt%Linoz_NMonths * & + Input_Opt%Linoz_NFields + ALLOCATE( linozData( Input_Opt%Linoz_NLevels, & + Input_Opt%Linoz_NLat, & + Input_Opt%Linoz_NMonths, & + Input_Opt%Linoz_NFields ), STAT=IERR) + IF (IERR .NE. 0) CALL ENDRUN('Failure while allocating linozData') + linozData = 0.0e+0_r8 + + IF ( MasterProc ) THEN + ! Read data in to Input_Opt%Linoz_TParm + CALL Linoz_Read( Input_Opt, RC ) + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Linoz_Read"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + ! Copy the data to a temporary array + linozData = REAL(Input_Opt%LINOZ_TPARM, r8) + ENDIF + CALL mpi_bcast(linozData, nLinoz, mpi_real8, masterprocid, mpicom, ierr) + IF ( ierr /= mpi_success ) then + CALL endrun(subname//': MPI_BCAST ERROR: linozData') + ENDIF + IF ( .NOT. MasterProc ) THEN + Input_Opt%LINOZ_TPARM = REAL(linozData,fp) + ENDIF + IF ( ALLOCATED( linozData ) ) DEALLOCATE(linozData) + ENDIF + + ! Note: The following calculations do not setup the gridcell areas. + ! In any case, we will need to be constantly updating this grid + ! to compensate for the "multiple chunks per processor" element + ALLOCATE(lonMidArr(maxGrid%nX,maxGrid%nY), STAT=IERR) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating lonMidArr') + ALLOCATE(lonEdgeArr(maxGrid%nX+1,maxGrid%nY+1), STAT=IERR) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating lonEdgeArr') + ALLOCATE(latMidArr(maxGrid%nX,maxGrid%nY), STAT=IERR) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating latMidArr') + ALLOCATE(latEdgeArr(maxGrid%nX+1,maxGrid%nY+1), STAT=IERR) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating latEdgeArr') + + ! We could try and get the data from CAM.. but the goal is to make this GC + ! component completely grid independent. So for now, we set to arbitrary + ! values + ! TODO: This needs more refinement. For now, this generates identical + ! State_Grid for all chunks + DO L = BEGCHUNK, ENDCHUNK + lonMidArr = 0.0e+0_f4 + latMidArr = 0.0e+0_f4 + dLonFix = 360.0e+0_fp / REAL(nX,fp) + dLatFix = 180.0e+0_fp / REAL(NCOL(L),fp) + DO I = 1, nX + ! Center of box, assuming dateline edge + lonVal = -180.0e+0_fp + (REAL(I-1,fp)*dLonFix) + DO J = 1, NCOL(L) + ! Center of box, assuming regular cells + latVal = -90.0e+0_fp + (REAL(J-1,fp)*dLatFix) + lonMidArr(I,J) = REAL((lonVal + (0.5e+0_fp * dLonFix)) * PI_180, f4) + latMidArr(I,J) = REAL((latVal + (0.5e+0_fp * dLatFix)) * PI_180, f4) + + ! Edges of box, assuming regular cells + lonEdgeArr(I,J) = REAL(lonVal * PI_180, f4) + latEdgeArr(I,J) = REAL(latVal * PI_180, f4) + ENDDO + ! Edges of box, assuming regular cells + lonEdgeArr(I,NCOL(L)+1) = REAL((lonVal + dLonFix) * PI_180, f4) + latEdgeArr(I,NCOL(L)+1) = REAL((latVal + dLatFix) * PI_180, f4) + ENDDO + DO J = 1, NCOL(L)+1 + ! Edges of box, assuming regular cells + latVal = -90.0e+0_fp + (REAL(J-1,fp)*dLatFix) + lonEdgeArr(nX+1,J) = REAL((lonVal + dLonFix) * PI_180, f4) + latEdgeArr(nX+1,J) = REAL((latVal) * PI_180, f4) + ENDDO + + CALL SetGridFromCtrEdges( Input_Opt = Input_Opt, & + State_Grid = State_Grid(L), & + lonCtr = lonMidArr, & + latCtr = latMidArr, & + lonEdge = lonEdgeArr, & + latEdge = latEdgeArr, & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "SetGridFromCtrEdges"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ENDDO + IF ( ALLOCATED( lonMidArr ) ) DEALLOCATE( lonMidArr ) + IF ( ALLOCATED( latMidArr ) ) DEALLOCATE( latMidArr ) + IF ( ALLOCATED( lonEdgeArr ) ) DEALLOCATE( lonEdgeArr ) + IF ( ALLOCATED( latEdgeArr ) ) DEALLOCATE( latEdgeArr ) + + ! Set the times held by "time_mod" + CALL Accept_External_Date_Time( value_NYMDb = Input_Opt%NYMDb, & + value_NHMSb = Input_Opt%NHMSb, & + value_NYMDe = Input_Opt%NYMDe, & + value_NHMSe = Input_Opt%NHMSe, & + value_NYMD = Input_Opt%NYMDb, & + value_NHMS = Input_Opt%NHMSb, & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Accept_External_Date_Time"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! Start by setting some dummy timesteps + CALL GC_Update_Timesteps(300.0E+0_r8) + + ! Initialize error module + CALL Init_Error( Input_Opt, RC ) + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Init_Error"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! Set a flag to denote if we should print ND70 debug output + prtDebug = ( Input_Opt%LPRT .and. MasterProc ) + + historyConfigFile = 'HISTORY.rc' + ! This requires geoschem_config.yml and HISTORY.rc to be in the run directory + ! This is the current way chosen to diagnose photolysis rates! + CALL Init_DiagList( MasterProc, historyConfigFile, Diag_List, RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Init_DiagList"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! Initialize the TaggedDiag_List (list of wildcards/tags per diagnostic) + CALL Init_TaggedDiagList( Input_Opt%amIroot, Diag_List, & + TaggedDiag_List, RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Init_TaggedDiagList"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + IF ( prtDebug ) THEN + CALL Print_DiagList( Input_Opt%amIRoot, Diag_List, RC ) + CALL Print_TaggedDiagList( Input_Opt%amIRoot, TaggedDiag_List, RC ) + ENDIF + + ! There are actually two copies of the history configuration, one is contained + ! within HistoryConfig to mimic the properties of GCHP. + ! + ! The above original implementation is similar to GC-Classic and WRF-GC, + ! and is used by geoschem_diagnostics_mod for lookups for certain diagnostic + ! fields for compatibility with CAM-chem outputs. + ! (hplin, 10/31/22) + CALL HistoryExports_SetServices(am_I_Root = masterproc, & + config_file = historyConfigFile, & + HistoryConfig = HistoryConfig, & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "HistoryExports_SetServices"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + DO I = BEGCHUNK, ENDCHUNK + Input_Opt%amIRoot = (MasterProc .AND. (I == BEGCHUNK)) + + CALL GC_Init_StateObj( Diag_List = Diag_List, & ! Diagnostic list obj + TaggedDiag_List = TaggedDiag_List, & ! TaggedDiag list obj + Input_Opt = Input_Opt, & ! Input Options + State_Chm = State_Chm(I), & ! Chemistry State + State_Diag = State_Diag(I), & ! Diagnostics State + State_Grid = maxGrid, & ! Grid State + State_Met = State_Met(I), & ! Meteorology State + RC = RC ) ! Success or failure + + ! Trap potential errors + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "GC_Init_StateObj"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! Start with v/v dry (CAM standard) + State_Chm(I)%Spc_Units = 'v/v dry' + + ENDDO + Input_Opt%amIRoot = MasterProc + + CALL GC_Init_Extra( Diag_List = Diag_List, & ! Diagnostic list obj + & Input_Opt = Input_Opt, & ! Input Options + & State_Chm = State_Chm(BEGCHUNK), & ! Chemistry State + & State_Diag = State_Diag(BEGCHUNK), & ! Diagnostics State + & State_Grid = maxGrid, & ! Grid State + & RC = RC ) ! Success or failure + + ! Trap potential errors + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "GC_Init_Extra"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + IF ( Input_Opt%LDryD ) THEN + !---------------------------------------------------------- + ! Get mapping between CESM dry deposited species and the + ! indices of State_Chm%DryDepVel. This needs to be done after + ! Init_Drydep + ! Thibaud M. Fritz - 04 Mar 2020 + !---------------------------------------------------------- + + ALLOCATE(map2GC_dryDep(nddvels), STAT=IERR) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failed to allocate map2GC_dryDep') + + DO N = 1, nddvels + ! Initialize index to -1 + map2GC_dryDep(N) = -1 + + IF ( drySpc_ndx(N) > 0 ) THEN + + ! Convert to upper case + SpcName = to_upper(drydep_list(N)) + + DO I = 1, State_Chm(BEGCHUNK)%nDryDep + IF ( TRIM( SpcName ) == TRIM( to_upper(depName(I)) ) ) THEN + map2GC_dryDep(N) = nDVZind(I) + EXIT + ENDIF + ENDDO + + ! Print out debug information + IF ( masterProc ) THEN + IF ( N == 1 ) Write(iulog,*) " ++ GEOS-Chem Dry deposition ++ " + IF ( map2GC_dryDep(N) > 0 ) THEN + Write(iulog,*) " CESM species: ", TRIM(drydep_list(N)), & + ' is matched with ', depName(map2GC_dryDep(N)) + ELSE + Write(iulog,*) " CESM species: ", TRIM(drydep_list(N)), & + ' has no match' + ENDIF + ENDIF + + ENDIF + ENDDO + ENDIF + +#if defined( MODAL_AERO ) + ! Initialize aqueous chem + CALL SOx_inti() + + ! Initialize aerosols + CALL aero_model_init( pbuf2d ) + + ! Initialize drydep + CALL drydep_inti( depvel_lnd_file ) +#endif + + IF ( gas_wetdep_method == 'NEU' ) THEN + ! Initialize MOZART's wet deposition + CALL Neu_wetdep_init() + ENDIF + + ! Set grid-cell area + DO N = BEGCHUNK, ENDCHUNK + ALLOCATE(Col_Area(State_Grid(N)%nY), STAT=IERR) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating Col_Area') + + CALL Get_Area_All_p(N, State_Grid(N)%nY, Col_Area) + + ! Set default value (in case of chunks with fewer columns) + State_Grid(N)%Area_M2 = 1.0e+10_fp + DO I = 1, State_Grid(N)%nX + DO J = 1, State_Grid(N)%nY + State_Grid(N)%Area_M2(I,J) = REAL(Col_Area(J) * Re**2,fp) + State_Met(N)%Area_M2(I,J) = State_Grid(N)%Area_M2(I,J) + ENDDO + ENDDO + + IF ( ALLOCATED( Col_Area ) ) DEALLOCATE(Col_Area) + ENDDO + + ! Initialize (mostly unused) diagnostic arrays + ! WARNING: This routine likely calls on modules which are currently + ! excluded from the GC-CESM build (eg diag03) + ! CALL Initialize( MasterProc, Input_Opt, 2, RC ) + ! CALL Initialize( Masterproc, Input_Opt, 3, RC ) + + ! Get Ap and Bp from CAM at pressure edges + ALLOCATE(Ap_CAM_Flip(nZ+1), STAT=IERR) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating Ap_CAM_Flip') + ALLOCATE(Bp_CAM_Flip(nZ+1), STAT=IERR) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating Bp_CAM_Flip') + + Ap_CAM_Flip = 0.0e+0_fp + Bp_CAM_Flip = 0.0e+0_fp + DO I = 1, nZ+1 + Ap_CAM_Flip(I) = hyai(nZ+2-I) * ps0 * 0.01e+0_r8 + Bp_CAM_Flip(I) = hybi(nZ+2-I) + ENDDO + + !----------------------------------------------------------------- + ! Pass external Ap and Bp to GEOS-Chem's Pressure_Mod + !----------------------------------------------------------------- + CALL Accept_External_ApBp( State_Grid = maxGrid, & ! Grid State + ApIn = Ap_CAM_Flip, & ! "A" term for hybrid grid + BpIn = Bp_CAM_Flip, & ! "B" term for hybrid grid + RC = RC ) ! Success or failure + + ! Print vertical coordinates + IF ( MasterProc ) THEN + WRITE( 6, '(a)' ) REPEAT( '=', 79 ) + WRITE( 6, '(a,/)' ) 'V E R T I C A L G R I D S E T U P' + WRITE( 6, '( ''Ap '', /, 6(f11.6,1x) )' ) Ap_CAM_Flip(1:maxGrid%nZ+1) + WRITE( 6, '(a)' ) + WRITE( 6, '( ''Bp '', /, 6(f11.6,1x) )' ) Bp_CAM_Flip(1:maxGrid%nZ+1) + WRITE( 6, '(a)' ) REPEAT( '=', 79 ) + ENDIF + + ! Trapping errors + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Accept_External_ApBp"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + IF ( ALLOCATED( Ap_CAM_Flip ) ) DEALLOCATE( Ap_CAM_Flip ) + IF ( ALLOCATED( Bp_CAM_Flip ) ) DEALLOCATE( Bp_CAM_Flip ) + + ! Once the initial met fields have been read in, we need to find + ! the maximum PBL level for the non-local mixing algorithm. + CALL Max_PblHt_For_Vdiff( Input_Opt = Input_Opt, & + State_Grid = State_Grid(BEGCHUNK), & + State_Met = State_Met(BEGCHUNK), & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Max_PblHt_for_Vdiff"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + IF ( Input_Opt%Its_A_FullChem_Sim .OR. & + Input_Opt%Its_An_Aerosol_Sim ) THEN + ! This also initializes Fast-JX + CALL Init_Chemistry( Input_Opt = Input_Opt, & + State_Chm = State_Chm(BEGCHUNK), & + State_Diag = State_Diag(BEGCHUNK), & + State_Grid = State_Grid(BEGCHUNK), & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Init_Chemistry"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + ENDIF + + ! hplin 3/3/23: note, since we moved UCX module variables to + ! individual State_Chm variables, Init_UCX has to be called + ! for all chunks (all State_Chm) to properly initialize all + ! variables. + IF ( Input_Opt%LChem ) THEN + DO I = BEGCHUNK, ENDCHUNK + CALL Init_UCX( Input_Opt = Input_Opt, & + State_Chm = State_Chm(I), & + State_Diag = State_Diag(I), & + State_Grid = State_Grid(I) ) + + ! Because not all CPUs in the communicator have the same amount of chunks, + ! it is only guaranteed that the first chunk in all CPUs can participate in + ! MPI_bcast of the NOXCOEFF array. So only the root CPU & root chunk will + ! read the NOXCOEFF array from disk, then broadcast to all other CPU's first + ! chunks, then remaining chunks can be copied locally without MPI. (hplin, 10/17/23) + IF( I == BEGCHUNK ) THEN + CALL mpi_bcast( State_Chm(I)%NOXCOEFF, size(State_Chm(I)%NOXCOEFF), mpi_real8, masterprocid, mpicom, ierr ) + IF ( ierr /= mpi_success ) CALL endrun('Error in mpi_bcast of NOXCOEFF in first chunk') + ELSE + State_CHM(I)%NOXCOEFF = State_Chm(BEGCHUNK)%NOXCOEFF + ENDIF + ENDDO + ENDIF + + IF ( Input_Opt%Linear_Chem ) THEN + CALL Init_Linear_Chem( Input_Opt = Input_Opt, & + State_Chm = State_Chm(BEGCHUNK), & + State_Met = State_Met(BEGCHUNK), & + State_Grid = maxGrid, & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Init_Linear_Chem"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + ENDIF + + IF ( Input_Opt%LSSalt ) THEN + CALL INIT_ISORROPIAII( State_Grid = maxGrid ) + ENDIF + + ! Get some indices + iH2O = Ind_('H2O') + iO3 = Ind_('O3') + iCO2 = Ind_('CO2') + iSO4 = Ind_('SO4') + ! The following indices are needed to compute invariants + iO = Ind_('O') + iH = Ind_('H') + iO2 = Ind_('O2') + + ! This is used to compute overhead ozone column + SpcInfo => State_Chm(BEGCHUNK)%SpcData(iO3)%Info + MWO3 = REAL(SpcInfo%MW_g,r8) + ! Free pointer + SpcInfo => NULL() + + l_H2SO4 = get_spc_ndx('H2SO4', ignore_case=.true.) + l_SO4 = get_spc_ndx('SO4', ignore_case=.true.) + + ! Get indices for physical fields in physics buffer + NDX_PBLH = pbuf_get_index('pblh' ) + NDX_FSDS = pbuf_get_index('FSDS' ) + NDX_CLDTOP = pbuf_get_index('CLDTOP' ) + NDX_CLDFRC = pbuf_get_index('CLD' ) + NDX_PRAIN = pbuf_get_index('PRAIN' ) + NDX_NEVAPR = pbuf_get_index('NEVAPR' ) + NDX_LSFLXPRC = pbuf_get_index('LS_FLXPRC') + NDX_LSFLXSNW = pbuf_get_index('LS_FLXSNW') + NDX_CMFDQR = pbuf_get_index('RPRDTOT' ) + + ! Get cloud water indices + CALL cnst_get_ind( 'CLDLIQ', ixCldLiq) + CALL cnst_get_ind( 'CLDICE', ixCldIce) + CALL cnst_get_ind( 'NUMLIQ', ixNDrop, abort=.False. ) + + CALL init_mean_mass() + CALL setinv_inti() + + !----------------------------------------------------------------------- + ! ... initialize tracer modules + !----------------------------------------------------------------------- + CALL tracer_cnst_init() + CALL tracer_srcs_init() + + IF ( ghg_chem ) THEN + CALL ghg_chem_init(phys_state, bndtvg, h2orates) + ENDIF + + ! Initialize diagnostics interface + CALL GC_Diagnostics_Init( Input_Opt = Input_Opt, & + State_Chm = State_Chm(BEGCHUNK), & + State_Met = State_Met(BEGCHUNK) ) + + ! Initialize emissions interface + CALL GC_Emissions_Init( ) + + hco_pbuf2d => pbuf2d + + ! Cleanup + Call Cleanup_State_Grid( maxGrid, RC ) + + if (masterproc) write(iulog,'(a)') 'chem_init: GEOS-Chem chemistry initialization complete' + + end subroutine chem_init + + !================================================================================================ + ! chem_timestep_init + !================================================================================================ + subroutine chem_timestep_init(phys_state, pbuf2d) + + ! CAM modules + use mo_flbc, only : flbc_chk + use mo_ghg_chem, only : ghg_chem_timestep_init + use physics_buffer, only : physics_buffer_desc + + TYPE(physics_state), INTENT(IN):: phys_state(begchunk:endchunk) + TYPE(physics_buffer_desc), POINTER :: pbuf2d(:,:) + + ! Not sure what we would realistically do here rather than in tend + + !----------------------------------------------------------------------- + ! Set fixed lower boundary timing factors + !----------------------------------------------------------------------- + CALL flbc_chk + + IF ( ghg_chem ) THEN + CALL ghg_chem_timestep_init(phys_state) + ENDIF + + end subroutine chem_timestep_init + + !================================================================================================ + ! subroutine gc_update_timesteps + !================================================================================================ + subroutine gc_update_timesteps(DT) + + ! GEOS-Chem modules + use Time_Mod, only : Set_Timesteps + + REAL(r8), INTENT(IN) :: DT + INTEGER :: DT_MIN + INTEGER, SAVE :: DT_MIN_LAST = -1 + + DT_MIN = NINT(DT) + + Input_Opt%TS_CHEM = DT_MIN + Input_Opt%TS_EMIS = DT_MIN + Input_Opt%TS_CONV = DT_MIN + Input_Opt%TS_DYN = DT_MIN + Input_Opt%TS_RAD = DT_MIN + + ! Only bother updating the module information if there's been a change + IF (DT_MIN .NE. DT_MIN_LAST) THEN + CALL Set_Timesteps( Input_Opt = Input_Opt, & + CHEMISTRY = DT_MIN, & + EMISSION = DT_MIN, & + DYNAMICS = DT_MIN, & + UNIT_CONV = DT_MIN, & + CONVECTION = DT_MIN, & + DIAGNOS = DT_MIN, & + RADIATION = DT_MIN ) + DT_MIN_LAST = DT_MIN + ENDIF + + end subroutine gc_update_timesteps + + !================================================================================================ + ! subroutine geoschem_readnl + !================================================================================================ + subroutine geoschem_readnl(nlfile) + ! Purpose: reads the namelist from cam/src/control/runtime_opts + + ! CAM modules + use spmd_utils, only : mpicom, masterprocid, mpi_character, mpi_success + use namelist_utils, only: find_group_name + use units, only: getunit, freeunit + + character(len=*), intent(in) :: nlfile ! filepath for file containing namelist input + integer :: unitn, ierr + character(len=*), parameter :: subname = 'geoschem_readnl' + + namelist /geoschem_nl/ geoschem_cheminputs + + ! Read namelist + IF ( MasterProc ) THEN + unitn = getunit() + OPEN( unitn, FILE=TRIM(nlfile), STATUS='old' ) + CALL find_group_name(unitn, 'geoschem_nl', STATUS=ierr) + IF ( ierr == 0 ) THEN + READ(unitn, geoschem_nl, IOSTAT=ierr) + IF ( ierr /= 0 ) THEN + CALL ENDRUN(subname // ':: ERROR reading namelist') + ENDIF + ENDIF + CLOSE(unitn) + CALL freeunit(unitn) + ENDIF + + ! Broadcast namelist variables + CALL mpi_bcast(geoschem_cheminputs, LEN(geoschem_cheminputs), mpi_character, masterprocid, mpicom, ierr) + IF ( ierr /= mpi_success ) then + CALL endrun(subname//': MPI_BCAST ERROR: geoschem_cheminputs') + ENDIF + + end subroutine geoschem_readnl + + !================================================================================================ + ! subroutine chem_timestep_tend + !================================================================================================ + subroutine chem_timestep_tend( state, ptend, cam_in, cam_out, dT, pbuf, fh2o ) + + ! CAM modules + use cam_history, only : outfld, hist_fld_active + use camsrfexch, only : cam_in_t, cam_out_t + use chem_mods, only : drySpc_ndx, map2GC_dryDep + use chem_mods, only : nfs, indexm, gas_pcnst + use gas_wetdep_opts, only : gas_wetdep_method + use mo_chem_utls, only : get_spc_ndx + use mo_flbc, only : flbc_set + use mo_ghg_chem, only : ghg_chem_set_flbc + use mo_mean_mass, only : set_mean_mass + use mo_neu_wetdep, only : neu_wetdep_tend + use mo_setinv, only : setinv + use orbit, only : zenith ! For computing SZA + use physics_buffer, only : physics_buffer_desc, pbuf_get_field, pbuf_old_tim_idx + use physics_buffer, only : pbuf_get_chunk, pbuf_get_index + use perf_mod, only : t_startf, t_stopf + use phys_grid, only : get_ncols_p, get_rlat_all_p, get_rlon_all_p + use phys_grid, only : get_area_all_p, get_lat_all_p, get_lon_all_p + use physconst, only : MWDry, Gravit + use rad_constituents, only : rad_cnst_get_info + use short_lived_species, only : get_short_lived_species_gc, set_short_lived_species_gc + use spmd_utils, only : masterproc + use time_manager, only : Get_Curr_Calday, Get_Curr_Date ! For computing SZA + use tropopause, only : Tropopause_findChemTrop, Tropopause_Find + use wv_saturation, only : QSat +#if defined( MODAL_AERO ) + use aero_model, only : aero_model_gasaerexch ! Aqueous chemistry and aerosol growth + use modal_aero_data, only : ntot_amode, nspec_amode + use modal_aero_data, only : nspec_max, nsoa + use modal_aero_data, only : lmassptr_amode, numptr_amode + use modal_aero_data, only : lptr_so4_a_amode + use modal_aero_data, only : lptr2_soa_a_amode, lptr2_soa_g_amode +#endif + + ! GEOS-Chem interface modules in CAM + use GeosChem_Emissions_Mod, only : GC_Emissions_Calc + use GeosChem_Diagnostics_Mod, only : GC_Diagnostics_Calc, wetdep_name, wtrate_name + use GeosChem_History_Mod, only : HistoryExports_SetDataPointers, CopyGCStates2Exports + + ! GEOS-Chem modules + use Aerosol_Mod, only : Set_AerMass_Diagnostic + use Calc_Met_Mod, only : Set_Dry_Surface_Pressure, AirQnt + use Chemistry_Mod, only : Do_Chemistry + use CMN_FJX_MOD, only : ZPJ + use CMN_Size_Mod, only : NSURFTYPE, PTop + use Diagnostics_Mod, only : Zero_Diagnostics_StartOfTimestep, Set_Diagnostics_EndofTimestep + use Drydep_Mod, only : Do_Drydep, DEPNAME, NDVZIND, Update_DryDepFreq + use FAST_JX_MOD, only : RXN_NO2, RXN_O3_1 + use GC_Grid_Mod, only : SetGridFromCtr + use HCO_Interface_GC_Mod,only : Compute_Sflx_For_Vdiff + use Linear_Chem_Mod, only : TrID_GC, GC_Bry_TrID, NSCHEM + use Linear_Chem_Mod, only : BrPtrDay, BrPtrNight, PLVEC, GMI_OH + use Olson_Landmap_Mod, only : Compute_Olson_Landmap + use Modis_LAI_Mod, only : Compute_XLAI + use PBL_Mix_Mod, only : Compute_PBL_Height + use PhysConstants, only : PI, PI_180, g0, AVO, Re, g0_100 + use Pressure_Mod, only : Set_Floating_Pressures, Accept_External_Pedge + use State_Chm_Mod, only : Ind_ + use State_Diag_Mod, only : get_TagInfo + use Time_Mod, only : Accept_External_Date_Time + use Toms_Mod, only : Compute_Overhead_O3 + use UCX_Mod, only : Set_H2O_Trac + use Unitconv_Mod, only : Convert_Spc_Units + use Wetscav_Mod, only : Setup_Wetscav + + REAL(r8), INTENT(IN) :: dT ! Time step + TYPE(physics_state), INTENT(IN) :: state ! Physics State variables + TYPE(physics_ptend), INTENT(OUT) :: ptend ! indivdual parameterization tendencies + TYPE(cam_in_t), INTENT(INOUT) :: cam_in + TYPE(cam_out_t), INTENT(IN) :: cam_out + TYPE(physics_buffer_desc), POINTER :: pbuf(:) + REAL(r8), OPTIONAL, INTENT(OUT) :: fh2o(PCOLS) ! h2o flux to balance source from chemistry + + ! Initial MMR for all species + REAL(r8) :: MMR_Beg(PCOLS,PVER,MAXVAL(map2GC(:))) + REAL(r8) :: MMR_End(PCOLS,PVER,MAXVAL(map2GC(:))) + + ! Logical to apply tendencies to mixing ratios + LOGICAL :: lq(pcnst) + + ! Indexing + INTEGER :: K, N, M, P, SM, ND + INTEGER :: I, J, L, nX, nY, nZ + + INTEGER :: LCHNK, NCOL + + REAL(r8), DIMENSION(state%NCOL) :: & + CSZA, & ! Cosine of solar zenith angle + CSZAmid, & ! Cosine of solar zenith angle at the mid timestep + Rlats, Rlons ! Chunk latitudes and longitudes (radians) + + REAL(fp) :: O3col(state%NCOL) ! Overhead O3 column (DU) + + REAL(r8), POINTER :: PblH(:) ! PBL height on each chunk [m] + REAL(r8), POINTER :: cldTop(:) ! Cloud top height [?] + REAL(r8), POINTER :: cldFrc(:,:) ! Cloud fraction [-] + REAL(r8), POINTER :: Fsds(:) ! Downward shortwave flux at surface [W/m2] + REAL(r8), POINTER :: PRain(:,:) ! Total stratiform precip. prod. (rain + snow) [kg/kg/s] + REAL(r8), POINTER :: NEvapr(:,:) ! Evaporation of total precipitation (rain + snow) [kg/kg/s] + REAL(r8), POINTER :: LsFlxPrc(:,:) ! Large-scale downward precip. flux at interface (rain + snow) [kg/m2/s] + REAL(r8), POINTER :: LsFlxSnw(:,:) ! Large-scale downward precip. flux at interface (snow only) [kg/m2/s] + REAL(r8), POINTER :: cmfdqr(:,:) ! Total convective precip. prod. (rain + snow) [kg/kg/s] + + REAL(r8) :: tmpMass + REAL(r8) :: cldW (state%NCOL,PVER) ! Cloud water (kg/kg) + REAL(r8) :: nCldWtr(state%NCOL,PVER) ! Droplet number concentration (#/kg) + + REAL(r8) :: relHum (state%NCOL,PVER) ! Relative humidity [0-1] + REAL(r8) :: satV (state%NCOL,PVER) ! Work arrays + REAL(r8) :: satQ (state%NCOL,PVER) ! Work arrays + REAL(r8) :: qH2O (state%NCOL,PVER) ! Specific humidity [kg/kg] + REAL(r8) :: h2ovmr (state%NCOL,PVER) ! H2O volume mixing ratio + REAL(r8) :: mBar (state%NCOL,PVER) ! Mean wet atmospheric mass [amu] + REAL(r8) :: invariants(state%NCOL,PVER,nfs) + REAL(r8) :: reaction_rates(1,1,1) ! Reaction rates (unused) + + ! For aerosol formation + REAL(r8) :: del_h2so4_gasprod(state%NCOL,PVER) + + REAL(r8) :: vmr0(state%NCOL,PVER,gas_pcnst) + REAL(r8) :: vmr1(state%NCOL,PVER,gas_pcnst) + REAL(r8) :: vmr2(state%NCOL,PVER,gas_pcnst) + + REAL(r8) :: wetdepflx(pcols,pcnst) ! Wet deposition fluxes (kg/m2/s) + +#if defined( MODAL_AERO ) + REAL(r8) :: binRatio(nspec_max,ntot_amode,state%NCOL,PVER) + + REAL(r8) :: SO4_gasRatio(state%NCOL,PVER) + + ! For SOA mapping + REAL(r8) :: totMass(state%NCOL,PVER) + REAL(r8) :: bulkMass(state%NCOL,PVER) + REAL(r8) :: tmpMW_g + CHARACTER(LEN=64) :: speciesName_1, speciesName_2, speciesName_3, speciesName_4 + INTEGER :: speciesId_1, speciesId_2, speciesId_3, speciesId_4 + INTEGER :: iMap, nMapping, iBin, binSOA_1, binSOA_2 + INTEGER :: K1, K2, K3, K4 + LOGICAL :: isSOA_aerosol + +#endif + + ! For emissions + REAL(r8) :: eflx(pcols,pver,pcnst) ! 3-D emissions in kg/m2/s + + ! For GEOS-Chem diagnostics + REAL(r8) :: mmr_tend(state%NCOL,PVER,gas_pcnst) + REAL(r8) :: wk_out(state%NCOL) + LOGICAL :: Found + + CHARACTER(LEN=shr_kind_cl) :: tagName + + REAL(r8), PARAMETER :: zlnd = 0.01_r8 ! Roughness length for soil [m] + REAL(r8), PARAMETER :: zslnd = 0.0024_r8 ! Roughness length for snow [m] + REAL(r8), PARAMETER :: zsice = 0.0400_r8 ! Roughness length for sea ice [m] + REAL(r8), PARAMETER :: zocn = 0.0001_r8 ! Roughness length for oean [m] + + REAL(f4) :: lonMidArr(1,PCOLS), latMidArr(1,PCOLS) + INTEGER :: iMaxLoc(1) + + REAL(r8) :: Col_Area(state%NCOL) + + ! Intermediate arrays + INTEGER :: Trop_Lev (PCOLS) + REAL(r8) :: Trop_P (PCOLS) + REAL(r8) :: Trop_T (PCOLS) + REAL(r8) :: Trop_Ht (PCOLS) + REAL(r8) :: SnowDepth(PCOLS) + REAL(r8) :: cld2D (PCOLS) + REAL(r8) :: Z0 (PCOLS) + REAL(r8) :: Sd_Ice, Sd_Lnd, Sd_Avg, Frc_Ice + + ! Estimating cloud optical depth + REAL(r8) :: TauCli(PCOLS,PVER) + REAL(r8) :: TauClw(PCOLS,PVER) + REAL(r8), PARAMETER :: re_m = 1.0e-05_r8 ! Cloud drop radius in m + REAL(r8), PARAMETER :: cldMin = 1.0e-02_r8 ! Minimum cloud cover + REAL(r8), PARAMETER :: cnst = 1.5e+00_r8 / (re_m * 1.0e+03_r8 * g0) + + ! Calculating SZA + REAL(r8) :: Calday + + CHARACTER(LEN=shr_kind_cl) :: SpcName + CHARACTER(LEN=shr_kind_cl) :: Prefix, FieldName + + LOGICAL :: FND + INTEGER :: SpcId + TYPE(Species), POINTER :: SpcInfo + TYPE(SfcMrObj), POINTER :: iSfcMrObj + + CHARACTER(LEN=63) :: OrigUnit + + REAL(r8) :: SlsData(PCOLS, PVER, nSls) + + INTEGER :: currYr, currMo, currDy, currTOD + INTEGER :: currYMD, currHMS, currHr, currMn, currSc + REAL(f4) :: currUTC + + TYPE(physics_buffer_desc), POINTER :: pbuf_chnk(:) ! slice of pbuf in chnk + REAL(r8), POINTER :: pbuf_ik(:,:) ! ptr to pbuf data (/pcols,pver/) + REAL(r8), POINTER :: pbuf_i(:) ! ptr to pbuf data (/pcols/) horizontal only (horiz_only) + INTEGER :: tmpIdx ! pbuf field id + + INTEGER :: TIM_NDX + INTEGER :: IERR + + INTEGER, SAVE :: iStep = 0 + LOGICAL, SAVE :: FIRST = .TRUE. + LOGICAL :: rootChunk + LOGICAL :: lastChunk + INTEGER :: RC + + + ! Initialize pointers + SpcInfo => NULL() + PblH => NULL() + cldTop => NULL() + cldFrc => NULL() + Fsds => NULL() + PRain => NULL() + NEvapr => NULL() + LsFlxPrc => NULL() + LsFlxSnw => NULL() + cmfdqr => NULL() + pbuf_chnk=> NULL() + pbuf_ik => NULL() + pbuf_i => NULL() + + ! LCHNK: which chunk we have on this process + LCHNK = state%LCHNK + ! NCOL: number of atmospheric columns on this chunk + NCOL = state%NCOL + + ! Root Chunk + rootChunk = ( MasterProc .and. (LCHNK==BEGCHUNK) ) + ! Last Chunk + lastChunk = ( MasterProc .and. (LCHNK==ENDCHUNK) ) + + ! Count the number of steps which have passed + IF ( LCHNK .EQ. BEGCHUNK ) iStep = iStep + 1 + + ! Need to update the timesteps throughout the code + CALL GC_Update_Timesteps(dT) + + ! For safety's sake + PTop = state%pint(1,1)*0.01e+0_fp + + ! Need to be super careful that the module arrays are updated and correctly + ! set. NOTE: First thing - you'll need to flip all the data vertically + + nX = 1 + nY = NCOL + nZ = PVER + + ! Update the grid lat/lons since they are module variables + ! Assume (!) that area hasn't changed for now, as GEOS-Chem will + ! retrieve this from State_Met which is chunked + !CALL get_rlat_all_p( LCHNK, NCOL, Rlats ) + !CALL get_rlon_all_p( LCHNK, NCOL, Rlons ) + Rlats(1:nY) = state%Lat(1:nY) + Rlons(1:nY) = state%Lon(1:nY) + + lonMidArr = 0.0e+0_f4 + latMidArr = 0.0e+0_f4 + DO I = 1, nX + DO J = 1, nY + lonMidArr(I,J) = REAL(Rlons(J), f4) + latMidArr(I,J) = REAL(Rlats(J), f4) + ENDDO + ENDDO + + ! Update the grid + CALL SetGridFromCtr( Input_Opt = Input_Opt, & + State_Grid = State_Grid(LCHNK), & + lonCtr = lonMidArr, & + latCtr = latMidArr, & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered within call to "SetGridFromCtr"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! Set area + CALL Get_Area_All_p( LCHNK, nY, Col_Area ) + + ! Field : AREA_M2 + ! Description: Grid box surface area + ! Unit : - + ! Dimensions : nX, nY + ! Note : Set default value (in case of chunks with fewer columns) + State_Grid(LCHNK)%Area_M2 = -1.0e+10_fp + State_Met(LCHNK)%Area_M2 = -1.0e+10_fp + State_Grid(LCHNK)%Area_M2(1,:nY) = REAL(Col_Area(:nY) * Re**2,fp) + State_Met(LCHNK)%Area_M2(1,:nY) = State_Grid(LCHNK)%Area_M2(1,:nY) + + ! 2. Copy tracers into State_Chm + ! Data was received in kg/kg dry + State_Chm(LCHNK)%Spc_Units = 'kg/kg dry' + ! Initialize ALL State_Chm species data to zero, not just tracers + DO N = 1, State_Chm(LCHNK)%nSpecies + State_Chm(LCHNK)%Species(N)%Conc = 0.0e+0_fp + ENDDO + + lq(:) = .False. + + ! Map and flip gaseous species + MMR_Beg = 0.0e+0_r8 + MMR_End = 0.0e+0_r8 + DO N = 1, pcnst + IF ( mapCnst(N) > 0 ) lq(N) = .True. + M = map2GC(N) + IF ( M <= 0 ) CYCLE + MMR_Beg(:nY,:nZ,M) = state%q(:nY,nZ:1:-1,N) + State_Chm(LCHNK)%Species(M)%Conc(1,:nY,:nZ) = REAL(MMR_Beg(:nY,:nZ,M),fp) + ENDDO + + ! We need to let CAM know that 'H2O' and 'Q' are identical + MMR_Beg(:nY,:nZ,iH2O) = state%q(:nY,nZ:1:-1,cQ) + State_Chm(LCHNK)%Species(iH2O)%Conc(1,:nY,:nZ) = REAL(MMR_Beg(:nY,:nZ,iH2O),fp) + + ! Retrieve previous value of species data + SlsData(:,:,:) = 0.0e+0_r8 + CALL get_short_lived_species_gc( SlsData, LCHNK, nY, pbuf ) + + IF ( iStep == 1 ) THEN + ! Retrieve list of species with surface boundary conditions (copied from + ! sfcvmr_mod.F90) + + ! Head of linked list + SfcMrHead => NULL() + iSfcMrObj => NULL() + SpcInfo => NULL() + + ! Loop over all species + DO N = 1, State_Chm(BEGCHUNK)%nSpecies + ! Species information + SpcInfo => State_Chm(BEGCHUNK)%SpcData(N)%Info + + ! Check if field exists (note: this needs to be less than 16 + ! characters long) + FieldName = 'HCO_'//TRIM(Prefix_SfcVMR)//TRIM(to_upper(SpcInfo%Name)) + M = pbuf_get_index(FieldName, RC) + IF ( M > 0 ) THEN + + ! Must have positive, non-zero MW + IF ( SpcInfo%MW_g <= 0.0_fp ) THEN + ErrMsg = 'Cannot use surface boundary condition for species ' & + // TRIM(SpcInfo%Name) // ' due to invalid MW!' + CALL ENDRUN(TRIM(ErrMsg)) + ENDIF + + ! Create new object, add to list + ALLOCATE( iSfcMrObj, STAT=RC ) + CALL GC_CheckVar( 'sfcvmr_mod.F90:iSfcMrObj', 0, RC ) + IF ( RC /= GC_SUCCESS ) CALL ENDRUN('Failure while allocating iSfcMrObj') + + iSfcMrObj%SpcID = N + iSfcMrObj%FldName = FieldName + iSfcMrObj%Next => SfcMrHead + SfcMrHead => iSfcMrObj + IF ( rootChunk ) THEN + WRITE( 6, 110 ) TRIM( SpcInfo%Name ), TRIM( iSfcMrObj%FldName ) + 110 FORMAT( '--> ', a, ' will use prescribed surface boundary ', & + 'conditions from field ', a ) + ENDIF + + ! Free the pointer + iSfcMrObj => NULL() + ENDIF + ENDDO + ENDIF + + !----------------------------------------------------------------------- + ! ... Reset certain GEOS-Chem diagnostics at start of timestep + !----------------------------------------------------------------------- + CALL Zero_Diagnostics_StartOfTimestep( Input_Opt = Input_Opt, & + State_Diag = State_Diag(LCHNK), & + RC = RC ) + + !----------------------------------------------------------------------- + ! ... Set atmosphere mean mass + !----------------------------------------------------------------------- + ! This is not meant for simulations of the ionosphere. mBar will then just + ! be set to mwdry and does not require to pass anything besides NCOL. We + ! can then just past a dummy array as the second argument + !CALL Set_mean_mass( NCOL, mmr, mBar ) + CALL Set_mean_mass( NCOL, vmr0, mBar ) + + ! Map and flip gaseous short-lived species + DO N = 1, nSls + M = map2GC_Sls(N) + IF ( M <= 0 ) CYCLE + State_Chm(LCHNK)%Species(M)%Conc(1,:nY,:nZ) = REAL(SlsData(:nY,nZ:1:-1,N),fp) + ENDDO + +#if defined( MODAL_AERO ) + ! NOTE: GEOS-Chem bulk aerosol concentrations (BCPI, BCPO, SO4, ...) are ZEROED OUT + ! here in order to be reconstructed from the modal concentrations. + ! + ! This means that any changes to the BULK mass will be ignored between the end + ! of the gas_phase_chemdr and the beginning of the next!! + ! + ! First reset State_Chm%Species to zero out MAM-inherited GEOS-Chem aerosols + DO M = 1, ntot_amode + DO SM = 1, nspec_amode(M) + P = map2MAM4(SM,M) ! Constituent index for GEOS-Chem + IF ( P > 0 ) K = map2GC(P) ! Index in State_Chm + IF ( K > 0 ) State_Chm(LCHNK)%Species(K)%Conc(1,:nY,:nZ) = 0.0e+00_fp + ENDDO + ENDDO + + ! Map and vertically flip aerosols + DO M = 1, ntot_amode + DO SM = 1, nspec_amode(M) + P = map2MAM4(SM,M) ! Constituent index for GEOS-Chem + IF ( P <= 0 ) CYCLE + N = lmassptr_amode(SM,M) + K = map2GC(P) ! Index in State_Chm + ! /!\ MAM aerosols (with cnst index N) is mapped onto GEOS-Chem + ! species (with cnst index P, which corresponds to index K in + ! State_Chm) + + ! Multiple MAM4 bins are mapped to same GEOS-Chem species + State_Chm(LCHNK)%Species(K)%Conc(1,:nY,:nZ) = State_Chm(LCHNK)%Species(K)%Conc(1,:nY,:nZ) & + + REAL(state%q(:nY,nZ:1:-1,N),fp) * & + adv_mass(mapCnst(P)) / & + adv_mass(mapCnst(N)) + ENDDO + ENDDO + + ! Compute ratios of bin to bulk mass + !------------------------------------------------------------------------------------------ + ! Notes for the indices used here (hplin 3/3/23): + ! + ! K = GEOS-Chem species index in State_Chm%Species(K). + ! P = constituent index for BULK lumped tracer in GEOS-Chem (BCPI, BCPO, DST1, DST4, SO4, SALA, SALC, OCPI, OCPO) + ! N = constituent index for MODAL tracer in MAM4 (bc_a1, bc_a4, ...) + ! each combination of species and mode is described by (SM, M) + ! SM = species (i.e., bc, dst, so4, ncl, pom) in mode M + ! M = mode number + ! constituent indices are used in state%q(column number,level number,constituent index) + ! chemical tracer index (NOT constituent index) is used in mo_sim_dat, e.g., adv_mass(tracer index) + ! + ! Mapping functions: maps from... ...to + ! mapCnst(constituent index) constituent index chemical tracer index + ! lmassptr_amode(SM, M) SM, M constituent index (modal) + ! map2GC(bulk constituent index) constituent index (bulk) GEOS-Chem species index (bulk) + ! map2MAM4(SM, M) SM, M (modal) constituent index (bulk) + ! (map2MAM4 is a N to 1 operation) + ! Query functions: + ! xname_massptr(SM, M) SM, M NAME of modal aer (bc_a1, bc_a4, ...) + !------------------------------------------------------------------------------------------ + binRatio = 0.0e+00_r8 + DO M = 1, ntot_amode + DO SM = 1, nspec_amode(M) + P = map2MAM4(SM,M) + IF ( P <= 0 ) CYCLE + K = map2GC(P) ! Index in State_Chm + N = lmassptr_amode(SM,M) + IF ( N < 0 ) CYCLE + DO J = 1, nY + DO L = 1, nZ + IF ( State_Chm(LCHNK)%Species(K)%Conc(1,J,nZ+1-L) > 0.0e+00_r8 ) THEN + binRatio(SM,M,J,L) = state%q(J,L,N) & + * adv_mass(mapCnst(P)) / adv_mass(mapCnst(N)) & + / REAL(State_Chm(LCHNK)%Species(K)%Conc(1,J,nZ+1-L), r8) + ENDIF + ENDDO + ENDDO + ! Overwrite MMR_Beg with value from MAM + MMR_Beg(:nY,:nZ,K) = State_Chm(LCHNK)%Species(K)%Conc(1,:nY,:nZ) + ENDDO + ENDDO + + ! Deal with secondary organic aerosols (SOAs). This mapping is using the + ! complex SOA option in GEOS-Chem. + ! MAM uses five volatility bins spanning saturation concentrations from 0.01 + ! to 100 ug/m3 (logarithmically). The complex SOA option has four volatility + ! bins that 0.1 to 100 ug/m3. We lump the lowest two bins in CESM2 to the + ! lowest bin in GEOS-Chem. + ! + ! The mapping goes as follows: + ! TSOA0 + ASOAN + SOAIE + SOAGX <- soa1_a* + soa2_a* + ! TSOA1 + ASOA1 <- soa3_a* + ! TSOA2 + ASOA2 <- soa4_a* + ! TSOA3 + ASOA3 <- soa5_a* + ! TSOG0 <- SOAG0 + SOAG1 + ! TSOG1 + ASOG1 <- SOAG2 + ! TSOG2 + ASOG2 <- SOAG3 + ! TSOG3 + ASOG3 <- SOAG4 + + IF ( iStep > 1 ) THEN + ! Do not perform this mapping on initialization as we first want to + ! overwrite soa*_a* with the GEOS-Chem SOAs. + nMapping = 8 + DO iMap = 1, nMapping + speciesName_1 = '' + speciesName_2 = '' + speciesName_3 = '' + speciesName_4 = '' + IF ( iMap == 1 ) THEN + binSOA_1 = 1 + binSOA_2 = 2 + speciesName_1 = 'TSOA0' + speciesName_2 = 'ASOAN' + speciesName_3 = 'SOAIE' + speciesName_4 = 'SOAGX' + ELSEIF ( iMap == 2 ) THEN + binSOA_1 = 3 + binSOA_2 = 3 + speciesName_1 = 'TSOA1' + speciesName_2 = 'ASOA1' + ELSEIF ( iMap == 3 ) THEN + binSOA_1 = 4 + binSOA_2 = 4 + speciesName_1 = 'TSOA2' + speciesName_2 = 'ASOA2' + ELSEIF ( iMap == 4 ) THEN + binSOA_1 = 5 + binSOA_2 = 5 + speciesName_1 = 'TSOA3' + speciesName_2 = 'ASOA3' + ELSEIF ( iMap == 5 ) THEN + binSOA_1 = 1 + binSOA_2 = 2 + speciesName_1 = 'TSOG0' + speciesName_2 = 'TSOG0' + ELSEIF ( iMap == 6 ) THEN + binSOA_1 = 3 + binSOA_2 = 3 + speciesName_1 = 'TSOG1' + speciesName_2 = 'ASOG1' + ELSEIF ( iMap == 7 ) THEN + binSOA_1 = 4 + binSOA_2 = 4 + speciesName_1 = 'TSOG2' + speciesName_2 = 'ASOG2' + ELSEIF ( iMap == 8 ) THEN + binSOA_1 = 5 + binSOA_2 = 5 + speciesName_1 = 'TSOG3' + speciesName_2 = 'ASOG3' + ELSE + CALL ENDRUN('Unknown SOA mapping!') + ENDIF + isSOA_aerosol = .False. + IF ( iMap <= 4 ) isSOA_aerosol = .True. + + ! Compute total mass from GEOS-Chem species. This sets the ratio between + ! speciesId_1 and speciesId_2 + totMass(:nY,:nZ) = 0.0e+00_r8 + + CALL cnst_get_ind( speciesName_1, speciesId_1, abort=.True. ) + CALL cnst_get_ind( speciesName_2, speciesId_2, abort=.False. ) + CALL cnst_get_ind( speciesName_3, speciesId_3, abort=.False. ) + CALL cnst_get_ind( speciesName_4, speciesId_4, abort=.False. ) + IF ( speciesId_1 > 0 ) totMass(:nY,:nZ) = totMass(:nY,:nZ) + state%q(:nY,:nZ,speciesId_1) + IF ( speciesId_2 > 0 ) totMass(:nY,:nZ) = totMass(:nY,:nZ) + state%q(:nY,:nZ,speciesId_2) + IF ( speciesId_3 > 0 ) totMass(:nY,:nZ) = totMass(:nY,:nZ) + state%q(:nY,:nZ,speciesId_3) + IF ( speciesId_4 > 0 ) totMass(:nY,:nZ) = totMass(:nY,:nZ) + state%q(:nY,:nZ,speciesId_4) + + ! Compute total bulk mass from MAM + bulkMass(:nY,:nZ) = 0.0e+00_r8 + IF ( isSOA_aerosol ) THEN + DO iBin = binSOA_1, binSOA_2 + DO M = 1, ntot_amode + N = lptr2_soa_a_amode(M,iBin) + IF ( N <= 0 ) CYCLE + tmpMW_g = adv_mass(mapCnst(N)) + bulkMass(:nY,:nZ) = bulkMass(:nY,:nZ) + state%q(:nY,:nZ,N) + ENDDO + ENDDO + ELSE + DO iBin = binSOA_1, binSOA_2 + N = lptr2_soa_g_amode(iBin) + IF ( N <= 0 ) CYCLE + tmpMW_g = adv_mass(mapCnst(N)) + bulkMass(:nY,:nZ) = bulkMass(:nY,:nZ) + state%q(:nY,:nZ,N) + ENDDO + ENDIF + + K1 = Ind_(speciesName_1) + K2 = Ind_(speciesName_2) + K3 = Ind_(speciesName_3) + K4 = Ind_(speciesName_4) + DO J = 1, nY + DO L = 1, nZ + ! Total SOA aerosol masses from GC are available. Partition according to the ratio given in speciesId_N to totMass summed above. + IF ( totMass(J,L) > 0.0e+00_r8 ) THEN + IF ( K1 > 0 ) State_Chm(LCHNK)%Species(K1)%Conc(1,J,L) = state%q(J,nZ+1-L,speciesId_1) / totMass(J,nZ+1-L) * bulkMass(J,nZ+1-L) * adv_mass(mapCnst(speciesId_1)) / tmpMW_g + IF ( K2 > 0 ) State_Chm(LCHNK)%Species(K2)%Conc(1,J,L) = state%q(J,nZ+1-L,speciesId_2) / totMass(J,nZ+1-L) * bulkMass(J,nZ+1-L) * adv_mass(mapCnst(speciesId_2)) / tmpMW_g + IF ( K3 > 0 ) State_Chm(LCHNK)%Species(K3)%Conc(1,J,L) = state%q(J,nZ+1-L,speciesId_3) / totMass(J,nZ+1-L) * bulkMass(J,nZ+1-L) * adv_mass(mapCnst(speciesId_3)) / tmpMW_g + IF ( K4 > 0 ) State_Chm(LCHNK)%Species(K4)%Conc(1,J,L) = state%q(J,nZ+1-L,speciesId_4) / totMass(J,nZ+1-L) * bulkMass(J,nZ+1-L) * adv_mass(mapCnst(speciesId_4)) / tmpMW_g + ELSE + ! Total SOA aerosol masses from GC are unknown. In this case partition the bulkMass by 1/2 to K1 and K2. + IF ( K1 == K2 ) THEN + ! ... go in same bin. This actually does not exist in the partitioning above. + State_Chm(LCHNK)%Species(K1)%Conc(1,J,L) = bulkMass(J,nZ+1-L) * adv_mass(mapCnst(speciesId_1)) / tmpMW_g + ELSE + State_Chm(LCHNK)%Species(K1)%Conc(1,J,L) = bulkMass(J,nZ+1-L) * adv_mass(mapCnst(speciesId_1)) / tmpMW_g / 2.0_r8 + State_Chm(LCHNK)%Species(K2)%Conc(1,J,L) = bulkMass(J,nZ+1-L) * adv_mass(mapCnst(speciesId_2)) / tmpMW_g / 2.0_r8 + ENDIF + ENDIF + ENDDO + ENDDO + IF ( K1 > 0 ) MMR_Beg(:nY,:nZ,K1) = State_Chm(LCHNK)%Species(K1)%Conc(1,:nY,:nZ) + IF ( K2 > 0 ) MMR_Beg(:nY,:nZ,K2) = State_Chm(LCHNK)%Species(K2)%Conc(1,:nY,:nZ) + IF ( K3 > 0 ) MMR_Beg(:nY,:nZ,K3) = State_Chm(LCHNK)%Species(K3)%Conc(1,:nY,:nZ) + IF ( K4 > 0 ) MMR_Beg(:nY,:nZ,K4) = State_Chm(LCHNK)%Species(K4)%Conc(1,:nY,:nZ) + ENDDO + ENDIF + + ! Add gas-phase H2SO4 to GEOS-Chem SO4 (which lumps SO4 aerosol and gaseous) + K = iSO4 + N = cH2SO4 + IF ( K > 0 .AND. N > 0 .AND. l_SO4 > 0 ) THEN + State_Chm(LCHNK)%Species(K)%Conc(1,:nY,:nZ) = & + State_Chm(LCHNK)%Species(K)%Conc(1,:nY,:nZ) & + + REAL(state%q(:nY,nZ:1:-1,N),fp) * & + adv_mass(l_SO4) / adv_mass(mapCnst(N)) + ! SO4_gasRatio is in mol/mol + SO4_gasRatio(:nY,:nZ) = state%q(:nY,:nZ,N) & + * adv_mass(l_SO4) / adv_mass(mapCnst(N)) & + / State_Chm(LCHNK)%Species(K)%Conc(1,:nY,nZ:1:-1) + MMR_Beg(:nY,:nZ,K) = State_Chm(LCHNK)%Species(K)%Conc(1,:nY,:nZ) + ENDIF +#endif + + ! Convert mass fluxes to VMR as needed for MAM4 aerosols (these operate on vmr0 - initial and vmr1 - end of timestep) + DO N = 1, gas_pcnst + ! See definition of map2chm + M = map2chm(N) + IF ( M > 0 ) THEN + ! Is a GEOS-Chem species? + vmr0(:nY,:nZ,N) = State_Chm(LCHNK)%Species(M)%Conc(1,:nY,nZ:1:-1) * & + MWDry / adv_mass(N) + ! We'll substract concentrations after chemistry later + mmr_tend(:nY,:nZ,N) = REAL(State_Chm(LCHNK)%Species(M)%Conc(1,:nY,nZ:1:-1),r8) + ELSEIF ( M < 0 ) THEN + ! Is a MAM4 species? Get VMR from state%q directly. + vmr0(:nY,:nZ,N) = state%q(:nY,:nZ,-M) * & + MWDry / adv_mass(N) + mmr_tend(:nY,:nZ,N) = state%q(:nY,:nZ,-M) + ENDIF + ENDDO + + ! If H2O tendencies are propagated to specific humidity, then make sure + ! that Q actually applies tendencies + IF ( Input_Opt%applyQtend ) lq(cQ) = .True. + + IF ( ghg_chem ) lq(1) = .True. + + ! Initialize tendency array + CALL Physics_ptend_init(ptend, state%psetcols, 'chemistry', lq=lq) + + ! Reset chemical tendencies + ptend%q(:,:,:) = 0.0e+0_r8 + + ! Determine current date and time + CALL Get_Curr_Date( yr = currYr, & + mon = currMo, & + day = currDy, & + tod = currTOD ) + + currYMD = (currYr*1000) + (currMo*100) + (currDy) + ! Deal with subdaily + currUTC = REAL(currTOD,f4)/3600.0e+0_f4 + currSc = 0 + currMn = 0 + currHr = 0 + DO WHILE (currTOD >= 3600) + currTOD = currTOD - 3600 + currHr = currHr + 1 + ENDDO + DO WHILE (currTOD >= 60) + currTOD = currTOD - 60 + currMn = currMn + 1 + ENDDO + currSc = currTOD + currHMS = (currHr*1000) + (currMn*100) + (currSc) + + ! Calculate COS(SZA) + Calday = Get_Curr_Calday( INT(dT/2) ) + CALL Zenith( Calday, Rlats, Rlons, CSZAmid, nY ) + + Calday = Get_Curr_Calday( ) + CALL Zenith( Calday, Rlats, Rlons, CSZA, nY ) + + ! Get all required data from physics buffer + TIM_NDX = pbuf_old_tim_idx() + CALL pbuf_get_field( pbuf, NDX_PBLH, PblH ) + CALL pbuf_get_field( pbuf, NDX_FSDS, Fsds ) + CALL pbuf_get_field( pbuf, NDX_CLDTOP, cldTop ) + CALL pbuf_get_field( pbuf, NDX_CLDFRC, cldFrc, START=(/1,1,TIM_NDX/), KOUNT=(/NCOL,PVER,1/) ) + CALL pbuf_get_field( pbuf, NDX_NEVAPR, NEvapr, START=(/1,1/), KOUNT=(/NCOL,PVER/)) + CALL pbuf_get_field( pbuf, NDX_PRAIN, PRain, START=(/1,1/), KOUNT=(/NCOL,PVER/)) + CALL pbuf_get_field( pbuf, NDX_LSFLXPRC, LsFlxPrc, START=(/1,1/), KOUNT=(/NCOL,PVERP/)) + CALL pbuf_get_field( pbuf, NDX_LSFLXSNW, LsFlxSnw, START=(/1,1/), KOUNT=(/NCOL,PVERP/)) + CALL pbuf_get_field( pbuf, NDX_CMFDQR, cmfdqr, START=(/1,1/), KOUNT=(/NCOL,PVER/)) + + ! Get VMR and MMR of H2O + h2ovmr = 0.0e0_fp + qH2O = 0.0e0_fp + ! Note MWDry = 28.966 g/mol + DO J = 1, nY + DO L = 1, nZ + qH2O(J,L) = REAL(state%q(J,L,cQ),r8) + ! Set GEOS-Chem's H2O mixing ratio to CAM's specific humidity 'q' + State_Chm(LCHNK)%Species(iH2O)%Conc(1,J,nZ+1-L) = qH2O(J,L) + h2ovmr(J,L) = qH2O(J,L) * MWDry / 18.016e+0_fp + ENDDO + ENDDO + + !----------------------------------------------------------------------- + ! ... Set the "invariants" + !----------------------------------------------------------------------- + CALL Setinv( invariants, state%t(:,:), h2ovmr, vmr0, & + state%pmid(:,:), nY, LCHNK, pbuf ) + + ! Calculate RH (range 0-1, note still level 1 = TOA) + relHum(:,:) = 0.0e+0_r8 + CALL QSat(state%t(:nY,:), state%pmid(:nY,:), satV, satQ, state%NCOL,PVER) + DO J = 1, nY + DO L = 1, nZ + relHum(J,L) = 0.622e+0_r8 * h2ovmr(J,L) / satQ(J,L) + relHum(J,L) = MAX( 0.0e+0_r8, MIN( 1.0e+0_r8, relHum(J,L) ) ) + ENDDO + ENDDO + + Z0 = 0.0e+0_r8 + DO J = 1, nY + Z0(J) = cam_in%landFrac(J) * zlnd & + + cam_in%iceFrac(J) * zsice & + + cam_in%ocnFrac(J) * zocn + IF (( cam_in%snowhLand(J) > 0.01_r8 ) .OR. & + ( cam_in%snowhIce(J) > 0.01_r8 )) THEN + ! Land is covered in snow + Z0(J) = zslnd + ENDIF + ENDDO + + ! Estimate cloud liquid water content and OD + TauCli = 0.0e+0_r8 + TauClw = 0.0e+0_r8 + + cldW(:nY,:nZ) = state%q(:nY,:nZ,ixCldLiq) + state%q(:nY,:nZ,ixCldIce) + IF ( ixNDrop > 0 ) nCldWtr(:nY,:nZ) = state%q(:nY,:nZ,ixNDrop) + + DO J = 1, nY + DO L = nZ, 1, -1 + ! ================================================================= + ! =========== Compute cloud optical depth based on ============ + ! =========== Liao et al. JGR, 104, 23697, 1999 ============ + ! ================================================================= + ! + ! Tau = 3/2 * LWC * dZ / ( \rho_w * r_e ) + ! dZ = - dP / ( \rho_air * g ) + ! since Pint is ascending, we can neglect the minus sign + ! + ! Tau = 3/2 * LWC * dP / ( \rho_air * r_e * \rho_w * g ) + ! LWC / \rho_air = Q + ! + ! Tau = 3/2 * Q * dP / ( r_e * rho_w * g ) + ! Tau(L) = 3/2 * Q(L) * (Pint(L+1) - Pint(L)) / (re * rho_w * g ) + ! Tau(L) = Q(L) * (Pint(L+1) - Pint(L)) * Cnst + ! Then divide by cloud fraction to get the in-cloud optical depth + + ! Unit check: | + ! Q : [kg H2O/kg air] | + ! Pint : [Pa]=[kg air/m/s^2] | + ! re : [m] | = 1.0e-5 + ! rho_w: [kg H2O/m^3] | = 1.0e+3 + ! g : [m/s^2] | = 9.81 + IF ( cldFrc(J,L) > cldMin ) THEN + TauClw(J,L) = state%q(J,L,ixCldLiq) & + * (state%pint(J,L+1)-state%pint(J,L)) & + * cnst / cldFrc(J,L) + TauClw(J,L) = MAX(TauClw(J,L), 0.0e+00_r8) + TauCli(J,L) = state%q(J,L,ixCldIce) & + * (state%pint(J,L+1)-state%pint(J,L)) & + * cnst / cldFrc(J,L) + TauCli(J,L) = MAX(TauCli(J,L), 0.0e+00_r8) + ENDIF + ENDDO + ENDDO + + ! Retrieve tropopause level + Trop_Lev = 0.0e+0_r8 + CALL Tropopause_FindChemTrop(state, Trop_Lev) + ! Back out the pressure + Trop_P = 1000.0e+0_r8 + DO J = 1, nY + Trop_P(J) = state%pmid(J,Trop_Lev(J)) * 0.01e+0_r8 + ENDDO + + ! Calculate snow depth + snowDepth = 0.0e+0_r8 + DO J = 1, nY + Sd_Ice = MAX(0.0e+0_r8,cam_in%snowhIce(J)) + Sd_Lnd = MAX(0.0e+0_r8,cam_in%snowhLand(J)) + Frc_Ice = MAX(0.0e+0_r8,cam_in%iceFrac(J)) + IF (Frc_Ice > 0.0e+0_r8) THEN + Sd_Avg = (Sd_Lnd*(1.0e+0_r8 - Frc_Ice)) + (Sd_Ice * Frc_Ice) + ELSE + Sd_Avg = Sd_Lnd + ENDIF + snowDepth(J) = Sd_Avg + ENDDO + + ! Field : ALBD + ! Description: Visible surface albedo + ! Unit : - + ! Dimensions : nX, nY + State_Met(LCHNK)%ALBD (1,:nY) = cam_in%asdir(:nY) + + ! Field : CLDFRC + ! Description: Column cloud fraction + ! Unit : - + ! Dimensions : nX, nY + ! Note : Estimate column cloud fraction as the maximum cloud + ! fraction in the column (pessimistic assumption) + DO J = 1, nY + State_Met(LCHNK)%CLDFRC(1,J) = MAXVAL(cldFrc(J,:)) + ENDDO + + ! Field : EFLUX, HFLUX + ! Description: Latent heat flux, sensible heat flux + ! Unit : W/m^2 + ! Dimensions : nX, nY + State_Met(LCHNK)%EFLUX (1,:nY) = cam_in%Lhf(:nY) + State_Met(LCHNK)%HFLUX (1,:nY) = cam_in%Shf(:nY) + + ! Field : LandTypeFrac + ! Description: Olson fraction per type + ! Unit : - (between 0 and 1) + ! Dimensions : nX, nY, NSURFTYPE + ! Note : Index 1 is water + DO N = 1, NSURFTYPE + Write(FieldName, '(a,i2.2)') 'HCO_LANDTYPE', N-1 + tmpIdx = pbuf_get_index(FieldName, rc) + IF ( tmpIdx < 0 .or. ( iStep == 1 ) ) THEN + IF ( rootChunk ) Write(iulog,*) "chem_timestep_tend: Field not found ", TRIM(FieldName) + ELSE + CALL pbuf_get_field(pbuf, tmpIdx, pbuf_i) + DO J = 1, nY + State_Met(LCHNK)%LandTypeFrac(1,J,N) = pbuf_i(J) + ENDDO + pbuf_i => NULL() + ENDIF + + Write(FieldName, '(a,i2.2)') 'HCO_XLAI', N-1 + tmpIdx = pbuf_get_index(FieldName, rc) + IF ( tmpIdx < 0 .or. ( iStep == 1 ) ) THEN + IF ( rootChunk ) Write(iulog,*) "chem_timestep_tend: Field not found ", TRIM(FieldName) + ELSE + CALL pbuf_get_field(pbuf, tmpIdx, pbuf_i) + DO J = 1, nY + State_Met(LCHNK)%XLAI_NATIVE(1,J,N) = pbuf_i(J) + ENDDO + pbuf_i => NULL() + ENDIF + ENDDO + + ! Field : FRCLND, FRLAND, FROCEAN, FRSEAICE, FRLAKE, FRLANDIC + ! Description: Olson land fraction + ! Fraction of land + ! Fraction of ocean + ! Fraction of sea ice + ! Fraction of lake + ! Fraction of land ice + ! Fraction of snow + ! Unit : - + ! Dimensions : nX, nY + State_Met(LCHNK)%FRCLND (1,:ny) = 1.e+0_fp - & + State_Met(LCHNK)%LandTypeFrac(1,:nY,1) ! Olson Land Fraction + State_Met(LCHNK)%FRLAND (1,:nY) = cam_in%landFrac(:nY) + State_Met(LCHNK)%FROCEAN (1,:nY) = cam_in%ocnFrac(:nY) + cam_in%iceFrac(:nY) + State_Met(LCHNK)%FRSEAICE (1,:nY) = cam_in%iceFrac(:nY) + State_Met(LCHNK)%FRLAKE (1,:nY) = 0.0e+0_fp + State_Met(LCHNK)%FRLANDIC (1,:nY) = 0.0e+0_fp + State_Met(LCHNK)%FRSNO (1,:nY) = 0.0e+0_fp + + ! Field : GWETROOT, GWETTOP + ! Description: Root and top soil moisture + ! Unit : - + ! Dimensions : nX, nY + State_Met(LCHNK)%GWETROOT (1,:nY) = 0.0e+0_fp + State_Met(LCHNK)%GWETTOP (1,:nY) = 0.0e+0_fp + + ! Field : LAI + ! Description: Leaf area index + ! Unit : m^2/m^2 + ! Dimensions : nX, nY + State_Met(LCHNK)%LAI (1,:nY) = 0.0e+0_fp + + ! Field : PARDR, PARDF + ! Description: Direct and diffuse photosynthetically active radiation + ! Unit : W/m^2 + ! Dimensions : nX, nY + State_Met(LCHNK)%PARDR (1,:nY) = 0.0e+0_fp + State_Met(LCHNK)%PARDF (1,:nY) = 0.0e+0_fp + + ! Field : PBLH + ! Description: PBL height + ! Unit : m + ! Dimensions : nX, nY + State_Met(LCHNK)%PBLH (1,:nY) = PblH(:nY) + + ! Field : PHIS + ! Description: Surface geopotential height + ! Unit : m + ! Dimensions : nX, nY + State_Met(LCHNK)%PHIS (1,:nY) = state%Phis(:nY) + + ! Field : PRECANV, PRECCON, PRECLSC, PRECTOT + ! Description: Anvil precipitation @ ground + ! Convective precipitation @ ground + ! Large-scale precipitation @ ground + ! Total precipitation @ ground + ! Unit : kg/m^2/s + ! Dimensions : nX, nY + State_Met(LCHNK)%PRECANV (1,:nY) = 0.0e+0_fp + State_Met(LCHNK)%PRECCON (1,:nY) = cam_out%Precc(:nY) + State_Met(LCHNK)%PRECLSC (1,:nY) = cam_out%Precl(:nY) + State_Met(LCHNK)%PRECTOT (1,:nY) = cam_out%Precc(:nY) + cam_out%Precl(:nY) + + ! Field : TROPP + ! Description: Tropopause pressure + ! Unit : hPa + ! Dimensions : nX, nY + State_Met(LCHNK)%TROPP (1,:nY) = Trop_P(:nY) + + ! Field : PS1_WET, PS2_WET + ! Description: Wet surface pressure at start and end of timestep + ! Unit : hPa + ! Dimensions : nX, nY + State_Met(LCHNK)%PS1_WET (1,:nY) = state%ps(:nY)*0.01e+0_fp + State_Met(LCHNK)%PS2_WET (1,:nY) = state%ps(:nY)*0.01e+0_fp + + ! Field : SLP + ! Description: Sea level pressure + ! Unit : hPa + ! Dimensions : nX, nY + State_Met(LCHNK)%SLP (1,:nY) = state%ps(:nY)*0.01e+0_fp + + ! Field : TS + ! Description: Surface temperature + ! Unit : K + ! Dimensions : nX, nY + State_Met(LCHNK)%TS (1,:nY) = cam_in%TS(:nY) + + ! Field : TSKIN + ! Description: Surface skin temperature + ! Remarks : NOT to be confused with TS (T at 2m) (hplin, 3/20/23) + ! Unit : K + ! Dimensions : nX, nY + State_Met(LCHNK)%TSKIN (1,:nY) = cam_in%SST(:nY) + + ! Field : SWGDN + ! Description: Incident radiation @ ground + ! Unit : W/m^2 + ! Dimensions : nX, nY + State_Met(LCHNK)%SWGDN (1,:nY) = fsds(:nY) + + ! Field : SNODP, SNOMAS + ! Description: Snow depth, snow mass + ! Unit : m, kg/m^2 + ! Dimensions : nX, nY + ! Note : Conversion from m to kg/m^2 + ! \rho_{ice} = 916.7 kg/m^3 + State_Met(LCHNK)%SNODP (1,:nY) = snowDepth(:nY) + State_Met(LCHNK)%SNOMAS (1,:nY) = snowDepth(:nY) * 916.7e+0_r8 + + ! Field : SUNCOS, SUNCOSmid + ! Description: COS(solar zenith angle) at current time and midpoint + ! of chemistry timestep + ! Unit : - + ! Dimensions : nX, nY + State_Met(LCHNK)%SUNCOS (1,:nY) = CSZA(:nY) + State_Met(LCHNK)%SUNCOSmid (1,:nY) = CSZAmid(:nY) + + ! Field : UVALBEDO + ! Description: UV surface albedo + ! Unit : - + ! Dimensions : nX, nY + IF ( Input_Opt%onlineAlbedo ) THEN + State_Met(LCHNK)%UVALBEDO(1,:nY) = cam_in%asdir(:nY) + ELSE + FieldName = 'HCO_UV_ALBEDO' + tmpIdx = pbuf_get_index(FieldName, RC) + IF ( tmpIdx < 0 .or. ( iStep == 1 ) ) THEN + IF ( rootChunk ) Write(iulog,*) "chem_timestep_tend: Field not found ", TRIM(FieldName) + State_Met(LCHNK)%UVALBEDO(1,:nY) = 0.0e+0_fp + ELSE + pbuf_chnk => pbuf_get_chunk(hco_pbuf2d, LCHNK) + CALL pbuf_get_field(pbuf_chnk, tmpIdx, pbuf_i) + State_Met(LCHNK)%UVALBEDO(1,:nY) = pbuf_i(:nY) + pbuf_chnk => NULL() + pbuf_i => NULL() + ENDIF + ENDIF + + ! Field : U10M, V10M + ! Description: E/W and N/S wind speed @ 10m height + ! Unit : m/s + ! Dimensions : nX, nY + State_Met(LCHNK)%U10M (1,:nY) = state%U(:nY,nZ) + State_Met(LCHNK)%V10M (1,:nY) = state%V(:nY,nZ) + + ! Field : USTAR + ! Description: Friction velocity + ! Unit : m/s + ! Dimensions : nX, nY + ! Note : We here combine the land friction velocity (fv) with + ! the ocean friction velocity (ustar) + DO J = 1, nY + State_Met(LCHNK)%USTAR (1,J) = & + cam_in%fv(J) * ( cam_in%landFrac(J)) & + + cam_in%uStar(J) * ( 1.0e+0_fp - cam_in%landFrac(J)) + ENDDO + + ! Field : Z0 + ! Description: Surface roughness length + ! Unit : m + ! Dimensions : nX, nY + State_Met(LCHNK)%Z0 (1,:nY) = Z0(:nY) + + ! Field : IODIDE + ! Description: Surface iodide concentration + ! Unit : nM + ! Dimensions : nX, nY + FieldName = 'HCO_iodide' + tmpIdx = pbuf_get_index(FieldName, RC) + IF ( tmpIdx < 0 .or. ( iStep == 1 ) ) THEN + IF ( rootChunk ) Write(iulog,*) "chem_timestep_tend: Field not found ", TRIM(FieldName) + State_Chm(LCHNK)%IODIDE(1,:nY) = 0.0e+0_fp + ELSE + pbuf_chnk => pbuf_get_chunk(hco_pbuf2d, LCHNK) + CALL pbuf_get_field(pbuf_chnk, tmpIdx, pbuf_i) + State_Chm(LCHNK)%IODIDE(1,:nY) = pbuf_i(:nY) + pbuf_chnk => NULL() + pbuf_i => NULL() + ENDIF + + ! Field : SALINITY + ! Description: Ocean salinity + ! Unit : PSU + ! Dimensions : nX, nY + ! Note : Possibly get ocean salinity from POP? + FieldName = 'HCO_salinity' + tmpIdx = pbuf_get_index(FieldName, RC) + IF ( tmpIdx < 0 .or. ( iStep == 1 ) ) THEN + IF ( rootChunk ) Write(iulog,*) "chem_timestep_tend: Field not found ", TRIM(FieldName) + State_Chm(LCHNK)%SALINITY(1,:nY) = 0.0e+0_fp + ELSE + pbuf_chnk => pbuf_get_chunk(hco_pbuf2d, LCHNK) + CALL pbuf_get_field(pbuf_chnk, tmpIdx, pbuf_i) + State_Chm(LCHNK)%SALINITY(1,:nY) = pbuf_i(:nY) + pbuf_chnk => NULL() + pbuf_i => NULL() + ENDIF + + ! Field : OMOC + ! Description: OM/OC ratio + ! Unit : - + ! Dimensions : nX, nY + IF ( currMo == 12 .or. currMo == 1 .or. currMo == 2 ) THEN + FieldName = 'HCO_OMOC_DJF' + ELSE IF ( currMo == 3 .or. currMo == 4 .or. currMo == 5 ) THEN + FieldName = 'HCO_OMOC_MAM' + ELSE IF ( currMo == 6 .or. currMo == 7 .or. currMo == 8 ) THEN + FieldName = 'HCO_OMOC_JJA' + ELSE IF ( currMo == 9 .or. currMo == 10 .or. currMo == 11 ) THEN + FieldName = 'HCO_OMOC_SON' + ENDIF + tmpIdx = pbuf_get_index(FieldName, rc) + IF ( tmpIdx < 0 .or. ( iStep == 1 ) ) THEN + ! there is an error here and the field was not found + IF ( rootChunk ) Write(iulog,*) "chem_timestep_tend: Field not found ", TRIM(FieldName) + ELSE + CALL pbuf_get_field(pbuf, tmpIdx, pbuf_i) + DO J = 1, nY + State_Chm(LCHNK)%OMOC(1,J) = pbuf_i(J) + ENDDO + pbuf_i => NULL() + ENDIF + + ! Three-dimensional fields on level edges + DO J = 1, nY + DO L = 1, nZ+1 + ! Field : PEDGE + ! Description: Wet air pressure at (vertical) level edges + ! Unit : hPa + ! Dimensions : nX, nY, nZ+1 + State_Met(LCHNK)%PEDGE (1,J,L) = state%pint(J,nZ+2-L)*0.01e+0_fp + + ! Field : CMFMC + ! Description: Upward moist convective mass flux + ! Unit : kg/m^2/s + ! Dimensions : nX, nY, nZ+1 + State_Met(LCHNK)%CMFMC (1,J,L) = 0.0e+0_fp + + ! Field : PFICU, PFLCU + ! Description: Downward flux of ice/liquid precipitation (convective) + ! Unit : kg/m^2/s + ! Dimensions : nX, nY, nZ+1 + State_Met(LCHNK)%PFICU (1,J,L) = 0.0e+0_fp + State_Met(LCHNK)%PFLCU (1,J,L) = 0.0e+0_fp + + ! Field : PFILSAN, PFLLSAN + ! Description: Downward flux of ice/liquid precipitation (Large-scale & anvil) + ! Unit : kg/m^2/s + ! Dimensions : nX, nY, nZ+1 + State_Met(LCHNK)%PFILSAN (1,J,L) = LsFlxSnw(J,nZ+2-L) ! kg/m2/s + State_Met(LCHNK)%PFLLSAN (1,J,L) = MAX(0.0e+0_fp,LsFlxPrc(J,nZ+2-L) - LsFlxSnw(J,nZ+2-L)) ! kg/m2/s + ENDDO + ENDDO + + DO J = 1, nY + ! Field : CLDTOPS + ! Description: Max cloud top height + ! Unit : level + ! Dimensions : nX, nY + State_Met(LCHNK)%CLDTOPS(1,J) = nZ + 1 - NINT(cldTop(J)) + ENDDO + + ! Three-dimensional fields on level centers + DO J = 1, nY + DO L = 1, nZ + ! Field : U, V + ! Description: E/W and N/S component of wind + ! Unit : m/s + ! Dimensions : nX, nY, nZ + State_Met(LCHNK)%U (1,J,L) = state%U(J,nZ+1-L) + State_Met(LCHNK)%V (1,J,L) = state%V(J,nZ+1-L) + + ! Field : OMEGA + ! Description: Updraft velocity + ! Unit : Pa/s + ! Dimensions : nX, nY, nZ + State_Met(LCHNK)%OMEGA (1,J,L) = state%Omega(J,nZ+1-L) + + ! Field : CLDF + ! Description: 3-D cloud fraction + ! Unit : - + ! Dimensions : nX, nY, nZ + State_Met(LCHNK)%CLDF (1,J,L) = cldFrc(J,nZ+1-L) + + ! Field : DTRAIN + ! Description: Detrainment flux + ! Unit : kg/m^2/s + ! Dimensions : nX, nY, nZ + State_Met(LCHNK)%DTRAIN (1,J,L) = 0.0e+0_fp ! Used in convection + + ! Field : DQRCU + ! Description: Convective precipitation production rate + ! Unit : kg/kg dry air/s + ! Dimensions : nX, nY, nZ + State_Met(LCHNK)%DQRCU (1,J,L) = 0.0e+0_fp ! Used in convection + + ! Field : DQRLSAN + ! Description: Large-scale precipitation production rate + ! Unit : kg/kg dry air/s + ! Dimensions : nX, nY, nZ + State_Met(LCHNK)%DQRLSAN (1,J,L) = PRain(J,nZ+1-L) ! kg/kg/s + + ! Field : QI, QL + ! Description: Cloud ice/water mixing ratio + ! Unit : kg/kg dry air + ! Dimensions : nX, nY, nZ + State_Met(LCHNK)%QI (1,J,L) = MAX(1.0e-10_fp, state%q(J,nZ+1-L,ixCldIce)) ! kg ice / kg dry air + State_Met(LCHNK)%QL (1,J,L) = MAX(1.0e-10_fp, state%q(J,nZ+1-L,ixCldLiq)) ! kg water / kg dry air + + ! Field : RH + ! Description: Relative humidity + ! Unit : % + ! Dimensions : nX, nY, nZ + State_Met(LCHNK)%RH (1,J,L) = relHum(J,nZ+1-L) * 100.0e+0_fp + + ! Field : TAUCLI, TAUCLW + ! Description: Optical depth of ice/H2O clouds + ! Unit : - + ! Dimensions : nX, nY, nZ + State_Met(LCHNK)%TAUCLI (1,J,L) = TauCli(J,nZ+1-L) + State_Met(LCHNK)%TAUCLW (1,J,L) = TauClw(J,nZ+1-L) + + ! Field : REEVAPCN + ! Description: Evaporation of convective precipitation + ! (w/r/t dry air) + ! Unit : kg + ! Dimensions : nX, nY, nZ + State_Met(LCHNK)%REEVAPCN (1,J,L) = 0.0e+0_fp + + ! Field : REEVAPLS + ! Description: Evaporation of large-scale + anvil precipitation + ! (w/r/t dry air) + ! Unit : kg/kg/s + ! Dimensions : nX, nY, nZ + State_Met(LCHNK)%REEVAPLS (1,J,L) = NEvapr(J,nZ+1-L) ! kg/kg/s + + ! Field : SPHU1, SPHU2 + ! Description: Specific humidity at current and next timestep + ! Unit : g H2O/ kg air + ! Dimensions : nX, nY, nZ + ! Note : Since we are using online meteorology, we do not have + ! access to the data at the next time step + ! Compute tendency in g H2O/kg air/s (tmmf, 1/13/20) ? + State_Met(LCHNK)%SPHU1 (1,J,L) = qH2O(J,nZ+1-L) * 1.0e+3_fp ! g/kg + State_Met(LCHNK)%SPHU2 (1,J,L) = qH2O(J,nZ+1-L) * 1.0e+3_fp ! g/kg + + ! Field : TMPU1, TMPU2 + ! Description: Temperature at current and next timestep + ! Unit : K + ! Dimensions : nX, nY, nZ + ! Note : Since we are using online meteorology, we do not have + ! access to the data at the next time step + ! Compute tendency in K/s (tmmf, 1/13/20) ? + State_Met(LCHNK)%TMPU1 (1,J,L) = state%t(J,nZ+1-L) + State_Met(LCHNK)%TMPU2 (1,J,L) = state%t(J,nZ+1-L) + ENDDO + ENDDO + ! Note: Setting DQRLSAN to zero in the top layer prevents upcoming NaNs + ! in the GEOS-Chem wet deposition routines. Given the altitude, it should + ! be zero anyway, this is just to prevent any numerical artifacts from + ! creeping in. + State_Met(LCHNK)%DQRLSAN (1,:nY,nZ) = 0.0e+00_fp + + ! Field : T + ! Description: Temperature at current time + ! Unit : K + ! Dimensions : nX, nY, nZ + ! Note : Since we are using online meteorology, we do not have + ! access to the data at the next time step + ! Compute tendency in K/s (tmmf, 1/13/20) ? + State_Met(LCHNK)%T = (State_Met(LCHNK)%TMPU1 + State_Met(LCHNK)%TMPU2)*0.5e+0_fp + + ! Field : SPHU + ! Description: Specific humidity at current time + ! Unit : g H2O/ kg air + ! Dimensions : nX, nY, nZ + ! Note : Since we are using online meteorology, we do not have + ! access to the data at the next time step + ! Compute tendency in g H2O/kg air/s (tmmf, 1/13/20) ? + State_Met(LCHNK)%SPHU = (State_Met(LCHNK)%SPHU1 + State_Met(LCHNK)%SPHU2)*0.5e+0_fp + + ! Field : OPTD + ! Description: Total in-cloud optical depth (visible band) + ! Unit : - + ! Dimensions : nX, nY, nZ + State_Met(LCHNK)%OPTD = State_Met(LCHNK)%TAUCLI + State_Met(LCHNK)%TAUCLW + + ! Pass time values obtained from the ESMF environment to GEOS-Chem + CALL Accept_External_Date_Time( value_NYMD = currYMD, & + value_NHMS = currHMS, & + value_YEAR = currYr, & + value_MONTH = currMo, & + value_DAY = currDy, & + value_DAYOFYR = INT(FLOOR(Calday)), & + value_HOUR = currHr, & + value_MINUTE = currMn, & + value_HELAPSED = 0.0e+0_f4, & + value_UTC = currUTC, & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Failed to update time in GEOS-Chem!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + CALL Accept_External_PEdge( State_Met = State_Met(LCHNK), & + State_Grid = State_Grid(LCHNK), & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Failed to update pressure edges!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! Field : PS1_DRY, PS2_DRY + ! Description: Dry surface pressure at current and next timestep + ! Unit : hPa + ! Dimensions : nX, nY, nZ+1 + ! Note : 1. Use the CAM PSDry fields instead of using the + ! GEOS-Chem calculation + ! 2. As we are using online meteorology, we do not + ! have access to the fields at the next time step + ! Compute Pa/s tendency? (tmmf, 1/13/20) + State_Met(LCHNK)%PS1_DRY (1,:nY) = state%PSDry(:nY) * 0.01e+0_fp + State_Met(LCHNK)%PS2_DRY (1,:nY) = state%PSDry(:nY) * 0.01e+0_fp + + ! Field : PSC2_WET, PSC2_DRY + ! Description: Interpolated wet and dry surface pressure at the + ! current time + ! Unit : hPa + ! Dimensions : nX, nY, nZ+1 + ! Note : As we are using online meteorology, we do not + ! have access to the fields at the next time step + ! Compute Pa/s tendency? (tmmf, 1/13/20) + State_Met(LCHNK)%PSC2_WET = State_Met(LCHNK)%PS1_WET + State_Met(LCHNK)%PSC2_DRY = State_Met(LCHNK)%PS1_DRY + + CALL Set_Floating_Pressures( State_Grid = State_Grid(LCHNK), & + State_Met = State_Met(LCHNK), & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Failed to set floating pressures!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! Set quantities of interest but do not change VMRs + ! This function updates: + ! ==================================================================== + ! (1) PEDGE : Moist air pressure at grid box bottom [hPa] + ! (2) PEDGE_DRY : Dry air partial pressure at box bottom [hPa] + ! (3) PMID : Moist air pressure at grid box centroid [hPa] + ! (4) PMID_DRY : Dry air partial pressure at box centroid [hPa] + ! (5) PMEAN : Altitude-weighted mean moist air pressure [hPa] + ! (6) PMEAN_DRY : Alt-weighted mean dry air partial pressure [hPa] + ! (7) DELP : Delta-P extent of grid box [hPa] + ! (Same for both moist and dry air since we + ! assume constant water vapor pressure + ! across box) + ! (8) AIRDEN : Mean grid box dry air density [kg/m^3] + ! (defined as total dry air mass/box vol) + ! (9) AIRNUMDEN : Mean grid box dry air number density [molec/m^3] + ! (10) MAIRDEN : Mean grid box moist air density [kg/m^3] + ! (defined as total moist air mass/box vol) + ! (11) AD : Total dry air mass in grid box [kg] + ! (12) ADMOIST : Total moist air mass in grid box [kg] + ! (13) BXHEIGHT : Vertical height of grid box [m] + ! (14) AIRVOL : Volume of grid box [m^3] + ! (15) MOISTMW : Molecular weight of moist air in box [g/mol] + ! (16) IsLand : Logical for grid cells over land [-] + ! (17) IsWater : Logical for grid cells over water [-] + ! (18) IsIce : Logical for grid cells over ice [-] + ! (19) IsSnow : Logical for grid cells over snow [-] + ! (20) InTroposph: Logical for tropospheric grid cells [-] + ! (21) InStratMes: Logical for non-tropospheric grid cells [-] + ! (22) InStratosp: Logical for stratospheric grid cells [-] + ! (23) InChemGrid: Logical for chemistry grid cells [-] + ! (24) LocalSolar: Local solar time [-] + ! (25) IsLocalNoo: Logical for local noon [-] + ! (26) TropLev : Maximum tropopause level [-] + ! (27) TropHt : Maximum tropopause height [km] + ! ==================================================================== + CALL AirQnt( Input_Opt = Input_Opt, & + State_Chm = State_Chm(LCHNK), & + State_Grid = State_Grid(LCHNK), & + State_Met = State_Met(LCHNK), & + RC = RC, & + Update_Mixing_Ratio = .False. ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Failed to calculate air properties!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! SDE 05/28/13: Set H2O to State_Chm tracer if relevant and, + ! if LSETH2O=F and LACTIVEH2O=T, update specific humidity + ! in the stratosphere + ! + ! NOTE: Specific humidity may change in SET_H2O_TRAC and + ! therefore this routine may call AIRQNT again to update + ! air quantities and tracer concentrations (ewl, 10/28/15) + IF ( Input_Opt%Its_A_Fullchem_Sim .and. iH2O > 0 ) THEN + CALL Set_H2O_Trac( SETSTRAT = Input_Opt%LSETH2O, & + Input_Opt = Input_Opt, & + State_Chm = State_Chm(LCHNK), & + State_Grid = State_Grid(LCHNK), & + State_Met = State_Met(LCHNK), & + RC = RC ) + + ! Trap potential errors + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Set_H2O_Trac" #1!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! Only force strat once if using UCX + IF (Input_Opt%LSETH2O) Input_Opt%LSETH2O = .FALSE. + ENDIF + + ! Do this after AirQnt, such that we overwrite GEOS-Chem isLand, isWater and + ! isIce, which are based on albedo. Rather, we use CLM landFranc, ocnFrac + ! and iceFrac. We also compute isSnow + DO J = 1, nY + iMaxLoc = MAXLOC( (/ State_Met(LCHNK)%FRLAND(1,J) + & + State_Met(LCHNK)%FRLANDIC(1,J) + & + State_Met(LCHNK)%FRLAKE(1,J), & + State_Met(LCHNK)%FRSEAICE(1,J), & + State_Met(LCHNK)%FROCEAN(1,J) - & + State_Met(LCHNK)%FRSEAICE(1,J) /) ) + IF ( iMaxLoc(1) == 3 ) iMaxLoc(1) = 0 + ! reset ocean to 0 + + IF ( iMaxLoc(1) == 0 ) THEN + State_Met(LCHNK)%isLand(1,J) = .False. + State_Met(LCHNK)%isWater(1,J) = .True. + State_Met(LCHNK)%isIce(1,J) = .False. + ELSEIF ( iMaxLoc(1) == 1 ) THEN + State_Met(LCHNK)%isLand(1,J) = .True. + State_Met(LCHNK)%isWater(1,J) = .False. + State_Met(LCHNK)%isIce(1,J) = .False. + ELSEIF ( iMaxLoc(1) == 2 ) THEN + State_Met(LCHNK)%isLand(1,J) = .False. + State_Met(LCHNK)%isWater(1,J) = .False. + State_Met(LCHNK)%isIce(1,J) = .True. + ELSE + Write(iulog,*) " iMaxLoc gets value: ", iMaxLoc + ErrMsg = 'Failed to figure out land/water' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + State_Met(LCHNK)%isSnow(1,J) = & + ( State_Met(LCHNK)%FRSEAICE(1,J) > 0.0e+0_fp & + .or. State_Met(LCHNK)%SNODP(1,J) > 0.01 ) + + ENDDO + + ! Do this after AirQnt in order to use AIRDEN and BXHEIGHT + DO J = 1, nY + O3col(J) = 0.0e+0_fp + DO L = 1, nZ + O3col(J) = O3col(J) & + + State_Chm(LCHNK)%Species(iO3)%Conc(1,J,L) & + * State_Met(LCHNK)%AIRDEN(1,J,L) & + * State_Met(LCHNK)%BXHEIGHT(1,J,L) + ENDDO + O3col(J) = O3col(J) * ( AVO / MWO3 ) / 1e+1_fp / 2.69e+16_fp + ENDDO + + ! Field : TO3 + ! Description: Total overhead ozone column + ! Unit : DU + ! Dimensions : nX, nY + State_Met(LCHNK)%TO3 (1,:nY) = O3col(:nY) + + IF ( Input_Opt%Linear_Chem .AND. & + State_Grid(LCHNK)%MaxChemLev /= State_Grid(LCHNK)%nZ ) THEN + IF ( iStep == 1 ) THEN + ALLOCATE( BrPtrDay ( 6 ), STAT=IERR ) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating BrPtrDay') + ALLOCATE( BrPtrNight( 6 ), STAT=IERR ) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating BrPtrNight') + DO N = 1, 6 + ! Skip if species is not defined + IF ( GC_Bry_TrID(N) <= 0 ) CYCLE + + ! Get Bry name + SpcName = State_Chm(LCHNK)%SpcData(GC_Bry_TrID(N))%Info%Name + + ! Construct field name using Bry name + PREFIX = 'GEOSCCM_'//TRIM(SpcName) + + ALLOCATE( BrPtrDay(N)%MR(1,PCOLS,nZ), STAT=IERR ) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating BrPtrDay%MR') + ALLOCATE( BrPtrNight(N)%MR(1,PCOLS,nZ), STAT=IERR ) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating BrPtrNight%MR') + + ! Get pointer to this field. These are the mixing ratios (pptv). + + ! Day + FieldName = TRIM(PREFIX) // '_DAY' + tmpIdx = pbuf_get_index(FieldName, RC) + IF ( tmpIdx < 0 .or. ( iStep == 1 ) ) THEN + IF ( rootChunk ) Write(iulog,*) "chem_timestep_tend: Field not found ", TRIM(FieldName) + BrPtrDay(N)%MR(1,:nY,nZ:1:-1) = 0.0e+0_f4 + ELSE + pbuf_chnk => pbuf_get_chunk(hco_pbuf2d, LCHNK) + CALL pbuf_get_field(pbuf_chnk, tmpIdx, pbuf_ik) + BrPtrDay(N)%MR(1,:nY,nZ:1:-1) = REAL(pbuf_ik(:nY,:nZ), f4) + pbuf_chnk => NULL() + pbuf_ik => NULL() + ENDIF + + ! Night + FieldName = TRIM(PREFIX) // '_NIGHT' + tmpIdx = pbuf_get_index(FieldName, RC) + IF ( tmpIdx < 0 .or. ( iStep == 1 ) ) THEN + IF ( rootChunk ) Write(iulog,*) "chem_timestep_tend: Field not found ", TRIM(FieldName) + BrPtrDay(N)%MR(1,:nY,nZ:1:-1) = 0.0e+0_f4 + ELSE + pbuf_chnk => pbuf_get_chunk(hco_pbuf2d, LCHNK) + CALL pbuf_get_field(pbuf_chnk, tmpIdx, pbuf_ik) + BrPtrNight(N)%MR(1,:nY,nZ:1:-1) = REAL(pbuf_ik(:nY,:nZ), f4) + pbuf_chnk => NULL() + pbuf_ik => NULL() + ENDIF + + ENDDO + + DO N = 1,NSCHEM + + ! Get GEOS-Chem species index + M = TrID_GC(N) + + ! Skip if species is not defined + IF ( M <= 0 ) CYCLE + + ! Get species name + SpcName = State_Chm(LCHNK)%SpcData(M)%Info%Name + + ! --------------------------------------------------------------- + ! Get pointers to fields + ! --------------------------------------------------------------- + + ! Production rates [v/v/s] + FieldName = 'GMI_PROD_'//TRIM(SpcName) + + ALLOCATE( PLVEC(N)%PROD(1,PCOLS,nZ), STAT=IERR ) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating PLVEC%PROD') + ALLOCATE( PLVEC(N)%LOSS(1,PCOLS,nZ), STAT=IERR ) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating PLVEC%PROD') + + ! Get pointer from HEMCO + tmpIdx = pbuf_get_index(FieldName, RC) + IF ( tmpIdx < 0 .or. ( iStep == 1 ) ) THEN + IF ( rootChunk ) Write(iulog,*) "chem_timestep_tend: Field not found ", TRIM(FieldName) + PLVEC(N)%PROD(1,:nY,nZ:1:-1) = 0.0e+0_f4 + FND = .False. + ELSE + pbuf_chnk => pbuf_get_chunk(hco_pbuf2d, LCHNK) + CALL pbuf_get_field(pbuf_chnk, tmpIdx, pbuf_ik) + PLVEC(N)%PROD(1,:nY,nZ:1:-1) = REAL(pbuf_ik(:nY,:nZ),f4) + FND = .True. + pbuf_chnk => NULL() + pbuf_ik => NULL() + ENDIF + + ! Warning message + IF ( .NOT. FND .AND. Input_Opt%amIRoot ) THEN + ErrMsg = 'Cannot find archived production rates for ' // & + TRIM(SpcName) // ' - will use value of 0.0. ' // & + 'To use archived rates, add the following field ' // & + 'to the HEMCO configuration file: '// TRIM( FieldName ) + CALL GC_Warning( ErrMsg, RC, ThisLoc ) + ENDIF + + ! Loss frequency [s-1] + FieldName = 'GMI_LOSS_'//TRIM(SpcName) + + ! Get pointer from HEMCO + tmpIdx = pbuf_get_index(FieldName, RC) + IF ( tmpIdx < 0 .or. ( iStep == 1 ) ) THEN + IF ( rootChunk ) Write(iulog,*) "chem_timestep_tend: Field not found ", TRIM(FieldName) + PLVEC(N)%LOSS(1,:nY,nZ:1:-1) = 0.0e+0_f4 + FND = .False. + ELSE + pbuf_chnk => pbuf_get_chunk(hco_pbuf2d, LCHNK) + CALL pbuf_get_field(pbuf_chnk, tmpIdx, pbuf_ik) + PLVEC(N)%LOSS(1,:nY,nZ:1:-1) = REAL(pbuf_ik(:nY,:nZ), f4) + FND = .True. + pbuf_chnk => NULL() + pbuf_ik => NULL() + ENDIF + + ! Warning message + IF ( .NOT. FND .AND. Input_Opt%amIRoot ) THEN + ErrMsg= 'Cannot find archived loss frequencies for ' // & + TRIM(SpcName) // ' - will use value of 0.0. ' // & + 'To use archived rates, add the following field ' // & + 'to the HEMCO configuration file: '//TRIM(FieldName) + CALL GC_Warning( ErrMsg, RC, ThisLoc ) + ENDIF + + ENDDO !N + + ! Get pointer to GMI_OH + + ALLOCATE( GMI_OH(1,PCOLS,nZ), STAT=IERR ) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating GMI_OH') + + tmpIdx = pbuf_get_index(FieldName, RC) + IF ( tmpIdx < 0 .or. ( iStep == 1 ) ) THEN + IF ( rootChunk ) Write(iulog,*) "chem_timestep_tend: Field not found ", TRIM(FieldName) + GMI_OH(1,:nY,nZ:1:-1) = 0.0e+0_f4 + ELSE + pbuf_chnk => pbuf_get_chunk(hco_pbuf2d, LCHNK) + CALL pbuf_get_field(pbuf_chnk, tmpIdx, pbuf_ik) + GMI_OH(1,:nY,nZ:1:-1) = REAL(pbuf_ik(:nY,:nZ), f4) + pbuf_chnk => NULL() + pbuf_ik => NULL() + ENDIF + ENDIF + + ENDIF + + ! This is not necessary as we prescribe CH4 surface mixing ratios + ! through CAM. + !! Prescribe methane surface concentrations throughout PBL + !IF ( ITS_A_FULLCHEM_SIM .and. id_CH4 > 0 ) THEN + ! + ! ! Set CH4 concentrations + ! CALL SET_CH4( Input_Opt = Input_Opt, & + ! State_Chm = State_Chm(LCHNK), & + ! State_Diag = State_Diag(LCHNK), & + ! State_Grid = State_Grid(LCHNK), & + ! State_Met = State_Met(LCHNK), & + ! RC = RC ) + ! + ! ! Trap potential errors + ! IF ( RC /= GC_SUCCESS ) THEN + ! ErrMsg = 'Error encountered in call to "SET_CH4"!' + ! CALL Error_Stop( ErrMsg, ThisLoc ) + ! ENDIF + !ENDIF + + ! Eventually initialize/reset wetdep + IF ( Input_Opt%LConv .OR. Input_Opt%LChem .OR. Input_Opt%LWetD ) THEN + CALL Setup_WetScav( Input_Opt = Input_Opt, & + State_Chm = State_Chm(LCHNK), & + State_Grid = State_Grid(LCHNK), & + State_Met = State_Met(LCHNK), & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Setup_WetScav"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + ENDIF + + !============================================================== + ! ***** C O M P U T E P B L H E I G H T etc. ***** + !============================================================== + ! Move this call from the PBL mixing routines because the PBL + ! height is used by drydep and some of the emissions routines. + ! (ckeller, 3/5/15) + ! This function updates: + ! ==================================================================== + ! (1) InPbl : Logical indicating if we are in the PBL [-] + ! (2) PBL_TOP_L : Number of layers in the PBL [-] + ! (3) PBL_TOP_hPa: Pressure at the top of the PBL [hPa] + ! (4) PBL_TOP_m : PBL height [m] + ! (5) PBL_THICK : PBL thickness [hPa] + ! (6) F_OF_PBL : Fraction of grid box within the PBL [-] + ! (7) F_UNDER_PBLTOP: Fraction of grid box underneath the PBL top [-] + ! (8) PBL_MAX_L : Model level where PBL top occurs [-] + ! ==================================================================== + CALL Compute_PBL_Height( Input_Opt = Input_Opt, & + State_Grid = State_Grid(LCHNK), & + State_Met = State_Met(LCHNK), & + RC = RC ) + + ! Trap potential errors + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Compute_PBL_Height"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + !-------------------------------------------------------------- + ! Test for emission timestep + ! Now always do emissions here, even for full-mixing + ! (ckeller, 3/5/15) + !-------------------------------------------------------------- + !================================================================== + ! ***** D R Y D E P O S I T I O N ***** + !================================================================== + !================================================================== + ! Compute dry deposition velocities + ! + ! CLM computes dry deposition velocities but only for gas-phase + ! species and only over land. We therefore need to both pass the + ! the CLM dry deposition velocities as well as compute them using + ! the GEOS-Chem dry deposition module. If using the CLM velocities, + ! then scale them with the ocean fraction; otherwise use GEOS-Chem + ! computed velocities. + ! + ! drydep_method must be set to DD_XLND. + ! + !================================================================== + ! + ! State_Chm expects dry deposition velocities in m/s, whereas + ! CLM returns land deposition velocities in cm/s! + ! + ! For now, dry deposition velocities are only computed for gases + ! (which is what CLM deals with). Dry deposition for aerosols is + ! work in progress. + ! + ! Thibaud M. Fritz - 27 Feb 2020 + !================================================================== + + IF ( Input_Opt%LDryD ) THEN + ! Compute the Olson landmap fields of State_Met + ! (e.g. State_Met%IREG, State_Met%ILAND, etc.) + CALL Compute_Olson_Landmap( Input_Opt = Input_Opt, & + State_Grid = State_Grid(LCHNK), & + State_Met = State_Met(LCHNK), & + RC = RC ) + + ! Trap potential errors + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Compute_Olson_Landmap"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! Compute State_Met%XLAI (for drydep) and State_Met%MODISLAI, + ! which is the average LAI per grid box (for soil NOx emissions) + CALL Compute_Xlai( Input_Opt = Input_Opt, & + State_Grid = State_Grid(LCHNK), & + State_Met = State_Met(LCHNK), & + RC = RC ) + + ! Trap potential errors + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Compute_Xlai"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! Compute drydep velocities and update State_Chm%DryDepVel + CALL Do_Drydep( Input_Opt = Input_Opt, & + State_Chm = State_Chm(LCHNK), & + State_Diag = State_Diag(LCHNK), & + State_Grid = State_Grid(LCHNK), & + State_Met = State_Met(LCHNK), & + RC = RC ) + + ! Trap potential errors + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Do_Drydep"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + DO N = 1, nddvels + + !! Print debug + !IF ( rootChunk ) THEN + ! IF ( N == 1 ) THEN + ! Write(iulog,*) "Number of GC dry deposition species = ", & + ! SIZE(State_Chm(LCHNK)%DryDepVel(:,:,:),3) + ! Write(iulog,*) "Number of CESM dry deposition species = ", & + ! nddvels + ! ENDIF + ! Write(iulog,*) "N = ", N + ! Write(iulog,*) "drySpc_ndx = ", drySpc_ndx(N) + ! Write(iulog,*) "GC index = ", map2GC_dryDep(N) + ! IF ( map2GC_dryDep(N) > 0 ) THEN + ! Write(iulog,*) "GC name = ", TRIM(DEPNAME(map2GC_dryDep(N))) + ! ENDIF + ! Write(iulog,*) "dry Species= ", TRIM(drydep_list(N)) + ! IF ( drySpc_ndx(N) > 0 ) THEN + ! Write(iulog,*) "tracerName = ", TRIM(tracerNames(drySpc_ndx(N))) + ! ENDIF + ! Write(iulog,*) "CLM-depVel = ", & + ! MAXVAL(cam_in%depvel(:nY,N)) * 1.0e-02_fp, " [m/s]" + ! IF ( map2GC_dryDep(N) > 0 ) THEN + ! Write(iulog,*) "GC-depVel = ", & + ! MAXVAL(State_Chm(LCHNK)%DryDepVel(1,:nY,map2GC_dryDep(N))), " [m/s]" + ! ENDIF + !ENDIF + + IF ( map2GC_dryDep(N) > 0 ) THEN + ! State_Chm%DryDepVel is in m/s + State_Chm(LCHNK)%DryDepVel(1,:nY,map2GC_dryDep(N)) = & + ! This first bit corresponds to the dry deposition + ! velocities over land as computed from CLM and + ! converted to m/s. This is scaled by the fraction + ! of land. + cam_in%depVel(:nY,N) * 1.0e-02_fp & + * MAX(0._fp, 1.0_fp - State_Met(LCHNK)%FROCEAN(1,:nY)) & + ! This second bit corresponds to the dry deposition + ! velocities over ocean and sea ice as computed from + ! GEOS-Chem. This is scaled by the fraction of ocean + ! and sea ice. + + State_Chm(LCHNK)%DryDepVel(1,:nY,map2GC_dryDep(N)) & + * State_Met(LCHNK)%FROCEAN(1,:nY) + ENDIF + ENDDO + + CALL Update_DryDepFreq( Input_Opt = Input_Opt, & + State_Chm = State_Chm(LCHNK), & + State_Diag = State_Diag(LCHNK), & + State_Grid = State_Grid(LCHNK), & + State_Met = State_Met(LCHNK), & + RC = RC ) + + ENDIF + + !=========================================================== + ! ***** M I X E D L A Y E R M I X I N G ***** + !=========================================================== + + ! Updates from Bob Yantosca, 06/2020 + ! Compute the surface flux for the non-local mixing, + ! (which means getting emissions & drydep from HEMCO) + ! and store it in State_Chm%Surface_Flux + ! + ! For CESM-GC, Surface_Flux will be equal to the opposite of the + ! dry deposition flux since emissions are loaded externally + ! ( SurfaceFlux = eflx - dflx = - dflx ) + IF ( Input_Opt%LTURB .and. Input_Opt%LNLPBL ) THEN + CALL Compute_Sflx_For_Vdiff( Input_Opt = Input_Opt, & + State_Chm = State_Chm(LCHNK), & + State_Diag = State_Diag(LCHNK), & + State_Grid = State_Grid(LCHNK), & + State_Met = State_Met(LCHNK), & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Compute_Sflx_for_Vdiff"!' + CALL Error_Stop( errMsg, thisLoc ) + ENDIF + ENDIF + + !----------------------------------------------------------------------- + ! Get emissions from HEMCO + Lightning + Fire + ! Add surface emissions to cam_in + !----------------------------------------------------------------------- + + CALL GC_Emissions_Calc( state = state, & + hco_pbuf2d = hco_pbuf2d, & + State_Met = State_Met(LCHNK), & + cam_in = cam_in, & + eflx = eflx, & + iStep = iStep ) + + !----------------------------------------------------------------------- + ! Add dry deposition flux + ! (stored as SurfaceFlux = -dflx) + !----------------------------------------------------------------------- + + IF ( Input_Opt%LDryD ) THEN + DO ND = 1, State_Chm(BEGCHUNK)%nDryDep + ! Get the species ID from the drydep ID + N = State_Chm(BEGCHUNK)%Map_DryDep(ND) + IF ( N <= 0 ) CYCLE + + M = map2GCinv(N) + IF ( M <= 0 ) CYCLE + + cam_in%cflx(1:nY,M) = cam_in%cflx(1:nY,M) & + + State_Chm(LCHNK)%SurfaceFlux(1,1:nY,N) + ENDDO + ENDIF + + !----------------------------------------------------------------------- + ! Add non-surface emissions + !----------------------------------------------------------------------- + + ! Use units of kg/m2 as State_Chm%Species to add emissions fluxes + CALL Convert_Spc_Units( Input_Opt = Input_Opt, & + State_Chm = State_Chm(LCHNK), & + State_Grid = State_Grid(LCHNK), & + State_Met = State_Met(LCHNK), & + OutUnit = 'kg/m2', & + RC = RC, & + OrigUnit = OrigUnit ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Convert_Spc_Units"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + DO N = 1, pcnst + M = map2GC(N) + IF ( M > 0 ) THEN + ! Add to GEOS-Chem species + State_Chm(LCHNK)%Species(M)%Conc(1,:nY,:nZ) = State_Chm(LCHNK)%Species(M)%Conc(1,:nY,:nZ) & + + eflx(:nY,nZ:1:-1,N) * dT + ELSEIF ( M < 0 ) THEN + ! Add to constituent (mostly for MAM4 aerosols) + ! Convert from kg/m2/s to kg/kg/s + ptend%q(:nY,nZ:1:-1,N) = ptend%q(:nY,nZ:1:-1,N) & + + eflx(:nY,nZ:1:-1,N) & + / ( g0_100 * State_Met(LCHNK)%DELP_DRY(1,:nY,:nZ) ) + ENDIF + ENDDO + + ! Convert back to original unit + CALL Convert_Spc_Units( Input_Opt = Input_Opt, & + State_Chm = State_Chm(LCHNK), & + State_Grid = State_Grid(LCHNK), & + State_Met = State_Met(LCHNK), & + OutUnit = OrigUnit, & + RC = RC ) + + ! Convert State_Chm%Species back to original units + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Convert_Spc_Units"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + !============================================================== + ! ***** C H E M I S T R Y ***** + !============================================================== + + call t_startf( 'chemdr' ) + + ! Get the overhead column O3 for use with FAST-J + IF ( Input_Opt%Its_A_FullChem_Sim .OR. & + Input_Opt%Its_An_Aerosol_Sim ) THEN + + IF ( Input_Opt%LChem ) THEN + CALL Compute_Overhead_O3( Input_Opt = Input_Opt, & + State_Grid = State_Grid(LCHNK), & + State_Chm = State_Chm(LCHNK), & + DAY = currDy, & + USE_O3_FROM_MET = Input_Opt%Use_O3_From_Met, & + TO3 = State_Met(LCHNK)%TO3, & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Compute_Overhead_O3"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + ENDIF + ENDIF + + IF ( Input_Opt%Its_A_Fullchem_Sim .and. iH2O > 0 ) THEN + CALL Set_H2O_Trac( SETSTRAT = .False. , & + Input_Opt = Input_Opt, & + State_Chm = State_Chm(LCHNK), & + State_Grid = State_Grid(LCHNK), & + State_Met = State_Met(LCHNK), & + RC = RC ) + + ! Trap potential errors + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Set_H2O_Trac" #2!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + ENDIF + + ! Here, we apply surface mixing ratios for long-lived species + ! (copied from sfcvmr_mod.F90) + ! Loop over all objects + iSfcMrObj => SfcMrHead + DO WHILE( ASSOCIATED( iSfcMrObj ) ) + + ! Get concentration for this species + tmpIdx = pbuf_get_index(TRIM(iSfcMrObj%FldName), RC) + IF ( tmpIdx < 0 .OR. (iStep == 1) ) THEN + IF ( rootChunk ) Write(iulog,*) "chem_timestep_tend: Field not found ", TRIM(iSfcMrObj%FldName) + ELSE + CALL pbuf_get_field(pbuf, tmpIdx, pbuf_i) + + ! Set mixing ratio in PBL + SpcInfo => State_Chm(LCHNK)%SpcData(iSfcMrObj%SpcID)%Info + N = SpcInfo%ModelID + IF ( N > 0 ) THEN + DO L = 1, nZ + DO J = 1, nY + IF ( State_Met(LCHNK)%F_UNDER_PBLTOP(1,J,L) > 0.0_fp ) THEN + State_Chm(LCHNK)%Species(N)%Conc(1,J,L) = & + ( pbuf_i(J) * 1.0e-9_fp ) & + / ( MWDry / SpcInfo%MW_g ) + ENDIF ! end selection of PBL boxes + ENDDO + ENDDO + ENDIF + ENDIF + + ! Point to next element in list + iSfcMrObj => iSfcMrObj%Next + ENDDO + + ! Reset photolysis rates + ZPJ = 0.0e+0_r8 + + ! Perform chemistry + CALL Do_Chemistry( Input_Opt = Input_Opt, & + State_Chm = State_Chm(LCHNK), & + State_Diag = State_Diag(LCHNK), & + State_Grid = State_Grid(LCHNK), & + State_Met = State_Met(LCHNK), & + RC = RC ) + + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Do_Chemistry"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! GEOS-Chem considers CO2 as a dead species and resets its concentration + ! internally. Right after the call to `Do_Chemistry`, State_Chm%Species(iCO2) + ! corresponds to the chemically-produced CO2. The real CO2 concentration + ! is thus the concentration before chemistry + the chemically-produced CO2. + State_Chm(LCHNK)%Species(iCO2)%Conc(1,:nY,:nZ) = State_Chm(LCHNK)%Species(iCO2)%Conc(1,:nY,:nZ) & + + MMR_Beg(:nY,:nZ,iCO2) + + ! Make sure State_Chm(LCHNK) is back in kg/kg dry! + IF ( TRIM(State_Chm(LCHNK)%Spc_Units) /= 'kg/kg dry' ) THEN + Write(iulog,*) 'Current unit = ', TRIM(State_Chm(LCHNK)%Spc_Units) + Write(iulog,*) 'Expected unit = kg/ kg dry' + CALL ENDRUN('Incorrect unit in GEOS-Chem State_Chm%Species') + ENDIF + + call t_stopf( 'chemdr' ) + + ! Save and write J-values to pbuf for HEMCO + ! in HCO_IN_JNO2, HCO_IN_JOH + FieldName = 'HCO_IN_JNO2' + tmpIdx = pbuf_get_index(FieldName, RC) + IF ( tmpIdx < 0 .or. ( iStep == 1 ) ) THEN + IF ( rootChunk ) Write(iulog,*) "chem_timestep_tend: Field not found ", TRIM(FieldName) + ELSE + pbuf_chnk => pbuf_get_chunk(hco_pbuf2d, LCHNK) + CALL pbuf_get_field(pbuf_chnk, tmpIdx, pbuf_i) + + ! RXN_NO2: NO2 + hv --> NO + O + pbuf_i(:nY) = ZPJ(1,RXN_NO2,1,:nY) + + pbuf_chnk => NULL() + pbuf_i => NULL() + ENDIF + + FieldName = 'HCO_IN_JOH' + tmpIdx = pbuf_get_index(FieldName, RC) + IF ( tmpIdx < 0 .or. ( iStep == 1 ) ) THEN + IF ( rootChunk ) Write(iulog,*) "chem_timestep_tend: Field not found ", TRIM(FieldName) + ELSE + pbuf_chnk => pbuf_get_chunk(hco_pbuf2d, LCHNK) + CALL pbuf_get_field(pbuf_chnk, tmpIdx, pbuf_i) + + ! RXN_O3_1: O3 + hv --> O2 + O + pbuf_i(:nY) = ZPJ(1,RXN_O3_1,1,:nY) + pbuf_chnk => NULL() + pbuf_i => NULL() + ENDIF + + DO N = 1, gas_pcnst + ! See definition of map2chm + M = map2chm(N) + IF ( M > 0 ) THEN + vmr1(:nY,:nZ,N) = State_Chm(LCHNK)%Species(M)%Conc(1,:nY,nZ:1:-1) * & + MWDry / adv_mass(N) + ELSEIF ( M < 0 ) THEN + vmr1(:nY,:nZ,N) = state%q(:nY,:nZ,-M) * & + MWDry / adv_mass(N) + ENDIF + ENDDO + + !============================================================== + ! ***** M A M G A S - A E R O S O L E X C H A N G E ***** + !============================================================== + +#if defined( MODAL_AERO ) + ! Repartition SO4 into H2SO4 and so4_a* + IF ( l_H2SO4 > 0 .AND. l_SO4 > 0 ) THEN + P = l_H2SO4 + ! SO4_gasRatio is mol(SO4) (gaseous) / mol(SO4) (gaseous+aerosol) + vmr1(:nY,:nZ,P) = SO4_gasRatio(:nY,:nZ) * vmr1(:nY,:nZ,l_SO4) + ! binRatio is mol(SO4) (current bin) / mol(SO4) (all bins) + DO M = 1, ntot_amode + N = lptr_so4_a_amode(M) + IF ( N <= 0 ) CYCLE + P = mapCnst(N) + vmr1(:nY,:nZ,P) = vmr1(:nY,:nZ,l_SO4) & + * ( 1.0_r8 - SO4_gasRatio(:nY,:nZ) ) & + * binRatio(iSulf(M),M,:nY,:nZ) + ENDDO + ENDIF + + ! Amount of chemically-produced H2SO4 (mol/mol) + ! This is archived from fullchem_mod.F90 using SO2 + OH rate from KPP (hplin, 1/25/23) + del_h2so4_gasprod(:nY,:nZ) = State_Chm(LCHNK)%H2SO4_PRDR(1,:nY,nZ:1:-1) + + call aero_model_gasaerexch( loffset = iFirstCnst - 1, & + ncol = NCOL, & + lchnk = LCHNK, & + troplev = Trop_Lev(:), & + delt = dT, & + reaction_rates = reaction_rates, & + tfld = state%t(:,:), & + pmid = state%pmid(:,:), & + pdel = state%pdel(:,:), & + mbar = mBar, & + relhum = relHum(:,:), & + zm = state%zm(:,:), & + qh2o = qH2O(:,:), & + cwat = cldW, & + cldfr = cldFrc, & + cldnum = nCldWtr, & + airdens = invariants(:,:,indexm), & + invariants = invariants, & + del_h2so4_gasprod = del_h2so4_gasprod, & + vmr0 = vmr0, & + vmr = vmr1, & + pbuf = pbuf ) + + ! Repartition MAM SOAs following mapping: + ! TSOA0 + ASOAN + SOAIE + SOAGX -> soa1_a* + soa2_a* + ! TSOA1 + ASOA1 -> soa3_a* + ! TSOA2 + ASOA2 -> soa4_a* + ! TSOA3 + ASOA3 -> soa5_a* + ! TSOG0 -> SOAG0 + SOAG1 + ! TSOG1 + ASOG1 -> SOAG2 + ! TSOG2 + ASOG2 -> SOAG3 + ! TSOG3 + ASOG3 -> SOAG4 + + ! Deal with aerosol SOA species + ! First deal with lowest two volatility bins + ! Only map TOSA0 (K1) and ASOAN (K2) to soa1_ and soa2_, according to Fritz et al. + ! SOAIE (K3) and SOAGX (K4) were mapped in the code but are inconsistent with the model description paper. + speciesName_1 = 'TSOA0' + speciesName_2 = 'ASOAN' + speciesName_3 = 'SOAIE' + speciesName_4 = 'SOAGX' + K1 = get_spc_ndx(TRIM(speciesName_1), ignore_case=.true.) + K2 = get_spc_ndx(TRIM(speciesName_2), ignore_case=.true.) + K3 = get_spc_ndx(TRIM(speciesName_3), ignore_case=.true.) + K4 = get_spc_ndx(TRIM(speciesName_4), ignore_case=.true.) + bulkMass(:nY,:nZ) = 0.0e+00_r8 + DO iBin = 1, 2 + DO M = 1, ntot_amode + N = lptr2_soa_a_amode(M,iBin) + IF ( N <= 0 ) CYCLE + bulkMass(:nY,:nZ) = bulkMass(:nY,:nZ) + state%q(:nY,:nZ,N) + ENDDO + ENDDO + DO iBin = 1, 2 + DO M = 1, ntot_amode + N = lptr2_soa_a_amode(M,iBin) + IF ( N <= 0 ) CYCLE + P = mapCnst(N) + IF ( P > 0 .AND. K1 > 0 .AND. K2 > 0 ) THEN + vmr1(:nY,:nZ,P) = state%q(:nY,:nZ,N) / bulkMass(:nY,:nZ) & + * (vmr1(:nY,:nZ,K1) + vmr1(:nY,:nZ,K2)) + ENDIF + ENDDO + ENDDO + + ! Now deal with other volatility bins + DO iBin = 3, nsoa + IF ( iBin == 3 ) THEN + speciesName_1 = 'TSOA1' + speciesName_2 = 'ASOA1' + ELSEIF ( iBin == 4 ) THEN + speciesName_1 = 'TSOA2' + speciesName_2 = 'ASOA2' + ELSEIF ( iBin == 5 ) THEN + speciesName_1 = 'TSOA3' + speciesName_2 = 'ASOA3' + ENDIF + K1 = get_spc_ndx(TRIM(speciesName_1), ignore_case=.true. ) + K2 = get_spc_ndx(TRIM(speciesName_2), ignore_case=.true. ) + bulkMass(:nY,:nZ) = 0.0e+00_r8 + DO M = 1, ntot_amode + N = lptr2_soa_a_amode(M,iBin) + IF ( N <= 0 ) CYCLE + bulkMass(:nY,:nZ) = bulkMass(:nY,:nZ) + state%q(:nY,:nZ,N) + ENDDO + DO M = 1, ntot_amode + N = lptr2_soa_a_amode(M,iBin) + IF ( N <= 0 ) CYCLE + P = mapCnst(N) + IF ( P > 0 .AND. K1 > 0 .AND. K2 > 0 ) THEN + vmr1(:nY,:nZ,P) = state%q(:nY,:nZ,N) / bulkMass(:nY,:nZ) & + * (vmr1(:nY,:nZ,K1) + vmr1(:nY,:nZ,K2)) + ENDIF + ENDDO + ENDDO + + ! Now deal with gaseous SOA species + ! Deal with lowest two volatility bins - TSOG0 corresponds to SOAG0 and SOAG1 + speciesName_1 = 'TSOG0' + K1 = get_spc_ndx(TRIM(speciesName_1), ignore_case=.true.) + N = lptr2_soa_g_amode(1) + P = mapCnst(N) + ! current mode other modes (this mapping was verified to be correct.) + vmr1(:nY,:nZ,P) = vmr0(:nY,:nZ,P) / (vmr0(:nY,:nZ,P) + vmr0(:nY,:nZ,mapCnst(lptr2_soa_g_amode(2)))) & + * vmr1(:nY,:nZ,K1) + N = lptr2_soa_g_amode(2) + P = mapCnst(N) + vmr1(:nY,:nZ,P) = vmr0(:nY,:nZ,P) / (vmr0(:nY,:nZ,P) + vmr0(:nY,:nZ,mapCnst(lptr2_soa_g_amode(1)))) & + * vmr1(:nY,:nZ,K1) + + ! Deal with other volatility bins + DO iBin = 3, nsoa + N = lptr2_soa_g_amode(iBin) + P = mapCnst(N) + IF ( iBin == 3 ) THEN + speciesName_1 = 'TSOG1' + speciesName_2 = 'ASOG1' + ELSEIF ( iBin == 4 ) THEN + speciesName_1 = 'TSOG2' + speciesName_2 = 'ASOG2' + ELSEIF ( iBin == 5 ) THEN + speciesName_1 = 'TSOG3' + speciesName_2 = 'ASOG3' + ENDIF + K1 = get_spc_ndx(TRIM(speciesName_1), ignore_case=.true.) + K2 = get_spc_ndx(TRIM(speciesName_2), ignore_case=.true.) + IF ( P > 0 .AND. K1 > 0 .AND. K2 > 0 ) vmr1(:nY,:nZ,P) = vmr1(:nY,:nZ,K1) + vmr1(:nY,:nZ,K2) + ENDDO + +#endif + + !============================================================== + ! ***** W E T D E P O S I T I O N (rainout + washout) ***** + !============================================================== + IF ( Input_Opt%LWetD ) THEN + + IF ( gas_wetdep_method == 'NEU' ) THEN + CALL Neu_wetdep_tend( LCHNK = LCHNK, & + NCOL = NCOL, & + mmr = state%q, & + pmid = state%pmid, & + pdel = state%pdel, & + zint = state%zi, & + tfld = state%t, & + delt = dT, & + prain = PRain, & + nevapr = NEvapr, & + cld = cldFrc, & + cmfdqr = cmfdqr, & + wd_tend = ptend%q, & + wd_tend_int = wetdepflx ) + ELSE + ErrMsg = 'Unknown gas_wetdep_method '//TRIM(gas_wetdep_method) + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ENDIF + + !============================================================== + ! ***** B O U N D A R Y C O N D I T I O N S ***** + !============================================================== + ! Set boundary conditions of long-lived species (most likely + ! CH4, OCS, N2O, CFC11, CFC12). + ! Note: This will overwrite the UCX boundary conditions + + CALL flbc_set( vmr1(:nY,:nZ,:), nY, LCHNK, mapCnst ) + + IF ( ghg_chem ) THEN + CALL ghg_chem_set_flbc( vmr1, nY ) + ENDIF + + DO N = 1, gas_pcnst + ! See definition of map2chm + M = map2chm(N) + IF ( M <= 0 ) CYCLE + State_Chm(LCHNK)%Species(M)%Conc(1,:nY,nZ:1:-1) = vmr1(:nY,:nZ,N) * & + adv_mass(N) / MWDry + ENDDO + + ! Make sure State_Chm(LCHNK) is back in kg/kg dry! + IF ( TRIM(State_Chm(LCHNK)%Spc_Units) /= 'kg/kg dry' ) THEN + Write(iulog,*) 'Current unit = ', TRIM(State_Chm(LCHNK)%Spc_Units) + Write(iulog,*) 'Expected unit = kg/ kg dry' + CALL ENDRUN('Incorrect unit in GEOS-Chem State_Chm%Species') + ENDIF + + ! Reset H2O MMR to the initial value (no chemistry tendency in H2O just yet) + State_Chm(LCHNK)%Species(iH2O)%Conc(1,:,:) = MMR_Beg(:,:,iH2O) + + ! Store unadvected species data + SlsData = 0.0e+0_r8 + DO N = 1, nSls + M = map2GC_Sls(N) + IF ( M <= 0 ) CYCLE + SlsData(:nY,nZ:1:-1,N) = REAL(State_Chm(LCHNK)%Species(M)%Conc(1,:nY,:nZ),r8) + ENDDO + CALL set_short_lived_species_gc( SlsData, LCHNK, nY, pbuf ) + + ! Apply tendencies to GEOS-Chem species + DO N = 1, pcnst + M = map2GC(N) + IF ( M <= 0 ) CYCLE + ! Add change in mass mixing ratio to tendencies. + ! For NEU wet deposition, the wet removal rates are added to + ! ptend. + MMR_End(:nY,:nZ,M) = REAL(State_Chm(LCHNK)%Species(M)%Conc(1,:nY,:nZ),r8) + ptend%q(:nY,nZ:1:-1,N) = ptend%q(:nY,nZ:1:-1,N) & + + (MMR_End(:nY,:nZ,M)-MMR_Beg(:nY,:nZ,M))/dT + ENDDO + +#if defined( MODAL_AERO ) + ! Here apply tendencies to MAM aerosols + ! Initial mass in bin SM is stored as state%q(N) + ! Final mass in bin SM is stored as binRatio(SM,M) * State_Chm(P) + ! + ! We decide to apply chemical tendencies to all MAM aerosols, + ! except so4, for which the chemically-produced sulfate gets + ! partitioned in aero_model_gasaerexch. + DO M = 1, ntot_amode + DO SM = 1, nspec_amode(M) + N = lmassptr_amode(SM,M) + P = mapCnst(N) + IF ( P <= 0 ) CYCLE + ! Apply tendency from MAM gasaerexch + ptend%q(:nY,:nZ,N) = ptend%q(:nY,:nZ,N) & + + (vmr1(:nY,:nZ,P) - vmr0(:nY,:nZ,P))/dT & + * adv_mass(P) / MWDry + P = map2MAM4(SM,M) + IF ( P <= 0 ) CYCLE + K = map2GC(P) + IF ( K <= 0 .or. K == iSO4 ) CYCLE + ! Apply MAM4 chemical tendencies owing to GEOS-Chem aerosol processing + ptend%q(:nY,:nZ,N) = ptend%q(:nY,:nZ,N) & + + (binRatio(SM,M,:nY,:nZ) * & + REAL(State_Chm(LCHNK)%Species(K)%Conc(1,:nY,nZ:1:-1),r8) & + * adv_mass(mapCnst(N)) / adv_mass(mapCnst(P)) & + - state%q(:nY,:nZ,N))/dT + ENDDO + N = numptr_amode(M) + P = mapCnst(N) + IF ( P <= 0 ) CYCLE + ptend%q(:nY,:nZ,N) = ptend%q(:nY,:nZ,N) & + + (vmr1(:nY,:nZ,P) - vmr0(:nY,:nZ,P))/dT & + * adv_mass(P) / MWDry + ENDDO + N = cH2SO4 + P = l_H2SO4 + IF ( P > 0 ) THEN + ptend%q(:nY,:nZ,N) = ptend%q(:nY,:nZ,N) & + + (vmr1(:nY,:nZ,P) - vmr0(:nY,:nZ,P))/dT & + * adv_mass(P) / MWDry + ENDIF + DO iBin = 1, nsoa + N = lptr2_soa_g_amode(iBin) + P = mapCnst(N) + IF ( P > 0 ) THEN + ptend%q(:nY,:nZ,N) = ptend%q(:nY,:nZ,N) & + + (vmr1(:nY,:nZ,P) - vmr0(:nY,:nZ,P))/dT & + * adv_mass(P) / MWDry + ENDIF + ENDDO +#endif + + DO N = 1, gas_pcnst + ! See definition of map2chm + M = map2chm(N) + IF ( M > 0 ) THEN + mmr_tend(:nY,:nZ,N) = ( REAL(State_Chm(LCHNK)%Species(M)%Conc(1,:nY,nZ:1:-1),r8) - mmr_tend(:nY,:nZ,N) ) / dT + ELSEIF ( M < 0 ) THEN + mmr_tend(:nY,:nZ,N) = ptend%q(:nY,:nZ,-M) + ENDIF + ENDDO + + IF ( Input_Opt%applyQtend ) THEN + ! Apply GEOS-Chem's H2O mixing ratio tendency to CAM's specific humidity + ! This requires to set lq(cQ) = lq(cH2O) ( = .True. ) + ptend%q(:,:,cQ) = ptend%q(:,:,cH2O) + ENDIF + + CALL GC_Diagnostics_Calc( Input_Opt = Input_Opt, & + State_Chm = State_Chm(LCHNK), & + State_Diag = State_Diag(LCHNK), & + State_Grid = State_Grid(LCHNK), & + State_Met = State_Met(LCHNK), & + cam_in = cam_in, & + state = state, & + mmr_tend = mmr_tend, & + LCHNK = LCHNK ) + + CALL Set_Diagnostics_EndofTimestep( Input_Opt = Input_Opt, & + State_Chm = State_Chm(LCHNK), & + State_Diag = State_Diag(LCHNK), & + State_Grid = State_Grid(LCHNK), & + State_Met = State_Met(LCHNK), & + RC = RC ) + + + IF ( State_Diag(LCHNK)%Archive_AerMass ) THEN + CALL Set_AerMass_Diagnostic( Input_Opt = Input_Opt, & + State_Chm = State_Chm(LCHNK), & + State_Diag = State_Diag(LCHNK), & + State_Grid = State_Grid(LCHNK), & + State_Met = State_Met(LCHNK), & + RC = RC ) + ENDIF + + ! Compute new GEOS-Chem diagnostics into CESM History (hplin, 10/31/22) + ! Note that the containers (data pointers) actually need to be updated every time step, + ! because the State_Chm(LCHNK) target changes. There is some registry lookup overhead + ! but mitigated by a check to the history field activeness. (hplin, 11/1/22) + CALL HistoryExports_SetDataPointers(rootChunk, & + HistoryConfig, State_Chm(LCHNK), & + State_Grid(LCHNK), & + State_Diag(LCHNK), State_Met(LCHNK), & + RC) + + CALL CopyGCStates2Exports( am_I_Root = rootChunk, & + Input_Opt = Input_Opt, & + State_Grid = State_Grid(LCHNK), & + HistoryConfig = HistoryConfig, & + LCHNK = LCHNK, & + RC = RC ) + + IF ( ghg_chem ) THEN + ptend%lq(1) = .True. + CALL outfld( 'CT_H2O_GHG', ptend%q(:,:,1), PCOLS, LCHNK ) + ENDIF + + !! Debug statements + !! Ozone tendencies + !IF ( rootChunk ) THEN + ! Write(iulog,*) " MMR_Beg = ", MMR_Beg(1,:,iO3) + ! Write(iulog,*) " MMR_End = ", MMR_End(1,:,iO3) + !ENDIF + + IF (PRESENT(fh2o)) THEN + fh2o(:nY) = 0.0e+0_r8 + !DO L = 1, nZ + ! fh2o(:nY) = fh2o(:nY) + ptend%q(:nY,L,iH2O)*state%pdel(:nY,L)/Gravit + !ENDDO + ENDIF + + ! Nullify all pointers + Nullify(PblH ) + Nullify(Fsds ) + Nullify(PRain ) + Nullify(LsFlxSnw) + Nullify(LsFlxPrc) + Nullify(cldTop ) + Nullify(cldFrc ) + Nullify(NEvapr ) + Nullify(cmfdqr ) + + IF ( rootChunk ) WRITE(iulog,*) 'GEOS-Chem Chemistry step ', iStep, ' completed' + IF ( lastChunk ) WRITE(iulog,*) 'Chemistry completed on all chunks of root CPU' + IF ( FIRST ) THEN + FIRST = .false. + ENDIF + + end subroutine chem_timestep_tend + + !================================================================================================ + ! subroutine chem_init_cnst + !================================================================================================ + subroutine chem_init_cnst(name, latvals, lonvals, mask, q) + + CHARACTER(LEN=*), INTENT(IN) :: name ! constituent name + REAL(r8), INTENT(IN) :: latvals(:) ! lat in degrees (NCOL) + REAL(r8), INTENT(IN) :: lonvals(:) ! lon in degrees (NCOL) + LOGICAL, INTENT(IN) :: mask(:) ! Only initialize where .true. + REAL(r8), INTENT(OUT) :: q(:,:) ! kg tracer/kg dry air (NCOL, PVER) + ! Used to initialize tracer fields if desired. + ! Will need a simple mapping structure as well as the CAM tracer registration + ! routines. + + INTEGER :: ilev, nlev, M + REAL(r8) :: QTemp, Min_MMR + + nlev = SIZE(q, 2) + + ! Retrieve a "background value" for this from the database + Min_MMR = 1.0e-38_r8 + CALL cnst_get_ind(TRIM(name), M, abort=.False.) + IF ( M > 0 ) Min_MMR = ref_MMR(M) + + DO ilev = 1, nlev + WHERE(mask) + ! Set to the minimum mixing ratio + q(:,ilev) = Min_MMR + END WHERE + ENDDO + + end subroutine chem_init_cnst + + !================================================================================================ + ! subroutine chem_final + !================================================================================================ + subroutine chem_final + + ! CAM modules + use short_lived_species, only : short_lived_species_final + + ! GEOS-Chem interface modules in CAM + use geoschem_emissions_mod, only : GC_Emissions_Final + use geoschem_history_mod, only : Destroy_HistoryConfig + + ! GEOS-Chem modules + use Aerosol_Mod, only : Cleanup_Aerosol + use Carbon_Mod, only : Cleanup_Carbon + use CMN_FJX_Mod, only : Cleanup_CMN_FJX + use Drydep_Mod, only : Cleanup_Drydep + use Dust_Mod, only : Cleanup_Dust + use Error_Mod, only : Cleanup_Error + use Fullchem_Mod, only : Cleanup_FullChem + use Input_Opt_Mod, only : Cleanup_Input_Opt + use Linear_Chem_Mod, only : Cleanup_Linear_Chem + use Pressure_Mod, only : Cleanup_Pressure + use Seasalt_Mod, only : Cleanup_Seasalt + use State_Chm_Mod, only : Cleanup_State_Chm + use State_Diag_Mod, only : Cleanup_State_Diag + use State_Grid_Mod, only : Cleanup_State_Grid + use State_Met_Mod, only : Cleanup_State_Met + use Sulfate_Mod, only : Cleanup_Sulfate + + ! Local variables + INTEGER :: I, RC + + ! Destroy the history interface between GC States and CAM exports + CALL Destroy_HistoryConfig(masterproc, HistoryConfig, RC) + + ! Finalize GEOS-Chem + + CALL Cleanup_Aerosol + CALL Cleanup_Carbon + CALL Cleanup_Drydep + CALL Cleanup_Dust + CALL Cleanup_FullChem( RC ) + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Cleanup_FullChem"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + CALL Cleanup_Pressure + CALL Cleanup_Seasalt + CALL Cleanup_Sulfate + CALL Cleanup_Linear_Chem + + CALL GC_Emissions_Final + + CALL short_lived_species_final() + + CALL Cleanup_CMN_FJX( RC ) + IF ( RC /= GC_SUCCESS ) THEN + ErrMsg = 'Error encountered in "Cleanup_CMN_FJX"!' + CALL Error_Stop( ErrMsg, ThisLoc ) + ENDIF + + ! Cleanup Input_Opt + CALL Cleanup_Input_Opt( Input_Opt, RC ) + + ! Loop over each chunk and cleanup the variables + DO I = BEGCHUNK, ENDCHUNK + CALL Cleanup_State_Chm ( State_Chm(I), RC ) + CALL Cleanup_State_Diag( State_Diag(I), RC ) + CALL Cleanup_State_Grid( State_Grid(I), RC ) + CALL Cleanup_State_Met ( State_Met(I), RC ) + ENDDO + CALL Cleanup_Error + + ! Finally deallocate state variables + IF ( ALLOCATED( State_Chm ) ) DEALLOCATE( State_Chm ) + IF ( ALLOCATED( State_Diag ) ) DEALLOCATE( State_Diag ) + IF ( ALLOCATED( State_Grid ) ) DEALLOCATE( State_Grid ) + IF ( ALLOCATED( State_Met ) ) DEALLOCATE( State_Met ) + + IF ( ALLOCATED( slvd_Lst ) ) DEALLOCATE( slvd_Lst ) + + RETURN + + end subroutine chem_final + + !================================================================================================ + ! subroutine chem_init_restart + !================================================================================================ + subroutine chem_init_restart(File) + + ! CAM modules + use pio, only : file_desc_t + use tracer_cnst, only : init_tracer_cnst_restart + use tracer_srcs, only : init_tracer_srcs_restart + + IMPLICIT NONE + + TYPE(file_desc_t) :: File + + WRITE(iulog,'(a)') 'chem_init_restart: init restarts for tracer sources and offline fields' + + ! + ! data for offline tracers + ! + call init_tracer_cnst_restart(File) + call init_tracer_srcs_restart(File) + !call init_linoz_data_restart(File) + + end subroutine chem_init_restart + + !================================================================================================ + ! subroutine chem_write_restart + !================================================================================================ + subroutine chem_write_restart( File ) + + ! CAM modules + use pio, only : file_desc_t + use tracer_cnst, only : write_tracer_cnst_restart + use tracer_srcs, only : write_tracer_srcs_restart + + IMPLICIT NONE + + TYPE(file_desc_t) :: File + + WRITE(iulog,'(a)') 'chem_write_restart: writing restarts for tracer sources and offline fields' + + ! data for offline tracers + call write_tracer_cnst_restart(File) + call write_tracer_srcs_restart(File) + + end subroutine chem_write_restart + + !================================================================================================ + ! subroutine chem_read_restart + !================================================================================================ + subroutine chem_read_restart( File ) + + ! CAM modules + use pio, only : file_desc_t + use tracer_cnst, only : read_tracer_cnst_restart + use tracer_srcs, only : read_tracer_srcs_restart + + IMPLICIT NONE + + TYPE(file_desc_t) :: File + + WRITE(iulog,'(a)') 'GCCALL CHEM_READ_RESTART' + + ! data for offline tracers + call read_tracer_cnst_restart(File) + call read_tracer_srcs_restart(File) + + end subroutine chem_read_restart + + !================================================================================================ + ! subroutine chem_emissions + !================================================================================================ + subroutine chem_emissions( state, cam_in, pbuf ) + + ! CAM modules + use camsrfexch, only : cam_in_t + use physics_buffer, only : physics_buffer_desc + + TYPE(physics_state), INTENT(IN) :: state ! Physics state variables + TYPE(cam_in_t), INTENT(INOUT) :: cam_in ! import state + TYPE(physics_buffer_desc), pointer :: pbuf(:) ! Physics buffer in chunk, for HEMCO + + INTEGER :: M, N + INTEGER :: nY + LOGICAL :: rootChunk + + nY = state%NCOL ! number of atmospheric columns on this chunk + rootChunk = ( MasterProc .and. (state%LCHNK .eq. BEGCHUNK) ) + + ! Reset surface fluxes + DO M = iFirstCnst, pcnst + !N = map2chm(M) + !IF ( N > 0 ) cam_in%cflx(1:nY,N) = 0.0e+0_r8 + cam_in%cflx(1:nY,M) = 0.0e+0_r8 + ENDDO + + end subroutine chem_emissions + +end module chemistry diff --git a/src/chemistry/geoschem/geoschem_diagnostics_mod.F90 b/src/chemistry/geoschem/geoschem_diagnostics_mod.F90 new file mode 100644 index 0000000000..447d2c29cd --- /dev/null +++ b/src/chemistry/geoschem/geoschem_diagnostics_mod.F90 @@ -0,0 +1,1374 @@ +MODULE GeosChem_Diagnostics_Mod + + ! CAM modules + use cam_history, only : fieldname_len + use cam_logfile, only : iulog + use chem_mods, only : gas_pcnst, map2chm, iFirstCnst + use constituents, only : pcnst + use mo_tracname, only : solsym + use ppgrid, only : begchunk, pver + use shr_const_mod, only : pi => shr_const_pi + use shr_kind_mod, only : r8 => shr_kind_r8, shr_kind_cl + use spmd_utils, only : MasterProc + use string_utils, only : to_upper + + ! GEOS-Chem modules + use ErrCode_Mod, only : GC_SUCCESS + + IMPLICIT NONE + + PRIVATE + + PUBLIC :: GC_Diagnostics_Init + PUBLIC :: GC_Diagnostics_Calc + PUBLIC :: wetdep_name, wtrate_name, dtchem_name + + CHARACTER(LEN=fieldname_len) :: srcnam(gas_pcnst) ! Names of source/sink tendencies + CHARACTER(LEN=fieldname_len) :: wetdep_name(gas_pcnst) ! Wet deposition tendencies + CHARACTER(LEN=fieldname_len) :: wtrate_name(gas_pcnst) ! Column tendencies for wet dep + CHARACTER(LEN=fieldname_len) :: dtchem_name(gas_pcnst) ! Chemical tendencies + + INTEGER :: aer_species(gas_pcnst) + + ! Chemical families + INTEGER :: NOx_species(3) + INTEGER :: NOy_species(62) + INTEGER :: HOx_species(4) + INTEGER :: ClOx_species(6) + INTEGER :: ClOy_species(11) + INTEGER :: tCly_species(30) + INTEGER :: BrOx_species(4) + INTEGER :: BrOy_species(9) + INTEGER :: tBry_species(18) + INTEGER :: SOx_species(2) + INTEGER :: NHx_species(2) + INTEGER :: TOTH_species(3) + REAL(r8) :: NOx_MWs(3) + REAL(r8) :: NOy_MWs(62) + REAL(r8) :: HOx_MWs(4) + REAL(r8) :: ClOx_MWs(6) + REAL(r8) :: ClOy_MWs(11) + REAL(r8) :: tCly_MWs(30) + REAL(r8) :: BrOx_MWs(4) + REAL(r8) :: BrOy_MWs(9) + REAL(r8) :: tBry_MWs(18) + REAL(r8) :: SOx_MWs(2) + REAL(r8) :: NHx_MWs(2) + REAL(r8) :: TOTH_MWs(3) + + REAL(r8), PARAMETER :: MW_NIT = 62.01 + REAL(r8), PARAMETER :: MW_HNO3 = 63.01 + REAL(r8), PARAMETER :: MW_HCl = 36.45 + REAL(r8), PARAMETER :: MW_H2O = 18.02 + + ! NOx species + INTEGER :: i_NO, i_NO2, i_N + ! NOy \ NOx species + INTEGER :: i_BrNO2, i_BrNO3, i_ClNO2, i_ClNO3, i_ETHLN, i_ETNO3, & + i_HNO2, i_HNO3, i_HNO4, i_ICN, i_ICNOO, i_IDHNBOO, & + i_IDHNDOO1, i_IDN, i_IDNOO, i_IHN1, i_IHN2, & + i_IHN3, i_IHN4, i_IHPNBOO, i_IHPNDOO, i_INA, i_INO, & + i_INO2B, i_INO2D, i_INPB, i_INPD, i_IONO, i_IONO2, & + i_IPRNO3, i_ISOPNOO1, i_ISOPNOO2, i_ITCN, i_ITHN, & + i_MACRNO2, i_MCRHN, i_MCRHNB, i_MENO3, i_MONITS, i_MONITU, & + i_MPAN, i_MPN, i_MVKN, i_N2O5, i_NO3, i_NPRNO3, i_OLND, & + i_OLNN, i_PAN, i_PPN, i_PRN1, i_PROPNN, i_PRPN, i_R4N1, & + i_R4N2, i_HONIT, i_IONITA, i_NIT, i_NITs + ! HOx + INTEGER :: i_H, i_OH, i_HO2, i_H2O2 + ! ClOx + INTEGER :: i_Cl, i_ClO, i_HOCl, i_Cl2, i_Cl2O2, i_OClO + ! tCly \ ClOx + INTEGER :: i_ClOO, i_HCl, i_BrCl, i_ICl, i_H1211, & + i_CFC115, i_CH3Cl, i_HCFC142b, i_HCFC22, i_CH2ICl, & + i_CFC114, i_CFC12, i_HCFC141b, i_HCFC123, i_CH2Cl2, & + i_CFC11, i_CH3CCl3, i_CHCl3, i_CCl4, i_CFC113, i_SALACL, & + i_SALCCL !ClNO2, ClNO3 already defined in NOy_species + ! BrOx + INTEGER :: i_Br, i_BrO, i_HOBr !BrCl already defined in tCly_species + ! Bry \ BrOx + INTEGER :: i_HBr, i_IBr, i_Br2, i_CH3Br, & + i_H1301, i_H2402, i_CH2Br2, i_CHBr3, i_BrSALA, i_BrSALC, & + i_CH2IBr + !BrNO2, BrNO3 already defined in NOy_speies + !H1211 already defined in tCly_species + ! SOx + INTEGER :: i_SO2, i_SO4 + ! NHx + INTEGER :: i_NH3, i_NH4 + ! TOTH + INTEGER :: i_CH4, i_H2O, i_H2 + + + ! Index in solsym + integer :: id_no,id_no3 + integer :: id_cfc11,id_cfc12 + integer :: id_ch4,id_h2o + integer :: id_o,id_o2,id_h,id_n2o + integer :: id_co2,id_o3,id_oh,id_ho2,id_so4_a1,id_so4_a2,id_so4_a3 + integer :: id_num_a2,id_num_a3,id_dst_a3,id_ncl_a3 + +! !REVISION HISTORY: +! 28 Oct 2020 - T. M. Fritz - Initial version + +CONTAINS + + SUBROUTINE GC_Diagnostics_Init( Input_Opt, State_Chm, State_Met ) + + ! CAM modules + use cam_history, only : addfld, add_default, horiz_only + use constituents, only : cnst_name, sflxnam, cnst_get_ind + use mo_chem_utls, only : get_spc_ndx + use phys_control, only : phys_getopts + + ! GEOS-Chem modules + use Input_Opt_Mod, only : OptInput + use State_Chm_Mod, only : ChmState + use State_Met_Mod, only : MetState + use State_Diag_Mod, only : get_TagInfo + use Species_Mod, only : Species + use Registry_Mod, only : MetaRegItem, RegItem + use State_Chm_Mod, only : Ind_ + use DryDep_Mod, only : depName + + TYPE(OptInput), INTENT(IN) :: Input_Opt ! Input options + TYPE(ChmState), INTENT(IN) :: State_Chm ! Chemistry State object + TYPE(MetState), INTENT(IN) :: State_Met ! Meteorology State object + + INTEGER :: M, N, K, SM + INTEGER :: idx + INTEGER :: RC + INTEGER :: bulkaero_species(20) + INTEGER :: id_so4, id_nh4no3 + INTEGER :: id_dst01, id_dst02, id_dst03, id_dst04 + INTEGER :: id_sslt01, id_sslt02, id_sslt03, id_sslt04 + INTEGER :: id_soa, id_oc1, id_oc2, id_cb1, id_cb2 + INTEGER :: id_soam,id_soai,id_soat,id_soab,id_soax + INTEGER :: id_bry, id_cly + INTEGER :: history_budget_histfile_num ! output history file number + ! for budget fields + + LOGICAL :: Found + LOGICAL :: history_aerosol ! Output the MAM aerosol + ! tendencies + LOGICAL :: history_chemistry + LOGICAL :: history_cesm_forcing + LOGICAL :: history_scwaccm_forcing + LOGICAL :: history_chemspecies_srf ! Output the chemistry + ! constituents species + ! in the surface layer + LOGICAL :: history_dust + LOGICAL :: history_budget ! output tendencies and state + ! variables for CAM + ! temperature, water vapor, + ! cloud ice and cloud + ! liquid budgets. + + CHARACTER(LEN=shr_kind_cl) :: SpcName + CHARACTER(LEN=shr_kind_cl) :: tagName + CHARACTER(LEN=shr_kind_cl) :: ThisLoc + CHARACTER(LEN=shr_kind_cl) :: ErrMsg + CHARACTER(LEN=2) :: unit_basename ! Units 'kg' or '1' + + ! Objects + TYPE(Species), POINTER :: SpcInfo + TYPE(MetaRegItem), POINTER :: Current + TYPE(RegItem ), POINTER :: Item + + !================================================================= + ! GC_Diagnostics_Init begins here! + !================================================================= + + ! Initialize pointers + SpcInfo => NULL() + Current => NULL() + Item => NULL() + + ! Assume a successful return until otherwise + RC = GC_SUCCESS + + CALL phys_getopts( history_aerosol_out = history_aerosol, & + history_chemistry_out = history_chemistry, & + history_chemspecies_srf_out = history_chemspecies_srf, & + history_budget_out = history_budget , & + history_budget_histfile_num_out = history_budget_histfile_num, & + history_cesm_forcing_out = history_cesm_forcing, & + history_scwaccm_forcing_out = history_scwaccm_forcing, & + history_dust_out = history_dust ) + + id_no3 = get_spc_ndx( 'NO3', ignore_case=.true. ) + id_o3 = get_spc_ndx( 'O3', ignore_case=.true. ) + id_oh = get_spc_ndx( 'OH', ignore_case=.true. ) + id_ho2 = get_spc_ndx( 'HO2', ignore_case=.true. ) + id_so4_a1 = get_spc_ndx( 'so4_a1', ignore_case=.true. ) + id_so4_a2 = get_spc_ndx( 'so4_a2', ignore_case=.true. ) + id_so4_a3 = get_spc_ndx( 'so4_a3', ignore_case=.true. ) + id_num_a2 = get_spc_ndx( 'num_a2', ignore_case=.true. ) + id_num_a3 = get_spc_ndx( 'num_a3', ignore_case=.true. ) + id_dst_a3 = get_spc_ndx( 'dst_a3', ignore_case=.true. ) + id_ncl_a3 = get_spc_ndx( 'ncl_a3', ignore_case=.true. ) + id_co2 = get_spc_ndx( 'CO2', ignore_case=.true. ) + id_no = get_spc_ndx( 'NO', ignore_case=.true. ) + id_h = get_spc_ndx( 'H', ignore_case=.true. ) + id_o = get_spc_ndx( 'O', ignore_case=.true. ) + id_o2 = get_spc_ndx( 'O2', ignore_case=.true. ) + id_ch4 = get_spc_ndx( 'CH4', ignore_case=.true. ) + id_h2o = get_spc_ndx( 'H2O', ignore_case=.true. ) + id_n2o = get_spc_ndx( 'N2O', ignore_case=.true. ) + id_cfc11 = get_spc_ndx( 'CFC11', ignore_case=.true. ) + id_cfc12 = get_spc_ndx( 'CFC12', ignore_case=.true. ) + + id_bry = get_spc_ndx( 'BRY', ignore_case=.true. ) + id_cly = get_spc_ndx( 'CLY', ignore_case=.true. ) + + id_dst01 = get_spc_ndx( 'DST01', ignore_case=.true. ) + id_dst02 = get_spc_ndx( 'DST02', ignore_case=.true. ) + id_dst03 = get_spc_ndx( 'DST03', ignore_case=.true. ) + id_dst04 = get_spc_ndx( 'DST04', ignore_case=.true. ) + id_sslt01 = get_spc_ndx( 'SSLT01', ignore_case=.true. ) + id_sslt02 = get_spc_ndx( 'SSLT02', ignore_case=.true. ) + id_sslt03 = get_spc_ndx( 'SSLT03', ignore_case=.true. ) + id_sslt04 = get_spc_ndx( 'SSLT04', ignore_case=.true. ) + id_soa = get_spc_ndx( 'SOA', ignore_case=.true. ) + !id_so4 = get_spc_ndx( 'SO4', ignore_case=.true. )i + id_so4 = -1 ! Don't pick up GEOS-Chem's SO4! + id_oc1 = get_spc_ndx( 'OC1', ignore_case=.true. ) + id_oc2 = get_spc_ndx( 'OC2', ignore_case=.true. ) + id_cb1 = get_spc_ndx( 'CB1', ignore_case=.true. ) + id_cb2 = get_spc_ndx( 'CB2', ignore_case=.true. ) + id_nh4no3 = get_spc_ndx( 'NH4NO3', ignore_case=.true. ) + id_soam = get_spc_ndx( 'SOAM', ignore_case=.true. ) + id_soai = get_spc_ndx( 'SOAI', ignore_case=.true. ) + id_soat = get_spc_ndx( 'SOAT', ignore_case=.true. ) + id_soab = get_spc_ndx( 'SOAB', ignore_case=.true. ) + id_soax = get_spc_ndx( 'SOAX', ignore_case=.true. ) + + bulkaero_species(:) = -1 + bulkaero_species(1:20) = (/ id_dst01, id_dst02, id_dst03, id_dst04, & + id_sslt01, id_sslt02, id_sslt03, id_sslt04, & + id_soa, id_so4, id_oc1, id_oc2, id_cb1, id_cb2, id_nh4no3, & + id_soam,id_soai,id_soat,id_soab,id_soax /) + aer_species(:) = -1 + n = 1 + do m = 1,gas_pcnst + k=0 + if ( any(bulkaero_species(:)==m) ) k=1 + if ( k==0 ) k = index(trim(solsym(m)), '_a') + if ( k==0 ) k = index(trim(solsym(m)), '_c') + if ( k>0 ) then ! must be aerosol species + aer_species(n) = m + n = n+1 + endif + enddo + + CALL Addfld( 'MASS', (/ 'lev' /), 'A', 'kg', 'Mass of grid box' ) + CALL Addfld( 'AREA', horiz_only, 'A', 'm2', 'Area of grid box' ) + CALL Addfld( 'HEIGHT', (/ 'ilev' /),'A','m', 'Geopotential height above surface at interfaces' ) + + ! Note that constituents are already output by default + ! Add all species as output fields if desired + DO N = 1, gas_pcnst + IF ( ANY( aer_species == N ) ) THEN + SpcName = TRIM(solsym(N)) + unit_basename = 'kg' + IF ( SpcName(1:3) == 'num' ) unit_basename = ' 1' + CALL AddFld( TRIM(SpcName), (/ 'lev' /), 'A', unit_basename//'/kg', & + TRIM(SpcName)//' concentration' ) + CALL AddFld( TRIM(SpcName)//'_SRF', horiz_only, 'A', unit_basename//'/kg', & + TRIM(SpcName)//' in bottom layer' ) + ELSE + M = map2chm(N) + SpcName = TRIM(solsym(N)) + CALL AddFld( TRIM(SpcName), (/ 'lev' /), 'A', 'mol/mol', & + TRIM(SpcName)//' volume mixing ratio') + CALL AddFld( TRIM(SpcName)//'_SRF', horiz_only, 'A', 'mol/mol', & + TRIM(SpcName)//' in bottom layer') + ENDIF + IF ( ( N /= id_cly ) .AND. ( N /= id_bry ) ) THEN + IF ( history_aerosol .OR. history_chemistry ) THEN + CALL Add_Default( TRIM(SpcName), 1, ' ' ) + ENDIF + IF ( history_chemspecies_srf ) THEN + CALL Add_Default( TRIM(SpcName)//'_SRF', 1, ' ' ) + ENDIF + ENDIF + + IF ( history_cesm_forcing ) THEN + IF ( N == id_o3 ) CALL Add_Default( TRIM(SpcName), 1, ' ') + IF ( N == id_oh ) CALL Add_Default( TRIM(SpcName), 1, ' ') + IF ( N == id_no3 ) CALL Add_Default( TRIM(SpcName), 1, ' ') + IF ( N == id_ho2 ) CALL Add_Default( TRIM(SpcName), 1, ' ') + + IF ( N == id_o3 ) CALL Add_Default( TRIM(SpcName), 8, ' ') + IF ( N == id_so4_a1 ) CALL Add_Default( TRIM(SpcName), 8, ' ') + IF ( N == id_so4_a2 ) CALL Add_Default( TRIM(SpcName), 8, ' ') + IF ( N == id_so4_a3 ) CALL Add_Default( TRIM(SpcName), 8, ' ') + + IF ( N == id_num_a2 ) CALL Add_Default( TRIM(SpcName), 8, ' ') + IF ( N == id_num_a3 ) CALL Add_Default( TRIM(SpcName), 8, ' ') + IF ( N == id_dst_a3 ) CALL Add_Default( TRIM(SpcName), 8, ' ') + IF ( N == id_ncl_a3 ) CALL Add_Default( TRIM(SpcName), 8, ' ') + + ENDIF + IF ( history_scwaccm_forcing ) THEN + IF ( N == id_co2 ) CALL Add_Default( TRIM(SpcName), 8, ' ') + IF ( N == id_h ) CALL Add_Default( TRIM(SpcName), 8, ' ') + IF ( N == id_no ) CALL Add_Default( TRIM(SpcName), 8, ' ') + IF ( N == id_o ) CALL Add_Default( TRIM(SpcName), 8, ' ') + IF ( N == id_o2 ) CALL Add_Default( TRIM(SpcName), 8, ' ') + IF ( N == id_o3 ) CALL Add_Default( TRIM(SpcName), 8, ' ') + IF ( N == id_h2o ) CALL Add_Default( TRIM(SpcName), 1, ' ') + IF ( N == id_ch4 ) CALL Add_Default( TRIM(SpcName), 1, ' ') + IF ( N == id_n2o ) CALL Add_Default( TRIM(SpcName), 1, ' ') + IF ( N == id_cfc11 ) CALL Add_Default( TRIM(SpcName), 1, ' ') + IF ( N == id_cfc12 ) CALL Add_Default( TRIM(SpcName), 1, ' ') + ENDIF + + IF (history_dust .AND. (index(TRIM(SpcName),'dst_') > 0)) THEN + CALL Add_Default( TRIM(SpcName), 1, ' ') + ENDIF + ENDDO + + IF ( Input_Opt%LDryD ) THEN + DO N = 1, State_Chm%nDryDep + SpcName = 'DV_'//to_upper(TRIM(depName(N))) + CALL AddFld( TRIM(SpcName), horiz_only, 'A', 'm/s', & + TRIM(SpcName)//' dry deposition velocity') + ENDDO + + DO N = 1, State_Chm%nAdvect + ! Get the species ID from the advected species ID + M = State_Chm%Map_Advect(N) + + ! Get info about this species from the species database + SpcInfo => State_Chm%SpcData(M)%Info + SpcName = 'DF_'//to_upper(TRIM(SpcInfo%Name)) + CALL AddFld( TRIM(SpcName), horiz_only, 'A', 'kg/m2/s', & + TRIM(SpcName)//' dry deposition flux') + IF ( history_chemistry ) THEN + CALL Add_Default( TRIM(SpcName), 1, ' ' ) + ENDIF + + ! Free pointer + SpcInfo => NULL() + ENDDO + ENDIF + + ! Chemical tendencies and surface fluxes + DO N = 1, gas_pcnst + IF ( map2chm(N) > 0 ) THEN + ! If this is a GEOS-Chem species then capitalize. This avoids + ! issues where Br2 /= BR2 + srcnam(N) = 'CT_'//to_upper(TRIM(solsym(N))) ! chem tendency (source/sink) + ELSE + ! For MAM aerosols, keep as it is (i.e. bc_a1) + srcnam(N) = 'CT_'//TRIM(solsym(N)) ! chem tendency (source/sink) + ENDIF + SpcName = srcnam(N) + CALL Addfld( TRIM(SpcName), (/ 'lev' /), 'A', 'kg/kg/s', TRIM(SpcName)//' source/sink' ) + + SpcName = TRIM(solsym(N)) + CALL cnst_get_ind( SpcName, M, abort=.false. ) + IF ( M > 0 ) THEN + IF (sflxnam(M)(3:5) == 'num') THEN ! name is in the form of "SF****" + unit_basename = ' 1' + ELSE + unit_basename = 'kg' + ENDIF + SpcName = sflxnam(M) + CALL Addfld ( TRIM(SpcName), horiz_only, 'A', unit_basename//'/m2/s', & + TRIM(solsym(N))//' surface flux') + IF ( history_aerosol .OR. history_chemistry ) THEN + CALL Add_Default( TRIM(SpcName), 1, ' ' ) + ENDIF + + IF ( history_cesm_forcing ) THEN + IF ( TRIM(SpcName(3:)) == 'NO' .OR. TRIM(SpcName(3:)) == 'NH3' ) THEN + CALL Add_Default( TRIM(SpcName), 1, ' ' ) + ENDIF + ENDIF + ENDIF + ENDDO + + ! Add chemical tendency of water vapor to water budget output + IF ( history_budget ) THEN + CALL Add_Default ('CT_H2O' , history_budget_histfile_num, ' ') + ENDIF + + ! Chemical tendencies + DO N = 1, gas_pcnst + M = map2chm(N) + IF ( M > 0 ) THEN + dtchem_name(N) = 'D'//to_upper(TRIM(solsym(N)))//'CHM' + ELSE + dtchem_name(N) = 'D'//TRIM(solsym(N))//'CHM' + ENDIF + SpcName = TRIM(dtchem_name(N)) + CALL Addfld( TRIM(SpcName), (/ 'lev' /), 'A', 'kg/s', & + 'net tendency from chemistry' ) + ENDDO + + i_NO = Ind_('NO') + i_NO2 = Ind_('NO2') + i_N = Ind_('N') + i_BrNO2 = Ind_('BrNO2') + i_BrNO3 = Ind_('BrNO3') + i_ClNO2 = Ind_('ClNO2') + i_ClNO3 = Ind_('ClNO3') + i_ETHLN = Ind_('ETHLN') + i_ETNO3 = Ind_('ETNO3') + i_HNO2 = Ind_('HNO2') + i_HNO3 = Ind_('HNO3') + i_HNO4 = Ind_('HNO4') + i_ICN = Ind_('ICN') + i_ICNOO = Ind_('ICNOO') + i_IDHNBOO = Ind_('IDHNBOO') + i_IDHNDOO1 = Ind_('IDHNDOO1') + i_IDN = Ind_('IDN') + i_IDNOO = Ind_('IDNOO') + i_IHN1 = Ind_('IHN1') + i_IHN2 = Ind_('IHN2') + i_IHN3 = Ind_('IHN3') + i_IHN4 = Ind_('IHN4') + i_IHPNBOO = Ind_('IHPNBOO') + i_IHPNDOO = Ind_('IHPNDOO') + i_INA = Ind_('INA') + i_INO = Ind_('INO') + i_INO2B = Ind_('INO2B') + i_INO2D = Ind_('INO2D') + i_INPB = Ind_('INPB') + i_INPD = Ind_('INPD') + i_IONO = Ind_('IONO') + i_IONO2 = Ind_('IONO2') + i_IPRNO3 = Ind_('IPRNO3') + i_ISOPNOO1 = Ind_('ISOPNOO1') + i_ISOPNOO2 = Ind_('ISOPNOO2') + i_ITCN = Ind_('ITCN') + i_ITHN = Ind_('ITHN') + i_MACRNO2 = Ind_('MACRNO2') + i_MCRHN = Ind_('MCRHN') + i_MCRHNB = Ind_('MCRHNB') + i_MENO3 = Ind_('MENO3') + i_MONITS = Ind_('MONITS') + i_MONITU = Ind_('MONITU') + i_MPAN = Ind_('MPAN') + i_MPN = Ind_('MPN') + i_MVKN = Ind_('MVKN') + i_N2O5 = Ind_('N2O5') + i_NO3 = Ind_('NO3') + i_NPRNO3 = Ind_('NPRNO3') + i_OLND = Ind_('OLND') + i_OLNN = Ind_('OLNN') + i_PAN = Ind_('PAN') + i_PPN = Ind_('PPN') + i_PRN1 = Ind_('PRN1') + i_PROPNN = Ind_('PROPNN') + i_PRPN = Ind_('PRPN') + i_R4N1 = Ind_('R4N1') + i_R4N2 = Ind_('R4N2') + i_HONIT = Ind_('HONIT') + i_IONITA = Ind_('IONITA') + i_NIT = Ind_('NIT') + i_NITs = Ind_('NITs') + i_H = Ind_('H') + i_OH = Ind_('OH') + i_HO2 = Ind_('HO2') + i_H2O2 = Ind_('H2O2') + i_Cl = Ind_('Cl') + i_ClO = Ind_('ClO') + i_HOCl = Ind_('HOCl') + i_Cl2 = Ind_('Cl2') + i_Cl2O2 = Ind_('Cl2O2') + i_OClO = Ind_('OClO') + i_ClOO = Ind_('ClOO') + i_HCl = Ind_('HCl') + i_ClNO2 = Ind_('ClNO2') + i_ClNO3 = Ind_('ClNO3') + i_BrCl = Ind_('BrCl') + i_ICl = Ind_('ICl') + i_H1211 = Ind_('H1211') + i_CFC115 = Ind_('CFC115') + i_CH3Cl = Ind_('CH3Cl') + i_HCFC142b = Ind_('HCFC142b') + i_HCFC22 = Ind_('HCFC22') + i_CH2ICl = Ind_('CH2ICl') + i_CFC114 = Ind_('CFC114') + i_CFC12 = Ind_('CFC12') + i_HCFC141b = Ind_('HCFC141b') + i_HCFC123 = Ind_('HCFC123') + i_CH2Cl2 = Ind_('CH2Cl2') + i_CFC11 = Ind_('CFC11') + i_CH3CCl3 = Ind_('CH3CCl3') + i_CHCl3 = Ind_('CHCl3') + i_CCl4 = Ind_('CCl4') + i_CFC113 = Ind_('CFC113') + i_SALACL = Ind_('SALACL') + i_SALCCL = Ind_('SALCCL') + i_Br = Ind_('Br') + i_BrO = Ind_('BrO') + i_BrCl = Ind_('BrCl') + i_HOBr = Ind_('HOBr') + i_HBr = Ind_('HBr') + i_BrNO2 = Ind_('BrNO2') + i_BrNO3 = Ind_('BrNO3') + i_IBr = Ind_('IBr') + i_Br2 = Ind_('Br2') + i_CH3Br = Ind_('CH3Br') + i_H1211 = Ind_('H1211') + i_H1301 = Ind_('H1301') + i_H2402 = Ind_('H2402') + i_CH2Br2 = Ind_('CH2Br2') + i_CHBr3 = Ind_('CHBr3') + i_BrSALA = Ind_('BrSALA') + i_BrSALC = Ind_('BrSALC') + i_CH2IBr = Ind_('CH2IBr') + i_SO2 = Ind_('SO2') + i_SO4 = Ind_('SO4') + i_NH3 = Ind_('NH3') + i_NH4 = Ind_('NH4') + i_CH4 = Ind_('CH4') + i_H2O = Ind_('H2O') + i_H2 = Ind_('H2') + + NOx_species = (/ i_N, i_NO, i_NO2 /) + NOy_species = (/ i_N, i_NO, i_NO2, i_BrNO2, i_BrNO3, i_ClNO2, i_ClNO3,& + i_ETHLN, i_ETNO3, i_HNO2, i_HNO3, i_HNO4, i_ICN, & + i_ICNOO, i_IDHNBOO, i_IDHNDOO1, i_IDN, & + i_IDNOO, i_IHN1, i_IHN2, i_IHN3, i_IHN4, i_IHPNBOO, & + i_IHPNDOO, i_INA, i_INO, i_INO2B, i_INO2D, i_INPB, & + i_INPD, i_IONO, i_IONO2, i_IPRNO3, i_ISOPNOO1, & + i_ISOPNOO2, i_ITCN, i_ITHN, i_MACRNO2, i_MCRHN, & + i_MCRHNB, i_MENO3, i_MONITS, i_MONITU, i_MPAN, i_MPN,& + i_MVKN, i_N2O5, i_NO3, i_NPRNO3, i_OLND, i_OLNN, & + i_PAN, i_PPN, i_PRN1, i_PROPNN, i_PRPN, i_R4N1, & + i_R4N2, i_HONIT, i_IONITA, i_NIT, i_NITs /) + HOx_species = (/ i_H, i_OH, i_HO2, i_H2O2 /) + ClOx_species = (/ i_Cl, i_ClO, i_HOCl, i_Cl2, i_Cl2O2, i_OClO /) + ClOy_species = (/ i_Cl, i_ClO, i_HOCl, i_Cl2, i_Cl2O2, i_OClO, & + i_HCl, i_ClNO3, i_BrCl, i_ICl, i_ClNO2 /) + tCly_species = (/ i_Cl, i_ClO, i_HOCl, i_Cl2, i_Cl2O2, i_OClO, i_ClOO, & + i_HCl, i_ClNO2, i_ClNO3, i_BrCl, i_ICl, i_H1211, & + i_CFC115, i_CH3Cl, i_HCFC142b, i_HCFC22, i_CH2ICl, & + i_CFC114, i_CFC12, i_HCFC141b, i_HCFC123, i_CH2Cl2, & + i_CFC11, i_CH3CCl3, i_CHCl3, i_CCl4, i_CFC113, & + i_SALACL, i_SALCCL /) + BrOx_species = (/ i_Br, i_BrO, i_BrCl, i_HOBr /) + BrOy_species = (/ i_Br, i_BrO, i_BrCl, i_HOBr, i_HBr, i_BrNO2, & + i_BrNO3, i_IBr, i_Br2 /) + tBry_species = (/ i_Br, i_BrO, i_BrCl, i_HOBr, i_HBr, i_BrNO2, & + i_BrNO3, i_IBr, i_Br2, i_CH3Br, i_H1211, i_H1301, & + i_H2402, i_CH2Br2, i_CHBr3, i_BrSALA, i_BrSALC, & + i_CH2IBr /) + SOx_species = (/ i_SO2, i_SO4 /) + NHx_species = (/ i_NH3, i_NH4 /) + TOTH_species = (/ i_CH4, i_H2O, i_H2 /) + + DO N = 1, SIZE(NOx_species) + idx = NOx_species(N) + IF ( idx > 0 ) THEN + SpcInfo => State_Chm%SpcData(idx)%Info + NOx_MWs(N) = REAL(SpcInfo%MW_g,r8) + SpcInfo => NULL() + ELSE + NOx_MWs(N) = -1.0e+00_r8 + ENDIF + ENDDO + + DO N = 1, SIZE(NOy_species) + idx = NOy_species(N) + IF ( idx > 0 ) THEN + SpcInfo => State_Chm%SpcData(idx)%Info + NOy_MWs(N) = REAL(SpcInfo%MW_g,r8) + SpcInfo => NULL() + ELSE + NOy_MWs(N) = -1.0e+00_r8 + ENDIF + ENDDO + + DO N = 1, SIZE(HOx_species) + idx = HOx_species(N) + IF ( idx > 0 ) THEN + SpcInfo => State_Chm%SpcData(idx)%Info + HOx_MWs(N) = REAL(SpcInfo%MW_g,r8) + SpcInfo => NULL() + ELSE + HOx_MWs(N) = -1.0e+00_r8 + ENDIF + ENDDO + + DO N = 1, SIZE(ClOx_species) + idx = ClOx_species(N) + IF ( idx > 0 ) THEN + SpcInfo => State_Chm%SpcData(idx)%Info + ClOx_MWs(N) = REAL(SpcInfo%MW_g,r8) + SpcInfo => NULL() + ELSE + ClOx_MWs(N) = -1.0e+00_r8 + ENDIF + ENDDO + + DO N = 1, SIZE(ClOy_species) + idx = ClOy_species(N) + IF ( idx > 0 ) THEN + SpcInfo => State_Chm%SpcData(idx)%Info + ClOy_MWs(N) = REAL(SpcInfo%MW_g,r8) + SpcInfo => NULL() + ELSE + ClOy_MWs(N) = -1.0e+00_r8 + ENDIF + ENDDO + + DO N = 1, SIZE(tCly_species) + idx = tCly_species(N) + IF ( idx > 0 ) THEN + SpcInfo => State_Chm%SpcData(idx)%Info + tCly_MWs(N) = REAL(SpcInfo%MW_g,r8) + SpcInfo => NULL() + ELSE + tCly_MWs(N) = -1.0e+00_r8 + ENDIF + ENDDO + + DO N = 1, SIZE(BrOx_species) + idx = BrOx_species(N) + IF ( idx > 0 ) THEN + SpcInfo => State_Chm%SpcData(idx)%Info + BrOx_MWs(N) = REAL(SpcInfo%MW_g,r8) + SpcInfo => NULL() + ELSE + BrOx_MWs(N) = -1.0e+00_r8 + ENDIF + ENDDO + + DO N = 1, SIZE(BrOy_species) + idx = BrOy_species(N) + IF ( idx > 0 ) THEN + SpcInfo => State_Chm%SpcData(idx)%Info + BrOy_MWs(N) = REAL(SpcInfo%MW_g,r8) + SpcInfo => NULL() + ELSE + BrOy_MWs(N) = -1.0e+00_r8 + ENDIF + ENDDO + + DO N = 1, SIZE(tBry_species) + idx = tBry_species(N) + IF ( idx > 0 ) THEN + SpcInfo => State_Chm%SpcData(idx)%Info + tBry_MWs(N) = REAL(SpcInfo%MW_g,r8) + SpcInfo => NULL() + ELSE + tBry_MWs(N) = -1.0e+00_r8 + ENDIF + ENDDO + + DO N = 1, SIZE(SOx_species) + idx = SOx_species(N) + IF ( idx > 0 ) THEN + SpcInfo => State_Chm%SpcData(idx)%Info + SOx_MWs(N) = REAL(SpcInfo%MW_g,r8) + SpcInfo => NULL() + ELSE + SOx_MWs(N) = -1.0e+00_r8 + ENDIF + ENDDO + + DO N = 1, SIZE(NHx_species) + idx = NHx_species(N) + IF ( idx > 0 ) THEN + SpcInfo => State_Chm%SpcData(idx)%Info + NHx_MWs(N) = REAL(SpcInfo%MW_g,r8) + SpcInfo => NULL() + ELSE + NHx_MWs(N) = -1.0e+00_r8 + ENDIF + ENDDO + + DO N = 1, SIZE(TOTH_species) + idx = TOTH_species(N) + IF ( idx > 0 ) THEN + SpcInfo => State_Chm%SpcData(idx)%Info + TOTH_MWs(N) = REAL(SpcInfo%MW_g,r8) + SpcInfo => NULL() + ELSE + TOTH_MWs(N) = -1.0e+00_r8 + ENDIF + ENDDO + + IF ( ANY(NOx_species <= 0 ) ) THEN + IF ( MasterProc ) Write(iulog,*) "NOx indices: ", NOx_species + ENDIF + IF ( ANY(NOy_species <= 0 ) ) THEN + IF ( MasterProc ) Write(iulog,*) "NOy indices: ", NOy_species + ENDIF + IF ( ANY(HOx_species <= 0 ) ) THEN + IF ( MasterProc ) Write(iulog,*) "HOx indices: ", HOx_species + ENDIF + IF ( ANY(ClOx_species <= 0 ) ) THEN + IF ( MasterProc ) Write(iulog,*) "ClOx indices: ", ClOx_species + ENDIF + IF ( ANY(ClOy_species <= 0 ) ) THEN + IF ( MasterProc ) Write(iulog,*) "ClOy indices: ", ClOy_species + ENDIF + IF ( ANY(tCly_species <= 0 ) ) THEN + IF ( MasterProc ) Write(iulog,*) "tCly indices: ", tCly_species + ENDIF + IF ( ANY(BrOx_species <= 0 ) ) THEN + IF ( MasterProc ) Write(iulog,*) "BrOx indices: ", BrOx_species + ENDIF + IF ( ANY(BrOy_species <= 0 ) ) THEN + IF ( MasterProc ) Write(iulog,*) "BrOy indices: ", BrOy_species + ENDIF + IF ( ANY(tBry_species <= 0 ) ) THEN + IF ( MasterProc ) Write(iulog,*) "tBry indices: ", tBry_species + ENDIF + IF ( ANY(SOx_species <= 0 ) ) THEN + IF ( MasterProc ) Write(iulog,*) "SOx indices: ", SOx_species + ENDIF + IF ( ANY(NHx_species <= 0 ) ) THEN + IF ( MasterProc ) Write(iulog,*) "NHx indices: ", NHx_species + ENDIF + IF ( ANY(TOTH_species <= 0 ) ) THEN + IF ( MasterProc ) Write(iulog,*) "TOTH indices: ", TOTH_species + ENDIF + + CALL Addfld( 'NOX', (/ 'lev' /), 'A', 'mol/mol', & + 'NOx molar mixing ratio' ) + CALL Addfld( 'NOY', (/ 'lev' /), 'A', 'mol/mol', & + 'NOy molar mixing ratio' ) + CALL Addfld( 'NOY_mmr', (/ 'lev' /), 'A', 'kg/kg', & + 'NOy mass mixing ratio' ) + CALL Addfld( 'NOY_SRF', horiz_only, 'A', 'mol/mol', & + 'Surface NOy molar mixing ratio' ) + CALL Addfld( 'HOX', (/ 'lev' /), 'A', 'mol/mol', & + 'HOx molar mixing ratio' ) + CALL Addfld( 'CLOX', (/ 'lev' /), 'A', 'mol/mol', & + 'ClOx molar mixing ratio' ) + CALL Addfld( 'CLOY', (/ 'lev' /), 'A', 'mol/mol', & + 'Total inorganic chlorine (ClOy) molar mixing ratio' ) + CALL Addfld( 'TCLY', (/ 'lev' /), 'A', 'mol/mol', & + 'Total Cl molar mixing ratio' ) + CALL Addfld( 'BROX', (/ 'lev' /), 'A', 'mol/mol', & + 'BrOx molar mixing ratio' ) + CALL Addfld( 'BROY', (/ 'lev' /), 'A', 'mol/mol', & + 'Total inorganic bromine (BrOy) molar mixing ratio' ) + CALL Addfld( 'TBRY', (/ 'lev' /), 'A', 'mol/mol', & + 'Total Br molar mixing ratio' ) + CALL Addfld( 'SOX', (/ 'lev' /), 'A', 'mol/mol', & + 'SOx molar mixing ratio' ) + CALL Addfld( 'SOX_mmr', (/ 'lev' /), 'A', 'kg/kg', & + 'SOx mass mixing ratio' ) + CALL Addfld( 'NHX', (/ 'lev' /), 'A', 'mol/mol', & + 'NHx molar mixing ratio' ) + CALL Addfld( 'NHX_mmr', (/ 'lev' /), 'A', 'kg/kg', & + 'NHx mass mixing ratio' ) + CALL Addfld( 'TOTH', (/ 'lev' /), 'A', 'mol/mol', & + 'Total H2 molar mixing ratio' ) + + CALL Addfld( 'SAD_STRAT', (/ 'lev' /), 'I', 'cm2/cm3', 'Stratospheric aerosol SAD' ) + CALL Addfld( 'SAD_SULFC', (/ 'lev' /), 'I', 'cm2/cm3', 'Chemical sulfate aerosol SAD' ) + CALL Addfld( 'SAD_PSC', (/ 'lev' /), 'I', 'cm2/cm3', 'PSC aerosol SAD' ) + CALL Addfld( 'RAD_SULFC', (/ 'lev' /), 'I', 'cm', 'Chemical sulfate radius' ) + CALL Addfld( 'RAD_PSC', (/ 'lev' /), 'I', 'cm', 'PSC aerosol radius' ) + CALL Addfld( 'SAD_TROP', (/ 'lev' /), 'I', 'cm2/cm3', 'Tropospheric aerosol SAD' ) + CALL Addfld( 'SAD_AERO', (/ 'lev' /), 'I', 'cm2/cm3', 'Aerosol surface area density' ) + IF ( history_cesm_forcing ) THEN + CALL Add_Default( 'SAD_AERO', 8, ' ' ) + ENDIF + CALL Addfld( 'REFF_AERO', (/ 'lev' /), 'I', 'cm', 'Aerosol effective radius') + CALL Addfld( 'SULF_TROP', (/ 'lev' /), 'I', 'cm2/cm3', 'Tropospheric sulfate area density') + + CALL Addfld( 'HNO3_TOTAL', (/ 'lev' /), 'I', 'mol/mol', 'Total HNO3' ) + CALL Addfld( 'HNO3_STS', (/ 'lev' /), 'I', 'mol/mol', 'STS condensed HNO3' ) + CALL Addfld( 'HNO3_NAT', (/ 'lev' /), 'I', 'mol/mol', 'NAT condensed HNO3' ) + CALL Addfld( 'HNO3_GAS', (/ 'lev' /), 'I', 'mol/mol', 'Gas phase HNO3' ) + CALL Addfld( 'H2O_GAS', (/ 'lev' /), 'I', 'mol/mol', 'Gas phase H2O' ) + CALL Addfld( 'HCL_TOTAL', (/ 'lev' /), 'I', 'mol/mol', 'Total HCl' ) + CALL Addfld( 'HCL_GAS', (/ 'lev' /), 'I', 'mol/mol', 'Gas phase HCl' ) + CALL Addfld( 'HCL_STS', (/ 'lev' /), 'I', 'mol/mol', 'STS condensend HCl' ) + + CALL Addfld( 'SZA', horiz_only, 'I', 'degrees', 'Solar Zenith Angle' ) + CALL Addfld( 'U_SRF', horiz_only, 'I', 'm/s', 'Horizontal wind velocity' ) + CALL Addfld( 'V_SRF', horiz_only, 'I', 'm/s', 'Vertical wind velocity' ) + CALL Addfld( 'Q_SRF', horiz_only, 'I', 'kg/kg', 'Specific humidity' ) + + CALL Addfld( 'CT_H2O_GHG', (/ 'lev' /), 'A','kg/kg/s', 'ghg-chem h2o source/sink' ) + + + ! Cleanup + Current => NULL() + Item => NULL() + + END SUBROUTINE GC_Diagnostics_Init + + SUBROUTINE GC_Diagnostics_Calc( Input_Opt, State_Chm, State_Diag, & + State_Grid, State_Met, cam_in, state, & + mmr_tend, LCHNK ) + + ! CAM modules + use cam_history, only : outfld, hist_fld_active + use camsrfexch, only : cam_in_t + use chem_mods, only : adv_mass + use constituents, only : cnst_name, sflxnam + use physconst, only : MWDry + use physics_types, only : physics_state + use spmd_utils, only : MasterProc + + ! GEOS-Chem modules + use CMN_Size_Mod, only : NDUST + use DryDep_Mod, only : depName, Ndvzind + use Input_Opt_Mod, only : OptInput + use Precision_Mod, only : f8 + use Species_Mod, only : Species + use State_Chm_Mod, only : ChmState + use State_Diag_Mod, only : DgnState, get_TagInfo + use State_Grid_Mod, only : GrdState + use State_Met_Mod, only : MetState + use Registry_Mod, only : MetaRegItem, RegItem, Registry_Lookup + use UCX_Mod, only : GET_STRAT_OPT + + TYPE(OptInput), INTENT(IN) :: Input_Opt ! Input options + TYPE(ChmState), INTENT(INOUT) :: State_Chm ! Chemistry State object + TYPE(DgnState), INTENT(IN) :: State_Diag ! Diag State object + TYPE(GrdState), INTENT(IN) :: State_Grid ! Grid State object + TYPE(MetState), INTENT(IN) :: State_Met ! Meteorology State object + TYPE(cam_in_t), INTENT(IN) :: cam_in ! import state + TYPE(physics_state), INTENT(IN) :: state ! Physics state variables + REAL(r8), INTENT(IN) :: mmr_tend(state%ncol,pver,gas_pcnst) + ! Net tendency from chemistry in kg/s + INTEGER, INTENT(IN) :: LCHNK ! Chunk number + + ! Integers + INTEGER :: I, J, L, M, N, ND, SM + INTEGER :: idx + INTEGER :: RC + INTEGER :: Rank ! Size of data + + INTEGER :: nY, nZ + + ! Logicals + LOGICAL :: Found + LOGICAL :: rootChunk + LOGICAL :: OnLevelEdges ! Is the data defined + ! on level edges (T/F) + + ! Strings + CHARACTER(LEN=shr_kind_cl) :: ThisLoc + CHARACTER(LEN=shr_kind_cl) :: ErrMsg + CHARACTER(LEN=shr_kind_cl) :: SpcName + CHARACTER(LEN=shr_kind_cl) :: tagName + + ! Real + REAL(r8) :: wgt + REAL(r8) :: MW + REAL(r8) :: RAER, REFF, SADSTRAT, XSASTRAT + + ! Arrays + REAL(r8) :: outTmp(State_Grid%nY,State_Grid%nZ) + REAL(r8) :: radTmp(State_Grid%nY,State_Grid%nZ) + + ! Floating-point data pointers (8-byte precision) + REAL(f8), POINTER :: Ptr0d_8 ! 0D 8-byte data + REAL(f8), POINTER :: Ptr1d_8(: ) ! 1D 8-byte data + REAL(f8), POINTER :: Ptr2d_8(:,: ) ! 2D 8-byte data + REAL(f8), POINTER :: Ptr3d_8(:,:,:) ! 3D 8-byte data + + ! Objects + TYPE(Species), POINTER :: SpcInfo + TYPE(MetaRegItem), POINTER :: Current + TYPE(RegItem ), POINTER :: Item + + !================================================================= + ! GC_Diagnostics_Calc begins here! + !================================================================= + + nY = State_Grid%nY + nZ = State_Grid%nZ + + ! Initialize pointers + SpcInfo => NULL() + Current => NULL() + Item => NULL() + + ! For error trapping + ErrMsg = '' + ThisLoc = ' -> at GC_Diagnostics_Calc (in chemistry/geoschem/geoschem_diagnostics_mod.F90)' + + ! Define rootChunk + rootChunk = ( MasterProc.and.(LCHNK==BEGCHUNK) ) + + CALL OutFld( 'AREA', State_Grid%Area_M2(1,:nY), nY, LCHNK) + CALL OutFld( 'MASS', State_Met%AD(1,:nY,nZ:1:-1), nY, LCHNK) + CALL Outfld( 'HEIGHT', state%zi(:nY,:), nY, LCHNK ) + + ! =============================================== + ! Diagnose chemical species (constituents and short-lived) + ! =============================================== + + DO N = 1, gas_pcnst + M = map2chm(N) + SpcName = TRIM(solsym(N)) + outTmp = 0.0e+00_r8 + IF ( adv_mass(N) > 0.0e+00_r8 .AND. M /= 0 .AND. & + (hist_fld_active(TRIM(SpcName)) .OR. hist_fld_active(TRIM(SpcName)//'_SRF')) ) THEN + IF ( M > 0 ) THEN + ! mol/mol + outTmp(:nY,:) = REAL(State_Chm%Species(M)%Conc(1,:nY,nZ:1:-1),r8) * MWDry / adv_mass(N) + ELSEIF ( ANY( aer_species == N ) ) THEN + ! kg/kg + outTmp(:nY,:) = state%q(:nY,:nZ,-M) + ELSE + ! mol/mol + outTmp(:nY,:) = state%q(:nY,:nZ,-M) * MWDry / adv_mass(N) + ENDIF + CALL OutFld( TRIM(SpcName), outTmp(:nY,:), nY, LCHNK ) + CALL OutFld( TRIM(SpcName)//'_SRF', outTmp(:nY,nZ), nY, LCHNK ) + ENDIF + ENDDO + + ! =============================================== + ! Diagnose chemical families (NOx, NOy, ...) + ! =============================================== + + SpcName = 'NOX' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp = 0.0e+00_r8 + DO N = 1, SIZE(NOx_species) + idx = NOx_species(N) + MW = NOx_MWs(N) + IF ( idx <= 0 .OR. MW <= 0.0e+00_r8 ) CYCLE + wgt = 1.0E+00_r8 + outTmp(:nY,:) = outTmp(:nY,:) & + + wgt * REAL(State_Chm%Species(idx)%Conc(1,:nY,nZ:1:-1),r8) * MWDry / MW + ENDDO + CALL Outfld( TRIM(SpcName), outTmp(:nY,:), nY, LCHNK ) + ENDIF + + SpcName = 'NOY' + IF ( hist_fld_active(TRIM(SpcName)) .OR. hist_fld_active(TRIM(SpcName)//'_SRF') ) THEN + outTmp = 0.0e+00_r8 + DO N = 1, SIZE(NOy_species) + idx = NOy_species(N) + MW = NOy_MWs(N) + IF ( idx <= 0 .OR. MW <= 0.0e+00_r8 ) CYCLE + wgt = 1.0E+00_r8 + IF ( idx == i_N2O5 .OR. idx == i_IDN .OR. idx == i_IDNOO ) THEN + wgt = 2.0E+00_r8 + ENDIF + outTmp(:nY,:) = outTmp(:nY,:) & + + wgt * REAL(State_Chm%Species(idx)%Conc(1,:nY,nZ:1:-1),r8) * MWDry / MW + ENDDO + CALL Outfld( TRIM(SpcName), outTmp(:nY,:), nY, LCHNK ) + ENDIF + SpcName = 'NOY_SRF' + IF ( hist_fld_active(TRIM(SpcName)) ) CALL Outfld( TRIM(SpcName), outTmp(:nY,nZ), nY, LCHNK ) + + SpcName = 'NOY_mmr' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp = 0.0e+00_r8 + DO N = 1, SIZE(NOy_species) + idx = NOy_species(N) + MW = NOy_MWs(N) + IF ( idx <= 0 .OR. MW <= 0.0e+00_r8 ) CYCLE + wgt = 1.0E+00_r8 + IF ( idx == i_N2O5 .OR. idx == i_IDN .OR. idx == i_IDNOO ) THEN + wgt = 2.0E+00_r8 + ENDIF + outTmp(:nY,:) = outTmp(:nY,:) + wgt * REAL(State_Chm%Species(idx)%Conc(1,:nY,nZ:1:-1),r8) + ENDDO + CALL Outfld( TRIM(SpcName), outTmp(:nY,:), nY, LCHNK ) + ENDIF + + SpcName = 'HOX' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp = 0.0e+00_r8 + DO N = 1, SIZE(HOx_species) + idx = HOx_species(N) + MW = HOx_MWs(N) + IF ( idx <= 0 .OR. MW <= 0.0e+00_r8 ) CYCLE + wgt = 1.0E+00_r8 + IF ( idx == i_H2O2 ) THEN + wgt = 2.0E+00_r8 + ENDIF + outTmp(:nY,:) = outTmp(:nY,:) & + + wgt * REAL(State_Chm%Species(idx)%Conc(1,:nY,nZ:1:-1),r8) * MWDry / MW + ENDDO + CALL Outfld( TRIM(SpcName), outTmp(:nY,:), nY, LCHNK ) + ENDIF + + SpcName = 'CLOX' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp = 0.0e+00_r8 + DO N = 1, SIZE(ClOx_species) + idx = ClOx_species(N) + MW = ClOx_MWs(N) + IF ( idx <= 0 .OR. MW <= 0.0e+00_r8 ) CYCLE + wgt = 1.0E+00_r8 + IF ( idx == i_Cl2 .OR. idx == i_Cl2O2 ) THEN + wgt = 2.0E+00_r8 + ENDIF + outTmp(:nY,:) = outTmp(:nY,:) & + + wgt * REAL(State_Chm%Species(idx)%Conc(1,:nY,nZ:1:-1),r8) * MWDry / MW + ENDDO + CALL Outfld( TRIM(SpcName), outTmp(:nY,:), nY, LCHNK ) + ENDIF + + SpcName = 'CLOY' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp = 0.0e+00_r8 + DO N = 1, SIZE(ClOy_species) + idx = ClOy_species(N) + MW = ClOy_MWs(N) + IF ( idx <= 0 .OR. MW <= 0.0e+00_r8 ) CYCLE + wgt = 1.0E+00_r8 + IF ( idx == i_Cl2 .OR. idx == i_Cl2O2 ) THEN + wgt = 2.0E+00_r8 + ENDIF + outTmp(:nY,:) = outTmp(:nY,:) & + + wgt * REAL(State_Chm%Species(idx)%Conc(1,:nY,nZ:1:-1),r8) * MWDry / MW + ENDDO + CALL Outfld( TRIM(SpcName), outTmp(:nY,:), nY, LCHNK ) + ENDIF + + SpcName = 'TCLY' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp = 0.0e+00_r8 + DO N = 1, SIZE(tCly_species) + idx = tCly_species(N) + MW = tCly_MWs(N) + IF ( idx <= 0 .OR. MW <= 0.0e+00_r8 ) CYCLE + wgt = 1.0E+00_r8 + IF ( idx == i_Cl2 .OR. idx == i_Cl2O2 .OR. idx == i_CFC114 .OR. & + idx == i_CFC12 .OR. idx == i_CH2Cl2 .OR. idx == i_HCFC123 .OR. & + idx == i_HCFC141b ) THEN + wgt = 2.0E+00_r8 + ELSEIF ( idx == i_CFC11 .OR. idx == i_CFC113 .OR. idx == i_CH3CCl3 .OR. & + idx == i_CHCl3 ) THEN + wgt = 3.0E+00_r8 + ELSEIF ( idx == i_CCl4 ) THEN + wgt = 4.0E+00_r8 + ENDIF + outTmp(:nY,:) = outTmp(:nY,:) & + + wgt * REAL(State_Chm%Species(idx)%Conc(1,:nY,nZ:1:-1),r8) * MWDry / MW + ENDDO + CALL Outfld( TRIM(SpcName), outTmp(:nY,:), nY, LCHNK ) + ENDIF + + SpcName = 'BROX' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp = 0.0e+00_r8 + DO N = 1, SIZE(BrOx_species) + idx = BrOx_species(N) + MW = BrOx_MWs(N) + IF ( idx <= 0 .OR. MW <= 0.0e+00_r8 ) CYCLE + wgt = 1.0E+00_r8 + outTmp(:nY,:) = outTmp(:nY,:) & + + wgt * REAL(State_Chm%Species(idx)%Conc(1,:nY,nZ:1:-1),r8) * MWDry / MW + ENDDO + CALL Outfld( TRIM(SpcName), outTmp(:nY,:), nY, LCHNK ) + ENDIF + + SpcName = 'BROY' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp = 0.0e+00_r8 + DO N = 1, SIZE(BrOy_species) + idx = BrOy_species(N) + MW = BrOy_MWs(N) + IF ( idx <= 0 .OR. MW <= 0.0e+00_r8 ) CYCLE + wgt = 1.0E+00_r8 + IF ( idx == i_Br2 ) THEN + wgt = 2.0E+00_r8 + ENDIF + outTmp(:nY,:) = outTmp(:nY,:) & + + wgt * REAL(State_Chm%Species(idx)%Conc(1,:nY,nZ:1:-1),r8) * MWDry / MW + ENDDO + CALL Outfld( TRIM(SpcName), outTmp(:nY,:), nY, LCHNK ) + ENDIF + + SpcName = 'TBRY' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp = 0.0e+00_r8 + DO N = 1, SIZE(tBry_species) + idx = tBry_species(N) + MW = tBry_MWs(N) + IF ( idx <= 0 .OR. MW <= 0.0e+00_r8 ) CYCLE + wgt = 1.0E+00_r8 + IF ( idx == i_Br2 .OR. idx == i_H2402 .OR. idx == i_CH2Br2 ) THEN + wgt = 2.0E+00_r8 + ENDIF + outTmp(:nY,:) = outTmp(:nY,:) & + + wgt * REAL(State_Chm%Species(idx)%Conc(1,:nY,nZ:1:-1),r8) * MWDry / MW + ENDDO + CALL Outfld( TRIM(SpcName), outTmp(:nY,:), nY, LCHNK ) + ENDIF + + SpcName = 'SOX' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp = 0.0e+00_r8 + DO N = 1, SIZE(SOx_species) + idx = SOx_species(N) + MW = SOx_MWs(N) + IF ( idx <= 0 .OR. MW <= 0.0e+00_r8 ) CYCLE + wgt = 1.0E+00_r8 + outTmp(:nY,:) = outTmp(:nY,:) & + + wgt * REAL(State_Chm%Species(idx)%Conc(1,:nY,nZ:1:-1),r8) * MWDry / MW + ENDDO + CALL Outfld( TRIM(SpcName), outTmp(:nY,:), nY, LCHNK ) + ENDIF + + SpcName = 'SOX_mmr' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp = 0.0e+00_r8 + DO N = 1, SIZE(SOx_species) + idx = SOx_species(N) + MW = SOx_MWs(N) + IF ( idx <= 0 .OR. MW <= 0.0e+00_r8 ) CYCLE + wgt = 1.0E+00_r8 + outTmp(:nY,:) = outTmp(:nY,:) + wgt * REAL(State_Chm%Species(idx)%Conc(1,:nY,nZ:1:-1),r8) + ENDDO + CALL Outfld( TRIM(SpcName), outTmp(:nY,:), nY, LCHNK ) + ENDIF + + SpcName = 'NHX' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp = 0.0e+00_r8 + DO N = 1, SIZE(NHx_species) + idx = NHx_species(N) + MW = NHx_MWs(N) + IF ( idx <= 0 .OR. MW <= 0.0e+00_r8 ) CYCLE + wgt = 1.0E+00_r8 + outTmp(:nY,:) = outTmp(:nY,:) & + + wgt * REAL(State_Chm%Species(idx)%Conc(1,:nY,nZ:1:-1),r8) * MWDry / MW + ENDDO + CALL Outfld( TRIM(SpcName), outTmp(:nY,:), nY, LCHNK ) + ENDIF + + SpcName = 'NHX_mmr' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp = 0.0e+00_r8 + DO N = 1, SIZE(NHx_species) + idx = NHx_species(N) + MW = NHx_MWs(N) + IF ( idx <= 0 .OR. MW <= 0.0e+00_r8 ) CYCLE + wgt = 1.0E+00_r8 + outTmp(:nY,:) = outTmp(:nY,:) + wgt * REAL(State_Chm%Species(idx)%Conc(1,:nY,nZ:1:-1),r8) + ENDDO + CALL Outfld( TRIM(SpcName), outTmp(:nY,:), nY, LCHNK ) + ENDIF + + SpcName = 'TOTH' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp = 0.0e+00_r8 + DO N = 1, SIZE(TOTH_species) + idx = TOTH_species(N) + MW = TOTH_MWs(N) + IF ( idx <= 0 .OR. MW <= 0.0e+00_r8 ) CYCLE + wgt = 1.0E+00_r8 + IF ( idx == i_CH4 ) THEN + wgt = 2.0E+00_r8 + ENDIF + outTmp(:nY,:) = outTmp(:nY,:) & + + wgt * REAL(State_Chm%Species(idx)%Conc(1,:nY,nZ:1:-1),r8) * MWDry / MW + ENDDO + CALL Outfld( TRIM(SpcName), outTmp(:nY,:), nY, LCHNK ) + ENDIF + + ! =============================================== + ! Diagnose GEOS-Chem aerosol quantities + ! =============================================== + + IF ( hist_fld_active('SAD_PSC') .OR. hist_fld_active('RAD_PSC') ) THEN + outTmp = 0.0e+00_r8 + radTmp = 0.0e+00_r8 + DO J = 1, nY + DO L = 1, nZ + CALL GET_STRAT_OPT(State_Chm,1,J,L,1,RAER,REFF,SADSTRAT,XSASTRAT) + outTmp(J,nZ+1-L) = SADSTRAT + radTmp(J,nZ+1-L) = RAER + ENDDO + ENDDO + CALL Outfld( 'SAD_PSC', outTmp(:nY,:), nY, LCHNK ) + CALL Outfld( 'RAD_PSC', radTmp(:nY,:), nY, LCHNK ) + ENDIF + + IF ( hist_fld_active('SAD_SULFC') .OR. hist_fld_active('RAD_SULFC') ) THEN + outTmp = 0.0e+00_r8 + DO J = 1, nY + DO L = 1, nZ + CALL GET_STRAT_OPT(State_Chm,1,J,L,2,RAER,REFF,SADSTRAT,XSASTRAT) + outTmp(J,nZ+1-L) = SADSTRAT + radTmp(J,nZ+1-L) = RAER + ENDDO + ENDDO + CALL Outfld( 'SAD_SULFC', outTmp(:nY,:), nY, LCHNK ) + CALL Outfld( 'RAD_SULFC', radTmp(:nY,:), nY, LCHNK ) + ENDIF + + IF ( hist_fld_active('SAD_AERO') .OR. hist_fld_active('SAD_TROP') ) THEN + outTmp(:nY,:) = SUM(State_Chm%AeroArea(1,:nY,nZ:1:-1,:), DIM=3) + CALL Outfld( 'SAD_AERO', outTmp(:nY,:), nY, LCHNK ) + ENDIF + + IF ( hist_fld_active('SAD_TROP') ) THEN + DO J = 1, nY + DO L = 1, nZ + IF ( .NOT. State_Met%InTroposphere(1,J,nZ+1-L) ) THEN + outTmp(J,L) = 0.0e+00_r8 + ENDIF + ENDDO + ENDDO + CALL Outfld( 'SAD_TROP', outTmp(:nY,:), nY, LCHNK ) + ENDIF + + IF ( hist_fld_active('REFF_AERO') ) THEN + !outTmp(:nY,:) = State_Chm%AeroRadi(1,:nY,nZ:1:-1,:) + !CALL Outfld( 'REFF_AERO', outTmp(:nY,:), nY, LCHNK ) + ENDIF + + IF ( hist_fld_active('SULF_TROP') ) THEN + outTmp(:nY,:) = State_Chm%AeroArea(1,:nY,nZ:1:-1,NDUST+1) + CALL Outfld( 'SULF_TROP', outTmp(:nY,:), nY, LCHNK ) + ENDIF + + ! =============================================== + ! Diagnose stratospheric quantities + ! =============================================== + + outTmp(:nY,:) = State_Chm%Species(i_HNO3)%Conc(1,:nY,nZ:1:-1) * MWDry / MW_HNO3 + CALL Outfld( 'HNO3_GAS', outTmp(:nY,:), nY, LCHNK ) + + ! TMMF, this requires to have access to the AERFRAC variable in ucx_mod. + !outTmp(:nY,:) = AERFRAC(1,:nY,nZ:1:-1,2) + !CALL Outfld( 'HNO3_STS', outTmp(:nY,:), nY, LCHNK ) + + outTmp = 0.0e+00_r8 + DO J = 1, nY + DO L = 1, nZ + IF ( State_Met%InTroposphere(1,J,nZ+1-L) ) CYCLE + outTmp(J,L) = State_Chm%Species(i_NIT)%Conc(1,J,nZ+1-L) * MWDry / MW_NIT + ENDDO + ENDDO + CALL Outfld( 'HNO3_NAT', outTmp(:nY,:), nY, LCHNK ) + + outTmp(:nY,:) = outTmp(:nY,:) + & + ! AERFRAC(1,:nY,nZ:1:-1,2) + & + State_Chm%Species(i_HNO3)%Conc(1,:nY,nZ:1:-1) * MWDry / MW_HNO3 + CALL Outfld( 'HNO3_TOTAL', outTmp(:nY,:), nY, LCHNK ) + + outTmp(:nY,:) = State_Chm%Species(i_H2O)%Conc(1,:nY,nZ:1:-1) * MWDry / MW_H2O + CALL Outfld( 'H2O_GAS', outTmp(:nY,:), nY, LCHNK ) + + outTmp(:nY,:) = State_Chm%Species(i_HCl)%Conc(1,:nY,nZ:1:-1) * MWDry / MW_HCl + CALL Outfld( 'HCL_GAS', outTmp(:nY,:), nY, LCHNK ) + + !outTmp(:nY,:) = AERFRAC(1,:nY,nZ:1:-1,3) + !CALL Outfld( 'HCL_STS', outTmp(:nY,:), nY, LCHNK ) + + outTmp(:nY,:) = 0.0e+00_r8 + !outTmp(:nY,:) = AERFRAC(1,:nY,nZ:1:-1,3) + outTmp(:nY,:) = outTmp(:nY,:) + & + State_Chm%Species(i_HCl)%Conc(1,:nY,nZ:1:-1) * MWDry / MW_HCl + CALL Outfld( 'HCL_TOTAL', outTmp(:nY,:), nY, LCHNK ) + + ! =============================================== + ! Diagnose dry deposition velocities and fluxes + ! =============================================== + + IF ( Input_Opt%LDryD ) THEN + DO N = 1, State_Chm%nDryDep + ND = NDVZIND(N) + SpcName = 'DV_'//to_upper(TRIM(depName(N))) + IF ( .NOT. hist_fld_active(TRIM(SpcName)) ) CYCLE + CALL OutFld( TRIM(SpcName), State_Chm%DryDepVel(1,:nY,ND), nY, LCHNK ) + ENDDO + + DO N = 1, State_Chm%nAdvect + ! Get the species ID from the advected species ID + L = State_Chm%Map_Advect(N) + + ! Get info about this species from the species database + SpcInfo => State_Chm%SpcData(L)%Info + SpcName = 'DF_'//to_upper(TRIM(SpcInfo%Name)) + + IF ( .NOT. hist_fld_active(TRIM(SpcName)) ) CYCLE + ! SurfaceFlux is Emissions - Drydep, but Emissions = 0, as it is applied + ! externally + CALL OutFld( TRIM(SpcName), -State_Chm%SurfaceFlux(1,:nY,N), nY, LCHNK ) + + ! Free pointer + SpcInfo => NULL() + ENDDO + ENDIF + + ! =============================================== + ! Diagnose surface fluxes (emissions - drydep) + ! =============================================== + + DO N = iFirstCnst, pcnst + SpcName = TRIM(sflxnam(N)) + IF ( TRIM(SpcName) == '' ) CYCLE + IF ( .NOT. hist_fld_active(TRIM(SpcName)) ) CYCLE + CALL OutFld( TRIM(SpcName), cam_in%cflx(:nY,N), nY, LCHNK ) + ENDDO + + ! =============================================== + ! Diagnose chemical tendencies + ! =============================================== + + ! Chemical tendencies in kg/kg/s + DO N = 1, gas_pcnst + SpcName = TRIM(srcnam(N)) + IF ( TRIM(SpcName) == '' ) CYCLE + IF ( .NOT. hist_fld_active(TRIM(SpcName)) ) CYCLE + CALL OutFld( TRIM(SpcName), mmr_tend(:nY,:nZ,N), nY, LCHNK ) + ENDDO + + ! Chemical tendencies in kg/s + DO N = 1, gas_pcnst + SpcName = TRIM(dtchem_name(N)) + IF ( .NOT. hist_fld_active(TRIM(SpcName)) ) CYCLE + outTmp = 0.0e+0_r8 + outTmp(:nY,:nZ) = mmr_tend(:nY,:nZ,N) * REAL(State_Met%AD(1,:nY,nZ:1:-1),r8) + CALL OutFld( TRIM(SpcName), outTmp(:nY,:), nY, LCHNK ) + ENDDO + + ! =============================================== + ! Diagnose fields corresponding to State_Met + ! =============================================== + + SpcName = 'SZA' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp(:nY,1) = ACOS(MIN(MAX(State_Met%SUNCOS(1,:nY),-1._r8),1._r8))/pi*180.e+0_r8 + CALL Outfld( TRIM(SpcName), outTmp(:nY,1) , nY, LCHNK ) + ENDIF + + SpcName = 'U_SRF' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp(:nY,:) = state%u(:nY,:) + CALL Outfld( TRIM(SpcName), outTmp(:nY,:) , nY, LCHNK ) + ENDIF + + SpcName = 'V_SRF' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp(:nY,:) = state%v(:nY,:) + CALL Outfld( TRIM(SpcName), outTmp(:nY,:) , nY, LCHNK ) + ENDIF + + SpcName = 'Q_SRF' + IF ( hist_fld_active(TRIM(SpcName)) ) THEN + outTmp(:nY,:) = State_Chm%Species(i_H2O)%Conc(1,:nY,nZ:1:-1) + CALL Outfld( TRIM(SpcName), outTmp(:nY,:) , nY, LCHNK ) + ENDIF + + ! Cleanup + Current => NULL() + Item => NULL() + Ptr0d_8 => NULL() + Ptr1d_8 => NULL() + Ptr2d_8 => NULL() + Ptr3d_8 => NULL() + + END SUBROUTINE GC_Diagnostics_Calc + + END MODULE GeosChem_Diagnostics_Mod + diff --git a/src/chemistry/geoschem/geoschem_emissions_mod.F90 b/src/chemistry/geoschem/geoschem_emissions_mod.F90 new file mode 100644 index 0000000000..9d9dfc6bd1 --- /dev/null +++ b/src/chemistry/geoschem/geoschem_emissions_mod.F90 @@ -0,0 +1,532 @@ +! Module geoschem_emissions_mod contains routines which retrieve +! emission fluxes from HEMCO and transfers it back to the CESM-GC interface +! 07 Oct 2020 - T. M. Fritz - Initial version +MODULE GeosChem_Emissions_Mod + + ! CAM modules + use cam_abortutils, only : endrun + use cam_logfile, only : iulog + use chem_mods, only : iFirstCnst + use constituents, only : pcnst, cnst_name + use shr_kind_mod, only : r8 => shr_kind_r8, shr_kind_cl + use shr_megan_mod, only : shr_megan_mechcomps, shr_megan_mechcomps_n + use spmd_utils, only : MasterProc + + IMPLICIT NONE + + PRIVATE + + PUBLIC :: GC_Emissions_Init + PUBLIC :: GC_Emissions_Calc + PUBLIC :: GC_Emissions_Final + + ! Constituent number for NO + INTEGER :: iNO + + ! Aerosol constituent number + INTEGER :: iBC1 + INTEGER :: iBC4 + INTEGER :: iH2SO4 + + INTEGER :: iBCPI + INTEGER :: iBCPO + INTEGER :: iOCPI + INTEGER :: iOCPO + INTEGER :: iSO4 + + ! MEGAN Emissions + INTEGER, ALLOCATABLE :: megan_indices_map(:) + REAL(r8), ALLOCATABLE :: megan_wght_factors(:) + + ! Cache for is_extfrc? + LOGICAL, ALLOCATABLE :: pcnst_is_extfrc(:) ! no idea why the indexing is not 1:gas_pcnst or why iFirstCnst can be < 0 + +CONTAINS + + SUBROUTINE GC_Emissions_Init( ) + + ! CAM modules + use cam_history, only : addfld, add_default, horiz_only + use chem_mods, only : adv_mass + use constituents, only : cnst_get_ind + use fire_emissions, only : fire_emissions_init + use infnan, only : NaN, assignment(=) + use mo_chem_utls, only : get_spc_ndx, get_extfrc_ndx + use phys_control, only : phys_getopts + use physics_types, only : physics_state + + ! Integers + INTEGER :: IERR + INTEGER :: N, II + + ! Logicals + LOGICAL :: history_aerosol + LOGICAL :: history_chemistry + LOGICAL :: history_cesm_forcing + + ! Strings + CHARACTER(LEN=shr_kind_cl) :: SpcName + CHARACTER(LEN=shr_kind_cl) :: Description + + ! Real + REAL(r8) :: MW + + CALL phys_getopts( history_aerosol_out = history_aerosol, & + history_chemistry_out = history_chemistry, & + history_cesm_forcing_out = history_cesm_forcing ) + + ! Get constituent index for NO + CALL cnst_get_ind('NO', iNO, abort=.True.) + + !----------------------------------------------------------------------- + ! ... MEGAN emissions + !----------------------------------------------------------------------- + IF ( shr_megan_mechcomps_n > 0 ) THEN + + ALLOCATE( megan_indices_map(shr_megan_mechcomps_n), STAT=IERR ) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating megan_indices_map') + ALLOCATE( megan_wght_factors(shr_megan_mechcomps_n), STAT=IERR ) + IF ( IERR .NE. 0 ) CALL ENDRUN('Failure while allocating megan_wght_factors') + megan_wght_factors(:) = NaN + + DO N = 1, shr_megan_mechcomps_n + SpcName = TRIM(shr_megan_mechcomps(N)%name) + + ! Special handlings for GEOS-Chem species + IF ( TRIM(SpcName) == 'HCN' ) THEN + SpcName = 'None' + MW = 27.025140_r8 ! Taken from pp_trop_strat_mam4_vbs + ELSEIF ( TRIM(SpcName) == 'C2H4' ) THEN + SpcName = 'None' + MW = 28.051600_r8 ! Taken from pp_trop_strat_mam4_vbs + ENDIF + !IF ( TRIM(SpcName) == 'MTERP' ) THEN + ! SpcName = 'MTPA' + !ELSEIF ( TRIM(SpcName) == 'BCARY' ) THEN + ! SpcName = 'None' + ! MW = 204.342600_r8 ! Taken from pp_trop_strat_mam4_vbs + !ELSEIF ( TRIM(SpcName) == 'CH3OH' ) THEN + ! SpcName = 'MOH' + !ELSEIF ( TRIM(SpcName) == 'C2H5OH' ) THEN + ! SpcName = 'EOH' + !ELSEIF ( TRIM(SpcName) == 'CH3CHO' ) THEN + ! SpcName = 'ALD2' + !ELSEIF ( TRIM(SpcName) == 'CH3COOH' ) THEN + ! SpcName = 'ACTA' + !ELSEIF ( TRIM(SpcName) == 'CH3COCH3' ) THEN + ! SpcName = 'ACET' + !ELSEIF ( TRIM(SpcName) == 'HCN' ) THEN + ! SpcName = 'None' + ! MW = 27.025140_r8 ! Taken from pp_trop_strat_mam4_vbs + !ELSEIF ( TRIM(SpcName) == 'C2H4' ) THEN + ! SpcName = 'None' + ! MW = 28.051600_r8 ! Taken from pp_trop_strat_mam4_vbs + !ELSEIF ( TRIM(SpcName) == 'C3H6' ) THEN + ! SpcName = 'PRPE' + !ELSEIF ( TRIM(SpcName) == 'BIGALK' ) THEN + ! ! BIGALK = Pentane + Hexane + Heptane + Tricyclene + ! SpcName = 'ALK4' + !ELSEIF ( TRIM(SpcName) == 'BIGENE' ) THEN + ! ! BIGENE = butene (C4H8) + ! SpcName = 'PRPE' ! Lumped >= C3 alkenes + !ELSEIF ( TRIM(SpcName) == 'TOLUENE' ) THEN + ! SpcName = 'TOLU' + !ENDIF + + CALL cnst_get_ind (SpcName, megan_indices_map(N), abort=.False.) + + II = get_spc_ndx(SpcName) + IF ( II > 0 ) THEN + SpcName = TRIM(shr_megan_mechcomps(N)%name) + megan_wght_factors(N) = adv_mass(II)*1.e-3_r8 ! kg/moles (to convert moles/m2/sec to kg/m2/sec) + Description = TRIM(SpcName)//' MEGAN emissions flux (released as '//TRIM(SpcName)//' in GEOS-Chem)' + ELSEIF ( TRIM(SpcName) == 'None' ) THEN + SpcName = TRIM(shr_megan_mechcomps(N)%name) + megan_wght_factors(N) = MW*1.e-3_r8 ! kg/moles + IF ( MasterProc ) Write(iulog,*) " MEGAN ", TRIM(SpcName), & + " emissions will be ignored as no species match in GEOS-Chem." + Description = TRIM(SpcName)//' MEGAN emissions flux (not released in GEOS-Chem)' + ELSE + SpcName = TRIM(shr_megan_mechcomps(N)%name) + CALL ENDRUN( 'chem_init: MEGAN compound not in chemistry mechanism : '//TRIM(SpcName)) + ENDIF + + ! MEGAN history fields + CALL Addfld( 'MEG_'//TRIM(SpcName), horiz_only, 'A', 'kg/m2/s', & + Description ) + + IF ( history_chemistry ) THEN + CALL Add_default('MEG_'//TRIM(SpcName), 1, ' ') + ENDIF + ENDDO + ENDIF + + DO N = iFirstCnst, pcnst + SpcName = TRIM(cnst_name(N))//'_XFRC' + CALL Addfld( TRIM(SpcName), (/ 'lev' /), 'A', 'molec/cm3/s', & + 'External forcing for '//TRIM(cnst_name(N))) + SpcName = TRIM(cnst_name(N))//'_CLXF' + CALL Addfld( TRIM(SpcName), horiz_only, 'A', 'molec/cm2/s', & + 'Vertically-integrated external forcing for '//TRIM(cnst_name(N))) + IF ( history_aerosol .OR. history_chemistry ) THEN + CALL Add_Default( TRIM(SpcName), 1, ' ' ) + ENDIF + IF ( history_cesm_forcing .AND. TRIM(cnst_name(N)) == 'NO2' ) THEN + CALL Add_Default( TRIM(SpcName), 1, ' ' ) + ENDIF + SpcName = TRIM(cnst_name(N))//'_CMXF' + CALL Addfld( TRIM(SpcName), horiz_only, 'A', 'kg/m2/s', & + 'Vertically-integrated external forcing for '//TRIM(cnst_name(N))) + IF ( history_aerosol .OR. history_chemistry ) THEN + CALL Add_Default( TRIM(SpcName), 1, ' ' ) + ENDIF + IF ( history_cesm_forcing .AND. TRIM(cnst_name(N)) == 'NO2' ) THEN + CALL Add_Default( TRIM(SpcName), 1, ' ' ) + ENDIF + ENDDO + + CALL Addfld( 'NO_Lightning', (/ 'lev' /), 'A','molec/cm3/s', & + 'lightning NO source' ) + + !----------------------------------------------------------------------- + ! ... Fire emissions + !----------------------------------------------------------------------- + CALL fire_emissions_init() + + ! Initialize pcnst_is_extfrc cache to avoid lengthy lookups in future timesteps + ! on the get_extfrc_ndx routine. (hplin 1/20/23) + if(.not. allocated(pcnst_is_extfrc)) then + allocate(pcnst_is_extfrc(pcnst - iFirstCnst + 1)) + endif + do n = iFirstCnst, pcnst + pcnst_is_extfrc(n - iFirstCnst + 1) = (get_extfrc_ndx(trim(cnst_name(n))) > 0) + enddo + + END SUBROUTINE GC_Emissions_Init + + SUBROUTINE GC_Emissions_Calc( state, hco_pbuf2d, State_Met, cam_in, eflx, iStep ) + ! Subroutine GC_Emissions_Calc retrieves emission fluxes + ! from HEMCO and returns a 3-D array of emission flux to the CESM-GC + ! interface. On top of passing data, this routine handles a number of checks. + + ! CAM modules + use aero_model, only : aero_model_emissions ! Aerosol emissions + use cam_history, only : outfld + use camsrfexch, only : cam_in_t + use constituents, only : cnst_get_ind, cnst_mw + use fire_emissions, only : fire_emissions_srf, fire_emissions_vrt ! Fire emissions + use mo_lightning, only : prod_NO! Lightning emissions + use physconst, only : rga, avogad + use physics_buffer, only : pbuf_get_index, pbuf_get_chunk + use physics_buffer, only : physics_buffer_desc, pbuf_get_field + use physics_types, only : physics_state + use ppgrid, only : pcols, pver, begchunk + use srf_field_check, only : active_Fall_flxvoc ! MEGAN emissions + use string_utils, only : to_upper + + ! GEOS-Chem modules + use PhysConstants, only : AVO, PI + use State_Met_Mod, only : MetState + + TYPE(physics_state), INTENT(IN ) :: state ! Physics state variables + TYPE(physics_buffer_desc), POINTER, INTENT(IN ) :: hco_pbuf2d(:,:) ! Pointer to 2-D pbuf + TYPE(MetState), INTENT(IN ) :: State_Met ! Meteorology State object + INTEGER, INTENT(IN ) :: iStep + + TYPE(cam_in_t), INTENT(INOUT) :: cam_in ! import state + REAL(r8), INTENT( OUT) :: eflx(pcols,pver,pcnst) ! 3-D emissions in kg/m2/s + + ! Integers + INTEGER :: LCHNK + INTEGER :: nY, nZ + INTEGER :: J, L, N + INTEGER :: RC ! return code + INTEGER :: tmpIdx ! pbuf field id + + INTEGER :: id_O3, id_HNO3 ! Species IDs for reuse + + ! Logical + LOGICAL :: rootChunk + + ! Objects + TYPE(physics_buffer_desc), POINTER :: pbuf_chnk(:) ! slice of pbuf in current chunk + + ! Real + REAL(r8), POINTER :: pbuf_ik(:,:) ! pointer to pbuf data (/pcols,pver/) + REAL(r8), POINTER :: pbuf_i(:) ! pointer to 2-D (1-D in CAM) data (/pcols/) + REAL(r8), DIMENSION(state%NCOL,PVER+1) :: zint ! Interface geopotential in km + REAL(r8), DIMENSION(state%NCOL) :: zsurf ! Surface height + REAL(r8) :: SCALFAC ! Multiplying factor + REAL(r8) :: megflx(pcols) ! For MEGAN emissions + REAL(r8), PARAMETER :: m2km = 1.e-3_r8 + + ! Strings + CHARACTER(LEN=shr_kind_cl) :: SpcName + CHARACTER(LEN=shr_kind_cl) :: fldname_ns ! field name HCO_* + + + ! Initialize pointers + pbuf_chnk => NULL() + pbuf_ik => NULL() + pbuf_i => NULL() + + ! LCHNK: which chunk we have on this process + LCHNK = state%LCHNK + ! nY: number of atmospheric columns on this chunk + nY = state%NCOL + nZ = PVER + rootChunk = ( MasterProc .AND. ( LCHNK.EQ.BEGCHUNK ) ) + + ! Initialize emission flux + eflx(:,:,:) = 0.0e+0_r8 + + DO N = iFirstCnst, pcnst + fldname_ns = 'HCO_'//TRIM(cnst_name(N)) + tmpIdx = pbuf_get_index(fldname_ns, RC) + + IF ( tmpIdx < 0 .OR. ( iStep == 1 ) ) THEN + IF ( rootChunk ) Write(iulog,'(a,a)') " GC_Emissions_Calc: Field not found ", & + TRIM(fldname_ns) + ELSE + ! This is already in chunk, retrieve it + pbuf_chnk => pbuf_get_chunk(hco_pbuf2d, LCHNK) + + ! Check if we need to get 3-D, or 2-D data + IF (pcnst_is_extfrc(N - iFirstCnst + 1)) THEN + CALL pbuf_get_field(pbuf_chnk, tmpIdx, pbuf_ik) + + IF ( .NOT. ASSOCIATED(pbuf_ik) ) THEN ! Sanity check + CALL ENDRUN("GC_Emissions_Calc: FATAL - tmpIdx > 0 but pbuf_ik not associated (E-1)") + ENDIF + + eflx(1:nY,:nZ,N) = pbuf_ik(1:nY,:nZ) + + ! Reset pointers + pbuf_ik => NULL() + ELSE ! 2-D + CALL pbuf_get_field(pbuf_chnk, tmpIdx, pbuf_i) + + IF ( .NOT. ASSOCIATED(pbuf_i) ) THEN ! Sanity check + CALL ENDRUN("GC_Emissions_Calc: FATAL - tmpIdx > 0 but pbuf_i not associated (E-2)") + ENDIF + + ! note: write to nZ level here as this is surface + eflx(1:nY,nZ,N) = pbuf_i(1:nY) + + ! Reset pointers + pbuf_i => NULL() + ENDIF + + pbuf_chnk => NULL() + + !IF ( MINVAL(eflx(:nY,:nZ,N)) < 0.0e+00_r8 ) THEN + ! Write(iulog,*) " GC_Emissions_Calc: HEMCO emission flux is negative for ", & + ! TRIM(cnst_name(N)), " with value ", MINVAL(eflx(:nY,:nZ,N)), " at ", & + ! MINLOC(eflx(:nY,:nZ,N)) + !ENDIF + + IF ( rootChunk .AND. (iStep == 2) .AND. ( MAXVAL(eflx(:nY,:nZ,N)) > 0.0e+0_r8 ) ) THEN + ! Only print this once + Write(iulog,'(a,a,a,a)') " GC_Emissions_Calc: HEMCO flux ", & + TRIM(fldname_ns), " added to ", TRIM(cnst_name(N)) + Write(iulog,'(a,a,E16.4)') " GC_Emissions_Calc: Maximum flux ", & + TRIM(fldname_ns), MAXVAL(eflx(:nY,:nZ,N)) + ENDIF + ENDIF + ENDDO + + !----------------------------------------------------------------------- + ! Deposition fluxes from HEMCO + !----------------------------------------------------------------------- + + ! Deposition velocities in HEMCO are now handled within HEMCO_CESM for a + ! hardcoded list of species, primarily for the SeaFlux extension. + ! This is not to be confused with dry deposition fluxes which are not + ! handled by HEMCO. + + ! Part 2: Handle special deposition fluxes for the ParaNOx extension + ! for PAR_O3_DEP and PAR_HNO3_DEP + CALL cnst_get_ind('O3', id_O3) + CALL cnst_get_ind('HNO3', id_HNO3) + + ! write(iulog,*) 'id_O3, cnst_name, id_HNO3, cnst_name', id_O3, cnst_name(id_O3), id_HNO3, cnst_name(id_HNO3) + + tmpIdx = pbuf_get_index('HCO_PAR_O3_DEP', RC) + IF(tmpIdx < 0 .OR. ( iStep == 1 )) then + ! No ParaNOx dep flux for O3 + ELSE + pbuf_chnk => pbuf_get_chunk(hco_pbuf2d, LCHNK) + CALL pbuf_get_field(pbuf_chnk, tmpIdx, pbuf_i) + + IF ( .NOT. ASSOCIATED(pbuf_i) ) THEN ! Sanity check + CALL ENDRUN("GC_Emissions_Calc: FATAL - tmpIdx > 0 but pbuf_i not associated (2)") + ENDIF + + ! apply loss flux to surface (level nZ) + eflx(1:NY,nZ,id_O3) = eflx(1:NY,nZ,id_O3) - pbuf_i(1:nY) + + !IF ( MINVAL(eflx(:nY,nZ,id_O3)) < 0.0e+00_r8 ) THEN + ! Write(iulog,*) " GC_Emissions_Calc: HEMCO sfc flux after ParaNOx is negative for O3 with value ", MINVAL(eflx(:nY,:nZ,id_O3)), " at ", & + ! MINLOC(eflx(:nY,nZ,id_O3)) + !ENDIF + + IF ( rootChunk .and. ( MINVAL(pbuf_i(1:nY)) < 0.0e+0_r8 ) ) THEN + Write(iulog,'(a,a,a,a)') " GC_Emissions_Calc: HEMCO dflx(paranox) O3 added to ", TRIM(cnst_name(id_O3)) + Write(iulog,'(a,a,E16.4)') " GC_Emissions_Calc: Minval dflx(paranox), eflx(sfc) O3 ", MINVAL(pbuf_i(1:nY)), MINVAL(eflx(:nY,nZ,id_O3)) + ENDIF + + ! Reset pointers + pbuf_i => NULL() + pbuf_chnk => NULL() + ENDIF + + tmpIdx = pbuf_get_index('HCO_PAR_HNO3_DEP', RC) + IF(tmpIdx < 0 .OR. ( iStep == 1 )) then + ! No ParaNOx dep flux for HNO3 + ELSE + pbuf_chnk => pbuf_get_chunk(hco_pbuf2d, LCHNK) + CALL pbuf_get_field(pbuf_chnk, tmpIdx, pbuf_i) + + IF ( .NOT. ASSOCIATED(pbuf_i) ) THEN ! Sanity check + CALL ENDRUN("GC_Emissions_Calc: FATAL - tmpIdx > 0 but pbuf_i not associated (3)") + ENDIF + + eflx(1:NY,nZ,id_HNO3) = eflx(1:NY,nZ,id_HNO3) - pbuf_i(1:nY) + + !IF ( MINVAL(eflx(:nY,nZ,id_HNO3)) < 0.0e+00_r8 ) THEN + ! Write(iulog,*) " GC_Emissions_Calc: HEMCO sfc flux after ParaNOx is negative for HNO3 with value ", MINVAL(eflx(:nY,nZ,id_HNO3)), " at ", & + ! MINLOC(eflx(:nY,nZ,id_HNO3)) + !ENDIF + + IF ( rootChunk .and. ( MINVAL(pbuf_i(1:nY)) < 0.0e+0_r8 ) ) THEN + Write(iulog,'(a,a,a,a)') " GC_Emissions_Calc: HEMCO dflx(paranox) HNO3 added to ", TRIM(cnst_name(id_HNO3)) + Write(iulog,'(a,a,E16.4)') " GC_Emissions_Calc: Minval dflx(paranox), eflx(sfc) HNO3 ", MINVAL(pbuf_i(1:nY)), MINVAL(eflx(:nY,nZ,id_HNO3)) + ENDIF + + ! Reset pointers + pbuf_i => NULL() + pbuf_chnk => NULL() + ENDIF + +#if defined( MODAL_AERO ) + + !----------------------------------------------------------------------- + ! Aerosol emissions (dust + seasalt) ... + !----------------------------------------------------------------------- + call aero_model_emissions( state, cam_in ) + + ! Since GEOS-Chem DST* aerosols are inherited from MAM's DST, we do not + ! need to feed MAM dust emissions into the GEOS-Chem DST* constituents + ! Same thing applies for sea salt. + + ! HEMCO aerosol emissions are fed to MAM through the HEMCO_Config.rc + ! where all GEOS-Chem aerosols (BCPI, BCPO, OCPI, OCPO, SO4) have been + ! replaced with the corresponding MAM aerosols + +#endif + + ! Output fields before lightning NO emissions are applied to eflx + ! Make sure that we do not include surface emissions in the diagnostics! + DO N = iFirstCnst, pcnst + SpcName = TRIM(cnst_name(N))//'_XFRC' + ! Convert from kg/m2/s to molec/cm3/s + ! Note 1: cnst_mw is in kg/kmole + ! Note 2: avogad is in molecules/kmole + CALL Outfld( TRIM(SpcName), eflx(:nY,:nZ,N) / State_Met%BXHEIGHT(1,:nY,nZ:1:-1) * 1.0E-06 / cnst_mw(N) * avogad, nY, LCHNK ) + + SpcName = TRIM(cnst_name(N))//'_CLXF' + ! Convert from kg/m2/s to molec/cm2/s + ! Note 1: cnst_mw is in kg/kmole + ! Note 2: avogad is in molecules/kmole + CALL Outfld( TRIM(SpcName), SUM(eflx(:nY,:nZ-1,N), DIM=2) * 1.0E-04 / cnst_mw(N) * avogad, nY, LCHNK ) + + SpcName = TRIM(cnst_name(N))//'_CMXF' + CALL Outfld( TRIM(SpcName), SUM(eflx(:nY,:nZ-1,N), DIM=2), nY, LCHNK ) + ENDDO + + !----------------------------------------------------------------------- + ! Lightning NO emissions + !----------------------------------------------------------------------- + N = iNO + + ! prod_NO is in atom N cm^-3 s^-1 <=> molec cm^-3 s^-1 + ! We need to convert this to kg NO/m2/s + ! Multiply by MWNO * BXHEIGHT * 1.0E+06 / AVO + ! = mole/molec * kg NO/mole * m * cm^3/m^3 + ! cnst_mw(N) is in g/mole + SCALFAC = cnst_mw(N) * 1.0E-03 * 1.0E+06 / AVO + DO J = 1, nY + DO L = 1, nZ + eflx(J,L,N) = eflx(J,L,N) & + + prod_NO(J,L,LCHNK) & + * State_Met%BXHEIGHT(1,J,nZ+1-L) & + * SCALFAC + ENDDO + ENDDO + + CALL Outfld( 'NO_Lightning', prod_NO(:nY,:nZ,LCHNK), nY, LCHNK ) + + !----------------------------------------------------------------------- + ! MEGAN emissions ... + !----------------------------------------------------------------------- + + IF ( active_Fall_flxvoc .AND. shr_megan_mechcomps_n > 0 ) THEN + ! set MEGAN fluxes + DO N = 1, shr_megan_mechcomps_n + DO J = 1, nY + megflx(J) = -cam_in%meganflx(J,N) * megan_wght_factors(N) + ENDDO + IF ( ( megan_indices_map(N) > 0 ) .AND. ( megan_wght_factors(N) > 0.0e+00_r8 ) ) THEN + DO J = 1, nY + cam_in%cflx(J,megan_indices_map(N)) = cam_in%cflx(J,megan_indices_map(N)) & + + megflx(J) + ENDDO + ENDIF + ! output MEGAN emis fluxes to history + CALL Outfld('MEG_'//TRIM(shr_megan_mechcomps(N)%name), megflx(:nY), nY, LCHNK) + ENDDO + ENDIF + + !----------------------------------------------------------------------- + ! Fire surface emissions if not elevated forcing + !----------------------------------------------------------------------- + + CALL fire_emissions_srf( LCHNK, nY, cam_in%fireflx, cam_in%cflx ) + + !----------------------------------------------------------------------- + ! Apply CLM emissions (for elevated forcing) + !----------------------------------------------------------------------- + + ! Compute geopotential height in km (needed for vertical distribution of + ! fire emissions + zsurf(:nY) = rga * state%phis(:nY) + DO L = 1, nZ + zint(:nY,L) = m2km * ( state%zi(:nY,L) + zsurf(:nY) ) + ENDDO + L = nZ+1 + zint(:nY,L) = m2km * ( state%zi(:nY,L) + zsurf(:nY) ) + + ! Distributed fire emissions if elevated forcing + ! extfrc is in molec/cm3/s + ! TMMF - vertical distribution of fire emissions is not implemented yet + !CALL fire_emissions_vrt( nY, LCHNK, zint, cam_in%fireflx, cam_in%fireztop, extfrc ) + + ! Near-surface emissions are now emitted directly to GEOS-Chem Species array + ! for consistency with CAM-chem implementation of HEMCO + ! (but not with GEOS-Chem standalone, where fluxes are mixed by the turbulence routines) + ! Refer to discussion here: https://github.com/ESCOMP/CAM/pull/560#discussion_r1084559191 + ! + ! To replicate old behavior, uncomment these two lines below: + ! cam_in%cflx(1:nY,:) = cam_in%cflx(1:nY,:) + eflx(1:nY,nZ,:) + ! eflx(1:nY,nZ,:) = 0.0e+00_r8 + + END SUBROUTINE GC_Emissions_Calc + + SUBROUTINE GC_Emissions_Final + + IF ( ALLOCATED( megan_indices_map ) ) DEALLOCATE( megan_indices_map ) + IF ( ALLOCATED( megan_wght_factors ) ) DEALLOCATE( megan_wght_factors ) + + END SUBROUTINE GC_Emissions_Final + +END MODULE GeosChem_Emissions_Mod diff --git a/src/chemistry/geoschem/geoschem_history_mod.F90 b/src/chemistry/geoschem/geoschem_history_mod.F90 new file mode 100644 index 0000000000..ef4c2044e1 --- /dev/null +++ b/src/chemistry/geoschem/geoschem_history_mod.F90 @@ -0,0 +1,1202 @@ +#define _ASSERT(cond,msg) if(.not.cond) then; print *, "assertion error: ", Iam, __LINE__; call endrun("assertion error - look above - in geoschem_history_mod.F90"); endif +#define _Iam_(name) character(len=255) :: Iam=name +#define __Iam__(name) integer :: STATUS; _Iam_(name) +! Above are compatibility shorthands to avoid excessive divergence from +! MAPL-based code. (hplin, 10/19/22) +!------------------------------------------------------------------------------ +! GEOS-Chem Global Chemical Transport Model ! +!------------------------------------------------------------------------------ +!BOP +! +! !MODULE: geoschem_history_mod.F90 +! +! !DESCRIPTION: Module GeosChem\_History\_Mod interfaces between the CAM history +! component, the HISTORY.rc configuration file, and the GEOS-Chem State registry. +! This module is based off GCHP\_HistoryExports\_Mod originally developed by +! Lizzie Lundgren for GCHP. +!\\ +!\\ +! !INTERFACE: +! +MODULE GeosChem_History_Mod +! +! !USES: +! + ! CAM modules + USE cam_abortutils, ONLY : endrun + + ! GEOS-Chem modules + USE DiagList_Mod, ONLY : DgnItem, DgnList + USE DiagList_Mod, ONLY : Init_DiagList, Print_DiagList + USE ErrCode_Mod, ONLY : GC_SUCCESS, GC_FAILURE, GC_ERROR + USE Precision_Mod, ONLY : fp, f4, f8 + USE TaggedDiagList_Mod, ONLY : TaggedDgnList + USE TaggedDiagList_Mod, ONLY : Init_TaggedDiagList, Print_TaggedDiagList + + IMPLICIT NONE + PRIVATE +! +! !PUBLIC MEMBER FUNCTIONS: +! + PUBLIC :: HistoryExports_SetServices + PUBLIC :: HistoryExports_SetDataPointers + PUBLIC :: CopyGCStates2Exports + PUBLIC :: Destroy_HistoryConfig +! +! !PRIVATE: +! + PRIVATE :: Init_HistoryConfig + PRIVATE :: Init_HistoryExport + PRIVATE :: Init_HistoryExportsList + PRIVATE :: Append_HistoryExportsList + PRIVATE :: Check_HistoryExportsList + PRIVATE :: Print_HistoryExportsList + ! +! !PUBLIC TYPES +! + ! History Configuration Object + TYPE, PUBLIC :: HistoryConfigObj + + CHARACTER(LEN=255) :: ROOT ! TODO: needed? + CHARACTER(LEN=255) :: ConfigFileName + LOGICAL :: ConfigFileRead + TYPE(HistoryExportsListObj), POINTER :: HistoryExportsList + TYPE(DgnList) :: DiagList + TYPE(TaggedDgnList) :: TaggedDiagList + + END TYPE HistoryConfigObj +! +! !PRIVATE TYPES +! + ! History Exports Linked List + TYPE :: HistoryExportsListObj + + TYPE(HistoryExportObj), POINTER :: head + INTEGER :: numExports + + END TYPE HistoryExportsListObj + + ! History Export Object + TYPE :: HistoryExportObj + + CHARACTER(LEN=255) :: name + CHARACTER(LEN=255) :: metadataID + CHARACTER(LEN=255) :: registryID + CHARACTER(LEN=255) :: long_name + CHARACTER(LEN=255) :: units + INTEGER :: vloc + INTEGER :: rank + INTEGER :: type + LOGICAL :: isMet + LOGICAL :: isChem + LOGICAL :: isDiag + TYPE(HistoryExportObj), POINTER :: next + + ! Pointers to temporaries for CAM Export and GEOS-Chem State + ! TODO: for now, include all possible data types in the registry. + REAL(fp), POINTER :: GCStateData0d + REAL(fp), POINTER :: GCStateData1d(:) + REAL(fp), POINTER :: GCStateData2d(:,:) + REAL(fp), POINTER :: GCStateData3d(:,:,:) + REAL(f4), POINTER :: GCStateData0d_4 + REAL(f4), POINTER :: GCStateData1d_4(:) + REAL(f4), POINTER :: GCStateData2d_4(:,:) + REAL(f4), POINTER :: GCStateData3d_4(:,:,:) + REAL(f8), POINTER :: GCStateData0d_8 + REAL(f8), POINTER :: GCStateData1d_8(:) + REAL(f8), POINTER :: GCStateData2d_8(:,:) + REAL(f8), POINTER :: GCStateData3d_8(:,:,:) + INTEGER, POINTER :: GCStateData0d_I + INTEGER, POINTER :: GCStateData1d_I(:) + INTEGER, POINTER :: GCStateData2d_I(:,:) + INTEGER, POINTER :: GCStateData3d_I(:,:,:) + + END TYPE HistoryExportObj +! +! !REVISION HISTORY: +! 01 Sep 2017 - E. Lundgren - Initial version for GCHP/GEOS +! 19 Oct 2022 - H.P. Lin - Adapted for CESM +! See https://github.com/geoschem/geos-chem for history +!EOP +!------------------------------------------------------------------------------ +!BOC + +CONTAINS +!------------------------------------------------------------------------------ +! GEOS-Chem Global Chemical Transport Model ! +!------------------------------------------------------------------------------ +!BOP +! +! !IROUTINE: Init_HistoryConfig +! +! !DESCRIPTION: +!\\ +!\\ +! !INTERFACE: +! + SUBROUTINE Init_HistoryConfig ( am_I_Root, HistoryConfig, configFile, RC ) +! +! !USES: +! +! +! !INPUT PARAMETERS: +! + LOGICAL, INTENT(IN) :: am_I_Root + CHARACTER(LEN=*), INTENT(IN) :: configFile +! +! !OUTPUT PARAMETERS: +! + TYPE(HistoryConfigObj), POINTER :: HistoryConfig + INTEGER, INTENT(OUT) :: RC +! +! !REVISION HISTORY: +! 01 Sep 2017 - E. Lundgren - Initial version +! See https://github.com/geoschem/geos-chem for history +!EOP +!------------------------------------------------------------------------------ +!BOC + __Iam__('Init_HistoryConfig (geoschem_history_mod.F90)') + RC = GC_SUCCESS + ALLOCATE(HistoryConfig) + HistoryConfig%ROOT = '' + HistoryConfig%ConfigFileName = TRIM(configFile) + HistoryConfig%ConfigFileRead = .FALSE. + + CALL Init_DiagList( am_I_Root, configFile, HistoryConfig%DiagList, RC ) + IF ( RC == GC_FAILURE ) THEN + _ASSERT(.FALSE., 'informative message here') + RETURN + ENDIF + ! Optional debugging + ! CALL Print_DiagList( am_I_Root, HistoryConfig%DiagList, RC ) + + CALL Init_TaggedDiagList( am_I_Root, HistoryConfig%DiagList, & + HistoryConfig%TaggedDiagList, RC ) + IF ( RC == GC_FAILURE ) THEN + _ASSERT(.FALSE., 'informative message here') + RETURN + ENDIF + ! Optional debugging + ! CALL Print_TaggedDiagList( am_I_Root, HistoryConfig%TaggedDiagList, RC ) + + + CALL Init_HistoryExportsList( am_I_Root, HistoryConfig, RC ) + IF ( RC == GC_FAILURE ) THEN + _ASSERT(.FALSE., 'informative message here') + RETURN + ENDIF + + ! Optional debugging + ! CALL Print_HistoryExportsList( am_I_Root, HistoryConfig, RC ) + + END SUBROUTINE Init_HistoryConfig +!EOC +!------------------------------------------------------------------------------ +! GEOS-Chem Global Chemical Transport Model ! +!------------------------------------------------------------------------------ +!BOP +! +! !IROUTINE: Init_HistoryExportsList +! +! !DESCRIPTION: +!\\ +!\\ +! !INTERFACE: +! + SUBROUTINE Init_HistoryExportsList ( am_I_Root, HistoryConfig, RC ) +! +! !USES: +! + ! GEOS-Chem modules + USE State_Chm_Mod, ONLY : Get_Metadata_State_Chm + USE State_Diag_Mod, ONLY : Get_Metadata_State_Diag + USE State_Met_Mod, ONLY : Get_Metadata_State_Met +! +! !INPUT PARAMETERS: +! + LOGICAL, INTENT(IN) :: am_I_Root +! +! !INPUT/OUTPUT PARAMETERS: +! + TYPE(HistoryConfigObj), POINTER :: HistoryConfig +! +! !OUTPUT PARAMETERS: +! + INTEGER, INTENT(OUT) :: RC +! +! !REVISION HISTORY: +! 01 Sep 2017 - E. Lundgren - initial version +! See https://github.com/geoschem/geos-chem for history +!EOP +!------------------------------------------------------------------------------ +!BOC +! +! !LOCAL VARIABLES: +! + INTEGER :: N, rank, vloc, type + CHARACTER(LEN=255) :: ErrMsg, desc, units, tag + LOGICAL :: isMet, isChem, isDiag, found + TYPE(HistoryExportObj), POINTER :: NewHistExp + TYPE(DgnItem), POINTER :: current + + ! ================================================================ + ! Init_HistoryExportsList begins here + ! ================================================================ + __Iam__('Init_HistoryExportsList (geoschem_history_mod.F90)') + RC = GC_SUCCESS + + ! Init + NewHistExp => NULL() + + ! Create HistoryExportsList object + ALLOCATE(HistoryConfig%HistoryExportsList) + HistoryConfig%HistoryExportsList%numExports = 0 + HistoryConfig%HistoryExportsList%head => NULL() + + ! Loop over entries in DiagList + current => HistoryConfig%DiagList%head + DO WHILE ( ASSOCIATED( current ) ) + + ! Skip diagnostics handled by HEMCO, non-standard for GEOS, + ! or species in the GCHP/GEOS internal state. + ! See diaglist_mod.F90 for criteria for assigning diagnostic state. + IF ( INDEX( current%state, 'HEMCO' ) == 1 .OR. & + INDEX( current%state, 'GEOS' ) == 1 .OR. & + INDEX( current%state, 'INTERNAL' ) == 1 ) THEN + current => current%next + CYCLE + ENDIF + + ! Check history exports list to see if already added (unless wildcard) + IF ( .NOT. current%isWildcard ) THEN + CALL Check_HistoryExportsList( am_I_Root, current%name, & + HistoryConfig%HistoryExportsList, & + found, RC ) + IF ( found ) THEN + current => current%next + CYCLE + ENDIF + ENDIF + + ! Get metadata using metadataID and state + ! If isTagged, then append to description + ! If isWildcard, shouldn't get here + ! The name of the export is simply name + Found = .TRUE. + isMet = .FALSE. + isChem = .FALSE. + isDiag = .FALSE. + IF ( TRIM(current%state) == 'MET' ) THEN + isMet = .TRUE. + CALL Get_Metadata_State_Met( am_I_Root, current%metadataID, & + Found, RC, desc=desc, units=units, & + rank=rank, type=type, vloc=vloc ) + ! TODO: need to add found to outputs of get_metadata_state_met + ELSEIF ( TRIM(current%state) == 'CHEM' ) THEN + isCHEM = .TRUE. + CALL Get_Metadata_State_Chm( am_I_Root, current%metadataID, & + Found, RC, desc=desc, units=units, & + rank=rank, type=type, vloc=vloc ) + ELSEIF ( TRIM(current%state) == 'DIAG' ) THEN + isDIAG = .TRUE. + CALL Get_Metadata_State_Diag( am_I_Root, current%metadataID, & + Found, RC, desc=desc, units=units, & + rank=rank, srcType=type, vloc=vloc ) + ELSE + RC = GC_FAILURE + ErrMsg = "Unknown state of item " // TRIM(current%name) // & + " in DiagList: " // TRIM(current%state) + EXIT + ENDIF + + IF ( .NOT. Found ) THEN + RC = GC_FAILURE + ErrMsg = "Metadata not found for " // TRIM(current%name) // & + " in state " // TRIM(current%state) + EXIT + ENDIF + + ! If wildcard is present + IF ( current%isWildcard ) THEN + ! Do nothing. This should never happen at this point since + ! Init_DiagList will exit with an error if wildcard is + ! encountered in HISTORY.rc while compiling with ESMF_. + + ! When it comes time to implement, create exports in a loop, + ! either for all species or for advected species only. Include + ! a check that the export was not already created. Loop over + ! AdvNames if wildcard is ADV. Loop over SpecNames for all other + ! cases, passing not found = OK so that not all are necessarily + ! output. Later on, after species database is initialized, exports + ! for only species in the specific wildcard will be associated + ! with data and thus included in the output file. + + ! If the meantime, skip wildcards if it gets here. + current => current%next + CYCLE + ENDIF + + ! If this item is for a specific tag, append description. + ! This will need revisiting since there may be tag-dependent + ! strings to append to long names + IF ( current%isTagged ) THEN + desc = TRIM(desc) // " for " // TRIM(current%tag) + ENDIF + + ! Create a new HistoryExportObj object + CALL Init_HistoryExport( am_I_Root, NewHistExp, & + name=current%name, & + metadataID=current%metadataID, & + registryID=current%registryID, & + long_name=desc, & + units=units, & + vloc=vloc, & + rank=rank, & + type=type, & + isMet=isMet, & + isChem=isChem, & + isDiag=isDiag, & + RC=RC ) + IF ( RC == GC_FAILURE ) THEN + ErrMsg = "History export init fail for " // TRIM(current%name) + EXIT + ENDIF + + ! Add new HistoryExportObj to linked list + CALL Append_HistoryExportsList( am_I_Root, NewHistExp, & + HistoryConfig, RC ) + IF ( RC == GC_FAILURE ) THEN + ErrMsg = "History export append fail for " // TRIM(current%name) + EXIT + ENDIF + + ! Set up for next item in DiagList + current => current%next + + ENDDO + current => NULL() + + IF ( RC == GC_SUCCESS ) THEN + HistoryConfig%ConfigFileRead = .TRUE. + ELSE + CALL GC_ERROR( ErrMsg, RC, Iam ) + _ASSERT(.FALSE., 'informative message here') + RETURN + ENDIF + + END SUBROUTINE Init_HistoryExportsList +!EOC +!------------------------------------------------------------------------------ +! GEOS-Chem Global Chemical Transport Model ! +!------------------------------------------------------------------------------ +!BOP +! +! !IROUTINE: Init_HistoryExport +! +! !DESCRIPTION: +!\\ +!\\ +! !INTERFACE: +! + SUBROUTINE Init_HistoryExport ( am_I_Root, NewHistExp, name, & + metadataID, registryID, long_name, & + units, vloc, rank, & + type, isMet, isChem, & + isDiag, RC ) +! +! !INPUT PARAMETERS: +! + LOGICAL, INTENT(IN) :: am_I_Root +! +! !OUTPUT PARAMETERS: +! + TYPE(HistoryExportObj), POINTER :: NewHistExp + CHARACTER(LEN=*), OPTIONAL :: name + CHARACTER(LEN=*), OPTIONAL :: metadataID + CHARACTER(LEN=*), OPTIONAL :: registryID + CHARACTER(LEN=*), OPTIONAL :: long_name + CHARACTER(LEN=*), OPTIONAL :: units + INTEGER, OPTIONAL :: vloc + INTEGER, OPTIONAL :: rank + INTEGER, OPTIONAL :: type + LOGICAL, OPTIONAL :: isMet + LOGICAL, OPTIONAL :: isChem + LOGICAL, OPTIONAL :: isDiag + INTEGER, OPTIONAL :: RC +! +! !REVISION HISTORY: +! 01 Sep 2017 - E. Lundgren - Initial version +! See https://github.com/geoschem/geos-chem for history +!EOP +!------------------------------------------------------------------------------ +!BOC + __Iam__('Init_HistoryExport (geoschem_history_mod.F90)') + RC = GC_SUCCESS + ALLOCATE(NewHistExp) + + IF ( PRESENT( name ) ) THEN + NewHistExp%name = TRIM(name) + ELSE + NewHistExp%name = '' + ENDIF + + IF ( PRESENT( metaDataId ) ) THEN + NewHistExp%metadataID = TRIM(metadataID) + ELSE + NewHistExp%metadataID = '' + ENDIF + + IF ( PRESENT( registryId ) ) THEN + NewHistExp%registryID = TRIM(registryID) + ELSE + NewHistExp%registryId = '' + ENDIF + + IF ( PRESENT( long_name ) ) THEN + NewHistExp%long_name = TRIM(long_name) + ELSE + NewHistExp%long_name = '' + ENDIF + + IF ( PRESENT( units ) ) THEN + NewHistExp%units = TRIM(units) + ELSE + NewHistExp%units = '' + ENDIF + + IF ( PRESENT( vloc ) ) THEN + NewHistExp%vloc = vloc + ELSE + NewHistExp%vloc = -1 + ENDIF + + IF ( PRESENT( rank ) ) THEN + NewHistExp%rank = rank + ELSE + NewHistExp%rank = -1 + ENDIF + + IF ( PRESENT( type ) ) THEN + NewHistExp%type = type + ELSE + NewHistExp%type = -1 + ENDIF + + IF ( PRESENT( isMet ) ) THEN + NewHistExp%isMet = isMet + ELSE + NewHistExp%isMet = .FALSE. + ENDIF + + IF ( PRESENT( isChem ) ) THEN + NewHistExp%isChem = isChem + ELSE + NewHistExp%isChem = .FALSE. + ENDIF + + IF ( PRESENT( isDiag ) ) THEN + NewHistExp%isDiag = isDiag + ELSE + NewHistExp%isDiag = .FALSE. + ENDIF + + NewHistExp%next => NULL() + NewHistExp%GCStateData0d => NULL() + NewHistExp%GCStateData1d => NULL() + NewHistExp%GCStateData2d => NULL() + NewHistExp%GCStateData3d => NULL() + NewHistExp%GCStateData0d_4 => NULL() + NewHistExp%GCStateData1d_4 => NULL() + NewHistExp%GCStateData2d_4 => NULL() + NewHistExp%GCStateData3d_4 => NULL() + NewHistExp%GCStateData0d_8 => NULL() + NewHistExp%GCStateData1d_8 => NULL() + NewHistExp%GCStateData2d_8 => NULL() + NewHistExp%GCStateData3d_8 => NULL() + NewHistExp%GCStateData0d_I => NULL() + NewHistExp%GCStateData1d_I => NULL() + NewHistExp%GCStateData2d_I => NULL() + NewHistExp%GCStateData3d_I => NULL() + + END SUBROUTINE Init_HistoryExport +!EOC +!------------------------------------------------------------------------------ +! GEOS-Chem Global Chemical Transport Model ! +!------------------------------------------------------------------------------ +!BOP +! +! !IROUTINE: Append_HistoryExportsList +! +! !DESCRIPTION: +!\\ +!\\ +! !INTERFACE: +! + SUBROUTINE Append_HistoryExportsList ( am_I_Root, HistoryExport, & + HistoryConfig, RC ) +! +! !USES: +! +! +! !INPUT PARAMETERS: +! + LOGICAL, INTENT(IN) :: am_I_Root + TYPE(HistoryExportObj), POINTER :: HistoryExport +! +! !INPUT/OUTPUT PARAMETERS: +! + TYPE(HistoryConfigObj), POINTER :: HistoryConfig +! +! !OUTPUT PARAMETERS: +! + INTEGER, INTENT(OUT) :: RC +! +! !REVISION HISTORY: +! 01 Sep 2017 - E. Lundgren - initial version +! See https://github.com/geoschem/geos-chem for history +!EOP +!------------------------------------------------------------------------------ +!BOC +! +! !LOCAL VARIABLES: +! + TYPE(HistoryExportObj), POINTER :: NewHistExp + + ! ================================================================ + ! Append_HistoryExportsList begins here + ! ================================================================ + __Iam__('Append_HistoryExportsList (geoschem_history_mod.F90)') + RC = GC_SUCCESS + + ! Add new object to the beginning of the linked list + HistoryExport%next => HistoryConfig%HistoryExportsList%head + HistoryConfig%HistoryExportsList%head => HistoryExport + + ! Update # of list items + HistoryConfig%HistoryExportsList%numExports = & + HistoryConfig%HistoryExportsList%numExports + 1 + + END SUBROUTINE Append_HistoryExportsList +!EOC +!------------------------------------------------------------------------------ +! GEOS-Chem Global Chemical Transport Model ! +!------------------------------------------------------------------------------ +!BOP +! +! !IROUTINE: Check_HistoryExportsList +! +! !DESCRIPTION: +!\\ +!\\ +! !INTERFACE: +! + SUBROUTINE Check_HistoryExportsList ( am_I_Root, name, & + ExportsList, found, RC ) +! +! !INPUT PARAMETERS: +! + LOGICAL, INTENT(IN) :: am_I_Root + CHARACTER(LEN=*), INTENT(IN) :: name + TYPE(HistoryExportsListObj), POINTER :: ExportsList +! +! !OUTPUT PARAMETERS: +! + LOGICAL, INTENT(OUT) :: found + INTEGER, INTENT(OUT) :: RC +! +! !REVISION HISTORY: +! 12 Sep 2017 - E. Lundgren - Initial version +! See https://github.com/geoschem/geos-chem for history +!EOP +!------------------------------------------------------------------------------ +!BOC +! +! !LOCAL VARIABLES: +! + TYPE(HistoryExportObj), POINTER :: current + + __Iam__('Check_HistoryExportsList (geoschem_history_mod.F90)') + RC = GC_SUCCESS + + ! Assume not found + found = .False. + + current => ExportsList%head + DO WHILE ( ASSOCIATED( current ) ) + IF ( current%name == name ) THEN + found = .TRUE. + RETURN + ENDIF + current => current%next + ENDDO + current => NULL() + + END SUBROUTINE Check_HistoryExportsList +!EOC +!------------------------------------------------------------------------------ +! GEOS-Chem Global Chemical Transport Model ! +!------------------------------------------------------------------------------ +!BOP +! +! !IROUTINE: HistoryExports_SetServices +! +! !DESCRIPTION: +!\\ +!\\ +! !INTERFACE: +! + SUBROUTINE HistoryExports_SetServices( am_I_Root, config_file, & + HistoryConfig, RC ) +! +! !USES: +! + ! CAM modules + USE cam_history, ONLY : addfld, add_default, horiz_only + + ! GEOS-Chem modules + USE Registry_Params_Mod, ONLY : VLocationCenter, VLocationEdge +! +! !INPUT PARAMETERS: +! + LOGICAL, INTENT(IN) :: am_I_Root + CHARACTER(LEN=*), INTENT(IN) :: config_file +! +! !INPUT AND OUTPUT PARAMETERS: +! + +! +! !OUTPUT PARAMETERS: +! + TYPE(HistoryConfigObj), POINTER :: HistoryConfig ! History config object + INTEGER, INTENT(OUT) :: RC +! +! !REMARKS: +! ! +! !REVISION HISTORY: +! 01 Sep 2017 - E. Lundgren - Initial version for GCHP/GEOS +! 19 Oct 2022 - H.P. Lin - Adapted for CESM +! See https://github.com/geoschem/geos-chem for history +!EOP +!------------------------------------------------------------------------------ +!BOC +! +! !LOCAL VARIABLES: +! + CHARACTER(LEN=255) :: ErrMsg + TYPE(HistoryExportObj), POINTER :: current + + ! ================================================================ + ! HistoryExports_SetServices begins here + ! ================================================================ + + ! For error handling (defines Iam and STATUS) + __Iam__('HistoryExports_SetServices (geoschem_history_mod.F90)') + RC = GC_SUCCESS + + ! Create a config object if it does not already exist + IF ( .NOT. ASSOCIATED(HistoryConfig) ) THEN + CALL Init_HistoryConfig( am_I_Root, HistoryConfig, config_file, RC ) + IF ( RC == GC_FAILURE ) THEN + _ASSERT(.FALSE., 'informative message here') + RETURN + ENDIF + ENDIF + + ! Loop over the History Exports list to add one export per item + IF ( am_I_Root ) THEN + WRITE(6,*) " " + WRITE(6,*) "Adding history variables to CAM History State:" + ENDIF + current => HistoryConfig%HistoryExportsList%head + DO WHILE ( ASSOCIATED( current ) ) + IF ( am_I_Root ) PRINT *, "adding export: ", TRIM(current%name) + ! Create an export for this item + IF ( current%rank == 3 ) THEN + IF ( current%vloc == VLocationCenter ) THEN + CALL addfld(trim(current%name), & + (/'lev'/), & + 'I', & + trim(current%units), & + trim(current%long_name) ) + IF ( RC == GC_FAILURE ) THEN + ErrMsg = "Problem adding 3D export for " // TRIM(current%name) + EXIT + ENDIF + ELSEIF ( current%vloc == VLocationEdge ) THEN + CALL addfld(trim(current%name), & + (/'ilev'/), & + 'I', & + trim(current%units), & + trim(current%long_name) ) + ELSE + IF ( am_I_Root ) THEN + PRINT *, "Unknown vertical location for ", & + TRIM(current%name) + ENDIF + ENDIF + ELSEIF ( current%rank == 2 ) THEN + CALL addfld(trim(current%name), & + horiz_only, & + 'I', & + trim(current%units), & + trim(current%long_name) ) + IF ( RC == GC_FAILURE ) THEN + ErrMsg = "Problem adding 2D export for " // TRIM(current%name) + EXIT + ENDIF + ELSE + RC = GC_FAILURE + ErrMsg = "Problem adding export for " // TRIM(current%name) // & + ". Rank is only implemented for 2 or 3!" + EXIT + ENDIF + + current => current%next + ENDDO + current => NULL() + + IF ( RC == GC_FAILURE ) THEN + CALL GC_ERROR( ErrMsg, RC, Iam ) + _ASSERT(.FALSE., 'informative message here') + RETURN + ENDIF + + END SUBROUTINE HistoryExports_SetServices +!EOC +!------------------------------------------------------------------------------ +! GEOS-Chem Global Chemical Transport Model ! +!------------------------------------------------------------------------------ +!BOP +! +! !IROUTINE: CopyGCStates2Exports +! +! !DESCRIPTION: +!\\ +!\\ +! !INTERFACE: +! + SUBROUTINE CopyGCStates2Exports( am_I_Root, Input_Opt, State_Grid, HistoryConfig, LCHNK, RC ) +! +! !USES: +! + ! CAM modules + USE cam_history, ONLY : hist_fld_active, outfld + USE shr_kind_mod, ONLY : shr_kind_r8 + + ! GEOS-Chem modules + USE HCO_Interface_GC_Mod, ONLY : HCOI_GC_WriteDiagn + USE Input_Opt_Mod, ONLY : OptInput + USE State_Grid_Mod, ONLY : GrdState +! +! !INPUT PARAMETERS: +! + LOGICAL, INTENT(IN) :: am_I_Root + TYPE(OptInput), INTENT(IN) :: Input_Opt + TYPE(GrdState), INTENT(IN) :: State_Grid + INTEGER, INTENT(IN) :: LCHNK ! Chunk number for CESM +! +! !INPUT AND OUTPUT PARAMETERS: +! + TYPE(HistoryConfigObj), POINTER :: HistoryConfig ! History config object +! +! !OUTPUT PARAMETERS: +! + INTEGER, INTENT(OUT) :: RC +! +! !REMARKS: +! ! +! !REVISION HISTORY: +! 01 Sep 2017 - E. Lundgren - Initial version for GCHP/GEOS +! 19 Oct 2022 - H.P. Lin - Adapted for CESM +! See https://github.com/geoschem/geos-chem for history +!EOP +!------------------------------------------------------------------------------ +!BOC +! +! !LOCAL VARIABLES: +! + INTEGER :: LMAX + CHARACTER(LEN=255) :: ErrMsg + TYPE(HistoryExportObj), POINTER :: current + + ! Temporaries for CAM exports. + ! Note that in CESM, State_Grid%NX is always length 1. (hplin, 11/16/22) + REAL(shr_kind_r8) :: outTmp_3D(State_Grid%NY, State_Grid%NZ) + REAL(shr_kind_r8) :: outTmp_2D(State_Grid%NY) + + ! ================================================================ + ! CopyGCStates2Exports begins here + ! ================================================================ + __Iam__('CopyGCStates2Exports (geoschem_history_mod.F90)') + RC = GC_SUCCESS + + ! Loop over the History Exports list + current => HistoryConfig%HistoryExportsList%head + DO WHILE ( ASSOCIATED( current ) ) + ! Skip if not active + if(.not. hist_fld_active(trim(current%name))) then + current => current%next + cycle + endif + + ! if (am_I_Root) THEN + ! print *, ' Copying ' // TRIM(current%name) + ! endif + IF ( current%rank == 2 ) THEN + IF ( ASSOCIATED( current%GCStateData2d ) ) THEN + outTmp_2D(1:State_Grid%NY) = current%GCStateData2d(1,1:State_Grid%NY) + ELSE IF ( ASSOCIATED( current%GCStateData2d_4 ) ) THEN + outTmp_2D(1:State_Grid%NY) = current%GCStateData2d_4(1,1:State_Grid%NY) + ELSE IF ( ASSOCIATED( current%GCStateData2d_8 ) ) THEN + outTmp_2D(1:State_Grid%NY) = current%GCStateData2d_8(1,1:State_Grid%NY) + ELSE IF ( ASSOCIATED( current%GCStateData2d_I ) ) THEN + ! Convert integer to float (integers not allowed in MAPL exports) + outTmp_2D(1:State_Grid%NY) = FLOAT(current%GCStateData2d_I(1,1:State_Grid%NY)) + ELSE + RC = GC_FAILURE + ErrMsg = "No GC 2D pointer found for " // TRIM(current%name) + EXIT + ENDIF + + ! Now call outfld to output for this chunk + call outfld(trim(current%name), & + outTmp_2D, & ! Chunk width always 1 + State_Grid%NY, & + LCHNK ) + ELSEIF ( current%rank == 3 ) THEN + IF ( ASSOCIATED( current%GCStateData3d ) ) THEN + outTmp_3D(1:State_Grid%NY, :) = current%GCStateData3d(1,1:State_Grid%NY,:) + ELSE IF ( ASSOCIATED( current%GCStateData3d_4 ) ) THEN + outTmp_3D(1:State_Grid%NY, :) = current%GCStateData3d_4(1,1:State_Grid%NY,:) + ELSE IF ( ASSOCIATED( current%GCStateData3d_8 ) ) THEN + outTmp_3D(1:State_Grid%NY, :) = current%GCStateData3d_8(1,1:State_Grid%NY,:) + ELSE IF ( ASSOCIATED( current%GCStateData3d_I ) ) THEN + outTmp_3D(1:State_Grid%NY, :) = FLOAT(current%GCStateData3d_I(1,1:State_Grid%NY,:)) + ELSE + RC = GC_FAILURE + ErrMsg = "No GC 3D pointer found for " // TRIM(current%name) + EXIT + ENDIF +#if defined( MODEL_CESM ) + ! If using GEOS-5, flip the data vertically to match model + ! convention + ! Also do this in CESM. (hplin, 10/31/22) + LMAX = SIZE(outTmp_3D, 2) + outTmp_3D(:,1:LMAX) = outTmp_3D(:,LMAX:1:-1) +#endif + + ! Now call outfld to output for this chunk + call outfld(trim(current%name), & + outTmp_3D, & ! Chunk width always 1. TOA is 1 + State_Grid%NY, & + LCHNK ) + ENDIF + + current => current%next + ENDDO + current => NULL() + + ! Error handling + IF ( RC == GC_FAILURE ) THEN + CALL GC_ERROR( ErrMsg, RC, Iam ) + _ASSERT(.FALSE., 'informative message here') + RETURN + ENDIF + END SUBROUTINE CopyGCStates2Exports +!EOC +!------------------------------------------------------------------------------ +! GEOS-Chem Global Chemical Transport Model ! +!------------------------------------------------------------------------------ +!BOP +! +! !IROUTINE: Print_HistoryExportsList +! +! !DESCRIPTION: +!\\ +!\\ +! !INTERFACE: +! + SUBROUTINE Print_HistoryExportsList( am_I_Root, HistoryConfig, RC ) +! +! !USES: +! +! +! !INPUT PARAMETERS: +! + LOGICAL, INTENT(IN) :: am_I_Root +! +! !INPUT AND OUTPUT PARAMETERS: +! + TYPE(HistoryConfigObj), POINTER :: HistoryConfig ! History config object +! +! !OUTPUT PARAMETERS: +! + INTEGER, INTENT(OUT) :: RC +! +! !REMARKS: +! ! +! !REVISION HISTORY: +! 01 Sep 2017 - E. Lundgren - Initial version +! See https://github.com/geoschem/geos-chem for history +!EOP +!------------------------------------------------------------------------------ +!BOC +! +! !LOCAL VARIABLES: +! + TYPE(HistoryExportObj), POINTER :: current + + ! ================================================================ + ! Print_HistoryExportsList begins here + ! ================================================================ + __Iam__('Print_HistoryExportsList (geoschem_history_mod.F90)') + RC = GC_SUCCESS + + ! Loop over the History Exports list + current => HistoryConfig%HistoryExportsList%head + IF ( am_I_Root ) PRINT *, '===========================' + IF ( am_I_Root ) PRINT *, 'History Exports List:' + IF ( am_I_Root ) PRINT *, ' ' + DO WHILE ( ASSOCIATED( current ) ) + IF ( am_I_Root ) THEN + PRINT *, "Name: ", TRIM(current%name) + PRINT *, " MetadataID: ", TRIM(current%metadataID) + PRINT *, " RegistryID: ", TRIM(current%registryID) + PRINT *, " Long name: ", TRIM(current%long_name) + PRINT *, " Units: ", TRIM(current%units) + PRINT *, " Vert loc: ", current%vloc + PRINT *, " Rank: ", current%rank + PRINT *, " Type: ", current%type + PRINT *, " isMet: ", current%isMet + PRINT *, " isChem: ", current%isChem + PRINT *, " isDiag: ", current%isDiag + PRINT *, " " + ENDIF + current => current%next + ENDDO + IF ( am_I_Root ) PRINT *, '===========================' + current => NULL() + + END SUBROUTINE Print_HistoryExportsList +!EOC +!------------------------------------------------------------------------------ +! GEOS-Chem Global Chemical Transport Model ! +!------------------------------------------------------------------------------ +!BOP +! +! !IROUTINE: HistoryExports_SetDataPointers +! +! !DESCRIPTION: +!\\ +!\\ +! !INTERFACE: +! + SUBROUTINE HistoryExports_SetDataPointers( am_I_Root, & + HistoryConfig, State_Chm, & + State_Grid, & + State_Diag, State_Met, & + RC ) +! +! !USES: +! + ! CAM modules + USE cam_history, ONLY : hist_fld_active + + ! GEOS-Chem modules + USE Registry_Mod, ONLY : Registry_Lookup + USE State_Chm_Mod, ONLY : ChmState + USE State_Diag_Mod, ONLY : DgnState + USE State_Grid_Mod, ONLY : GrdState + USE State_Met_Mod, ONLY : MetState +! +! !INPUT PARAMETERS: +! + LOGICAL, INTENT(IN) :: am_I_Root +! +! !INPUT AND OUTPUT PARAMETERS: +! + TYPE(HistoryConfigObj), POINTER :: HistoryConfig ! History config obj + TYPE(GrdState), INTENT(INOUT) :: State_Grid ! Grid State obj + TYPE(ChmState), INTENT(INOUT) :: State_Chm ! Chemistry State obj + TYPE(MetState), INTENT(INOUT) :: State_Met ! Meteorology State obj + TYPE(DgnState), INTENT(INOUT) :: State_Diag ! Diagnostics State obj +! +! !OUTPUT PARAMETERS: +! + INTEGER, INTENT(OUT) :: RC +! +! !REMARKS: +! ! +! !REVISION HISTORY: +! 01 Sep 2017 - E. Lundgren - Initial version +! See https://github.com/geoschem/geos-chem for history +!EOP +!------------------------------------------------------------------------------ +!BOC +! +! !LOCAL VARIABLES: +! + CHARACTER(LEN=255) :: ErrMsg + TYPE(HistoryExportObj), POINTER :: current + + ! ================================================================ + ! HistoryExports_SetDataPointers begins here + ! ================================================================ + __Iam__('HistoryExports_SetDataPointers') + RC = GC_SUCCESS + + IF ( am_I_Root ) THEN + WRITE(6,*) " " + WRITE(6,*) "Setting history variable pointers to GC and Export States" + ENDIF + + ! Loop over the History Exports list + current => HistoryConfig%HistoryExportsList%head + DO WHILE ( ASSOCIATED( current ) ) + ! Skip if not active + if(.not. hist_fld_active(trim(current%name))) then + current => current%next + cycle + endif + + ! Get pointer to GC state data + !IF ( am_I_Root ) WRITE(6,*) current%name + IF ( current%isMET ) THEN + CALL Registry_Lookup( am_I_Root = am_I_Root, & + Registry = State_Met%Registry, & + RegDict = State_Met%RegDict, & + State = State_Met%State, & + Variable = current%registryID, & + Ptr2d_4 = current%GCStateData2d_4, & + Ptr2d_8 = current%GCStateData2d_8, & + Ptr2d_I = current%GCStateData2d_I, & + Ptr3d_4 = current%GCStateData3d_4, & + Ptr3d_8 = current%GCStateData3d_8, & + Ptr3d_I = current%GCStateData3d_I, & + RC = RC ) + ELSEIF ( current%isChem ) THEN + CALL Registry_Lookup( am_I_Root = am_I_Root, & + Registry = State_Chm%Registry, & + RegDict = State_Chm%RegDict, & + State = State_Chm%State, & + Variable = current%registryID, & + Ptr2d_4 = current%GCStateData2d_4, & + Ptr2d_8 = current%GCStateData2d_8, & + Ptr2d_I = current%GCStateData2d_I, & + Ptr3d_4 = current%GCStateData3d_4, & + Ptr3d_8 = current%GCStateData3d_8, & + Ptr3d_I = current%GCStateData3d_I, & + RC = RC ) + ELSEIF ( current%isDiag ) THEN + CALL Registry_Lookup( am_I_Root = am_I_Root, & + Registry = State_Diag%Registry, & + RegDict = State_Diag%RegDict, & + State = State_Diag%State, & + Variable = current%registryID, & + Ptr2d_4 = current%GCStateData2d_4, & + Ptr2d_8 = current%GCStateData2d_8, & + Ptr2d_I = current%GCStateData2d_I, & + Ptr3d_4 = current%GCStateData3d_4, & + Ptr3d_8 = current%GCStateData3d_8, & + Ptr3d_I = current%GCStateData3d_I, & + RC = RC ) + ENDIF + IF ( RC == GC_FAILURE ) THEN + ErrMsg = "Registry pointer not found for " // TRIM(current%name) // & + ". Check that the tag (e.g. species) is valid " // & + "for this diagnostic." + EXIT + ENDIF + + !! debugging + !IF ( Am_I_Root) THEN + ! WRITE(6,*) TRIM(current%name) + !ENDIF + + current => current%next + ENDDO + current => NULL() + + ! Optional debugging + !WRITE(6,*) "hplin debug: after HistoryExports_SetDataPointers" + !CALL Print_HistoryExportsList( am_I_Root, HistoryConfig, RC ) + + IF ( RC == GC_FAILURE ) THEN + CALL GC_ERROR( ErrMsg, RC, Iam ) + _ASSERT(.FALSE., 'informative message here') + RETURN + ENDIF + + END SUBROUTINE HistoryExports_SetDataPointers +!EOC +!------------------------------------------------------------------------------ +! GEOS-Chem Global Chemical Transport Model ! +!------------------------------------------------------------------------------ +!BOP +! +! !IROUTINE: Destroy_HistoryConfig +! +! !DESCRIPTION: Subroutine Destroy_HistoryConfig deallocates a HistoryConfig +! object and all of its member objects including the linked list of +! HistoryExport objects. +!\\ +!\\ +! !INTERFACE: +! + SUBROUTINE Destroy_HistoryConfig ( am_I_Root, HistoryConfig, RC ) +! +! !INPUT PARAMETERS: +! + LOGICAL, INTENT(IN) :: am_I_Root ! root CPU? + TYPE(HistoryConfigObj), POINTER :: HistoryConfig +! +! !INPUT/OUTPUT PARAMETERS: +! + INTEGER, INTENT(INOUT) :: RC ! Success? +! +! !REVISION HISTORY: +! 01 Sep 2017 - E. Lundgren - Initial version +! See https://github.com/geoschem/geos-chem for history +!EOP +!------------------------------------------------------------------------------ +!BOC +! +! !LOCAL VARIABLES: +! + TYPE(HistoryExportObj), POINTER :: current + TYPE(HistoryExportObj), POINTER :: next + + ! ================================================================ + ! Destroy_HistoryConfig begins here + ! ================================================================ + __Iam__('Destroy_HistoryConfig (geoschem_history_mod.F90)') + + current => NULL() + next => NULL() + + ! Destroy each item in the linked list of HistoryExport objects + current => HistoryConfig%HistoryExportsList%head + IF ( ASSOCIATED( current ) ) next => current%next + DO WHILE ( ASSOCIATED( current ) ) + DEALLOCATE( current, STAT=RC ) + _ASSERT( RC == GC_SUCCESS, 'informative message here' ) + IF ( .NOT. ASSOCIATED ( next ) ) EXIT + current => next + next => current%next + ENDDO + + ! Deallocate the HistoryExportsList object + DEALLOCATE( HistoryConfig%HistoryExportsList, STAT=RC ) + _ASSERT( RC == GC_SUCCESS, 'informative message here' ) + + ! Deallocate the HistoryConfig object + DEALLOCATE( HistoryConfig, STAT=RC ) + _ASSERT( RC == GC_SUCCESS, 'informative message here' ) + + ! Final cleanup + current => NULL() + next => NULL() + + END SUBROUTINE Destroy_HistoryConfig +!EOC +END MODULE GeosChem_History_Mod diff --git a/src/chemistry/geoschem/m_spc_id.F90 b/src/chemistry/geoschem/m_spc_id.F90 new file mode 100644 index 0000000000..14a949048d --- /dev/null +++ b/src/chemistry/geoschem/m_spc_id.F90 @@ -0,0 +1,3 @@ + module m_spc_id + implicit none + end module m_spc_id diff --git a/src/chemistry/geoschem/mo_sim_dat.F90 b/src/chemistry/geoschem/mo_sim_dat.F90 new file mode 100644 index 0000000000..13f5740645 --- /dev/null +++ b/src/chemistry/geoschem/mo_sim_dat.F90 @@ -0,0 +1,384 @@ + + module mo_sim_dat + + private + public :: set_sim_dat + + contains + + subroutine set_sim_dat + + use chem_mods, only : clscnt, cls_rxt_cnt, clsmap, permute, adv_mass, fix_mass, crb_mass + use chem_mods, only : diag_map + use chem_mods, only : phtcnt, rxt_tag_cnt, rxt_tag_lst, rxt_tag_map + use chem_mods, only : pht_alias_lst, pht_alias_mult + use chem_mods, only : extfrc_lst, inv_lst, slvd_lst + use chem_mods, only : enthalpy_cnt, cph_enthalpy, cph_rid, num_rnts, rxntot + use cam_abortutils,only : endrun + use mo_tracname, only : solsym + use chem_mods, only : frc_from_dataset + use chem_mods, only : is_scalar, is_vector + use shr_kind_mod, only : r8 => shr_kind_r8 + use cam_logfile, only : iulog + + implicit none + +!-------------------------------------------------------------- +! ... local variables +!-------------------------------------------------------------- + integer :: ios + + ! is_scalar = .false. + ! is_vector = .true. + + ! clscnt(:) = (/ 30, 0, 0, 191, 0 /) + + ! cls_rxt_cnt(:,1) = (/ 37, 61, 0, 30 /) + ! cls_rxt_cnt(:,4) = (/ 23, 174, 326, 191 /) + + ! GEOS-Chem tracers (advected species) are placed first along MAM + ! aerosols, as those will be constituents. MAM requires that there + ! is a linear mapping between solsym and constituents + + ! ewl notes: added HMS (for GEOS-Chem 13.3) + ! added AONITA, AROMP4, AROMP5, BALD, BENZP, BZCO3H, + ! BZPAN, C2H2, C2H4, CSL, ETHN, ETHP, MCT, NPHEN, PHEN for 14.0 + ! Removed non-advected GEOS-Chem species for 14.0, except CO2 + ! which is a constituent, as well as OH and HO2 for diagnostic + ! output. + ! + ! Currently include GC advected species (233), MAM aerosols (33), CO2 (1), + ! and OH and HO2 (2). + ! If changed, update to match solsym length: + ! 1. cam/bld/configure variable $chem_nadv + ! 2. cam/src/chemistry/geoschem/chem_mods.F90 vars gas_pcnst and nTracersMax + ! Also update adv_mass to store MWs for species in solsym (ewl, 8/8/22) + solsym(:269) = (/ 'ACET ', & + 'ACTA ','AERI ', & + 'ALD2 ','ALK4 ','ASOA1 ', & + 'ASOA2 ','ASOA3 ','ASOAN ', & + 'ASOG1 ','ASOG2 ','ASOG3 ', & + 'AONITA ','AROMP4 ','AROMP5 ', & + 'ATOOH ','BALD ','BCPI ', & + 'BCPO ','BENZ ','BENZP ', & + 'BR ','BR2 ','BRCL ', & + 'BRNO2 ','BRNO3 ','BRO ', & + 'BRSALA ','BRSALC ','BZCO3H ', & + 'BZPAN ','C2H2 ','C2H4 ', & + 'C2H6 ','C3H8 ','CCL4 ', & + 'CFC11 ','CFC113 ','CFC114 ', & + 'CFC115 ','CFC12 ','CH2BR2 ', & + 'CH2CL2 ','CH2I2 ','CH2IBR ', & + 'CH2ICL ','CH2O ','CH3BR ', & + 'CH3CCL3 ','CH3CL ','CH3I ', & + 'CH4 ','CHBR3 ','CHCL3 ', & + 'CL ','CL2 ','CL2O2 ', & + 'CLNO2 ','CLNO3 ','CLO ', & + 'CLOO ','CLOCK ','CO ', & + 'CSL ', & + 'DMS ','DST1 ','DST2 ', & + 'DST3 ','DST4 ','EOH ', & + 'ETHLN ','ETHN ','ETHP ', & + 'ETNO3 ','ETP ', & + 'GLYC ','GLYX ', & + 'H1211 ','H1301 ','H2402 ', & + 'H2O ','H2O2 ','HAC ', & + 'HBR ','HC5A ','HCFC123 ', & + 'HCFC141B ','HCFC142B ','HCFC22 ', & + 'HCL ','HCOOH ','HI ', & + 'HMHP ','HMML ','HMS ', & + 'HNO2 ', & + 'HNO3 ','HNO4 ','HOBR ', & + 'HOCL ','HOI ','HONIT ', & + 'HPALD1 ','HPALD2 ','HPALD3 ', & + 'HPALD4 ','HPETHNL ','I ', & + 'I2 ','I2O2 ','I2O3 ', & + 'I2O4 ','IBR ','ICHE ', & + 'ICL ','ICN ','ICPDH ', & + 'IDC ','IDCHP ','IDHDP ', & + 'IDHPE ','IDN ','IEPOXA ', & + 'IEPOXB ','IEPOXD ','IHN1 ', & + 'IHN2 ','IHN3 ','IHN4 ', & + 'INDIOL ','INO ','INPB ', & + 'INPD ','IO ','IONITA ', & + 'IONO ','IONO2 ','IPRNO3 ', & + 'ISALA ','ISALC ','ISOP ', & + 'ITCN ','ITHN ','LIMO ', & + 'LVOC ','LVOCOA ','MACR ', & + 'MACR1OOH ','MAP ','MCRDH ', & + 'MCRENOL ','MCRHN ','MCRHNB ', & + 'MCRHP ','MCT ','MEK ', & + 'MENO3 ', & + 'MGLY ','MOH ','MONITA ', & + 'MONITS ','MONITU ','MP ', & + 'MPAN ','MPN ','MSA ', & + 'MTPA ','MTPO ','MVK ', & + 'MVKDH ','MVKHC ','MVKHCB ', & + 'MVKHP ','MVKN ','MVKPC ', & + 'N2O ','N2O5 ','NH3 ', & + 'NH4 ','NIT ','NITS ', & + 'NO ','NO2 ','NO3 ', & + 'NPHEN ', & + 'NPRNO3 ','O3 ','OCLO ', & + 'OCPI ','OCPO ','OCS ', & + 'OIO ','PAN ','PFE ', & + 'PHEN ', & + 'PIP ','PP ','PPN ', & + 'PROPNN ','PRPE ','PRPN ', & + 'PYAC ','R4N2 ','R4P ', & + 'RA3P ','RB3P ','RCHO ', & + 'RIPA ','RIPB ','RIPC ', & + 'RIPD ','RP ','SALA ', & + 'SALAAL ','SALACL ','SALC ', & + 'SALCAL ','SALCCL ','SO2 ', & + 'SO4 ','SO4S ','SOAGX ', & + 'SOAIE ','TOLU ','TSOA0 ', & + 'TSOA1 ','TSOA2 ','TSOA3 ', & + 'TSOG0 ','TSOG1 ','TSOG2 ', & + 'TSOG3 ','XYLE ','bc_a1 ', & + 'bc_a4 ','dst_a1 ','dst_a2 ', & + 'dst_a3 ','ncl_a1 ','ncl_a2 ', & + 'ncl_a3 ','num_a1 ','num_a2 ', & + 'num_a3 ','num_a4 ','pom_a1 ', & + 'pom_a4 ','so4_a1 ','so4_a2 ', & + 'so4_a3 ','soa1_a1 ','soa1_a2 ', & + 'soa2_a1 ','soa2_a2 ','soa3_a1 ', & + 'soa3_a2 ','soa4_a1 ','soa4_a2 ', & + 'soa5_a1 ','soa5_a2 ','H2SO4 ', & + 'SOAG0 ','SOAG1 ','SOAG2 ', & + 'SOAG3 ','SOAG4 ','CO2 ', & + 'HO2 ','OH ' /) +!non-advected GEOS-Chem species in 14.0 (beware this includes OH and HO2 already listed above) +! 'LBRO2H ','LBRO2N ','LISOPOH ', & +! 'LISOPNO3 ','LTRO2H ','LTRO2N ', & +! 'LXRO2H ','LXRO2N ','SO4H1 ', & +! 'SO4H2 ','SO4H3 ','SO4H4 ', & +! 'POX ','LOX ','PCO ', & +! 'LCO ','PSO4 ','LCH4 ', & +! 'PH2O2 ','BRO2 ','TRO2 ', & +! 'N ','XRO2 ','HPALD2OO ', & +! 'HPALD1OO ','INA ','C4HVP1 ', & +! 'C4HVP2 ','IDNOO ','ICNOO ', & +! 'ISOPNOO2 ','ROH ','ISOPNOO1 ', & +! 'IDHNDOO1 ','IDHNDOO2 ','H ', & +! 'IHPOO2 ','IHPOO1 ','IHPOO3 ', & +! 'IHPNDOO ','ICHOO ','R4N1 ', & +! 'PRN1 ','MVKOHOO ','MCROHOO ', & +! 'MACR1OO ','PO2 ','OLNN ', & +! 'OLND ','ETO2 ','IHPNBOO ', & +! 'RCO3 ','LIMO2 ','KO2 ', & +! 'IEPOXAOO ','IEPOXBOO ','CH3CHOO ', & +! 'PIO2 ','IDHNBOO ','A3O2 ', & +! 'IHOO4 ','IHOO1 ','INO2D ', & +! 'INO2B ','MACRNO2 ','ATO2 ', & +! 'OTHRO2 ','R4O2 ','B3O2 ', & +! 'CH2OO ','MCO3 ','MO2 ', & +! 'O1D ','OH ','HO2 ', & +! 'O ','H2 ','N2 ', & +! 'O2 ','RCOOH ' /) + + inv_lst(: 6) = (/ 'M ', 'N2 ', 'O2 ', & + 'H2 ', 'MOH ', 'RCOOH ' /) + + fix_mass(: 6) = (/ 0.00000000_r8, 28.0134800_r8, 31.9988000_r8, 2.020000_r8, 32.050000_r8, & + 74.090000_r8 /) + + adv_mass(:269) = (/ 58.090000_r8, 60.060000_r8, 126.900000_r8, 44.060000_r8, 58.120000_r8, & + 150.000000_r8, 150.000000_r8, 150.000000_r8, 150.000000_r8, 150.000000_r8, & + 150.00000_r8, 150.000000_r8, 189.12_r8, 68.08_r8, 98.10_r8, & + 90.0900000_r8, 106.12_r8, 12.010000_r8, 12.010000_r8, & + 78.120000_r8, 110.11_r8, 79.900000_r8, 159.800000_r8, 115.450000_r8, & + 125.910000_r8, 141.910000_r8, 95.900000_r8, 79.900000_r8, & + 79.900000_r8, 138.12_r8, 183.12_r8, 26.05_r8, 28.05_r8, 30.080000_r8, & + 44.110000_r8, 153.820000_r8, 137.370000_r8, 187.380000_r8, 170.920000_r8, & + 154.470000_r8, 120.910000_r8, 173.830000_r8, 84.930000_r8, 267.840000_r8, & + 220.840000_r8, 176.380000_r8, 30.030000_r8, 94.940000_r8, 133.350000_r8, & + 50.450000_r8, 141.940000_r8, 16.050000_r8, 252.730000_r8, 119.350000_r8, & + 35.450000_r8, 70.900000_r8, 102.910000_r8, 81.450000_r8, 97.450000_r8, & + 51.450000_r8, 67.450000_r8, 1.000000_r8, 28.010000_r8, 108.14_r8, 62.130000_r8, & + 29.000000_r8, 29.000000_r8, 29.000000_r8, 29.000000_r8, 46.080000_r8, & + 105.060000_r8, 107.07_r8, 78.07_r8, 91.080000_r8, 62.080000_r8, 60.060000_r8, 58.040000_r8, & + 165.360000_r8, 148.910000_r8, 259.820000_r8, 18.020000_r8, 34.020000_r8, & + 74.080000_r8, 80.910000_r8, 100.130000_r8, 152.930000_r8, 116.940000_r8, & + 100.500000_r8, 86.470000_r8, 36.450000_r8, 46.030000_r8, 127.910000_r8, & + 64.050000_r8, 102.100000_r8, 110.000000_r8, 47.010000_r8, 63.010000_r8, 79.010000_r8, & + 96.910000_r8, 52.450000_r8, 143.890000_r8, 215.000000_r8, 116.130000_r8, & + 116.130000_r8, 116.130000_r8, 116.130000_r8, 76.060000_r8, 126.900000_r8, & + 253.800000_r8, 285.800000_r8, 301.800000_r8, 317.800000_r8, 206.900000_r8, & + 116.130000_r8, 162.450000_r8, 145.130000_r8, 150.150000_r8, 98.110000_r8, & + 148.130000_r8, 168.170000_r8, 150.150000_r8, 192.150000_r8, 106.140000_r8, & + 106.140000_r8, 106.140000_r8, 147.150000_r8, 147.150000_r8, 147.150000_r8, & + 147.150000_r8, 102.000000_r8, 156.910000_r8, 163.150000_r8, 163.150000_r8, & + 142.900000_r8, 14.010000_r8, 172.910000_r8, 188.910000_r8, 105.110000_r8, & + 126.900000_r8, 126.900000_r8, 68.130000_r8, 195.150000_r8, 197.170000_r8, & + 136.260000_r8, 154.190000_r8, 154.190000_r8, 70.100000_r8, 102.100000_r8, & + 76.060000_r8, 104.120000_r8, 86.100000_r8, 149.110000_r8, 149.110000_r8, & + 120.120000_r8, 124.0_r8, 72.110000_r8, 77.050000_r8, 72.070000_r8, 32.050000_r8, & + 14.010000_r8, 215.280000_r8, 215.280000_r8, 48.050000_r8, 147.100000_r8, & + 93.050000_r8, 96.100000_r8, 136.260000_r8, 136.260000_r8, 70.090000_r8, & + 105.130000_r8, 102.100000_r8, 102.100000_r8, 120.120000_r8, 149.120000_r8, & + 118.100000_r8, 44.020000_r8, 108.020000_r8, 17.040000_r8, 18.050000_r8, & + 62.010000_r8, 31.400000_r8, 30.010000_r8, 46.010000_r8, 62.010000_r8, & + 139.11_r8, 105.110000_r8, 48.000000_r8, 67.450000_r8, 12.010000_r8, 12.010000_r8, & + 60.070000_r8, 158.900000_r8, 121.060000_r8, 55.850000_r8, 94.11_r8, 186.280000_r8, & + 92.110000_r8, 135.080000_r8, 119.080000_r8, 42.090000_r8, 137.110000_r8, & + 88.070000_r8, 119.100000_r8, 90.140000_r8, 76.110000_r8, 76.110000_r8, & + 58.090000_r8, 118.150000_r8, 118.150000_r8, 118.150000_r8, 118.150000_r8, & + 90.090000_r8, 31.400000_r8, 31.400000_r8, 35.450000_r8, 31.400000_r8, & + 31.400000_r8, 35.450000_r8, 64.040000_r8, 96.060000_r8, 31.400000_r8, & + 58.040000_r8, 118.150000_r8, 92.150000_r8, 150.000000_r8, 150.000000_r8, & + 150.000000_r8, 150.000000_r8, 150.000000_r8, 150.000000_r8, 150.000000_r8, & + 150.000000_r8, 106.180000_r8, 12.011000_r8, 12.011000_r8, 135.064039_r8, & + 135.064039_r8, 135.064039_r8, 58.442468_r8, 58.442468_r8, 58.442468_r8, & + 1.007400_r8, 1.007400_r8, 1.007400_r8, 1.007400_r8, 12.011000_r8, & + 12.011000_r8, 115.107340_r8, 115.107340_r8, 115.107340_r8, 250.445000_r8, & + 250.445000_r8, 250.445000_r8, 250.445000_r8, 250.445000_r8, 250.445000_r8, & + 250.445000_r8, 250.445000_r8, 250.445000_r8, 250.445000_r8, 98.078400_r8, & + 250.445000_r8, 250.445000_r8, 250.445000_r8, 250.445000_r8, 250.445000_r8, & + 44.010000_r8, 33.0100000_r8, 17.0100000_r8 /) + + extfrc_lst(: 34) = (/ 'NO ', 'CO ', 'SO2 ', 'SO4 ', & + 'NH3 ', 'ACET ', 'ALD2 ', 'ALK4 ', & + 'C2H6 ', 'C3H8 ', 'CH2O ', 'PRPE ', & + 'MACR ', 'RCHO ', 'BCPI ', 'OCPI ', & + 'HNO2 ', 'NO2 ', 'so4_a1 ', 'num_a1 ', & + 'H2O ', 'bc_a4 ', 'pom_a4 ', 'num_a4 ', & + 'MEK ', 'POG1 ', 'POG2 ', 'MTPA ', & + 'BENZ ', 'TOLU ', 'XYLE ', 'NAP ', & + 'EOH ', 'MOH ' /) + + frc_from_dataset(: 34) = (/ .false., .false., .false., .false., & + .false., .false., .false., .false., & + .false., .false., .false., .false., & + .false., .false., .false., .false., & + .false., .false., .false., .false., & + .false., .false., .false., .false., & + .false., .false., .false., .false., & + .false., .false., .false., .false., & + .false., .false. /) + + ! crb_mass(:221) = (/ 60.055000_r8, 60.055000_r8, 12.011000_r8, 12.011000_r8, 12.011000_r8, & + ! 180.165000_r8, 72.066000_r8, 72.066000_r8, 72.066000_r8, 60.055000_r8, & + ! 48.044000_r8, 60.055000_r8, 60.055000_r8, 72.066000_r8, 60.055000_r8, & + ! 48.044000_r8, 0.000000_r8, 0.000000_r8, 0.000000_r8, 0.000000_r8, & + ! 0.000000_r8, 84.077000_r8, 84.077000_r8, 24.022000_r8, 24.022000_r8, & + ! 24.022000_r8, 24.022000_r8, 24.022000_r8, 36.033000_r8, 36.033000_r8, & + ! 36.033000_r8, 72.066000_r8, 12.011000_r8, 12.011000_r8, 12.011000_r8, & + ! 12.011000_r8, 24.022000_r8, 24.022000_r8, 24.022000_r8, 12.011000_r8, & + ! 12.011000_r8, 12.011000_r8, 12.011000_r8, 24.022000_r8, 24.022000_r8, & + ! 12.011000_r8, 24.022000_r8, 36.033000_r8, 36.033000_r8, 24.022000_r8, & + ! 24.022000_r8, 12.011000_r8, 12.011000_r8, 12.011000_r8, 12.011000_r8, & + ! 0.000000_r8, 0.000000_r8, 0.000000_r8, 0.000000_r8, 0.000000_r8, & + ! 12.011000_r8, 12.011000_r8, 12.011000_r8, 12.011000_r8, 12.011000_r8, & + ! 84.077000_r8, 24.022000_r8, 0.000000_r8, 0.000000_r8, 0.000000_r8, & + ! 12.011000_r8, 24.022000_r8, 0.000000_r8, 24.022000_r8, 24.022000_r8, & + ! 0.000000_r8, 0.000000_r8, 24.022000_r8, 0.000000_r8, 0.000000_r8, & + ! 0.000000_r8, 24.022000_r8, 24.022000_r8, 12.011000_r8, 0.000000_r8, & + ! 12.011000_r8, 12.011000_r8, 0.000000_r8, 0.000000_r8, 0.000000_r8, & + ! 0.000000_r8, 0.000000_r8, 48.044000_r8, 60.055000_r8, 36.033000_r8, & + ! 60.055000_r8, 60.055000_r8, 60.055000_r8, 60.055000_r8, 60.055000_r8, & + ! 60.055000_r8, 60.055000_r8, 60.055000_r8, 156.143000_r8, 48.044000_r8, & + ! 48.044000_r8, 48.044000_r8, 48.044000_r8, 48.044000_r8, 120.110000_r8, & + ! 48.044000_r8, 0.000000_r8, 0.000000_r8, 0.000000_r8, 60.055000_r8, & + ! 60.055000_r8, 0.000000_r8, 0.000000_r8, 0.000000_r8, 0.000000_r8, & + ! 0.000000_r8, 12.011000_r8, 12.011000_r8, 0.000000_r8, 0.000000_r8, & + ! 0.000000_r8, 36.033000_r8, 120.110000_r8, 0.000000_r8, 0.000000_r8, & + ! 0.000000_r8, 0.000000_r8, 0.000000_r8, 0.000000_r8, 0.000000_r8, & + ! 12.011000_r8, 48.044000_r8, 24.022000_r8, 84.077000_r8, 72.066000_r8, & + ! 72.066000_r8, 72.066000_r8, 12.011000_r8, 12.011000_r8, 36.033000_r8, & + ! 36.033000_r8, 0.000000_r8, 0.000000_r8, 0.000000_r8, 0.000000_r8, & + ! 0.000000_r8, 0.000000_r8, 0.000000_r8, 0.000000_r8, 180.165000_r8, & + ! 180.165000_r8, 180.165000_r8, 180.165000_r8, 180.165000_r8, 180.165000_r8, & + ! 180.165000_r8, 180.165000_r8, 180.165000_r8, 180.165000_r8, 180.165000_r8, & + ! 180.165000_r8, 180.165000_r8, 180.165000_r8, 180.165000_r8, 12.011000_r8, & + ! 264.242000_r8, 84.077000_r8, 120.110000_r8, 120.110000_r8, 120.110000_r8, & + ! 120.110000_r8, 108.099000_r8, 84.077000_r8, 84.077000_r8, 60.055000_r8, & + ! 96.088000_r8, 96.088000_r8, 96.088000_r8, 96.088000_r8, 0.000000_r8, & + ! 0.000000_r8, 84.077000_r8, 60.055000_r8, 72.066000_r8, 84.077000_r8, & + ! 24.022000_r8, 36.033000_r8, 72.066000_r8, 24.022000_r8, 12.011000_r8, & + ! 60.055000_r8, 48.044000_r8, 24.022000_r8, 24.022000_r8, 0.000000_r8, & + ! 12.011000_r8, 60.055000_r8, 60.055000_r8, 48.044000_r8, 48.044000_r8, & + ! 48.044000_r8, 48.044000_r8, 48.044000_r8, 120.110000_r8, 0.000000_r8, & + ! 0.000000_r8, 72.066000_r8, 36.033000_r8, 36.033000_r8, 120.110000_r8, & + ! 120.110000_r8, 84.077000_r8, 60.055000_r8, 96.088000_r8, 96.088000_r8, & + ! 0.000000_r8 /) + + ! fix_mass(: 3) = (/ 0.00000000_r8, 28.0134800_r8, 31.9988000_r8 /) + + ! clsmap(: 30,1) = (/ 3, 21, 33, 34, 35, 36, 37, 38, 39, 40, & + ! 41, 43, 44, 46, 54, 55, 61, 63, 71, 78, & + ! 82, 83, 84, 113, 122, 123, 148, 170, 185, 186 /) + ! clsmap(:191,4) = (/ 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, & + ! 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, & + ! 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, & + ! 42, 45, 47, 48, 49, 50, 51, 52, 53, 56, & + ! 57, 58, 59, 60, 62, 64, 65, 66, 67, 68, & + ! 69, 70, 72, 73, 74, 75, 76, 77, 79, 80, & + ! 81, 85, 86, 87, 88, 89, 90, 91, 92, 93, & + ! 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, & + ! 104, 105, 106, 107, 108, 109, 110, 111, 112, 114, & + ! 115, 116, 117, 118, 119, 120, 121, 124, 125, 126, & + ! 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, & + ! 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, & + ! 147, 149, 150, 151, 152, 153, 154, 155, 156, 157, & + ! 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, & + ! 168, 169, 171, 172, 173, 174, 175, 176, 177, 178, & + ! 179, 180, 181, 182, 183, 184, 187, 188, 189, 190, & + ! 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, & + ! 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, & + ! 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, & + ! 221 /) + + ! permute(:191,4) = (/ 121, 120, 1, 2, 144, 46, 85, 47, 86, 96, & + ! 68, 117, 75, 60, 81, 174, 61, 187, 110, 62, & + ! 78, 70, 111, 64, 79, 71, 149, 90, 39, 65, & + ! 189, 161, 38, 147, 166, 108, 102, 134, 91, 184, & + ! 45, 36, 183, 148, 155, 40, 50, 52, 69, 3, & + ! 4, 5, 41, 132, 151, 142, 176, 162, 114, 42, & + ! 138, 177, 49, 133, 57, 175, 83, 131, 136, 154, & + ! 58, 156, 72, 43, 139, 113, 107, 164, 89, 123, & + ! 34, 165, 73, 104, 74, 106, 145, 169, 82, 67, & + ! 84, 152, 6, 7, 8, 37, 9, 190, 185, 179, & + ! 141, 87, 10, 11, 12, 13, 188, 186, 76, 80, & + ! 59, 97, 44, 98, 48, 77, 14, 15, 109, 88, & + ! 103, 167, 140, 63, 16, 17, 18, 19, 20, 21, & + ! 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, & + ! 32, 33, 35, 53, 115, 118, 99, 150, 153, 116, & + ! 51, 54, 55, 124, 56, 92, 105, 146, 100, 93, & + ! 137, 135, 119, 173, 182, 129, 112, 66, 125, 178, & + ! 94, 168, 171, 170, 126, 172, 143, 122, 159, 180, & + ! 181, 95, 130, 160, 158, 157, 127, 163, 128, 101, & + ! 191 /) + + ! diag_map(:191) = (/ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, & + ! 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, & + ! 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, & + ! 31, 32, 33, 39, 45, 46, 49, 52, 55, 59, & + ! 62, 65, 68, 71, 74, 81, 87, 91, 96, 100, & + ! 109, 116, 121, 125, 134, 142, 147, 150, 155, 158, & + ! 161, 164, 168, 172, 176, 180, 184, 190, 193, 199, & + ! 205, 211, 214, 219, 224, 229, 234, 240, 245, 250, & + ! 258, 266, 272, 278, 284, 290, 296, 302, 308, 314, & + ! 320, 326, 334, 340, 347, 353, 356, 363, 367, 376, & + ! 384, 391, 397, 403, 409, 415, 423, 431, 435, 443, & + ! 451, 459, 467, 476, 483, 494, 503, 507, 515, 522, & + ! 533, 544, 552, 563, 576, 583, 594, 610, 621, 630, & + ! 640, 649, 657, 661, 666, 677, 687, 695, 709, 726, & + ! 732, 739, 744, 761, 787, 809, 819, 827, 841, 856, & + ! 865, 874, 886, 898, 911, 915, 928, 950, 969, 985, & + ! 996,1007,1024,1044,1060,1072,1083,1108,1130,1153, & + ! 1186,1205,1236,1250,1263,1276,1296,1390,1448,1473, & + ! 1621,1672,1699,1734,1776,1837,1862,1893,1917,1996, & + ! 2022 /) + + ! slvd_lst(: 34) = (/ 'ACBZO2 ', 'ALKO2 ', 'BENZO2 ', 'BZOO ', 'C2H5O2 ', & + ! 'C3H7O2 ', 'C6H5O2 ', 'CH3CO3 ', 'CH3O2 ', 'DICARBO2 ', & + ! 'ENEO2 ', 'EO ', 'EO2 ', 'HO2 ', 'HOCH2OO ', & + ! 'ISOPAO2 ', 'ISOPBO2 ', 'MACRO2 ', 'MALO2 ', 'MCO3 ', & + ! 'MDIALO2 ', 'MEKO2 ', 'NTERPO2 ', 'O1D ', 'OH ', & + ! 'PHENO2 ', 'PO2 ', 'RO2 ', 'TERP2O2 ', 'TERPO2 ', & + ! 'TOLO2 ', 'XO2 ', 'XYLENO2 ', 'XYLOLO2 ' /) + + end subroutine set_sim_dat + + end module mo_sim_dat diff --git a/src/chemistry/modal_aero/aero_model.F90 b/src/chemistry/modal_aero/aero_model.F90 index 34ad6bb634..43ef5caa33 100644 --- a/src/chemistry/modal_aero/aero_model.F90 +++ b/src/chemistry/modal_aero/aero_model.F90 @@ -182,13 +182,13 @@ subroutine aero_model_init( pbuf2d ) use mo_chem_utls, only: get_inv_ndx use cam_history, only: addfld, add_default, horiz_only - use mo_chem_utls, only: get_rxt_ndx, get_spc_ndx + use mo_chem_utls, only: get_spc_ndx use modal_aero_data, only: cnst_name_cw use modal_aero_data, only: modal_aero_data_init use rad_constituents,only: rad_cnst_get_info use dust_model, only: dust_init, dust_names, dust_active, dust_nbin, dust_nnum use seasalt_model, only: seasalt_init, seasalt_names, seasalt_active,seasalt_nbin - use drydep_mod, only: inidrydep + use aer_drydep_mod, only: inidrydep use wetdep, only: wetdep_init use modal_aero_calcsize, only: modal_aero_calcsize_init @@ -670,7 +670,7 @@ end subroutine aero_model_init subroutine aero_model_drydep ( state, pbuf, obklen, ustar, cam_in, dt, cam_out, ptend ) use dust_sediment_mod, only: dust_sediment_tend - use drydep_mod, only: d3ddflux, calcram + use aer_drydep_mod, only: d3ddflux, calcram use modal_aero_data, only: qqcw_get_field use modal_aero_data, only: cnst_name_cw use modal_aero_data, only: alnsg_amode diff --git a/src/chemistry/modal_aero/modal_aero_data.F90 b/src/chemistry/modal_aero/modal_aero_data.F90 index 1f1d8075fb..15b247584d 100644 --- a/src/chemistry/modal_aero/modal_aero_data.F90 +++ b/src/chemistry/modal_aero/modal_aero_data.F90 @@ -459,7 +459,7 @@ subroutine modal_aero_data_init(pbuf2d) lptr2_soa_g_amode(:) = -1 soa_ndx = 0 do i = 1, pcnst - if (cnst_name(i)(:4) == 'SOAG') then + if (cnst_name(i)(:4) == 'SOAG' .and. cnst_name(i)(:5) /= 'SOAGX') then soa_ndx = soa_ndx+1 lptr2_soa_g_amode(soa_ndx) = i endif diff --git a/src/chemistry/modal_aero/modal_aero_gasaerexch.F90 b/src/chemistry/modal_aero/modal_aero_gasaerexch.F90 index 1ab0c0fccb..d45b0d46af 100644 --- a/src/chemistry/modal_aero/modal_aero_gasaerexch.F90 +++ b/src/chemistry/modal_aero/modal_aero_gasaerexch.F90 @@ -108,7 +108,7 @@ subroutine modal_aero_gasaerexch_sub( & use physconst, only: gravit, mwdry, rair use cam_abortutils, only: endrun use spmd_utils, only: iam, masterproc - +use phys_control, only: cam_chempkg_is implicit none @@ -263,7 +263,11 @@ subroutine modal_aero_gasaerexch_sub( & ! set gas species indices call cnst_get_ind( 'H2SO4', l_so4g, .false. ) call cnst_get_ind( 'NH3', l_nh4g, .false. ) - call cnst_get_ind( 'MSA', l_msag, .false. ) + if ( .not. cam_chempkg_is('geoschem_mam4') ) then + call cnst_get_ind( 'MSA', l_msag, .false. ) + else + l_msag = 0 + endif l_so4g = l_so4g - loffset l_nh4g = l_nh4g - loffset l_msag = l_msag - loffset diff --git a/src/chemistry/modal_aero/sox_cldaero_mod.F90 b/src/chemistry/modal_aero/sox_cldaero_mod.F90 index bacf94246c..2500aa37e5 100644 --- a/src/chemistry/modal_aero/sox_cldaero_mod.F90 +++ b/src/chemistry/modal_aero/sox_cldaero_mod.F90 @@ -13,7 +13,7 @@ module sox_cldaero_mod use modal_aero_data, only : cnst_name_cw, specmw_so4_amode use chem_mods, only : adv_mass use physconst, only : gravit - use phys_control, only : phys_getopts + use phys_control, only : phys_getopts, cam_chempkg_is use cldaero_mod, only : cldaero_uptakerate use chem_mods, only : gas_pcnst @@ -229,6 +229,12 @@ subroutine sox_cldaero_update( & dqdt_aqhprxn(:,:) = 0.0_r8 dqdt_aqo3rxn(:,:) = 0.0_r8 + ! Avoid double counting in-cloud sulfur oxidation when running with + ! GEOS-Chem. If running with GEOS-Chem then sulfur oxidation + ! is performed internally to GEOS-Chem. Here, we just return to the + ! parent routine and thus we do not apply tendencies calculated by MAM. + if ( cam_chempkg_is('geoschem_mam4') ) return + lev_loop: do k = 1,pver col_loop: do i = 1,ncol cloud: if (cldfrc(i,k) >= 1.0e-5_r8) then diff --git a/src/chemistry/mozart/chemistry.F90 b/src/chemistry/mozart/chemistry.F90 index ff42e870d9..9c6396e262 100644 --- a/src/chemistry/mozart/chemistry.F90 +++ b/src/chemistry/mozart/chemistry.F90 @@ -1333,10 +1333,12 @@ subroutine chem_final() use mee_ionization, only: mee_ion_final use rate_diags, only: rate_diags_final use species_sums_diags, only: species_sums_final + use short_lived_species, only: short_lived_species_final call mee_ion_final() call rate_diags_final() call species_sums_final() + call short_lived_species_final() end subroutine chem_final diff --git a/src/chemistry/mozart/mo_chem_utls.F90 b/src/chemistry/mozart/mo_chem_utls.F90 index 1620422e12..dbed06c9e8 100644 --- a/src/chemistry/mozart/mo_chem_utls.F90 +++ b/src/chemistry/mozart/mo_chem_utls.F90 @@ -8,29 +8,43 @@ module mo_chem_utls contains - integer function get_spc_ndx( spc_name ) + integer function get_spc_ndx( spc_name, ignore_case ) !----------------------------------------------------------------------- ! ... return overall species index associated with spc_name !----------------------------------------------------------------------- use chem_mods, only : gas_pcnst use mo_tracname, only : tracnam => solsym + use string_utils, only : to_upper implicit none !----------------------------------------------------------------------- ! ... dummy arguments !----------------------------------------------------------------------- - character(len=*), intent(in) :: spc_name + character(len=*), intent(in) :: spc_name + logical, intent(in), optional :: ignore_case !----------------------------------------------------------------------- ! ... local variables !----------------------------------------------------------------------- integer :: m + logical :: convert_to_upper + logical :: match + + convert_to_upper = .false. + if ( present( ignore_case ) ) then + convert_to_upper = ignore_case + endif get_spc_ndx = -1 do m = 1,gas_pcnst - if( trim( spc_name ) == trim( tracnam(m) ) ) then + if ( .not. convert_to_upper ) then + match = trim( spc_name ) == trim( tracnam(m) ) + else + match = trim( to_upper( spc_name ) ) == trim( to_upper( tracnam(m) ) ) + endif + if( match ) then get_spc_ndx = m exit end if diff --git a/src/chemistry/mozart/mo_neu_wetdep.F90 b/src/chemistry/mozart/mo_neu_wetdep.F90 index c3974b7e37..78b3779fe4 100644 --- a/src/chemistry/mozart/mo_neu_wetdep.F90 +++ b/src/chemistry/mozart/mo_neu_wetdep.F90 @@ -51,7 +51,7 @@ subroutine neu_wetdep_init ! use constituents, only : cnst_get_ind,cnst_mw use cam_history, only : addfld, add_default, horiz_only - use phys_control, only : phys_getopts + use phys_control, only : phys_getopts, cam_chempkg_is ! integer :: m,l character*20 :: test_name @@ -85,6 +85,9 @@ subroutine neu_wetdep_init ! mapping based on the MOZART4 wet removal subroutine; ! this might need to be redone (JFL: Sep 2010) ! +! Skip mapping if using GEOS-Chem; all GEOS-Chem species are in dep_data_file +! (heff table) specified in namelist drv_flds_in (EWL: Dec 2022) + if ( .not. cam_chempkg_is('geoschem_mam4') ) then select case( trim(test_name) ) ! ! CCMI: added SO2t and NH_50W @@ -108,6 +111,7 @@ subroutine neu_wetdep_init case( 'SOAGbb4' ) test_name = 'SOAGff4' end select + endif ! do l = 1,n_species_table ! diff --git a/src/chemistry/mozart/short_lived_species.F90 b/src/chemistry/mozart/short_lived_species.F90 index 76fb30b20e..37a43d90bb 100644 --- a/src/chemistry/mozart/short_lived_species.F90 +++ b/src/chemistry/mozart/short_lived_species.F90 @@ -12,7 +12,6 @@ module short_lived_species use ppgrid, only : pcols, pver, begchunk, endchunk use spmd_utils, only : masterproc - implicit none save @@ -23,24 +22,36 @@ module short_lived_species public :: short_lived_species_writeic public :: initialize_short_lived_species public :: set_short_lived_species + public :: set_short_lived_species_gc ! for GEOS-Chem chemistry public :: get_short_lived_species + public :: get_short_lived_species_gc ! for GEOS-Chem chemistry public :: slvd_index public :: pbf_idx + public :: short_lived_species_final integer :: pbf_idx integer :: map(nslvd) character(len=*), parameter :: pbufname = 'ShortLivedSpecies' + real(r8), allocatable :: slvd_ref_mmr(:) + contains !--------------------------------------------------------------------- !--------------------------------------------------------------------- - subroutine register_short_lived_species + subroutine register_short_lived_species (ref_mmr) use physics_buffer, only : pbuf_add_field, dtype_r8 + real(r8), optional :: ref_mmr(nslvd) + if ( nslvd < 1 ) return + if ( present(ref_mmr) ) then + allocate(slvd_ref_mmr(nslvd)) + slvd_ref_mmr = ref_mmr + endif + call pbuf_add_field(pbufname,'global',dtype_r8,(/pcols,pver,nslvd/),pbf_idx) end subroutine register_short_lived_species @@ -94,6 +105,7 @@ subroutine initialize_short_lived_species(ncid_ini, pbuf2d) use mo_tracname, only : solsym use ncdio_atm, only : infld use pio, only : file_desc_t + use phys_control, only : cam_chempkg_is use physics_buffer, only : physics_buffer_desc, pbuf_set_field implicit none @@ -124,19 +136,31 @@ subroutine initialize_short_lived_species(ncid_ini, pbuf2d) allocate(tmpptr(pcols,pver,begchunk:endchunk)) do m=1,nslvd - n = map(m) - fieldname = solsym(n) + + if (cam_chempkg_is('geoschem_mam4')) then + fieldname = trim(slvd_lst(m)) + else + n = map(m) + fieldname = solsym(n) + end if + call infld( fieldname,ncid_ini,dim1name, 'lev', dim2name, 1, pcols, 1, pver, begchunk, endchunk, & tmpptr, found, gridname='physgrid') if (.not.found) then - tmpptr(:,:,:) = 1.e-36_r8 + if ( allocated(slvd_ref_mmr) ) then + tmpptr(:,:,:) = slvd_ref_mmr(m) + else + tmpptr(:,:,:) = 1.e-36_r8 + endif endif call pbuf_set_field(pbuf2d, pbf_idx, tmpptr, start=(/1,1,m/),kount=(/pcols,pver,1/)) if (masterproc) write(iulog,*) fieldname, ' is set to short-lived' + if ( allocated(slvd_ref_mmr) .and. masterproc) write(iulog,'(a, E16.5E4)') ' --> reference MMR: ', slvd_ref_mmr(m) + enddo deallocate(tmpptr) @@ -166,6 +190,29 @@ subroutine set_short_lived_species( q, lchnk, ncol, pbuf ) end subroutine set_short_lived_species +!--------------------------------------------------------------------- +!--------------------------------------------------------------------- + subroutine set_short_lived_species_gc( q, lchnk, ncol, pbuf ) + + use physics_buffer, only : physics_buffer_desc, pbuf_set_field + + implicit none + + ! 3rd dimension of out array is nslvd if using GEOS-Chem chemistry + real(r8), intent(in) :: q(pcols,pver,nslvd) + integer, intent(in) :: lchnk, ncol + type(physics_buffer_desc), pointer :: pbuf(:) + + integer :: m + + if ( nslvd < 1 ) return + + do m=1,nslvd + call pbuf_set_field(pbuf, pbf_idx, q(:,:,m), start=(/1,1,m/),kount=(/pcols,pver,1/)) + enddo + + end subroutine set_short_lived_species_gc + !--------------------------------------------------------------------- !--------------------------------------------------------------------- subroutine get_short_lived_species( q, lchnk, ncol, pbuf ) @@ -191,6 +238,31 @@ subroutine get_short_lived_species( q, lchnk, ncol, pbuf ) endsubroutine get_short_lived_species +!--------------------------------------------------------------------- +!--------------------------------------------------------------------- + subroutine get_short_lived_species_gc( q, lchnk, ncol, pbuf ) + use physics_buffer, only : physics_buffer_desc, pbuf_get_field + + implicit none + + ! 3rd dimension of out array is nslvd if using GEOS-Chem chemistry + real(r8), intent(inout) :: q(pcols,pver,nslvd) + integer, intent(in) :: lchnk, ncol + type(physics_buffer_desc), pointer :: pbuf(:) + real(r8),pointer :: tmpptr(:,:) + + + integer :: m + + if ( nslvd < 1 ) return + + do m=1,nslvd + call pbuf_get_field(pbuf, pbf_idx, tmpptr, start=(/1,1,m/), kount=(/ pcols,pver,1 /)) + q(:ncol,:,m) = tmpptr(:ncol,:) + enddo + + endsubroutine get_short_lived_species_gc + !--------------------------------------------------------------------- !--------------------------------------------------------------------- function slvd_index( name ) @@ -214,4 +286,12 @@ function slvd_index( name ) endfunction slvd_index +!--------------------------------------------------------------------- +!--------------------------------------------------------------------- + subroutine short_lived_species_final + + if ( allocated(slvd_ref_mmr) ) deallocate(slvd_ref_mmr) + + end subroutine short_lived_species_final + end module short_lived_species diff --git a/src/cpl/nuopc/atm_import_export.F90 b/src/cpl/nuopc/atm_import_export.F90 index 8c28b120fa..baadd00865 100644 --- a/src/cpl/nuopc/atm_import_export.F90 +++ b/src/cpl/nuopc/atm_import_export.F90 @@ -243,6 +243,7 @@ subroutine advertise_fields(gcomp, flds_scalar_name, rc) call fldlist_add(fldsToAtm_num, fldsToAtm, 'Si_snowh' ) call fldlist_add(fldsToAtm_num, fldsToAtm, 'So_ssq' ) call fldlist_add(fldsToAtm_num, fldsToAtm, 'So_re' ) + call fldlist_add(fldsToAtm_num, fldsToAtm, 'So_ustar' ) call fldlist_add(fldsToAtm_num, fldsToAtm, 'Sx_u10' ) call fldlist_add(fldsToAtm_num, fldsToAtm, 'Faxx_taux' ) call fldlist_add(fldsToAtm_num, fldsToAtm, 'Faxx_tauy' ) diff --git a/src/physics/cam/constituents.F90 b/src/physics/cam/constituents.F90 index 49a3fab61d..b93cf060b3 100644 --- a/src/physics/cam/constituents.F90 +++ b/src/physics/cam/constituents.F90 @@ -173,7 +173,7 @@ subroutine cnst_add (name, mwc, cpc, qminc, & padv = padv+1 ind = padv if (padv > pcnst) then - write(errmsg, *) sub//': FATAL: advected tracer index greater than pcnst=', pcnst + write(errmsg, *) sub//': FATAL: advected tracer (', trim(name), ') index is greater than number of constituents' call endrun(errmsg) end if @@ -379,7 +379,7 @@ subroutine cnst_get_ind (name, ind, abort) if (present(abort)) abort_on_error = abort if (abort_on_error) then - write(iulog, *) sub//': FATAL: name:', name, ' not found in list:', cnst_name(:) + write(iulog, *) sub//': FATAL: name:', name, ' not found in constituent list: ', cnst_name(:) call endrun(sub//': FATAL: name not found') end if diff --git a/test/system/TR8.sh b/test/system/TR8.sh index e107c702d3..f56c9bc636 100755 --- a/test/system/TR8.sh +++ b/test/system/TR8.sh @@ -54,12 +54,12 @@ fi #Check Chemistry if [ -d "${CAM_ROOT}/components/cam" ]; then -ruby $ADDREALKIND_EXE -r r8 -l 1 -d $CAM_ROOT/components/cam/src/chemistry +ruby $ADDREALKIND_EXE -r r8 -l 1 -d $CAM_ROOT/components/cam/src/chemistry -s geoschem rc=`expr $? + $rc` else -ruby $ADDREALKIND_EXE -r r8 -l 1 -d $CAM_ROOT/src/chemistry +ruby $ADDREALKIND_EXE -r r8 -l 1 -d $CAM_ROOT/src/chemistry -s geoschem rc=`expr $? + $rc` fi