-
Notifications
You must be signed in to change notification settings - Fork 322
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
base: master
Are you sure you want to change the base?
Conversation
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.
@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).
src/biogeophys/OzoneMod.F90
Outdated
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) |
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.
- 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.
- 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?
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.
done
|
||
! calculate o3 flux per timestep | ||
if(sabv > 0._r8)then !daytime | ||
o3fluxperdt = o3fluxcrit * dtime * 0.000001_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.
Could we avoid the unit conversion here by setting o3flux and o3_flux_threshold to the units that we need?
- You already convert a precursor to o3flux in line 495, so you could embed this conversion there.
- The latter (o3_flux_threshold) could be done in lines 511 to 523.
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 units of o3flux, o3_flux_threshold, and o3uptake are commonly used, especially in observational community, so I don't want to change them
src/biogeophys/OzoneMod.F90
Outdated
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 |
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.
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.
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.
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))) |
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 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)
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.
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))) |
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 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)
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.
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))) |
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.
Grasses and crops seem to match what I see in Li et al. (in prep).
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.
see above reply
rs_z(p,iv) = min(1._r8/gs, rsmax0) | ||
rs_z(p,iv) = rs_z(p,iv) / o3coefg(p) |
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.
@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?
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 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)) |
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.
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.
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.
add as local variables
src/biogeophys/OzoneMod.F90
Outdated
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 |
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.
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.
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.
Done
@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.... |
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.
@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 |
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.
@lifang0209 should any of these be added to the parameter file, so they can be modified rather than hardcoded 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.
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) |
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 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.
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.
thanks
else | ||
leafturn = 0._r8 | ||
endif | ||
|
||
! o3 uptake decay based on leaf lifetime for evergreen plants | ||
decay = o3uptake * leafturn * dtimeh | ||
decay = o3uptake * leafturn * dtime |
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 this OK to do? Because dtimeh is in hours and dtime is in seconds, won't this change answers for the older versions?
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 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( & |
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 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.
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.
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.
|
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