Skip to content

Conversation

@mattldawson
Copy link

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:

  • missing descriptions for units for variables
  • missing descriptions of functions and function arguments
  • unused variables
  • cryptically named variables
  • dead code blocks
  • potential to break up large code blocks, and introduce reusable/testable functions
  • compare MAM and CARMA code for consistency
  • identify host-model vs aerosol-model portions of the code
  • separate configuration data from algorithms
  • magic numbers

	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)
Copy link

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.

Copy link
Collaborator

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, &
Copy link

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
Copy link

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.

Copy link
Collaborator

@tilmes tilmes Dec 17, 2024

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.

Copy link
Collaborator

@K20shores K20shores left a 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
Copy link
Collaborator

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?

Copy link
Author

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.

Comment on lines +244 to +247
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
Copy link
Collaborator

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?

Copy link
Collaborator

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.


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 ????
Copy link
Collaborator

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

Copy link
Collaborator

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.

Comment on lines 709 to 711
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
Copy link
Collaborator

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?

Copy link
Collaborator

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...

Comment on lines 760 to 767
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)
Copy link
Collaborator

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?

Comment on lines 796 to 797
pso4 = rah2o2 * 7.4e4_r8*EXP(6621._r8*work1(i)) * h2o2g * patm_x &
* 1.23_r8 *EXP(3120._r8*work1(i)) * so2g * patm_x
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also these values

knudsen = 3.0_r8*gasdiffus/(gasspeed*rad_cd)

! following assumes accomodation coefficient = 0.65
! (Adams & Seinfeld, 2002, JGR, and references therein)
Copy link
Collaborator

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

Copy link
Collaborator

@K20shores K20shores left a 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

Comment on lines +105 to +106
! rad_cd = (drop radius in cm), computed from liquid water and drop number,
! then bounded by 0.5 and 50.0 micrometers
Copy link
Collaborator

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?

Copy link
Collaborator

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
Copy link
Collaborator

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
Copy link
Collaborator

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

Copy link
Collaborator

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.

Comment on lines +116 to +124
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
Copy link
Collaborator

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?

Copy link
Collaborator

@dmleung dmleung Dec 19, 2024

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.

Comment on lines +126 to +134
! 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)
Copy link
Collaborator

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?

Copy link
Collaborator

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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
! *(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

Copy link
Collaborator

@dmleung dmleung Dec 19, 2024

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)
Copy link
Collaborator

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)
Copy link
Collaborator

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.

Copy link
Collaborator

@K20shores K20shores left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

carma_clouds.F90

Comment on lines +13 to +15
!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
Copy link
Collaborator

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?

Copy link
Collaborator

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
Copy link
Collaborator

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?

Copy link
Collaborator

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
Copy link
Collaborator

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?

Suggested change
!integer, allocatable :: bin_idx(:,:) ! table for local indexing of modal aero number and mmr

Copy link
Collaborator

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...

Comment on lines +307 to +310
!st if (id_nh3>0) then
!st delnh3 = nh3g(i,k) - xnh3(i,k)
!st delnh4 = - delnh3
!st endif
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
!st if (id_nh3>0) then
!st delnh3 = nh3g(i,k) - xnh3(i,k)
!st delnh4 = - delnh3
!st endif

Comment on lines +353 to +357
!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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
!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 )
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
!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)))) )
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
! 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)))) )
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
! fwetrem = max( 0.0_r8, (1.0_r8-exp(-min(100._r8,dtime*frh2o2_c*clwlrat(i,k)))) )

Comment on lines +435 to +439
!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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
!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

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?

Copy link
Collaborator

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.

Comment on lines 114 to 115
if (masterproc) then
write(iulog,*) 'sox_inti: has_sox = ',has_sox

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.

Copy link
Collaborator

@dmleung dmleung Jan 15, 2025

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

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.

Copy link
Collaborator

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).

Comment on lines 821 to 824
else ! ???? bug ????
xso2(i,k)=1.e-20_r8
xh2o2(i,k)=xh2o2(i,k)-xso2(i,k)
endif

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?

Copy link
Collaborator

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

Comment on lines 137 to 139
!-----------------------------------------------------------------------
!-----------------------------------------------------------------------
subroutine setsox( state, &

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.

Copy link
Collaborator

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).

