diff --git a/.github/workflows/lint-fortran.yaml b/.github/workflows/lint-fortran.yaml new file mode 100644 index 0000000..b5ae2b5 --- /dev/null +++ b/.github/workflows/lint-fortran.yaml @@ -0,0 +1,16 @@ +# ----------------------------------------------------------------------------- +# (C) Crown copyright Met Office. All rights reserved. +# The file LICENCE, distributed with this code, contains details of the terms +# under which the code may be used. +# ----------------------------------------------------------------------------- + +name: Lint Fortran + +on: + pull_request: + +jobs: + fortitude-lint: + uses: MetOffice/growss/.github/workflows/fortran-lint.yaml@main + with: + runner: "ubuntu-24.04" diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 27b4447..de11f63 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -5,3 +5,7 @@ | james-bruten-mo | James Bruten | Met Office | 2025-12-09 | | andrewcoughtrie | Andrew Coughtrie | Met Office | 2026-02-10 | | jennyhickson | Jenny Hickson | Met Office | 2026-03-02 | +| Pierre-siddall | Pierre Siddall | Met Office | 2026-03-13 | +| JoeCartonKelly-MO | Joseph Carton-Kelly | Met Office | 2026-04-15 | +| yg460-cam | Yao Ge | University of Cambridge | 2026-04-17 | +| theabro | Nathan Luke Abraham | NCAS & University of Cambridge | 2026-03-19 | diff --git a/fortitude.toml b/fortitude.toml new file mode 100644 index 0000000..a765ac6 --- /dev/null +++ b/fortitude.toml @@ -0,0 +1,5 @@ +[check] +select=["E000","C031","C032","C043","C051", +"C091","C082","C141","OB011","OB021","OB051", +"OB061","MOD001","PORT011","PORT012","PORT021", +"FORT001","FORT002","FORT003","FORT004","FORT005"] diff --git a/src/control/core/diagnostics/asad_chem_flux_diags.F90 b/src/control/core/diagnostics/asad_chem_flux_diags.F90 index 4f3beb0..7208558 100644 --- a/src/control/core/diagnostics/asad_chem_flux_diags.F90 +++ b/src/control/core/diagnostics/asad_chem_flux_diags.F90 @@ -1412,7 +1412,7 @@ FUNCTION cd_findreaction( numprods, reactants, products, & zhook_handle) RETURN END IF - CYCLE + CYCLE search_reactants END IF ! Otherwise check the names of the products diff --git a/src/control/core/diagnostics/asad_flux_dat.F90 b/src/control/core/diagnostics/asad_flux_dat.F90 index 09c003a..332aeed 100644 --- a/src/control/core/diagnostics/asad_flux_dat.F90 +++ b/src/control/core/diagnostics/asad_flux_dat.F90 @@ -8766,6 +8766,20 @@ MODULE asad_flux_dat [' ',' ',' ',' ']) & ] +! Add extra het chem fluxes for both troposphere and stratosphere +! Reactions are used in both StratTrop and CRI-Strat2 +! Note that B85 is a biomolecular reaction but is aping a heterogeneous one +TYPE(asad_flux_defn), PARAMETER, PUBLIC :: & + het_chem_n2o5_h2o(2) = [ & +! B85 N2O5+H2O +asad_flux_defn('RXN',50993,'B',.FALSE.,0,4, & +['N2O5 ','H2O '], & +['HONO2 ','HONO2 ',' ',' ']), & +! PSC N2O5-H2O H04 +asad_flux_defn('RXN',50994,'H',.FALSE.,0,4, & +['N2O5 ','H2O '], & +['HONO2 ','HONO2 ',' ',' ']) & +] PUBLIC :: asad_load_default_fluxes @@ -8853,6 +8867,7 @@ SUBROUTINE asad_load_default_fluxes TYPE(asad_flux_defn), ALLOCATABLE, SAVE :: aa_ch4_budget_loss(:) TYPE(asad_flux_defn), ALLOCATABLE, SAVE :: aa_ch4_drydep(:) TYPE(asad_flux_defn), ALLOCATABLE, SAVE :: aa_ch4_ste(:) +TYPE(asad_flux_defn), ALLOCATABLE, SAVE :: aa_het_chem_n2o5_h2o(:) INTEGER :: p1 ! start position in asad_chemical_fluxes array INTEGER :: p2 ! end position in asad_chemical_fluxes array @@ -9025,6 +9040,9 @@ SUBROUTINE asad_load_default_fluxes ALLOCATE(aa_ch4_ste(SIZE(asad_ch4_ste))) aa_ch4_ste = asad_ch4_ste END IF + ! aa_het_chem_n2o5_h2o + ALLOCATE(aa_het_chem_n2o5_h2o(SIZE(het_chem_n2o5_h2o))) + aa_het_chem_n2o5_h2o = het_chem_n2o5_h2o ELSE IF (ukca_config%l_ukca_cristrat) THEN ! Select the asad diagnostics appropriate for CRI-Strat chemistry and @@ -9233,6 +9251,9 @@ SUBROUTINE asad_load_default_fluxes ALLOCATE(aa_oxidN_wetdep(SIZE(cri_oxidN_wetdep))) aa_oxidN_wetdep = cri_oxidN_wetdep END IF + ! aa_het_chem_n2o5_h2o + ALLOCATE(aa_het_chem_n2o5_h2o(SIZE(het_chem_n2o5_h2o))) + aa_het_chem_n2o5_h2o = het_chem_n2o5_h2o ELSE IF (ukca_config%l_ukca_offline .OR. ukca_config%l_ukca_offline_be) THEN @@ -9306,6 +9327,8 @@ SUBROUTINE asad_load_default_fluxes n_chemical_fluxes = n_chemical_fluxes + SIZE(aa_ch4_drydep) IF (ALLOCATED(aa_ch4_ste)) & n_chemical_fluxes = n_chemical_fluxes + SIZE(aa_ch4_ste) +IF (ALLOCATED(aa_het_chem_n2o5_h2o)) & + n_chemical_fluxes = n_chemical_fluxes + SIZE(aa_het_chem_n2o5_h2o) ALLOCATE(asad_chemical_fluxes(n_chemical_fluxes)) @@ -9449,6 +9472,11 @@ SUBROUTINE asad_load_default_fluxes asad_chemical_fluxes(p1:p2) = aa_ch4_ste(:) p1 = p2 + 1 END IF +IF (ALLOCATED(aa_het_chem_n2o5_h2o)) THEN + p2 = p1 + SIZE(aa_het_chem_n2o5_h2o) - 1 + asad_chemical_fluxes(p1:p2) = aa_het_chem_n2o5_h2o(:) + p1 = p2 + 1 +END IF IF (p2 /= n_chemical_fluxes) THEN cmessage = ' n_chemical_fluxes and p2 are different' @@ -9502,6 +9530,7 @@ SUBROUTINE asad_load_default_fluxes END IF ! Deallocate the generic arrays +IF (ALLOCATED(aa_het_chem_n2o5_h2o)) DEALLOCATE(aa_het_chem_n2o5_h2o) IF (ALLOCATED(aa_ch4_ste)) DEALLOCATE(aa_ch4_ste) IF (ALLOCATED(aa_ch4_drydep)) DEALLOCATE(aa_ch4_drydep) IF (ALLOCATED(aa_ch4_budget_loss)) DEALLOCATE(aa_ch4_budget_loss) diff --git a/src/control/core/interface/ukca_config_specification_mod.F90 b/src/control/core/interface/ukca_config_specification_mod.F90 index 9213f95..a32b743 100644 --- a/src/control/core/interface/ukca_config_specification_mod.F90 +++ b/src/control/core/interface/ukca_config_specification_mod.F90 @@ -360,6 +360,7 @@ MODULE ukca_config_specification_mod ! B-E Offline Oxidants scheme LOGICAL :: l_fix_ukca_h2so4_ystore ! True to fix storage of H2SO4 in ASAD ! N-R schemes for updating in GLOMAP + LOGICAL :: l_fix_ukca_n2o5_h2o ! True to filter N2O5+H2O to strat and trop only ! Settings for managing photolysis environmental driver ! requirements on behalf of external UKCA Photolysis code @@ -443,6 +444,10 @@ MODULE ukca_config_specification_mod REAL :: acc_cor_scav_scaling ! Scaling factor for scavenging ! parameters for the accumulation and ! coarse modes + REAL :: solinsol_hygro_ratio(4) ! SOL/INSOL hygroscopicity ratios + ! cp_su, cp_cl, cp_bc, cp_oc + ! This ratio only affects the wet + ! part of the aerosol ! -- GLOMAP deposition configuration options -- LOGICAL :: l_ddepaer ! True for aerosol dry deposition @@ -949,6 +954,7 @@ SUBROUTINE init_ukca_configuration() ukca_config%l_fix_drydep_so2_water = .FALSE. ukca_config%l_fix_ukca_offox_h2o_fac = .FALSE. ukca_config%l_fix_ukca_h2so4_ystore = .FALSE. +ukca_config%l_fix_ukca_n2o5_h2o = .FALSE. ! -- Settings for managing Photolysis driver requirements ukca_config%i_photol_scheme = imdi @@ -996,6 +1002,7 @@ SUBROUTINE init_ukca_configuration() glomap_config%l_dust_mp_ageing = .FALSE. glomap_config%dry_depvel_acc_scaling = rmdi glomap_config%acc_cor_scav_scaling = rmdi +glomap_config%solinsol_hygro_ratio(:) = rmdi ! -- GLOMAP deposition configuration options -- glomap_config%l_ddepaer = .FALSE. @@ -1122,6 +1129,7 @@ SUBROUTINE ukca_get_config( & hno3_uptake_coeff, & sigwmin, & sigma_updraught_scaling, & + solinsol_hygro_ratio, & l_cal360, & l_ukca_chem_aero, & l_ukca_mode, & @@ -1179,6 +1187,7 @@ SUBROUTINE ukca_get_config( & l_fix_drydep_so2_water, & l_fix_ukca_offox_h2o_fac, & l_fix_ukca_h2so4_ystore, & + l_fix_ukca_n2o5_h2o, & l_ukca_chem, l_ukca_trop, l_ukca_aerchem, l_ukca_raq, l_ukca_raqaero, & l_ukca_offline_be, l_ukca_tropisop, l_ukca_strattrop, l_ukca_strat, & l_ukca_offline, l_ukca_cristrat, l_ukca_stratcfc, l_ukca_achem, & @@ -1324,6 +1333,7 @@ SUBROUTINE ukca_get_config( & REAL, OPTIONAL, INTENT(OUT) :: ph_fit_coeff_b REAL, OPTIONAL, INTENT(OUT) :: ph_fit_intercept REAL, OPTIONAL, INTENT(OUT) :: hno3_uptake_coeff +REAL, OPTIONAL, INTENT(OUT) :: solinsol_hygro_ratio(4) LOGICAL, OPTIONAL, INTENT(OUT) :: l_cal360 LOGICAL, OPTIONAL, INTENT(OUT) :: l_ukca_chem_aero @@ -1395,6 +1405,7 @@ SUBROUTINE ukca_get_config( & LOGICAL, OPTIONAL, INTENT(OUT) :: l_fix_drydep_so2_water LOGICAL, OPTIONAL, INTENT(OUT) :: l_fix_ukca_offox_h2o_fac LOGICAL, OPTIONAL, INTENT(OUT) :: l_fix_ukca_h2so4_ystore +LOGICAL, OPTIONAL, INTENT(OUT) :: l_fix_ukca_n2o5_h2o LOGICAL, OPTIONAL, INTENT(OUT) :: l_ukca_chem LOGICAL, OPTIONAL, INTENT(OUT) :: l_ukca_trop LOGICAL, OPTIONAL, INTENT(OUT) :: l_ukca_aerchem @@ -1685,6 +1696,8 @@ SUBROUTINE ukca_get_config( & l_fix_ukca_offox_h2o_fac = ukca_config%l_fix_ukca_offox_h2o_fac IF (PRESENT(l_fix_ukca_h2so4_ystore)) & l_fix_ukca_h2so4_ystore = ukca_config%l_fix_ukca_h2so4_ystore +IF (PRESENT(l_fix_ukca_n2o5_h2o)) & + l_fix_ukca_n2o5_h2o = ukca_config%l_fix_ukca_n2o5_h2o ! -- UKCA internal configuration variables IF (PRESENT(l_ukca_chem)) l_ukca_chem = ukca_config%l_ukca_chem @@ -1748,6 +1761,8 @@ SUBROUTINE ukca_get_config( & IF (PRESENT(mode_incld_so2_rfrac)) & mode_incld_so2_rfrac = glomap_config%mode_incld_so2_rfrac IF (PRESENT(l_aero_rainout)) l_aero_rainout = glomap_config%l_aero_rainout +IF (PRESENT(solinsol_hygro_ratio)) solinsol_hygro_ratio(:) = & + glomap_config%solinsol_hygro_ratio(:) IF (PRESENT(l_cv_rainout)) l_cv_rainout = glomap_config%l_cv_rainout IF (PRESENT(i_mode_nucscav)) i_mode_nucscav = glomap_config%i_mode_nucscav IF (PRESENT(l_impc_scav)) l_impc_scav = glomap_config%l_impc_scav diff --git a/src/control/core/top_level/ukca_setup_mod.F90 b/src/control/core/top_level/ukca_setup_mod.F90 index affe86e..9a347c1 100644 --- a/src/control/core/top_level/ukca_setup_mod.F90 +++ b/src/control/core/top_level/ukca_setup_mod.F90 @@ -129,6 +129,7 @@ SUBROUTINE ukca_setup(error_code, & ph_fit_intercept, & sigwmin, & sigma_updraught_scaling, & + solinsol_hygro_ratio, & const_rmol, & const_tfs, & const_rho_water, & @@ -207,6 +208,7 @@ SUBROUTINE ukca_setup(error_code, & l_fix_ukca_h2dd_x, & l_fix_ukca_offox_h2o_fac, & l_fix_ukca_h2so4_ystore, & + l_fix_ukca_n2o5_h2o, & l_mode_bhn_on, & l_mode_bln_on, & l_ddepaer, & @@ -461,6 +463,7 @@ SUBROUTINE ukca_setup(error_code, & REAL, OPTIONAL, INTENT(IN) :: sigwmin REAL, OPTIONAL, INTENT(IN) :: hno3_uptake_coeff REAL, OPTIONAL, INTENT(IN) :: sigma_updraught_scaling +REAL, OPTIONAL, INTENT(IN) :: solinsol_hygro_ratio(4) REAL, OPTIONAL, INTENT(IN) :: const_rmol REAL, OPTIONAL, INTENT(IN) :: const_tfs REAL, OPTIONAL, INTENT(IN) :: const_rho_water @@ -540,6 +543,7 @@ SUBROUTINE ukca_setup(error_code, & LOGICAL, OPTIONAL, INTENT(IN) :: l_fix_ukca_h2dd_x LOGICAL, OPTIONAL, INTENT(IN) :: l_fix_ukca_offox_h2o_fac LOGICAL, OPTIONAL, INTENT(IN) :: l_fix_ukca_h2so4_ystore +LOGICAL, OPTIONAL, INTENT(IN) :: l_fix_ukca_n2o5_h2o LOGICAL, OPTIONAL, INTENT(IN) :: l_mode_bhn_on LOGICAL, OPTIONAL, INTENT(IN) :: l_mode_bln_on LOGICAL, OPTIONAL, INTENT(IN) :: l_ddepaer @@ -600,6 +604,8 @@ SUBROUTINE ukca_setup(error_code, & LOGICAL :: l_strat_scheme_selected ! True if a Stratospheric scheme is ! selected for chemistry +REAL :: sum_solinsol_hygro_ratio ! Sum of solinsol hygroscopicity ratios (=1) + INTEGER (KIND=jpim), PARAMETER :: zhook_in = 0 ! DrHook tracing entry INTEGER (KIND=jpim), PARAMETER :: zhook_out = 1 ! DrHook tracing exit REAL (KIND=jprb) :: zhook_handle ! DrHook tracing @@ -1232,6 +1238,13 @@ SUBROUTINE ukca_setup(error_code, & ukca_config%l_fix_ukca_h2so4_ystore = l_fix_ukca_h2so4_ystore END IF +IF (ukca_config%i_ukca_chem == i_ukca_chem_strattrop .OR. & + ukca_config%i_ukca_chem == i_ukca_chem_cristrat) THEN + ukca_config%l_fix_ukca_n2o5_h2o = .TRUE. + IF (PRESENT(l_fix_ukca_n2o5_h2o)) & + ukca_config%l_fix_ukca_n2o5_h2o = l_fix_ukca_n2o5_h2o +END IF + ! Settings for managing photolysis environmental driver ! requirements on behalf of external UKCA Photolysis code @@ -1313,6 +1326,9 @@ SUBROUTINE ukca_setup(error_code, & END IF + IF (PRESENT(solinsol_hygro_ratio)) & + glomap_config%solinsol_hygro_ratio(:) = solinsol_hygro_ratio(:) + ! -- GLOMAP deposition configuration options -- ! Dry deposition @@ -1721,6 +1737,22 @@ SUBROUTINE ukca_setup(error_code, & END IF +! Ensure the sum of solinsol_hygro_ratio is 1 +IF (ukca_config%l_ukca_mode .AND. & + glomap_config%i_mode_setup == 11) THEN + sum_solinsol_hygro_ratio = 0.0 + DO i = 1,4 + sum_solinsol_hygro_ratio = sum_solinsol_hygro_ratio + & + glomap_config%solinsol_hygro_ratio(i) + END DO + IF (PRESENT(solinsol_hygro_ratio)) THEN + glomap_config%solinsol_hygro_ratio(:) = & + glomap_config%solinsol_hygro_ratio(:) / sum_solinsol_hygro_ratio + ELSE + glomap_config%solinsol_hygro_ratio(:) = [1.0, 0.0, 0.0, 0.0] + END IF +END IF + ! Initialise chemical definition arrays CALL ukca_chem1_init() diff --git a/src/science/core/aerosols/glomap/ukca_volume_mode.F90 b/src/science/core/aerosols/glomap/ukca_volume_mode.F90 index 2e90de8..d38e11f 100644 --- a/src/science/core/aerosols/glomap/ukca_volume_mode.F90 +++ b/src/science/core/aerosols/glomap/ukca_volume_mode.F90 @@ -156,8 +156,8 @@ SUBROUTINE ukca_volume_mode( glomap_variables_local, nbox, nd, md, mdt, & USE ukca_config_constants_mod, ONLY: rho_water, avogadro, rho_so4 USE ukca_mode_setup, ONLY: nmodes, nanion, ncation, & - cp_su, cp_oc, cp_cl, & - cp_so, cp_nh4, cp_no3, cp_nn + cp_su, cp_oc, cp_cl, cp_so, & + cp_nh4, cp_no3, cp_nn USE yomhook, ONLY: lhook, dr_hook USE parkind1, ONLY: jprb, jpim @@ -240,6 +240,7 @@ SUBROUTINE ukca_volume_mode( glomap_variables_local, nbox, nd, md, mdt, & REAL :: mmwrhow REAL :: f_ao REAL :: cl(nbox,-nanion:ncation) !ION CONCS (MOL/CC OF AIR) +REAL :: mdcopy(nbox,nmodes,glomap_variables_local%ncp) !Redistributes ions for solinsol REAL :: tmp1(nbox,nmodes) ! @@ -280,6 +281,7 @@ SUBROUTINE ukca_volume_mode( glomap_variables_local, nbox, nd, md, mdt, & soluble => glomap_variables_local%soluble x => glomap_variables_local%x + !at this point in the code, the value of RP does not matter rp(:)=100.0e-9 ! dummy value CALL ukca_vapour(nbox,t,pmid,s,rp,wts,rhosol_strat) @@ -297,6 +299,7 @@ SUBROUTINE ukca_volume_mode( glomap_variables_local, nbox, nd, md, mdt, & denom(:)=0.0 ! define on all points to avoid error denom2(:)=0.0 +mdcopy(:,:,:) = md(:,:,:) ! assigning md input values to mdcopy ! Correct relative humidities to lie within the range of 10-90% corrh(:)=rh(:) @@ -348,8 +351,21 @@ SUBROUTINE ukca_volume_mode( glomap_variables_local, nbox, nd, md, mdt, & cl(:,i)=0.0 ! set all concentrations to zero initially END DO - IF (component(imode,cp_su)) THEN ! assume all H2SO4 --> SO4 - cl(:,-2)=md(:,imode,cp_su)/avogadro ! [SO4] in moles/cc (air) + ! If SOLINSOL then redistribute sulphate md into other species + ! hygro_ratio(3) is black carbon which is important to include + ! in the ratio but isn't used in this routine + IF (glomap_config%i_mode_setup == 11) THEN + mdcopy(:,imode,cp_su) = glomap_config%solinsol_hygro_ratio(1)* & + (md(:,imode,cp_su)) + mdcopy(:,imode,cp_cl) = glomap_config%solinsol_hygro_ratio(2)* & + (md(:,imode,cp_su)) + mdcopy(:,imode,cp_oc) = glomap_config%solinsol_hygro_ratio(4)* & + (md(:,imode,cp_su)) + END IF + + IF (component(imode,cp_su) .OR. (glomap_config%i_mode_setup == 11)) THEN + ! assume all H2SO4 --> SO4 + cl(:,-2)=mdcopy(:,imode,cp_su)/avogadro ! [SO4] in moles/cc (air) END IF IF (component(imode,cp_so)) THEN @@ -361,8 +377,8 @@ SUBROUTINE ukca_volume_mode( glomap_variables_local, nbox, nd, md, mdt, & ! .. whereas CL needs to be in moles of aged organic (MM=0.15kg/mol). END IF - IF (component(imode,cp_oc)) THEN - cl(:,-2)=cl(:,-2)+(fhyg_aom/avogadro)*(md(:,imode,cp_oc)/f_ao) + IF (component(imode,cp_oc) .OR. (glomap_config%i_mode_setup == 11)) THEN + cl(:,-2)=cl(:,-2)+(fhyg_aom/avogadro)*(mdcopy(:,imode,cp_oc)/f_ao) ! .. Increment concentration of SO4 ions to represent the ! .. presence of hygroscopic aged organic aerosol mass in CP_OC. ! .. Assume it has uptake behaviour at fraction FHYG_AOM of SO4. @@ -376,9 +392,10 @@ SUBROUTINE ukca_volume_mode( glomap_variables_local, nbox, nd, md, mdt, & ! END IF - IF (component(imode,cp_cl)) THEN ! assume complete dissociation - cl(:,3)=md(:,imode,cp_cl)/avogadro ! [Na] in moles per cc (air) - cl(:,-4)=md(:,imode,cp_cl)/avogadro ! [Cl] in moles per cc (air) + IF (component(imode,cp_cl) .OR. (glomap_config%i_mode_setup == 11)) THEN + ! assume complete dissociation + cl(:,3)=mdcopy(:,imode,cp_cl)/avogadro ! [Na] in moles per cc (air) + cl(:,-4)=mdcopy(:,imode,cp_cl)/avogadro ! [Cl] in moles per cc (air) END IF ! Nitrate scheme diff --git a/src/science/core/chemistry/asad/asad_bimol.F90 b/src/science/core/chemistry/asad/asad_bimol.F90 index 9f092ac..10d99a0 100644 --- a/src/science/core/chemistry/asad/asad_bimol.F90 +++ b/src/science/core/chemistry/asad/asad_bimol.F90 @@ -75,7 +75,7 @@ MODULE asad_bimol_mod CONTAINS -SUBROUTINE asad_bimol( n_points ) +SUBROUTINE asad_bimol( n_points, stratflag_opt ) USE asad_mod, ONLY: t, t300, specf, spb, ab, rk, tnd, p, & wp, f, peps, nbrkx, jpspb, jpcspf, jpbk @@ -91,6 +91,8 @@ SUBROUTINE asad_bimol( n_points ) IMPLICIT NONE INTEGER, INTENT(IN) :: n_points +! optional argument used for masking off the stratosphere +LOGICAL, INTENT(IN), OPTIONAL :: stratflag_opt(n_points) ! Local variables @@ -112,6 +114,7 @@ SUBROUTINE asad_bimol( n_points ) INTEGER, SAVE :: ics2oh = 0 INTEGER, SAVE :: ih2o = 0 INTEGER, SAVE :: iho2no = 0 +INTEGER, SAVE :: in2o5_h2o = 0 INTEGER :: jtr INTEGER :: j INTEGER :: jr @@ -145,6 +148,10 @@ SUBROUTINE asad_bimol( n_points ) REAL :: tmp1(1:n_points) REAL :: inv_t(1:n_points) +! logical array for masking-off the stratosphere +! default to False unless changed by stratflag_opt +LOGICAL :: stratflag(n_points) + ! ErrorStatus INTEGER :: errcode=0 ! Error flag (0 = OK) CHARACTER(LEN=errormessagelength) :: cmessage ! Error return message @@ -167,6 +174,12 @@ SUBROUTINE asad_bimol( n_points ) ratioa2b(:) = 0.0 ratiob2total(:) = 0.0 +! set stratflag from optional argument - only True in the stratosphere +stratflag(:) = .FALSE. +IF (PRESENT(stratflag_opt)) THEN + stratflag = stratflag_opt +END IF + ! 1. Calculate bimolecular rate coefficients ! --------- ----------- ---- ------------ @@ -255,6 +268,14 @@ SUBROUTINE asad_bimol( n_points ) iho2no = asad_findreaction( r1, r2, prods, 1, spb, nbrkx, & jpbk+1, jpspb ) + ! Find B85: N2O5 + H2O -> HONO2 + HONO2. + r1 = 'N2O5 ' + r2 = 'H2O ' + prods(1) = 'HONO2 ' + prods(2) = 'HONO2 ' + in2o5_h2o = asad_findreaction( r1, r2, prods, 2, spb, nbrkx, & + jpbk+1, jpspb ) + IF (ukca_config%l_ukca_chem_aero) THEN r1 = 'DMS ' @@ -408,6 +429,13 @@ SUBROUTINE asad_bimol( n_points ) 8.53e-4*(1e-2*p(1:n_points))-1.73)/100.0 END IF +! Keep B85 (N2O5 + H2O) in the troposphere only when fix is enabled. +IF (ukca_config%l_fix_ukca_n2o5_h2o .AND. in2o5_h2o > 0) THEN + WHERE (stratflag(1:n_points)) + rk(1:n_points,in2o5_h2o) = 0.0 + END WHERE +END IF + ! SO3 + H2O: 2nd H2O molecule dealt with here by multiplying rate by [H2O] IF ( ih2o /= 0 .AND. iso3h2o /= 0 ) THEN ! water is an advected tracer diff --git a/src/science/core/chemistry/asad/asad_cdrive.F90 b/src/science/core/chemistry/asad/asad_cdrive.F90 index 16f22b9..87887c1 100644 --- a/src/science/core/chemistry/asad/asad_cdrive.F90 +++ b/src/science/core/chemistry/asad/asad_cdrive.F90 @@ -225,7 +225,7 @@ SUBROUTINE asad_cdrive(ftr, pp, pt, pq, co2_1d, cld_f, cld_l, & ! 4. Calculate reaction rate coefficients ! --------- -------- ---- ------------ -CALL asad_bimol (n_points) +CALL asad_bimol (n_points, stratflag) CALL asad_trimol(n_points) ! Calculate aqueous-phase SO2 oxdn. and tropospheric heterogeneous rates diff --git a/src/science/core/chemistry/ukca_hetero_mod.F90 b/src/science/core/chemistry/ukca_hetero_mod.F90 index a0637c7..0a6ba9b 100644 --- a/src/science/core/chemistry/ukca_hetero_mod.F90 +++ b/src/science/core/chemistry/ukca_hetero_mod.F90 @@ -490,9 +490,18 @@ SUBROUTINE ukca_hetero(n_points, have_nat, stratflag) rk(:,n_hocl_hcl) = hk(:,3) END IF + ! Optionally filter N2O5+H2O by stratflag to prevent double-counting. IF (n_n2o5_h2o > 0) THEN ! 4. N2O5 + H2O -> 2 HNO3 - rk(:,n_n2o5_h2o) = hk(:,4) + IF (ukca_config%l_fix_ukca_n2o5_h2o) THEN + WHERE (stratflag) + rk(:,n_n2o5_h2o) = hk(:,4) + ELSE WHERE + rk(:,n_n2o5_h2o) = 0.0 + END WHERE + ELSE + rk(:,n_n2o5_h2o) = hk(:,4) + END IF END IF IF (n_n2o5_hcl > 0) THEN @@ -659,14 +668,27 @@ SUBROUTINE ukca_hetero(n_points, have_nat, stratflag) WHERE ( zh2o > peps ) rk(:,n_clono2_h2o) = kpsc(:,2) / zh2o - rk(:,n_n2o5_h2o) = kpsc(:,4) / zh2o rk(:,n_brono2_h2o) = kpsc(:,8) / zh2o ELSE WHERE rk(:,n_clono2_h2o) = 0.0 - rk(:,n_n2o5_h2o) = 0.0 rk(:,n_brono2_h2o) = 0.0 END WHERE + ! Optionally filter N2O5+H2O by stratflag to prevent double-counting. + IF (ukca_config%l_fix_ukca_n2o5_h2o) THEN + WHERE ( zh2o > peps .AND. stratflag ) + rk(:,n_n2o5_h2o) = kpsc(:,4) / zh2o + ELSE WHERE + rk(:,n_n2o5_h2o) = 0.0 + END WHERE + ELSE + WHERE ( zh2o > peps ) + rk(:,n_n2o5_h2o) = kpsc(:,4) / zh2o + ELSE WHERE + rk(:,n_n2o5_h2o) = 0.0 + END WHERE + END IF + WHERE ( zhbr > peps ) rk(:,n_hobr_hbr) = kpsc(:,9) / zhbr rk(:,n_hocl_hbr) = kpsc(:,10) / zhbr