-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cloud chemistry module review #5
base: develop-1-cloud-chemistry
Are you sure you want to change the base?
Cloud chemistry module review #5
Conversation
modified: .gitmodules modified: src/physics/cam/carma_intr.F90 modified: src/physics/carma/base (new commits) modified: src/physics/carma/cam/carma_intr.F90 modified: src/physics/carma/cam/carma_precision_mod.F90
mm = bin_idx(m, l) | ||
call rad_cnst_get_bin_props_by_idx(0, m, l,spectype=spectype) | ||
if (trim(spectype) == 'sulfate') then | ||
so4mmr(i,k) = so4mmr(i,k) + qcw(i,k,mm) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This loop can be condensed and re-ordered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure how this would work?
!---------------------------------------------------------------------------------- | ||
! Update the mixing ratios | ||
!---------------------------------------------------------------------------------- | ||
subroutine sox_cldaero_update( state, & |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could split subroutine into sox_cldaero_tend to compute tendencies and sox_cldaero_update to apply tendencies.
|
||
conc_obj%no3c(:,:) = 0._r8 | ||
|
||
if (mode7) then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Index gymnastics is confusing here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This indexing can be removed, similar to what is done in carma_clouds.F90 Line 72 to 102.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewing cloud_aqueous_chemistry.F90
logical :: modal_aerosols | ||
|
||
call phys_getopts( prog_modal_aero_out=modal_aerosols ) | ||
cloud_borne = modal_aerosols .or. carma_do_cloudborne |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does cloud_borne
logically mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, this needs to be described clearly in the source code. Recent conversations with people who have worked on CAM for years revealed that there is a lot of confusion about what this actually means.
!----------------------------------------------------------------------- | ||
integer, parameter :: itermax = 20 | ||
real(r8), parameter :: ph0 = 5.0_r8 ! INITIAL PH VALUES | ||
real(r8), parameter :: const0 = 1.e3_r8/6.023e23_r8 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 / avogadro?
real(r8), parameter :: xa0 = 11._r8 | ||
real(r8), parameter :: xb0 = -.1_r8 | ||
real(r8), parameter :: xa1 = 1.053_r8 | ||
real(r8), parameter :: xb1 = -4.368_r8 | ||
real(r8), parameter :: xa2 = 1.016_r8 | ||
real(r8), parameter :: xb2 = -2.54_r8 | ||
real(r8), parameter :: xa3 = .816e-32_r8 | ||
real(r8), parameter :: xb3 = .259_r8 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we maybe have a description of these, where they are from (reference?), units?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe none of these variables are used (grep them and there is only one occurrence -- here). I have no idea why they are here. I think we can remove them. Other people can confirm this.
real(r8), parameter :: kh0 = 9.e3_r8 ! HO2(g) -> Ho2(a) | ||
real(r8), parameter :: kh1 = 2.05e-5_r8 ! HO2(a) -> H+ + O2- | ||
real(r8), parameter :: kh2 = 8.6e5_r8 ! HO2(a) + ho2(a) -> h2o2(a) + o2 | ||
real(r8), parameter :: kh3 = 1.e8_r8 ! HO2(a) + o2- -> h2o2(a) + o2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have a reference for these or is this just known in the ether of science? Is it applicable forever and always or only for a certain range of environmental conditions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure about this. This module is Mary's expertise and she can confirm and add references.
real(r8), parameter :: kh2 = 8.6e5_r8 ! HO2(a) + ho2(a) -> h2o2(a) + o2 | ||
real(r8), parameter :: kh3 = 1.e8_r8 ! HO2(a) + o2- -> h2o2(a) + o2 | ||
real(r8), parameter :: Ra = 8314._r8/101325._r8 ! universal constant (atm)/(M-K) | ||
real(r8), parameter :: xkw = 1.e-14_r8 ! water acidity |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
real(r8), parameter :: xkw = 1.e-14_r8 ! water acidity | |
real(r8), parameter :: water_acidity = 1.e-14_r8 ! water acidity |
So let's call it that
|
||
if ( cloud_borne ) then | ||
r2h2o2 = r1h2o2*xl & ! mole/L(w)/s * L(w)/fm3(a) = mole/fm3(a)/s | ||
/ const0*1.e+6_r8 & ! correct a bug here ???? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well did the bug get corrected? Is the correction const0
or the multiplication of const0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no idea about this. Someone needs to figure this out... Maybe in our regular Tue meetings.
if ( .not. cloud_borne) then ! this seems to be specific to aerosols that are not cloud borne | ||
xh2o2(i,k) = xh2o2(i,k) + r2h2o2*dtime ! updated h2o2 by het production | ||
endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are we sure it's specific? If so, why is it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I remember Simone and Mary talked about this a while ago and nobody knew what happened here. I am not sure if they now have an updated view on this...
rah2o2 = 8.e4_r8 * EXP( -3650._r8*work1(i) ) & | ||
/ (.1_r8 + xph(i,k)) | ||
|
||
!------------------------------------------------------------------------ | ||
! ... S(IV)+ O3 | ||
!------------------------------------------------------------------------ | ||
rao3 = 4.39e11_r8 * EXP(-4131._r8/tz) & | ||
+ 2.56e3_r8 * EXP(-996._r8 /tz) /xph(i,k) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we have a citation for these values?
pso4 = rah2o2 * 7.4e4_r8*EXP(6621._r8*work1(i)) * h2o2g * patm_x & | ||
* 1.23_r8 *EXP(3120._r8*work1(i)) * so2g * patm_x |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also these values
! S(IV) + H2O2 = S(VI) | ||
!............................ | ||
|
||
IF (XL .ge. 1.e-8_r8) THEN !! WHEN CLOUD IS PRESENTED |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
presented what? why is 1e-8
the value we care about? Is it empirical? Numerical? If numerical, was it derived when we only had single precision and we could make it smaller now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea it's a small positive definite. You can use greater than zero instead of great or equal to a positive definite, I suppose. There are a lot of these small numbers in different parts of CAM-Chem. There are 1e-20, 1e-30, and so on.
@@ -0,0 +1,894 @@ | |||
!---------------------------------------------------------------------------------- | |||
! Cloud aqueous chemistry |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tilmes @marybarthbrock - Could we get a high-level description of the purpose of this module in science terms?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say the purpose of this module is to calculate the sulfate formation due to various oxidation/condensation pathways (e.g., SO2 oxidized by ozone, SO2 oxidized by H2O2, H2SO4 condensation) of cloud chemistry. In the cloud chemistry subroutine setsox(), gas-phase species will be consumed and cloud-borne aerosol species be produced, and the main outputs are these two arrays: qin() and qcw(). Sulfate production rates (e.g., aqso4) and cloud pH changes (xphlwc) are also calculated and outputted by setsox().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me
integer :: id_so2, id_nh3, id_hno3, id_h2o2, id_o3, id_ho2 | ||
integer :: id_so4, id_h2so4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should state what array(s) these are indices for
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can write something like
"these indices are mainly for two arrays: qin and qcw -- qin is the volume mixing ratio (mol/mol or m3/m3) for chemical tracers (gas and aerosol), and qcw is the volume mixing ratio of cloud-borne aerosols. e.g., qin(i,k,id_so2) where i is grid, k is vertical level, and id_so2 is the index for SO2."
Feel free to rewrite this in a succinct way.
integer :: id_so2, id_nh3, id_hno3, id_h2o2, id_o3, id_ho2 | ||
integer :: id_so4, id_h2so4 | ||
|
||
logical :: has_sox = .true. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should have a description of what it means and how it is used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mary or Francis will have a better idea on this
|
||
!----------------------------------------------------------------------- | ||
!----------------------------------------------------------------------- | ||
subroutine sox_inti |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
subroutine sox_inti | |
subroutine initialize_cloud_aqueous_chemistry |
!----------------------------------------------------------------------- | ||
subroutine sox_inti | ||
!----------------------------------------------------------------------- | ||
! ... initialize the hetero sox routine |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should include a brief description of what is being initialized
if (cloud_borne) then | ||
id_h2so4 = get_spc_ndx( 'H2SO4' ) | ||
else | ||
id_so4 = get_spc_ndx( 'SO4' ) | ||
endif | ||
id_msa = get_spc_ndx( 'MSA' ) | ||
|
||
inv_so2 = .false. | ||
id_so2 = get_inv_ndx( 'SO2' ) | ||
inv_so2 = id_so2 > 0 | ||
if ( .not. inv_so2 ) then | ||
id_so2 = get_spc_ndx( 'SO2' ) | ||
endif | ||
|
||
inv_NH3 = .false. | ||
id_NH3 = get_inv_ndx( 'NH3' ) | ||
inv_NH3 = id_NH3 > 0 | ||
if ( .not. inv_NH3 ) then | ||
id_NH3 = get_spc_ndx( 'NH3' ) | ||
endif | ||
|
||
inv_HNO3 = .false. | ||
id_HNO3 = get_inv_ndx( 'HNO3' ) | ||
inv_HNO3 = id_hno3 > 0 | ||
if ( .not. inv_HNO3 ) then | ||
id_HNO3 = get_spc_ndx( 'HNO3' ) | ||
endif | ||
|
||
inv_H2O2 = .false. | ||
id_H2O2 = get_inv_ndx( 'H2O2' ) | ||
inv_H2O2 = id_H2O2 > 0 | ||
if ( .not. inv_H2O2 ) then | ||
id_H2O2 = get_spc_ndx( 'H2O2' ) | ||
endif | ||
|
||
inv_HO2 = .false. | ||
id_HO2 = get_inv_ndx( 'HO2' ) | ||
inv_HO2 = id_HO2 > 0 | ||
if ( .not. inv_HO2 ) then | ||
id_HO2 = get_spc_ndx( 'HO2' ) | ||
endif | ||
|
||
inv_o3 = get_inv_ndx( 'O3' ) > 0 | ||
if (inv_o3) then | ||
id_o3 = get_inv_ndx( 'O3' ) | ||
else | ||
id_o3 = get_spc_ndx( 'O3' ) | ||
endif | ||
inv_ho2 = get_inv_ndx( 'HO2' ) > 0 | ||
if (inv_ho2) then | ||
id_ho2 = get_inv_ndx( 'HO2' ) | ||
else | ||
id_ho2 = get_spc_ndx( 'HO2' ) | ||
endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is repetitive but inconsistent logic used here (e.g., different names for sulfate, some species checked for get_inv_ndx()
and others not). I would suggest a data structure for species-specific data like this, with some common functionality for identifying indices, etc., and standardization of logic across all species for consistency.
!----------------------------------------------------------------------- | ||
!----------------------------------------------------------------------- | ||
subroutine setsox( state, & |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tilmes @marybarthbrock - could we get a brief (2-3 sentence) high-level description of what this function is doing in science terms? (there is a comment inside the function that might be a starting point, but I wasn't sure if this describes the entirety of what this function does)
We should also rename the function to be more descriptive (e.g., solve_cloud_aqueous_chemistry()
or similar)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can consider using Doxygen style comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For me, a very brief description of setsox will be
"This subroutine calculates the formation of sulfate and updates sulfate concentration due to cloud aqueous chemistry (i.e., updating qin / qcw). This subroutine also outputs the production rates (in kg/m2/s) of various reactions of sulfur species with various oxidants (i.e., outputting things like aqso4_h2o2 and aqh2so4)."
Something along the line... Mary and Simone et al. can add onto this...
#ifdef USE_MAM | ||
use mam_clouds, only : sox_cldaero_update, sox_cldaero_create_obj, sox_cldaero_destroy_obj | ||
#endif | ||
#ifdef USE_CLOUDS | ||
use carma_clouds, only : sox_cldaero_update, sox_cldaero_create_obj, sox_cldaero_destroy_obj | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How are the CPP conditional blocks being used in conjunction with the abstract aerosol interface? It seems like we might want to make sure we're using a consistent approach to permitting the use of multiple aerosol modules.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
after some discussion, we should look into how USE_CLOUDS
and USE_MAM
are being set and used
|
||
real(r8), intent(out) :: aqso4(:,:) ! aqueous phase chemistry | ||
real(r8), intent(out) :: aqh2so4(:,:) ! aqueous phase chemistry | ||
real(r8), intent(out) :: aqso4_h2o2(:) ! SO4 aqueous phase chemistry due to H2O2 (kg/m2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Units of these production rates should be kg/m2/s instead of kg/m2.
real(r8), intent(out) :: aqso4(:,:) ! aqueous phase chemistry | ||
real(r8), intent(out) :: aqh2so4(:,:) ! aqueous phase chemistry | ||
real(r8), intent(out) :: aqso4_h2o2(:) ! SO4 aqueous phase chemistry due to H2O2 (kg/m2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
real(r8), intent(out) :: aqso4(:,:) ! aqueous phase chemistry | |
real(r8), intent(out) :: aqh2so4(:,:) ! aqueous phase chemistry | |
real(r8), intent(out) :: aqso4_h2o2(:) ! SO4 aqueous phase chemistry due to H2O2 (kg/m2) | |
real(r8), intent(out) :: aqso4(:,:) ! aqueous phase chemistry (kg/m2/s) | |
real(r8), intent(out) :: aqh2so4(:,:) ! aqueous phase chemistry (kg/m2/s) | |
real(r8), intent(out) :: aqso4_h2o2(:) ! SO4 aqueous phase chemistry due to H2O2 (kg/m2/s) |
knudsen = 3.0_r8*gasdiffus/(gasspeed*rad_cd) | ||
|
||
! following assumes accomodation coefficient = 0.65 | ||
! (Adams & Seinfeld, 2002, JGR, and references therein) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it this paper? If so, let's add a full bibliographical citation that includes the DOI
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cloud_utilities.F90
reivew
! rad_cd = (drop radius in cm), computed from liquid water and drop number, | ||
! then bounded by 0.5 and 50.0 micrometers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but WHY is it bounded? Because a reference says we have to? Because numerically only values beteween 0.5 - 50 are valid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to line 115 they applied the bounds at line 118 etc to avoid occasional unphysical values. I have no idea why that is the case though.
!---------------------------------------------------------------------------------- | ||
|
||
function cldaero_uptakerate( xl, cldnum, cfact, cldfrc, tfld, press ) result( uptkrate ) | ||
use mo_constants, only : pi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use a constant from another module here, but in cloud_aqueous_chemistry.F90
we defined several constants within the code. We should probably push those constants into mo_constants
if they don't exist
! radxnum_cd = (drop radius)*(drop number conc) | ||
! volx34pi_cd = (3/4*pi) * (liquid water volume in cm^3/cm^3) | ||
|
||
volx34pi_cd = xl*0.75_r8/pi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the significance of 3/(4 pi)
? WHY
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given xl (liquid water content) they want to estimate the droplet radius.
V = 4*pi/3 * R^3
So
V * 0.75/pi is related to the radius. See line 113 below.
if (radxnum_cd .le. volx34pi_cd*4.0e4_r8) then | ||
radxnum_cd = volx34pi_cd*4.0e4_r8 | ||
rad_cd = 50.0e-4_r8 | ||
else if (radxnum_cd .ge. volx34pi_cd*4.0e8_r8) then | ||
radxnum_cd = volx34pi_cd*4.0e8_r8 | ||
rad_cd = 0.5e-4_r8 | ||
else | ||
rad_cd = radxnum_cd/num_cd | ||
end if |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is 4.0e4_r8
arbitrary? Why this value? what makes radxnum_cd
and why is 4.0e4_r8
related to that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think theses are just bounds. They didn't want radxnum_cd
to be smaller than volx34pi_cd*4.0e4
and bigger than volx34pi_cd*4.0e8
. Again, I don't know why. This is a question for Mary.
! gasdiffus = h2so4 gas diffusivity from mosaic code (cm^2/s) | ||
! (pmid must be Pa) | ||
gasdiffus = 0.557_r8 * (tfld**1.75_r8) / press | ||
|
||
! gasspeed = h2so4 gas mean molecular speed from mosaic code (cm/s) | ||
gasspeed = 1.455e4_r8 * sqrt(tfld/98.0_r8) | ||
|
||
! knudsen number | ||
knudsen = 3.0_r8*gasdiffus/(gasspeed*rad_cd) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is from mosaic, do we have a reference for where these values come from and why? Do we know if they are always valid or does mosaic assume something we don't?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can think of a reference for knudsen number (I am sure some textbooks will have this equation) but Mary probably knows better
! | ||
! first-order uptake rate is | ||
! 4*pi*(drop radius)*(drop number conc) | ||
! *(gas diffusivity)*(fuchs sutugin correction) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
! *(gas diffusivity)*(fuchs sutugin correction) | |
! *(gas diffusivity)*(fuchs sutugin correction) | |
! Fuchs Sutugin correction: | |
! Fuchs, N.A., Sutugin, A.G., 1971. HIGH-DISPERSED AEROSOLS, in: Hidy, G.M., Brock, J.R. (Eds.), | |
! Topics in Current Aerosol Research, International Reviews in Aerosol Physics and Chemistry. Pergamon, p. 1. | |
! https://doi.org/10.1016/B978-0-08-016674-2.50006-6 |
Is this a correct reference? Took me forever to track down
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes this is correct. A lot of papers on sulfur chemistry also used Fuchs' formulation. E.g., see
https://agupubs.onlinelibrary.wiley.com/doi/full/10.1029/2005jd005870
real(r8), intent(out) :: aqso4(:,:) ! aqueous phase chemistry | ||
real(r8), intent(out) :: aqh2so4(:,:) ! aqueous phase chemistry | ||
real(r8), intent(out) :: aqso4_h2o2(:) ! SO4 aqueous phase chemistry due to H2O2 (kg/m2) | ||
real(r8), intent(out) :: aqso4_o3(:) ! SO4 aqueous phase chemistry due to O3 (kg/m2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The unit of this is also kg/m2/s
real(r8), intent(out) :: aqso4_o3(:) ! SO4 aqueous phase chemistry due to O3 (kg/m2) | ||
real(r8), intent(in), optional :: yph_in ! ph value | ||
real(r8), intent(out), optional :: aqso4_h2o2_3d(:, :) ! 3D SO4 aqueous phase chemistry due to H2O2 (kg/m2) | ||
real(r8), intent(out), optional :: aqso4_o3_3d(:, :) ! 3D SO4 aqueous phase chemistry due to O3 (kg/m2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two have a unit of kg/m2/s too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
carma_clouds.F90
!st use modal_aero_data, only : ntot_amode, modeptr_accum, lptr_so4_cw_amode, lptr_msa_cw_amode | ||
!st use modal_aero_data, only : numptrcw_amode, lptr_nh4_cw_amode | ||
!st use modal_aero_data, only : cnst_name_cw, specmw_so4_amode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just delete this since it's commented out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I think so
|
||
integer :: id_msa, id_h2so4, id_so2, id_h2o2, id_nh3 | ||
|
||
real(r8), parameter :: small_value = 1.e-20_r8 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In other places (the backward euler solver) small
is set to 1e-40
. I imagine there are other versions of small
throughout CAM. Do we happen to know why this particular value of small
is more useful for clouds?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it depends on the order of magnitudes of the variables concerned. in the code here the order of magnitudes of chemical tracers (e.g., SO2, H2O2) are typically anywhere between 1e-14 (0.01 ppt) to 1e-5 or (10 ppm) in volume mixing ratios. People then want to disregard the reactions once the concentrations are low enough.
The small_value can definitely change depending on the situation and variable concerned. E.g., the small_value for relative humidity should be 0.1 instead of 1e-20. For solving differential equations, I think it makes sense that people try to make things as accurate as possible by using smaller small_values even for chemical tracers. But I think there's always wiggle room, e.g., setting 1e-30 in the solver probably doesn't hurt at all.
integer :: i,k,mm | ||
|
||
! local indexing for bins | ||
!integer, allocatable :: bin_idx(:,:) ! table for local indexing of modal aero number and mmr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we can delete commented out code?
!integer, allocatable :: bin_idx(:,:) ! table for local indexing of modal aero number and mmr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess Mary is the best to answer this. Mary needs to review the code changes...
!st if (id_nh3>0) then | ||
!st delnh3 = nh3g(i,k) - xnh3(i,k) | ||
!st delnh4 = - delnh3 | ||
!st endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!st if (id_nh3>0) then | |
!st delnh3 = nh3g(i,k) - xnh3(i,k) | |
!st delnh4 = - delnh3 | |
!st endif |
!st dmsadt_gasuptk_tomsa = dmsadt_gasuptk | ||
!st if (ntot_msa_c == 0) then | ||
!st dmsadt_gasuptk_tomsa = 0.0_r8 | ||
!st dmsadt_gasuptk_toso4 = dmsadt_gasuptk | ||
!st end if |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!st dmsadt_gasuptk_tomsa = dmsadt_gasuptk | |
!st if (ntot_msa_c == 0) then | |
!st dmsadt_gasuptk_tomsa = 0.0_r8 | |
!st dmsadt_gasuptk_toso4 = dmsadt_gasuptk | |
!st end if |
!H2SO4 not updated in Pengfei's model | ||
!st TEST with H2SO4 uptake | ||
qin(i,k,id_h2so4) = qin(i,k,id_h2so4) - dso4dt_gasuptk * dtime * cldfrc(i,k) | ||
!qin(i,k,id_h2so4) = MAX( qin(i,k,id_h2so4), small_value ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!qin(i,k,id_h2so4) = MAX( qin(i,k,id_h2so4), small_value ) |
!st if (id_msa > 0) qin(i,k,id_msa) = qin(i,k,id_msa) - dmsadt_gasuptk * dtime * cldfrc(i,k) | ||
|
||
! so2 -- the first order loss rate for so2 is frso2_c*clwlrat(i,k) | ||
! fwetrem = max( 0.0_r8, (1.0_r8-exp(-min(100._r8,dtime*frso2_c*clwlrat(i,k)))) ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
! fwetrem = max( 0.0_r8, (1.0_r8-exp(-min(100._r8,dtime*frso2_c*clwlrat(i,k)))) ) |
qin(i,k,id_so2) = MAX( qin(i,k,id_so2), small_value ) | ||
|
||
! h2o2 -- the first order loss rate for h2o2 is frh2o2_c*clwlrat(i,k) | ||
! fwetrem = max( 0.0_r8, (1.0_r8-exp(-min(100._r8,dtime*frh2o2_c*clwlrat(i,k)))) ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
! fwetrem = max( 0.0_r8, (1.0_r8-exp(-min(100._r8,dtime*frh2o2_c*clwlrat(i,k)))) ) |
!st if (id_nh3>0) then | ||
!st dqdt_aq = delnh3/dtime*cldfrc(i,k) | ||
!st dqdt = dqdt_aq | ||
!st qin(i,k,id_nh3) = qin(i,k,id_nh3) + dqdt * dtime | ||
!st endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!st if (id_nh3>0) then | |
!st dqdt_aq = delnh3/dtime*cldfrc(i,k) | |
!st dqdt = dqdt_aq | |
!st qin(i,k,id_nh3) = qin(i,k,id_nh3) + dqdt * dtime | |
!st endif |
integer :: id_so4, id_h2so4 | ||
|
||
logical :: has_sox = .true. | ||
logical :: inv_so2, inv_nh3, inv_hno3, inv_h2o2, inv_ox, inv_nh4no3, inv_ho2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does inv_*
stand for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to Francis and Simone, inv_* stands for invariant. So, it sounds like in certain compsets people holds these tracers (SO2, NH3, H2O2, etc.) constant. In most cases nowadays I don't think we use invariant tracers, so the code mostly uses id_so2 instead of inv_so2, for instance. But, it's used for some compsets.
Francis is the one to answer this question.
if (masterproc) then | ||
write(iulog,*) 'sox_inti: has_sox = ',has_sox |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does masterproc
mean? (There is a typo: sox_inti -> sox_init.)
It doesn't appear to provide useful information, and it also duplicates the following logs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
masterproc comes from utils/spmd_utils.F90 and is a flag called when scientists want to write() something in a log file.
Yes, that's a typo and can be changed to sox_init.
! = ( fact1_hno3 )/(1 + fact2_hno3 *(1 + fact3_hno3/hplus) | ||
! [hno3-] = ehno3/hplus | ||
xk = 2.1e5_r8 *EXP( 8700._r8*work1(i) ) | ||
xe = 15.4_r8 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some variables (line435, line 492 and so on) that are constant should be defined outside the loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure about this. Line 435 and line 458 are different, so the existing approach is to declare xe as real(r8) and update xe for different reactions. xe is a constant in line 435 but get updated in line 458 as a function of work1 (which is dependent on temperature tfld).
else ! ???? bug ???? | ||
xso2(i,k)=1.e-20_r8 | ||
xh2o2(i,k)=xh2o2(i,k)-xso2(i,k) | ||
endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we verify if this code is valid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these lines mean that if you remove lines 821-824, lines 818-820 sometimes will not work. This is probably because xso2 was too small (smaller than 1.e-20_r8), but I'm not sure why that is the case. This is something that Mary should comment on.
BTW, I think line 822 can be
xso2(i,k)=small_value
!----------------------------------------------------------------------- | ||
!----------------------------------------------------------------------- | ||
subroutine setsox( state, & |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is large and difficult to test. I think it would be beneficial to break it down into smaller functions for each step, or a series of steps to make it more testable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I think since the code is being refactored it is up to the software engineers to design how the code looks like, as long as it looks good to Mary (this is her science after all).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a-okay with breaking the code down into smaller functions.
call sox_cldaero_update( state, & | ||
pbuf, ncol, lchnk, loffset, dtime, mbar, pdel, press, tfld, cldnum, cldfrc, cfact, cldconc%xlwc, & | ||
xdelso4hp, xh2so4, xso4, xso4_init, nh3g, hno3g, xnh3, xhno3, xnh4c, xno3c, xmsa, xso2, xh2o2, qcw, qin, & | ||
aqso4, aqh2so4, aqso4_h2o2, aqso4_o3, aqso4_h2o2_3d=aqso4_h2o2_3d, aqso4_o3_3d=aqso4_o3_3d ) | ||
|
||
xphlwc(:,:) = 0._r8 | ||
do k = 1, pver | ||
do i = 1, ncol | ||
if (cldfrc(i,k)>=1.e-5_r8 .and. lwc(i,k)>=1.e-8_r8) then | ||
xphlwc(i,k) = -1._r8*log10(xph(i,k)) * lwc(i,k) | ||
endif | ||
end do | ||
end do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding of this function is that it sets variables to update cldaero
by calling sox_cldaero_update(). There are also variables with intent(out) (eg. xphlwc
in line 880) that aren't used for updating cldaero
. What is the purpose of those variables, and how are they used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there are two questions here:
-
sox_cldaero_update() is a function that calculates and outputs the production rates of sulfate (so4) with cloud chemistry through several pathways (e.g., so4 formed due to o3 oxidation, due to h2o2 oxidation, etc). So, looking at sox_cldaero_update() under carmo_aero/carma_cloud.F90, the only intent(out) variables are aqso4, aqh2so4, aqso4_h2o2, ... which are sulfate production rates (kg/m2/s). sox_cldaero_update() is called here in aerosol/cloud_aqueous_chemistry.F90, so setsox() gets these arrays as intent(out) too (see line 215). And, when setsox() is called in the aerosol interface, we will see that they are outputted by setsox() just to be set as history variables, as follows:
call outfld( 'AQSO4_H2O2', aqso4_h2o2(:ncol), ncol, lchnk) call outfld( 'AQSO4_O3', aqso4_o3(:ncol), ncol, lchnk) call outfld( 'XPH_LWC', xphlwc(:ncol,:), ncol, lchnk )
-
I think lines 875-878 and lines 880-887 are two different things. xphlwc is a separate intent(out) variable that setsox() wants to output when setsox() is called. It is also a history variable in the aerosol interface, as follow:
call outfld( 'XPH_LWC', xphlwc(:ncol,:), ncol, lchnk )
=====
I copied the above code for outfld() from the CESM2.2 version. In the older CESM2.2, modal_aero/aero_model.F90 calls setsox(), setsox() calculates xphlwc and calls sox_cldaero_update() that calculates aqso4 etc, and aero_model.F90 gets them and do outfld(), so scientists get the history fields for plotting and analyses. I'm not sure where setsox() is called in this new CAM tag, and Francis and Simone will know better where the aerosol interface is. You can also grep it in this new CAM code.
But basically in one sentence, the purpose of sox_cldaero_update() in CESM2.2 is to output sulfate formation rates to aero_model.F90 so scientists get the history fields.
fwetrem = 0.0_r8 ! don't include h2o2 wet removal here | ||
|
||
dqdt_wr = -fwetrem*xh2o2(i,k)/dtime*cldfrc(i,k) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If fwetrem
is zero, then dqdt_wr
should also be zero, which seems to be unnecessary logic. Could you confirm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. I'm not sure why h2o2 and so2 wet removals were decided to set as zero. I think Mary should make a decision on whether fwetrem should be non-zero (i.e., using line 424 instead of line 425).
But yes, if we just keep the code as is, I would say this can be deleted or commented out (preferred).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe fwetrem for h2o2 and so2 is set to zero because wet removal (or wet scavenging) is computed elsewhere in CAM-chem (the Neu & Prather scheme, mo_neu_wetdep.F90). Let's confirm this with Francis and Simone. If it's correct, then I suggest removing these lines of code.
Reimplements four cloud chemistry modules with new file and module names so that the code can be reviewed.
closes #1
From issue #1, things to consider in review: