Skip to content
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

Add new scheme Li2024 of ozone plant damage #2302

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

lifang0209
Copy link

Description of changes

Changes include:
OzoneMod.F90: add an option for scheme of Li2024:
Include add: (i) CalcOzoneUptakeLi2024OnePoint to calculate ozone uptake for a single point, and change the name of old module for Lombardozzi2015 and Falk schemes as CalcOzoneUptakeLFOnePoint. (ii) add CalcOzoneStressLi2024 and CalcOzoneStressLi2024OnePoint to calculate ozone stress.

Change CanopyFluxesMod.F90, OzoneBaseMod.F90, OzoneOffMod.F90 to read the variable sabv that CalcOzoneUptake use.
subroutine CalcOzoneUptake(this, bounds, num_exposedvegp, filter_exposedvegp, &
forc_pbot, forc_th, rssun, rssha, rb, ram, tlai, forc_o3, sabv)

In bld/namelist_files/namelist_definition_ctsm.xml: add stress_li2024 as the new option

Specific notes

Contributors other than yourself, if any: @slevis-lmwg @dlawrenncar @danicalombardozzi @ekluzek

CTSM Issues Fixed (include github issue #):

Are answers expected to change (and if so in what way)?
Yes, when using the new scheme

Any User Interface Changes (namelist or namelist defaults changes)?
new namelist option: stress_li2024

Testing performed, if any:
@lifang0209 ran I2000Sp and I2000BgcCrop

@slevis-lmwg slevis-lmwg added PR status: work in progress PR: author feels this is NOT ready to merge to master tag: enh - new science enhancement that brings in new science capabilities labels Dec 28, 2023
@lifang0209 lifang0209 marked this pull request as ready for review January 15, 2024 01:58
Copy link
Contributor

@slevis-lmwg slevis-lmwg left a comment

Choose a reason for hiding this comment

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

@lifang0209 thank you for this nice update to the ozone code.

  • After we talked, I reviewed the code more carefully, posted a few comments, and requested a few improvements.
  • Scientifically the most pressing is to resolve the inconsistency between the formulas appearing in the code versus those documented in Li et al. (in prep).

if (tlai > lai_thresh) then
! o3 uptake decay
if (pftcon%evergreen(pft_type) == 1) then
leafturn = 1._r8/(pftcon%leaf_long(pft_type)*365._r8*24._r8)
Copy link
Contributor

Choose a reason for hiding this comment

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

  1. Please replace 365 with dayspyr and 3600 with secsphr. I don't think that we have a parameter for 24, but please replace if we do.
  2. Could we change leafturn to units of seconds^-1 by multiplying the denominator by secspyear (instead of by 365 * 24) and then get rid of dtimeh?

Copy link
Author

Choose a reason for hiding this comment

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

done


! calculate o3 flux per timestep
if(sabv > 0._r8)then !daytime
o3fluxperdt = o3fluxcrit * dtime * 0.000001_r8
Copy link
Contributor

@slevis-lmwg slevis-lmwg Jan 17, 2024

Choose a reason for hiding this comment

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

Could we avoid the unit conversion here by setting o3flux and o3_flux_threshold to the units that we need?

  1. You already convert a precursor to o3flux in line 495, so you could embed this conversion there.
  2. The latter (o3_flux_threshold) could be done in lines 511 to 523.

Copy link
Author

Choose a reason for hiding this comment

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

The units of o3flux, o3_flux_threshold, and o3uptake are commonly used, especially in observational community, so I don't want to change them

o3coefv = max(0._r8, min(1._r8, 0.943_r8 * exp(-0.0085*o3uptake)))
o3coefg = max(0._r8, min(1._r8, 0.943_r8 * exp(-0.0058*o3uptake)))
end if
if(pft_type >= 9 .and. pft_type <= 11)then !Shrub
Copy link
Contributor

Choose a reason for hiding this comment

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

Please replace these numbers with is_shrub that you can find in pftconMod.F90.

Similarly change the other numbers in this section with parameters that already exist in pftconMod.F90.

Copy link
Author

Choose a reason for hiding this comment

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

Done

! Determine parameter values for this pft
if(pft_type >= 1 .and. pft_type <= 3)then !Needleleaf tree
o3coefv = max(0._r8, min(1._r8, 1.005_r8 - 0.0064_r8 * o3uptake))
o3coefg = max(0._r8, min(1._r8, 0.965_r8 * o3uptake ** (-0.041)))
Copy link
Contributor

Choose a reason for hiding this comment

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

These right-hand-sides do not match what I see in Li et al. (in prep), which says

1.014-0.010 POD0.5
0.975-0.037 ln(POD0.5)

Copy link
Author

Choose a reason for hiding this comment

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

formulas in the code are consistent with the Eqs.2-3 in our submitted MS that I sent you on Jan 17. You used an old MS version. The difference is in the data pre-processing which changed the sample of observations we used to generate the response functions shown in Eqs. 2-3. According to suggestion of ecophysiologist Felicity Hayes (our coauthor and the chair of LRTAP ozone impacts on vegetation in observations), in pre-processing, we conducted linear regression using the relative values and corresponding PODY for individual plant species in a study, and data with intercept falling outside the range of 0.9 to 1.1 are removed based on LRTAP convention to ensure a usable response relationship.

end if
if(pft_type >= 9 .and. pft_type <= 11)then !Shrub
o3coefv = max(0._r8, min(1._r8, 1.000_r8-0.074_r8 * log(o3uptake)))
o3coefg = max(0._r8, min(1._r8, 0.991_r8-0.060_r8 * log(o3uptake)))
Copy link
Contributor

Choose a reason for hiding this comment

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

These right-hand-sides also do not match what I see in Li et al. (in prep), which says

1.142 POD5**(-0.140)
1.075 POD5**(-0.104)

Copy link
Author

Choose a reason for hiding this comment

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

see above reply

end if
if (pft_type >= 15)then !Crop
o3coefv = max(0._r8, min(1._r8, 0.909_r8 - 0.028_r8 * log(o3uptake)))
o3coefg = max(0._r8, min(1._r8, 1.005_r8 - 0.169_r8 * tanh(o3uptake)))
Copy link
Contributor

Choose a reason for hiding this comment

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

Grasses and crops seem to match what I see in Li et al. (in prep).

Copy link
Author

Choose a reason for hiding this comment

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

see above reply

rs_z(p,iv) = min(1._r8/gs, rsmax0)
rs_z(p,iv) = rs_z(p,iv) / o3coefg(p)
Copy link
Contributor

Choose a reason for hiding this comment

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

@lifang0209 explained to me that moving the effect from the resistance to the conductance, also moves it to the current time step rather than the following time step. I have not checked the validity of that statement.

However @lifang0209 you said that you had tested and found the effect of this change on the simulation to be very small. Could you post some sort of documentation here (possibly a figure) that would make this point clear?

Copy link
Author

Choose a reason for hiding this comment

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

I have added comment in the PhotosynthesisMod.F90:
! Fang Li revised o3 impact on gs
! now: multiply gs response factor (o3coefg) with gs
! clm5.0: skipped the effect on gs and had rs divided by o3coefg,
! which led to a one timestep delay in the o3 impact on gs,
! although the impact on an annual scale is quite small

!-----------------------------------------------------------------------

! convert o3 from mol/mol to nmol m^-3
o3concnmolm3 = forc_ozone * 1.e9_r8 * (forc_pbot/(forc_th*SHR_CONST_RGAS*0.001_r8))
Copy link
Contributor

Choose a reason for hiding this comment

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

When you finalize this conversion (see my other comment), then replace any remaining hardwiring with parameter names. If the conversion parameters already exist elsewhere, please reuse them, and otherwise add them locally.

Copy link
Author

Choose a reason for hiding this comment

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

add as local variables

if (pftcon%evergreen(pft_type) == 1) then
lai_thresh=0._r8 ! evergreens grow year-round
else ! for deciduous vegetation
if(pft_type == 10)then !temperate shrub
Copy link
Contributor

Choose a reason for hiding this comment

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

As you did with "evergreen" above, please replace 10 with the shrub name from pftconMod.F90.

Similarly replace numbers below with pft names and use is_shrub, is_grass for these types in particular.

For 15 I think you can use nc3crop. All of these are in pftconMod.F90.

Copy link
Author

Choose a reason for hiding this comment

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

Done

@wwieder wwieder added this to Slow roast (incremental or external progress) in CTSM-CLM6 development highlights Jan 18, 2024
@ekluzek
Copy link
Contributor

ekluzek commented Jan 19, 2024

@lifang0209 thanks so much for your work here and this contribution! There is some more work to be done on this, so I'm converting it to a draft. That doesn't change the importance of this, it just marks it more clearly as there's more work to do here....

@ekluzek ekluzek marked this pull request as draft January 19, 2024 17:33
Copy link
Contributor

@ekluzek ekluzek left a comment

Choose a reason for hiding this comment

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

@lifang0209 thanks so much for the contribution. I have a few suggestions. Mostly that @slevis-lmwg and I will discuss and work on. I had one question for you about dtimeh though...

real(r8) :: decay ! o3uptake decay rate based on leaf lifetime (mmol m^-2)

character(len=*), parameter :: subname = 'CalcOzoneUptakeOnePoint'
! o3:h2o resistance ratio defined by Sitch et al. 2007
real(r8), parameter :: ko3 = 1.67_r8
Copy link
Contributor

Choose a reason for hiding this comment

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

@lifang0209 should any of these be added to the parameter file, so they can be modified rather than hardcoded here?

Copy link
Author

Choose a reason for hiding this comment

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

Hi Erik, thanks for your time and suggestions.
The correct (observational) value for ko3 should be
real(r8), parameter :: ko3 = 1.51_r8
which used in CalcOzoneUptakeLi2024OnePoint in the same file (OzoneMod.F90).
I'm not sure if it's good to set different values of a parameter for different schemes in the parameter file.

@@ -494,21 +640,21 @@ subroutine CalcOzoneUptakeOnePoint( &
endif

if (pftcon%evergreen(pft_type) == 1) then
leafturn = 1._r8/(pftcon%leaf_long(pft_type)*365._r8*24._r8)
leafturn = 1._r8/(pftcon%leaf_long(pft_type)*avg_dayspyr*secspday)
Copy link
Contributor

Choose a reason for hiding this comment

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

I very much like using functions and constants to get things like the number of days in a year, and the seconds per day. That's an important improvement.

Copy link
Author

Choose a reason for hiding this comment

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

thanks

else
leafturn = 0._r8
endif

! o3 uptake decay based on leaf lifetime for evergreen plants
decay = o3uptake * leafturn * dtimeh
decay = o3uptake * leafturn * dtime
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this OK to do? Because dtimeh is in hours and dtime is in seconds, won't this change answers for the older versions?

Copy link
Author

Choose a reason for hiding this comment

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

According to Sam's suggestion, leafturn is changed from per hour to per second. Correspondingly, we should use dtime (in seconds) instead of dtimeh (in hours).

@@ -424,9 +444,125 @@ subroutine CalcOzoneUptake(this, bounds, num_exposedvegp, filter_exposedvegp, &
end associate

end subroutine CalcOzoneUptake
!-----------------------------------------------------------------------
subroutine CalcOzoneUptakeLi2024OnePoint( &
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should actually be spun off into it's own file. That would be better in keeping with the OO nature of how this is built. But, @slevis-lmwg and I will discuss, and I need to look at it more closely.

Copy link
Author

Choose a reason for hiding this comment

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

Thanks. That's not my area of expertise; I'll leave it to you and Sam to decide.
If you do this, don't forget move CalcOzoneUptakeLFOnePoint.
CalcOzoneUptakeLi2024OnePoint and CalcOzoneUptakeLFOnePoint are used to calculate the ozone uptake for a single point. The former is used for the new scheme (li2024), and the latter for the lombardozzi2015 and falk schemes.

@lifang0209
Copy link
Author

@lifang0209 thanks so much for the contribution. I have a few suggestions. Mostly that @slevis-lmwg and I will discuss and work on. I had one question for you about dtimeh though...

@lifang0209 lifang0209 closed this Jan 19, 2024
CTSM-CLM6 development highlights automation moved this from Slow roast (incremental or external progress) to Ready to eat (Done!) Jan 19, 2024
@lifang0209 lifang0209 reopened this Jan 19, 2024
CTSM-CLM6 development highlights automation moved this from Ready to eat (Done!) to On the grill (work in progress) Jan 19, 2024
@lifang0209 lifang0209 marked this pull request as ready for review January 19, 2024 19:03
@ekluzek ekluzek marked this pull request as draft January 20, 2024 07:06
@wwieder wwieder moved this from On the grill (work in progress) to Back Burner (or lower priority) in CTSM-CLM6 development highlights Apr 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR status: work in progress PR: author feels this is NOT ready to merge to master tag: enh - new science enhancement that brings in new science capabilities
Projects
Status: Back Burner (or lower priority)
CTSM-CLM6 development highlights
  
Back Burner (or lower priority)
Development

Successfully merging this pull request may close these issues.

None yet

3 participants