diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index cfda233..fad5ab4 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -7,3 +7,4 @@ | jennyhickson | Jenny Hickson | Met Office | 2026-03-02 | | Pierre-siddall | Pierre Siddall | Met Office | 2026-03-13 | | yg460-cam | Yao Ge | University of Cambridge | 2026-04-17 | +| theabro | Nathan Luke Abraham | NCAS & University of Cambridge | 2026-03-19 | 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..9550b85 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 @@ -949,6 +950,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 @@ -1179,6 +1181,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, & @@ -1395,6 +1398,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 +1689,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 diff --git a/src/control/core/top_level/ukca_setup_mod.F90 b/src/control/core/top_level/ukca_setup_mod.F90 index affe86e..f444ea1 100644 --- a/src/control/core/top_level/ukca_setup_mod.F90 +++ b/src/control/core/top_level/ukca_setup_mod.F90 @@ -207,6 +207,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, & @@ -540,6 +541,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 @@ -1232,6 +1234,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 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