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

Reduce duplication between caps #1086

Merged
merged 17 commits into from
Sep 25, 2020
Merged

Conversation

slevis-lmwg
Copy link
Contributor

Description of changes

The mct, nuopc and lilac caps duplicate some code as documented in #918
and this PR attempts to reduce such duplication.

Specific notes

Contributors other than yourself, if any:
@billsacks

CTSM Issues Fixed (include github issue #):
#918

Are answers expected to change (and if so in what way)?
Theoretically no if we functionalize the exact code that repeats between caps; however, the repeating calculation of qsat can instead be replaced with a call to already existing subroutine QSat. I have now confirmed round-off rms diffs (< 1e-17 in my test's cprnc.out file) when making subr. QSat identical to the repeating caps calculation. On the other hand, my qsat values change by more than 1e-2 when I call the model's unchanged QSat algorithm. Despite these larger diffs, I recommend this solution to enhance consistency throughout the CTSM.

Any User Interface Changes (namelist or namelist defaults changes)?
No.

Testing performed, if any:
At the time that I created this PR, I had completed this test:
./create_test ERS_D_Ld6.f10_f10_musgs.I1850Clm45BgcCrop.cheyenne_intel.clm-clm50CMIP6frc -c /glade/p/cgd/tss/ctsm_baselines/ctsm1.0.dev105
which passed except in comparison to ctsm_baselines as explained above.

qsat = 0.622_r8*e / (forc_pbot - 0.378_r8*e)
qsat_old = 0.622_r8*e / (forc_pbot - 0.378_r8*e)
call QSat_temp(forc_t, forc_pbot, dum1, dum2, qsat_kg_kg, dum3)
if (qsat_kg_kg - qsat_old > 1.e-14_r8) write(iulog,*) 'qsat_new, qsat_old =', qsat_kg_kg, qsat_old ! slevis diag
Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the subr. call above, QSat_temp returns round-off diffs, while QSat returns diffs that exceed round-off.

I propose that we call QSat for consistency throughout the CTSM, despite the larger diffs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@billsacks I will request a code review of my changes this far, in order to initiate a discussion on whether this solution is acceptable.

Once this piece is resolved, we can discuss the functionalizing of remaining sections of repeating code in the caps.

This has two benefits:

(1) It prevents callers from needing to create dummy variables to hold
    outputs that they don't actually need.

(2) If neither qsdT nor esdT are needed, then we can avoid doing some
    expensive calculations.
@billsacks
Copy link
Member

@slevisconsulting sorry for my slow reply here. Yes, you raise a very good point – that we should probably be using the existing QSat routine consistently. From a quick look, it looks like the existing QSat routine uses a higher-order polynomial approximation, explaining the greater-than-roundoff-level diffs you were seeing. Is that your understanding, too? (I wanted to make sure that these greater-than-roundoff-level diffs are expected, and they do seem to be.) Thanks for doing the careful checking you have done so far.

Your changes here inspired me to make the changes in #1094 - here and in some other places, not all of the output arguments from QSat are needed. It feels to me like the changes in #1094 lead to improved code clarity (avoiding the need for temporary, unused variables) and possibly some performance improvements (avoiding some expensive calculations of the derivatives if they aren't needed). Do you want to take a quick look at that and let me know if that looks like a good path forward? If so, it seems like the approach would be:

  1. I bring Make most QSat output arguments optional #1094 to master (bfb)

  2. You make the minimal changes to use the existing QSat (answer changing)

  3. Then you make the additional changes to remove duplication (should be bfb)

(I'm suggesting separating the bfb from answer-changing mods because the bfb mods are so extensive and I don't want accidental answer changes to get mixed in with them.)

Does that sound like a good plan? As always, things are more difficult than I originally imagined, but I think this will be helpful cleanup; do you agree?

@slevis-lmwg
Copy link
Contributor Author

@billsacks
Yes to all your questions.

I will review #1094 by Monday.

@billsacks
Copy link
Member

@slevisconsulting given our backlog of tags, I thought it would be best if I revised my above plan so that this only results in a single tag, rather than 3. But I think it's still best if the full test suite is run separately on each stage, to verify that changes that are expected to be bfb are indeed bfb.

I just ran the full test suite on #1094 and it was bit-for-bit with master. So I'd suggest that you merge that branch into yours... but you may want to first back out some of your temporary changes in QSatMod to avoid merge conflicts, then you can reapply them later. Then you can move forward with development of this branch.

@slevis-lmwg
Copy link
Contributor Author

slevis-lmwg commented Aug 7, 2020

the approach would be:

  1. I bring Make most QSat output arguments optional #1094 to master (bfb)
  2. You make the minimal changes to use the existing QSat (answer changing)
  3. Then you make the additional changes to remove duplication (should be bfb)

(I'm suggesting separating the bfb from answer-changing mods because the bfb mods are so extensive and I don't want accidental answer changes to get mixed in with them.)

@billsacks I wanted to be clear about the next steps:

  1. I have completed the modified step (1)
  2. (a) I ran my single test (same as above) and confirmed round-off diffs when calling QSat_temp as reported before
    (b) So based on your suggestion, I should proceed to running the full test suite with all my mods but without using QSat_temp, and I should generate a temporary baseline
  3. Additional changes (should be bfb)

@billsacks
Copy link
Member

@billsacks I wanted to be clear about the next steps:

  1. I have completed the modified step (1)
  2. (a) I ran my single test (same as above) and confirmed round-off diffs when calling QSat_temp as reported before
    (b) So based on your suggestion, I should proceed to running the full test suite with all my mods but without using QSat_temp, and I should generate a temporary baseline
  3. Additional changes (should be bfb)

Yes, that is my thinking if it makes sense to you.

@slevis-lmwg
Copy link
Contributor Author

slevis-lmwg commented Aug 10, 2020

Testing confirmed on izumi and cheyenne:
Round-off diffs when calling QSat_temp from src/cpl/mct/lnd_import_export.F90

Testing confirmed on izumi and cheyenne:
Greater than round-off when calling QSat from src/cpl/mct/lnd_import_export.F90

Generated temp. baselines:
/fs/cgd/csm/ccsm_baselines/ctsm1.0.105_qsat
/glade/p/cgd/tss/ctsm_baselines/ctsm1.0.105_qsat

@slevis-lmwg
Copy link
Contributor Author

Izumi test suite passed!

@slevis-lmwg
Copy link
Contributor Author

Cheyenne test suite passed.

@billsacks this work is ready for review.

Questions regarding my implementation:

  1. Placement of new file lnd_import_export_utils.F90 in /src/utils/. I tried placing in a new /src/cpl/utils but didn't know how to make the new file visible during the build.
  2. Treatment of optional fields: Tempting to place those in the same new subroutine but also seemed a bit outside the scope of Some science-y processing of atmospheric imports duplicated between caps #918 and I wasn't sure if it was worth the additional effort right now.
  3. Further modularization: Eg place the error checks in their own subroutine. Seems logical but not sure if worth the additional effort right now.

@billsacks
Copy link
Member

billsacks commented Aug 21, 2020

  1. Placement of new file lnd_import_export_utils.F90 in /src/utils/. I tried placing in a new /src/cpl/utils but didn't know how to make the new file visible during the build.
  • Putting this in src/cpl/utils is the right thing to do here. You should just need to add a line in cime_config/buildlib for this to work
  1. Treatment of optional fields: Tempting to place those in the same new subroutine but also seemed a bit outside the scope of Some science-y processing of atmospheric imports duplicated between caps #918 and I wasn't sure if it was worth the additional effort right now.

I agree (not necessary right now)

  1. Further modularization: Eg place the error checks in their own subroutine. Seems logical but not sure if worth the additional effort right now.

I also agree (not necessary right now)

Copy link
Member

@billsacks billsacks left a comment

Choose a reason for hiding this comment

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

Thanks a lot for this! I have two optional requests inline; if you don't have time and/or don't agree with me on them, then they aren't critical. See also my comment from a few minutes ago in this PR.

Thanks for consolidating some of the error checking code in addition to the code that sets derived quantities!

Comment on lines 95 to 121
!--------------------------
! Error checks
!--------------------------

! Check that solar, specific-humidity, and LW downward aren't negative
do g = begg, endg
if ( atm2lnd_inst%forc_lwrad_not_downscaled_grc(g) <= 0.0_r8 ) then
call shr_sys_abort( subname//&
' ERROR: Longwave down sent from the atmosphere model is negative or zero' )
end if
if ( (atm2lnd_inst%forc_solad_grc(g,1) < 0.0_r8) .or. &
(atm2lnd_inst%forc_solad_grc(g,2) < 0.0_r8) .or. &
(atm2lnd_inst%forc_solai_grc(g,1) < 0.0_r8) .or. &
(atm2lnd_inst%forc_solai_grc(g,2) < 0.0_r8) ) then
call shr_sys_abort( subname//&
' ERROR: One of the solar fields (indirect/diffuse, vis or near-IR)'// &
' from the atmosphere model is negative or zero' )
end if
if ( wateratm2lndbulk_inst%forc_q_not_downscaled_grc(g) < 0.0_r8 )then
call shr_sys_abort( subname//&
' ERROR: Bottom layer specific humidty sent from the atmosphere model is less than zero' )
end if
end do

! Make sure relative humidity is properly bounded
! atm2lnd_inst%forc_rh_grc(g) = min( 100.0_r8, atm2lnd_inst%forc_rh_grc(g) )
! atm2lnd_inst%forc_rh_grc(g) = max( 0.0_r8, atm2lnd_inst%forc_rh_grc(g) )
Copy link
Member

@billsacks billsacks Aug 21, 2020

Choose a reason for hiding this comment

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

Not critical, but:

  • [optional] it would be best if this error check code was put in its own subroutine, since it is pretty unrelated to the rest of the subroutine, and the name of the subroutine doesn't give any indication that it also is doing this error checking.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made the change and submitted this test:
./create_test ERS_D_Ld6.f10_f10_musgs.I1850Clm45BgcCrop.cheyenne_intel.clm-clm50CMIP6frc -c /glade/p/cgd/tss/ctsm_baselines/ctsm1.0.105_qsat PASS

Question:
Ok that I replaced the corresponding code with a call to this new subroutine? Or would you write the new call three times, once after each call derive_quantities?

Copy link
Member

@billsacks billsacks Aug 24, 2020

Choose a reason for hiding this comment

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

Thanks @slevisconsulting ! I was imagining writing the new call three times, once after each call to derive_quantities. I know that means a bit of duplication, but the benefit is that, when you're reading through the top-level routine, it becomes more clear what's happening. As it's written now, when you read through the top-level routine, there's no indication that there is any error checking being done, unless you really dig deep into the implementation. The alternative would be to rename 'derive_quantities' to 'derive_quantities_and_check_for_errors', but my general feeling is that if you have a subroutine name that implies two separate activities, then it deserves to be split into its two pieces which are called in succession from the top level.

  • So, if you agree, can you please move the call to check_for_errors to the 3 top-level routines?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made the change and submitted this test again:
./create_test ERS_D_Ld6.f10_f10_musgs.I1850Clm45BgcCrop.cheyenne_intel.clm-clm50CMIP6frc -c /glade/p/cgd/tss/ctsm_baselines/ctsm1.0.105_qsat PASS

Comment on lines 92 to 95
real(r8), pointer :: forc_rainc_grc (:) => null() ! convective rain (mm/s)
real(r8), pointer :: forc_rainl_grc (:) => null() ! large scale rain (mm/s)
real(r8), pointer :: forc_snowc_grc (:) => null() ! convective snow (mm/s)
real(r8), pointer :: forc_snowl_grc (:) => null() ! large scale snow (mm/s)
Copy link
Member

@billsacks billsacks Aug 21, 2020

Choose a reason for hiding this comment

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

Not critical, but:

  • [optional] I feel it would be best if we avoided adding these variables to atm2lndType. Since they are only needed in the scope of lnd_import, I think they can be local arrays in that routine (declared like forc_rainc(bounds%begg:bounds%endg), as in the prior version of the lilac and nuopc caps), then passed individually to your new shared subroutine. (i.e., the new subroutine would have 4 extra arguments, accepting these 4 arrays). The point of this would be to avoid bloating this derived type. But I don't feel strongly about this.

Copy link
Contributor Author

@slevis-lmwg slevis-lmwg Aug 24, 2020

Choose a reason for hiding this comment

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

I made the change and submitted this test:
./create_test ERS_D_Ld6.f10_f10_musgs.I1850Clm45BgcCrop.cheyenne_intel.clm-clm50CMIP6frc -c /glade/p/cgd/tss/ctsm_baselines/ctsm1.0.105_qsat PASS

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I forgot to remove these from atm2lndType, so doing that now.

@slevis-lmwg
Copy link
Contributor Author

slevis-lmwg commented Aug 24, 2020

Putting this in src/cpl/utils is the right thing to do here. You should just need to add a line in cime_config/buildlib for this to work

I made the change and submitted this test:
./create_test ERS_D_Ld6.f10_f10_musgs.I1850Clm45BgcCrop.cheyenne_intel.clm-clm50CMIP6frc -c /glade/p/cgd/tss/ctsm_baselines/ctsm1.0.105_qsat PASS

...from within subroutine derive_quantities to after each
call derive_quantities
Copy link
Member

@billsacks billsacks left a comment

Choose a reason for hiding this comment

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

Looks great now - thank you!

Fang Li's latest Fire version - includes allowing clm5.1 phys version. New physics option is added
called "clm5_1", with currently the new feature to use the latest fire changes. This has some
adjustments to the fire model and includes some changes to the parameter file. Other new features
will be added into clm5_1 in future tags.

Also bring in mksurfdata changes for the raw urban dataset change. This adds some changes to
mksurfdata for a new urban raw dataset, as well as preparation for new changes for some other
urban changes that will be a future part of clm5_1. Also use the half degree lightning dataset
by default for clm5_1.

Start adding a new test list ctsm_sci that tests all the scientifically supported compsets.
Some of those tests fail due to existing issues, that will be fixed later.

Some more work done to change clm to ctsm, and allow for ctsm as a component.
@slevis-lmwg
Copy link
Contributor Author

Test-suite on cheyenne: PASS

@slevis-lmwg
Copy link
Contributor Author

Test-suite on izumi: PASS

@slevis-lmwg
Copy link
Contributor Author

@billsacks this PR is ready for merge. Thanks.

@billsacks billsacks merged commit f3e44c8 into ESCOMP:master Sep 25, 2020
@billsacks
Copy link
Member

Thanks @slevisconsulting . FYI I made some very minor ChangeLog edits in 3ea45a6.

@slevis-lmwg slevis-lmwg deleted the reduce_duplication branch February 5, 2021 20:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done (non release/external)
Development

Successfully merging this pull request may close these issues.

2 participants