Skip to content

Commit e2932c9

Browse files
committed
Merge branch 'refactor_cnallocation'
Refactor NutrientCompetition / CNAllocation to provide hooks for AgSys Major refactor of NutrientCompetition / CNAllocation to provide hooks for AgSys crop model: separates the NutrientCompetition modules into pieces based on (1) consolidating duplicate code between the Clm45 and FlexibleCN versions, and (2) separating pieces that will vs. won't be used for crop patches when running with the upcoming AgSys crop model. I have restored the old CNAllocationMod, with some of the responsibilities that it used to have. (I'm not sure it's appropriate to have the calculation of gpp and maint resp in CNAllocationMod, but I left it there because it has always been combined with the allocation code, including back when we had a separate CNAllocationMod.)
2 parents 82a63cc + 6ec5166 commit e2932c9

15 files changed

+1655
-1319
lines changed

doc/ChangeLog

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,82 @@
11
===============================================================
2+
Tag name: ctsm5.1.dev092
3+
Originator(s): sacks (Bill Sacks)
4+
Date: Fri Apr 29 18:31:48 MDT 2022
5+
One-line Summary: Refactor NutrientCompetition / CNAllocation to provide hooks for AgSys
6+
7+
Purpose and description of changes
8+
----------------------------------
9+
10+
Major refactor of NutrientCompetition / CNAllocation to provide hooks
11+
for AgSys crop model: separates the NutrientCompetition modules into
12+
pieces based on (1) consolidating duplicate code between the Clm45 and
13+
FlexibleCN versions, and (2) separating pieces that will vs. won't be
14+
used for crop patches when running with the upcoming AgSys crop model.
15+
16+
I have restored the old CNAllocationMod, with some of the
17+
responsibilities that it used to have. (I'm not sure it's appropriate to
18+
have the calculation of gpp and maint resp in CNAllocationMod, but I
19+
left it there because it has always been combined with the allocation
20+
code, including back when we had a separate CNAllocationMod.)
21+
22+
23+
Significant changes to scientifically-supported configurations
24+
--------------------------------------------------------------
25+
26+
Does this tag change answers significantly for any of the following physics configurations?
27+
(Details of any changes will be given in the "Answer changes" section below.)
28+
29+
[Put an [X] in the box for any configuration with significant answer changes.]
30+
31+
[ ] clm5_1
32+
33+
[ ] clm5_0
34+
35+
[ ] ctsm5_0-nwp
36+
37+
[ ] clm4_5
38+
39+
40+
Testing summary:
41+
----------------
42+
43+
regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing):
44+
45+
cheyenne ---- PASS
46+
izumi ------- PASS
47+
48+
49+
Answer changes
50+
--------------
51+
52+
Changes answers relative to baseline: NO - though potential for answer
53+
changes in unusual cases
54+
55+
One change in this tag has the potential for answer changes in
56+
unusual cases, even though no answer changes were observed in
57+
testing: Previously, only the FlexibleCN code applied some logic that
58+
changed crop allocation fractions during the grainfill period if
59+
peaklai had been reached. I have changed this so that this logic is
60+
applied with or without FlexibleCN (because I have moved this block
61+
of code to the CNAllocation module, which is shared between the
62+
FlexibleCN and non-FlexibleCN versions). I thought this would change
63+
answers, but it appears not to, at least based on the tests in the
64+
test suite as well as an extra 5-year test I did at f19 resolution
65+
(ERS_Ly5.f19_g17.IHistClm45BgcCrop.cheyenne_intel.clm-cropMonthOutput).
66+
It's possible that this changes answers in rare cases or with an
67+
unusual combination of options that we don't test: specifically, it
68+
might change answers for the atypical situation where you are running
69+
with FUN but not FlexibleCN. (In this case, I believe this change is
70+
the correct thing to do.)
71+
72+
73+
Other details
74+
-------------
75+
Pull Requests that document the changes (include PR ids):
76+
https://github.com/ESCOMP/CTSM/pull/1705
77+
78+
===============================================================
79+
===============================================================
280
Tag name: ctsm5.1.dev091
381
Originator(s): rgknox (Ryan Knox,rgknox@lbl.gov)
482
Date: Fri Apr 22 14:11:50 EDT 2022

doc/ChangeSum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
Tag Who Date Summary
22
============================================================================================================================
3+
ctsm5.1.dev092 sacks 04/29/2022 Refactor NutrientCompetition / CNAllocation to provide hooks for AgSys
34
ctsm5.1.dev091 rgknox 04/22/2022 clm decomp method is now passed to fates to enabled mimics coupling
45
ctsm5.1.dev090 samrabin 03/31/2022 Fix misleading name of "gddplant"
56
ctsm5.1.dev089 sacks 03/31/2022 For CLM45 apply peaklai to aleaf in grainfill

src/biogeochem/CNAllocationMod.F90

Lines changed: 542 additions & 0 deletions
Large diffs are not rendered by default.

src/biogeochem/CNDriverMod.F90

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ module CNDriverMod
1111
use decompMod , only : bounds_type
1212
use perf_mod , only : t_startf, t_stopf
1313
use clm_varctl , only : use_nitrif_denitrif, use_nguardrail
14-
use clm_varctl , only : iulog, use_crop
14+
use clm_varctl , only : iulog, use_crop, use_crop_agsys
1515
use SoilBiogeochemDecompCascadeConType, only : mimics_decomp, century_decomp, decomp_method
1616
use CNSharedParamsMod , only : use_fun
1717
use CNVegStateType , only : cnveg_state_type
@@ -42,7 +42,6 @@ module CNDriverMod
4242
use SaturatedExcessRunoffMod , only : saturated_excess_runoff_type
4343
use ActiveLayerMod , only : active_layer_type
4444
use SoilWaterRetentionCurveMod , only : soil_water_retention_curve_type
45-
use CropReprPoolsMod , only : nrepr
4645
!
4746
! !PUBLIC TYPES:
4847
implicit none
@@ -83,7 +82,8 @@ end subroutine CNDriverInit
8382

8483
!-----------------------------------------------------------------------
8584
subroutine CNDriverNoLeaching(bounds, &
86-
num_soilc, filter_soilc, num_soilp, filter_soilp, num_pcropp, filter_pcropp, &
85+
num_soilc, filter_soilc, num_soilp, filter_soilp, &
86+
num_pcropp, filter_pcropp, num_soilnopcropp, filter_soilnopcropp, &
8787
num_exposedvegp, filter_exposedvegp, num_noexposedvegp, filter_noexposedvegp, &
8888
cnveg_state_inst, &
8989
cnveg_carbonflux_inst, cnveg_carbonstate_inst, &
@@ -114,6 +114,8 @@ subroutine CNDriverNoLeaching(bounds,
114114
use clm_varpar , only: nlevdecomp, ndecomp_cascade_transitions, ndecomp_pools
115115
use subgridAveMod , only: p2c
116116
use CropType , only: crop_type
117+
use CNAllocationMod , only: calc_gpp_mr_availc, calc_crop_allocation_fractions
118+
use CNAllocationMod , only: calc_allometry
117119
use CNNDynamicsMod , only: CNNDeposition,CNNFixation, CNNFert, CNSoyfix,CNFreeLivingFixation
118120
use CNMRespMod , only: CNMResp
119121
use CNFUNMod , only: CNFUNInit !, CNFUN
@@ -151,6 +153,8 @@ subroutine CNDriverNoLeaching(bounds,
151153
integer , intent(in) :: filter_soilp(:) ! filter for soil patches
152154
integer , intent(in) :: num_pcropp ! number of prog. crop patches in filter
153155
integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches
156+
integer , intent(in) :: num_soilnopcropp ! number of non-prog. crop soil patches in filter
157+
integer , intent(in) :: filter_soilnopcropp(:) ! filter for non-prog. crop soil patches
154158
integer , intent(in) :: num_exposedvegp ! number of points in filter_exposedvegp
155159
integer , intent(in) :: filter_exposedvegp(:) ! patch filter for non-snow-covered veg
156160
integer , intent(in) :: num_noexposedvegp ! number of points in filter_noexposedvegp
@@ -203,8 +207,6 @@ subroutine CNDriverNoLeaching(bounds,
203207
real(r8):: pmnf_decomp_cascade(bounds%begc:bounds%endc,1:nlevdecomp,1:ndecomp_cascade_transitions) !potential mineral N flux, from one pool to another
204208
real(r8):: p_decomp_npool_to_din(bounds%begc:bounds%endc,1:nlevdecomp,1:ndecomp_cascade_transitions) ! potential flux to dissolved inorganic N
205209
real(r8):: p_decomp_cn_gain(bounds%begc:bounds%endc,1:nlevdecomp,1:ndecomp_pools) ! C:N ratio of the flux gained by the receiver pool
206-
real(r8):: arepr(bounds%begp:bounds%endp,nrepr) ! reproductive allocation coefficient(s) (only used for use_crop)
207-
real(r8):: aroot(bounds%begp:bounds%endp) ! root allocation coefficient (only used for use_crop)
208210
integer :: begp,endp
209211
integer :: begc,endc
210212

@@ -386,16 +388,44 @@ subroutine CNDriverNoLeaching(bounds,
386388

387389
end if
388390

391+
call t_startf('cnalloc')
392+
call calc_gpp_mr_availc( &
393+
bounds, num_soilp, filter_soilp, &
394+
crop_inst, photosyns_inst, canopystate_inst, &
395+
cnveg_carbonstate_inst, cnveg_carbonflux_inst, &
396+
c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst)
397+
398+
if (.not. use_crop_agsys) then
399+
call calc_crop_allocation_fractions(bounds, num_pcropp, filter_pcropp, &
400+
crop_inst, cnveg_state_inst)
401+
end if
402+
403+
call calc_allometry(num_soilp, filter_soilp, &
404+
cnveg_carbonflux_inst, cnveg_state_inst)
405+
call t_stopf('cnalloc')
406+
389407
call t_startf('calc_plant_nutrient_demand')
408+
! We always call calc_plant_nutrient_demand for natural veg patches, but only call
409+
! it for crop patches if NOT running with AgSys (since AgSys calculates the relevant
410+
! output variables in its own way).
390411
call nutrient_competition_method%calc_plant_nutrient_demand ( &
391-
bounds, num_soilp, filter_soilp, &
392-
photosyns_inst, crop_inst, canopystate_inst, &
393-
cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, &
394-
c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, &
395-
cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, &
396-
soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, &
397-
energyflux_inst, &
398-
aroot=aroot(begp:endp), arepr=arepr(begp:endp,:))
412+
bounds, &
413+
num_soilnopcropp, filter_soilnopcropp, .false., &
414+
crop_inst, canopystate_inst, &
415+
cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, &
416+
cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, &
417+
soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, &
418+
energyflux_inst)
419+
if (.not. use_crop_agsys) then
420+
call nutrient_competition_method%calc_plant_nutrient_demand ( &
421+
bounds, &
422+
num_pcropp, filter_pcropp, .true., &
423+
crop_inst, canopystate_inst, &
424+
cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, &
425+
cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, &
426+
soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, &
427+
energyflux_inst)
428+
end if
399429

400430
! get the column-averaged plant_ndemand (needed for following call to SoilBiogeochemCompetition)
401431

@@ -429,8 +459,6 @@ subroutine CNDriverNoLeaching(bounds,
429459
c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, &
430460
cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, &
431461
soilbiogeochem_nitrogenstate_inst, &
432-
aroot=aroot(begp:endp), &
433-
arepr=arepr(begp:endp,:), &
434462
fpg_col=soilbiogeochem_state_inst%fpg_col(begc:endc))
435463
call t_stopf('calc_plant_nutrient_competition')
436464

src/biogeochem/CNPhenologyMod.F90

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ module CNPhenologyMod
2828
use CNVegnitrogenstateType , only : cnveg_nitrogenstate_type
2929
use CNVegnitrogenfluxType , only : cnveg_nitrogenflux_type
3030
use CropType , only : crop_type
31+
use CropType , only : cphase_planted, cphase_leafemerge
32+
use CropType , only : cphase_grainfill, cphase_harvest
3133
use pftconMod , only : pftcon
3234
use SoilStateType , only : soilstate_type
3335
use TemperatureType , only : temperature_type
@@ -48,6 +50,7 @@ module CNPhenologyMod
4850
public :: CNPhenologyreadNML ! Read namelist
4951
public :: CNPhenologyInit ! Initialization
5052
public :: CNPhenology ! Update
53+
public :: CropPhase ! Get the current phase of each crop patch
5154

5255
! !PUBLIC for unit testing
5356
public :: CNPhenologySetNML ! Set the namelist setttings explicitly for unit tests
@@ -2012,7 +2015,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , &
20122015
offset_flag(p) = 0._r8 ! carbon and nitrogen transfers
20132016

20142017
if (croplive(p)) then
2015-
cphase(p) = 1._r8
2018+
cphase(p) = cphase_planted
20162019

20172020
! call vernalization if winter temperate cereal planted, living, and the
20182021
! vernalization factor is not 1;
@@ -2045,8 +2048,14 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , &
20452048
hui(p) = max(hui(p),huigrain(p))
20462049
endif
20472050

2051+
! The following conditionals are similar to those in CropPhase. However, they
2052+
! differ slightly because here we are potentially setting a new crop phase,
2053+
! whereas CropPhase is just designed to get the current, already-determined
2054+
! phase. However, despite these differences: if you make changes to the
2055+
! following conditionals, you should also check to see if you should make
2056+
! similar changes in CropPhase.
20482057
if (leafout(p) >= huileaf(p) .and. hui(p) < huigrain(p) .and. idpp < mxmat(ivt(p))) then
2049-
cphase(p) = 2._r8
2058+
cphase(p) = cphase_leafemerge
20502059
if (abs(onset_counter(p)) > 1.e-6_r8) then
20512060
onset_flag(p) = 1._r8
20522061
onset_counter(p) = dt
@@ -2076,7 +2085,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , &
20762085
harvest_count(p) = harvest_count(p) + 1
20772086
crop_inst%hdates_thisyr(p, harvest_count(p)) = real(jday, r8)
20782087
croplive(p) = .false. ! no re-entry in greater if-block
2079-
cphase(p) = 4._r8
2088+
cphase(p) = cphase_harvest
20802089
if (tlai(p) > 0._r8) then ! plant had emerged before harvest
20812090
offset_flag(p) = 1._r8
20822091
offset_counter(p) = dt
@@ -2106,7 +2115,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , &
21062115
! Use CN's simple formula at least as a place holder (slevis)
21072116

21082117
else if (hui(p) >= huigrain(p)) then
2109-
cphase(p) = 3._r8
2118+
cphase(p) = cphase_grainfill
21102119
bglfr(p) = 1._r8/(leaf_long(ivt(p))*avg_dayspyr*secspday)
21112120
end if
21122121

@@ -2144,6 +2153,67 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , &
21442153

21452154
end subroutine CropPhenology
21462155

2156+
!-----------------------------------------------------------------------
2157+
subroutine CropPhase(bounds, num_pcropp, filter_pcropp, &
2158+
crop_inst, cnveg_state_inst, crop_phase)
2159+
!
2160+
! !DESCRIPTION:
2161+
! Get the current phase of each crop patch.
2162+
!
2163+
! The returned values (in crop_phase) are from the set of cphase_* values defined in
2164+
! CropType. The returned values in crop_phase are only valid for patches where
2165+
! croplive is true; the values are undefined where croplive is false and should not be
2166+
! used there!
2167+
!
2168+
! This has logic similar to that in CropPhenology. If you make changes here, you
2169+
! should also check if similar changes need to be made in CropPhenology.
2170+
!
2171+
! !ARGUMENTS:
2172+
type(bounds_type) , intent(in) :: bounds
2173+
integer , intent(in) :: num_pcropp ! number of prog crop patches in filter
2174+
integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches
2175+
type(crop_type) , intent(in) :: crop_inst
2176+
type(cnveg_state_type) , intent(in) :: cnveg_state_inst
2177+
real(r8) , intent(inout) :: crop_phase(bounds%begp:)
2178+
!
2179+
! !LOCAL VARIABLES:
2180+
integer :: p, fp
2181+
2182+
character(len=*), parameter :: subname = 'CropPhase'
2183+
!-----------------------------------------------------------------------
2184+
SHR_ASSERT_ALL_FL((ubound(crop_phase) == [bounds%endp]), sourcefile, __LINE__)
2185+
2186+
associate( &
2187+
croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] Flag, true if planted, not harvested
2188+
hui => crop_inst%hui_patch , & ! Input: [real(r8) (:) ] gdd since planting (gddplant)
2189+
leafout => crop_inst%gddtsoi_patch , & ! Input: [real(r8) (:) ] gdd from top soil layer temperature
2190+
huileaf => cnveg_state_inst%huileaf_patch , & ! Input: [real(r8) (:) ] heat unit index needed from planting to leaf emergence
2191+
huigrain => cnveg_state_inst%huigrain_patch & ! Input: [real(r8) (:) ] same to reach vegetative maturity
2192+
)
2193+
2194+
do fp = 1, num_pcropp
2195+
p = filter_pcropp(fp)
2196+
2197+
if (croplive(p)) then
2198+
! Start with cphase_planted, but this might get changed in the later
2199+
! conditional blocks.
2200+
crop_phase(p) = cphase_planted
2201+
if (leafout(p) >= huileaf(p) .and. hui(p) < huigrain(p)) then
2202+
crop_phase(p) = cphase_leafemerge
2203+
else if (hui(p) >= huigrain(p)) then
2204+
! Since we know croplive is true, any hui greater than huigrain implies that
2205+
! we're in the grainfill stage: if we were passt gddmaturity then croplive
2206+
! would be false.
2207+
crop_phase(p) = cphase_grainfill
2208+
end if
2209+
end if
2210+
end do
2211+
2212+
end associate
2213+
2214+
end subroutine CropPhase
2215+
2216+
21472217
!-----------------------------------------------------------------------
21482218
subroutine CropPhenologyInit(bounds)
21492219
!

0 commit comments

Comments
 (0)