Copy link
Collaborator

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.

Comment on lines 875 to 887
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

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?

Copy link
Collaborator

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:

  1. 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 )
    
  2. 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.

Comment on lines +425 to +427
fwetrem = 0.0_r8 ! don't include h2o2 wet removal here

dqdt_wr = -fwetrem*xh2o2(i,k)/dtime*cldfrc(i,k)

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?

Copy link
Collaborator

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).

Copy link
Collaborator

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.

Matt Dawson and others added 2 commits February 5, 2025 08:59
* draft cloud test

* fix dependencies

* create snapshot of cloud chemistry

* update cloud chemistry test

* update snapshot

* draft cloud chemistry regression test

* fix output of cloud chem snapshot data

* move test data

* finish cloud chem test against snapshot

* add test against original cloud module

* clean up

* remove PFunit
@marybarthbrock
Copy link
Collaborator

marybarthbrock commented Feb 25, 2025 via email

* rename, remove boilerplate

* rename function args

* update constants

* renaming

* remove unused variables

* remove unused dependencies

* renaming

* more clean-up

* address review comments

* remove duplicate log info

* fix units in description

* address review comments
!-----------------------------------------------------------------
! ... co2 effects
!-----------------------------------------------------------------
co2g = 330.e-6_r8 !330 ppm = 330.e-6 atm
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to use the CO2 concentration from the model state? (also, this value seems like it might be low for the current atmosphere, unless I'm misinterpreting this) @dmleung - what do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I think so! I think in older model treatments CO2 is always fixed, but there is no reason we need to stick with it now given we have active CO2 chemistry in the chem_mech.in. We can follow the treatment of so2g and h2o2g, which are also from the model state.

@marybarthbrock
Copy link
Collaborator

marybarthbrock commented Feb 27, 2025 via email

* add snapshot files for CARMA and BAM cloud chemistry

* update CARMA and BAM test data

* add missing CARMA fields

* add missing fields to CARMA snapshot

* add missing fields for CARMA snapshot

* add bam and carma tests

conc_obj => cldaero_allocate()

conc_obj%xlwc(:ncol,:) = lwc(:ncol,:)*cfact(:ncol,:) ! cloud water L(water)/L(air)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mattldawson By the end of our Tue meeting we touched upon this a bit -- and I am still a little baffled by the unit for conc_obj%xlwc. It looks like [lwc] has a unit of kg water / kg air, and [cfact] is also constructed to be kg air / L air (I checked cfact's calculation and that unit looks right to me). If so, then it looks like xlwc should have a unit of kg water / L air, which is different from the comment here.
Not sure how important this is, but just trying to make sure things are right. What do you think? Let me know if I was wrong.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I struggled with this too. I think they might have just been doing an implicit conversion with the (approximate) density of water (1kg/L). That's all I could come up with.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That actually makes sense, thanks for that. Maybe we can add a bit onto the comment here?

Suggested change
conc_obj%xlwc(:ncol,:) = lwc(:ncol,:)*cfact(:ncol,:) ! cloud water L(water)/L(air)
conc_obj%xlwc(:ncol,:) = lwc(:ncol,:)*cfact(:ncol,:) ! cloud water in L(water)/L(air), assuming water has a density of ~ 1 kg (water) / L (water).

Comment on lines +267 to +270
! volume mixing ratios for cloud chemistry species
real(r8), dimension(ncol,pver) :: xhno3, xh2o2, xso2, xso4, xno3, &
xnh3, xnh4, xo3, xph, xho2, xh2so4, xmsa, xso4_init

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @mattldawson and @dmleung, are those vmr variables for gas species, aerosols or a mix of both? NH3, O3 look gas, but SO4, NH4 seem aerosols. Doesn't aerosol use mass mixing ratio?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these variables come from species_vmr. See the code below, which is later of this module:
call so4%mixing_ratio( species_vmr, fixed_concentrations, air_number_density, xso4 )
And, yes, species_vmr stores both gas-phase and aerosol-phase species in volume mixing ratio (here). Simone said aerosol species could be stored as mass mixing ratio elsewhere but i don't know where. Aerosols are definitely outputted as mmr in the history files, but here they are vmr.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants