-
Notifications
You must be signed in to change notification settings - Fork 25
V73: Improved root zone expansion #374
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
base: main
Are you sure you want to change the base?
Changes from 3 commits
1ba05fe
f722013
da72708
8f21d07
fddb58d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -140,6 +140,7 @@ module ac_run | |
| GetSaltInfiltr, & | ||
| GetSimulation_FromDayNr, & | ||
| GetSimulation_IrriECw, & | ||
| GetSimulParam_RootPercentZmin, & | ||
| GetSimulation_SalinityConsidered, & | ||
| GetSimulation_Storage_Btotal, & | ||
| GetSimulation_SumGDD, & | ||
|
|
@@ -599,6 +600,8 @@ module ac_run | |
| real(dp) :: BprevSum, YprevSum, SumGDDcuts, HItimesBEF | ||
| real(dp) :: ScorAT1, ScorAT2, HItimesAT1, HItimesAT2, HItimesAT | ||
| real(dp) :: alfaHI, alfaHIAdj | ||
| real(dp) :: tDaysZmin ! time to reach Zmin (Days) | ||
| real(dp) :: tGDDZmin ! time to reach Zmin (GDDays) | ||
|
|
||
| !! DelayedGermination | ||
| integer(int32) :: NextSimFromDayNr !! the Simulation.FromDayNr for next run if delayed germination and KeepSWC | ||
|
|
@@ -5328,6 +5331,11 @@ subroutine InitializeSimulationRunPart2() | |
| GetCrop_RootShape(),& | ||
| GetCrop_ModeCycle())) | ||
| end if | ||
| ! 16.3 Time to reach Zmin | ||
| call CalculateTimeToReachZmin(GetCrop_RootMin(),GetCrop_RootMax(),GetCrop_RootShape(), & | ||
| GetCrop_DaysToGermination(),GetCrop_DaysToMaxRooting(), & | ||
| GetCrop_GDDaysToGermination(),GetCrop_GDDaysToMaxRooting(), & | ||
| GetCrop_ModeCycle(),tDaysZmin,tGDDZmin) | ||
|
|
||
| ! 17. Multiple cuttings | ||
| call SetNrCut(0) | ||
|
|
@@ -6509,6 +6517,116 @@ subroutine GetPotValSF(DAP, SumGDDAdjCC, PotValSF) | |
| PotValSF = 100._dp * (1._dp/GetCCxCropWeedsNoSFstress()) * PotValSF | ||
| end subroutine GetPotValSF | ||
|
|
||
|
|
||
| subroutine CalculateTimeToReachZmin(ZMin, ZMax, RootShape, L0, LZmax, GGDL0, GDDLZmax, & | ||
| TheModeCycle, tDaysZmin, tGDDZmin) | ||
| real(dp), intent(in) :: ZMin | ||
| real(dp), intent(in) :: ZMax | ||
| integer(int8), intent(in) :: RootShape | ||
| integer(int32), intent(in) :: L0 | ||
| integer(int32), intent(in) :: LZmax | ||
| integer(int32), intent(in) :: GGDL0 | ||
| integer(int32), intent(in) :: GDDLZmax | ||
| integer(intEnum), intent(in) :: TheModeCycle | ||
| real(dp), intent(inout) :: tDaysZmin | ||
| real(dp), intent(inout) :: tGDDZmin | ||
|
|
||
| real(dp) :: Zini, Zfunction | ||
|
|
||
| tDaysZmin = real(L0, kind=dp) | ||
| tGDDZmin = real(GGDL0, kind=dp) | ||
|
|
||
| if ((GetSimulParam_RootPercentZmin() < 100) .and. (ZMin < ZMax)) then | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 100._dp? Not sure anymore if that was needed for >, < operations.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's best if we put dp I think (and also consistent with the rest of the code).
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would say so too. No harm. |
||
| Zini = ZMin * (real(GetSimulParam_RootPercentZmin(), kind=dp)/100._dp) | ||
|
|
||
| Zfunction = exp( (real(RootShape, kind=dp)/10._dp) * & | ||
| log( (ZMin - Zini) / (ZMax - Zini) ) ) | ||
|
|
||
| select case (TheModeCycle) | ||
| case (modeCycle_GDDays) | ||
| if (GDDLZmax > (GGDL0/2._dp)) then | ||
| tGDDZmin = Zfunction * real((GDDLZmax - (GGDL0/2._dp)), kind=dp) + & | ||
| real((GGDL0/2._dp), kind=dp) | ||
| end if | ||
| case default | ||
| if (LZmax > (L0/2._dp)) then | ||
| tDaysZmin = Zfunction * real((LZmax - (L0/2._dp)), kind=dp) + & | ||
| real((L0/2._dp), kind=dp) | ||
| end if | ||
| end select | ||
| end if | ||
| end subroutine CalculateTimeToReachZmin | ||
|
|
||
|
|
||
| subroutine CalculateRootingDepth(tDaysZmin, tGDDZmin, ZiPrev, GDDayi, RootingDepth) | ||
| real(dp), intent(in) :: tDaysZmin | ||
| real(dp), intent(in) :: tGDDZmin | ||
| real(dp), intent(in) :: ZiPrev | ||
| real(dp), intent(in) :: GDDayi | ||
| real(dp), intent(inout) :: RootingDepth | ||
|
|
||
| real(dp) :: tCalDayReal, tCalGDD, CalSumGDDPrev | ||
| real(dp) :: Zini, Zfunction | ||
| integer(int32) :: tCalDay | ||
| integer(int32) :: tCalDayNow | ||
|
|
||
| ! initialize | ||
| tCalDayNow = GetDayNri() - GetCrop_Day1() + 1 ! actual time (calendar day) | ||
| tCalDay = tCalDayNow | ||
| tCalGDD = GetSimulation_SumGDD() ! actual time (sum of GDD) | ||
| CalSumGDDPrev = GetSumGDDPrev() ! sum of GDD at previous day | ||
|
|
||
| ! Adjust time after planting (tCalDay OR tCalGDD) to simulate root zone expansion | ||
| Zini = GetCrop_RootMin() * (real(GetSimulParam_RootPercentZmin(), kind=dp)/100._dp) | ||
|
|
||
| if ((GetCrop_RootMin() < GetCrop_RootMax()) .and. (ZiPrev > Zini)) then | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just wondering, is it even possible to have RootMin>RootMax? Shouldn't the code raise an error if it's defined like that in the crop parameters? Now if it does not enter the if statement, nothing happens
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's probably there to avoid that this part is executed when RootMin == RootMax which then causes errors. True that RootMin should never be higher than RootMax, however, I don't think the current AquaCrop standalone catches non-meaningful parameter combinations elsewhere in the code. The GUI maybe. |
||
| ! valid for calendar and GDD time | ||
| Zfunction = exp( (real(GetCrop_RootShape(), kind=dp)/10._dp) * & | ||
| log( (ZiPrev - Zini) / (GetCrop_RootMax() - Zini) ) ) | ||
|
|
||
| select case (GetCrop_ModeCycle()) | ||
| case (modeCycle_GDDays) | ||
| ! growing degree days | ||
| if (GetSimulation_SumGDD() > tGDDZmin) then | ||
| ! root zone expansion can exceed Zmin | ||
| ! time to simulate root zone expansion is given by Zr of previous day (ZiPrev) | ||
| CalSumGDDPrev = Zfunction * & | ||
| real((GetCrop_GDDaysToMaxRooting() - (GetCrop_GDDaysToGermination()/2._dp)), kind=dp) + & | ||
| real((GetCrop_GDDaysToGermination()/2._dp), kind=dp) | ||
|
|
||
| tCalGDD = CalSumGDDPrev + GDDayi ! add GDD of today to get adjusted sum GDD | ||
| if (tCalGDD > GetSimulation_SumGDD()) then | ||
| tCalGDD = GetSimulation_SumGDD() | ||
| end if | ||
| end if | ||
|
|
||
| case default | ||
| ! calendar days | ||
| if (real(tCalDayNow, kind=dp) > tDaysZmin) then | ||
| ! root zone expansion can exceed Zmin | ||
| ! time to simulate root zone expansion is given by Zr of previous day (ZiPrev) | ||
| tCalDayReal = Zfunction * & | ||
| real((GetCrop_DaysToMaxRooting() - (GetCrop_DaysToGermination()/2._dp)), kind=dp) + & | ||
| real((GetCrop_DaysToGermination()/2._dp), kind=dp) | ||
|
|
||
| tCalDay = roundc(tCalDayReal, mold=1_int32) + 1 ! integer value + 1 day | ||
| if (tCalDay > tCalDayNow) tCalDay = tCalDayNow | ||
| end if | ||
| end select | ||
| end if | ||
| ! calculate root zone expansion with time after planting given by Zr of previous day (ZiPrev) | ||
| RootingDepth = AdjustedRootingDepth( & | ||
| GetPlotVarCrop_ActVal(), GetPlotVarCrop_PotVal(), GetTpot(), GetTact(), GetStressLeaf(), & | ||
| GetStressSenescence(), tCalDay, GetCrop_DaysToGermination(), GetCrop_DaysToMaxRooting(), GetCrop_DaysToHarvest(), & | ||
| GetCrop_GDDaysToGermination(), GetCrop_GDDaysToMaxRooting(), GetCrop_GDDaysToHarvest(), & | ||
| CalSumGDDPrev, tCalGDD, & | ||
| GetCrop_RootMin(), GetCrop_RootMax(), ZiPrev, GetCrop_RootShape(), & | ||
| GetCrop_ModeCycle() ) | ||
|
|
||
| if (RootingDepth < ZiPrev) RootingDepth = ZiPrev | ||
| end subroutine CalculateRootingDepth | ||
|
|
||
|
|
||
| !! ===END Subroutines and functions for AdvanceOneTimeStep === | ||
|
|
||
|
|
||
|
|
@@ -6792,6 +6910,7 @@ subroutine AdvanceOneTimeStep(WPi, HarvestNow) | |
| ScorAT1_temp, ScorAT2_temp, HItimesAT1_temp, & | ||
| HItimesAT2_temp, HItimesAT_temp, alfaHI_temp, & | ||
| alfaHIAdj_temp, TESTVAL | ||
| real(dp) :: RootingDepth_temp | ||
| logical :: WaterTableInProfile_temp, NoMoreCrop_temp | ||
|
|
||
| ! 1. Get ETo | ||
|
|
@@ -6955,18 +7074,21 @@ subroutine AdvanceOneTimeStep(WPi, HarvestNow) | |
| if (((GetDayNri()-GetSimulation_DelayedDays()) >= GetCrop_Day1()) .and. & | ||
| ((GetDayNri()-GetSimulation_DelayedDays()) <= GetCrop_DayN())) then | ||
| ! rooting depth at DAP (at Crop.Day1, DAP = 1) | ||
| call SetRootingDepth(AdjustedRootingDepth(GetPlotVarCrop_ActVal(), & | ||
| GetPlotVarCrop_PotVal(), GetTpot(), GetTact(), & | ||
| GetStressLeaf(), GetStressSenescence(), & | ||
| (GetDayNri()-GetCrop_Day1()+1), & | ||
| GetCrop_DaysToGermination(), & | ||
| GetCrop_DaysToMaxRooting(), GetCrop_DaysToHarvest(), & | ||
| GetCrop_GDDaysToGermination(), & | ||
| GetCrop_GDDaysToMaxRooting(), & | ||
| GetCrop_GDDaysToHarvest(), GetSumGDDPrev(), & | ||
| (GetSimulation_SumGDD()), GetCrop_RootMin(), & | ||
| GetCrop_RootMax(), GetZiprev(), GetCrop_RootShape(), & | ||
| GetCrop_ModeCycle())) | ||
| call CalculateRootingDepth(tDaysZmin,tGDDZmin,& | ||
| GetZiPrev(),GetGDDayi(),RootingDepth_temp) | ||
| call SetRootingDepth(RootingDepth_temp) | ||
| !call SetRootingDepth(AdjustedRootingDepth(GetPlotVarCrop_ActVal(), & | ||
|
||
| ! GetPlotVarCrop_PotVal(), GetTpot(), GetTact(), & | ||
| ! GetStressLeaf(), GetStressSenescence(), & | ||
| ! (GetDayNri()-GetCrop_Day1()+1), & | ||
| ! GetCrop_DaysToGermination(), & | ||
| ! GetCrop_DaysToMaxRooting(), GetCrop_DaysToHarvest(), & | ||
| ! GetCrop_GDDaysToGermination(), & | ||
| ! GetCrop_GDDaysToMaxRooting(), & | ||
| ! GetCrop_GDDaysToHarvest(), GetSumGDDPrev(), & | ||
| ! (GetSimulation_SumGDD()), GetCrop_RootMin(), & | ||
| ! GetCrop_RootMax(), GetZiprev(), GetCrop_RootShape(), & | ||
| ! GetCrop_ModeCycle())) | ||
| call SetZiprev(GetRootingDepth()) | ||
| ! IN CASE rootzone drops below groundwate table | ||
| if ((GetZiAqua() >= 0._dp) .and. (GetRootingDepth() > & | ||
|
|
||
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.
(1) not quite understood why a small value needs to be consistently added in one direction (it is also not really serving as a zero threshold in the same sense as used earlier in the code) (2) typo "ensures"
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 had to do a similar trick in LDT to get the right number of compartments. I think it has basically the same result but instead of having a new CropZx_eff, I adjusted one if statement.
See L185 https://github.com/KUL-RSDA/LISF/blob/working/ac72_GMD/ldt/params/AquaCrop/define_AC_compartments.F90
I am not sure why this happens (small rounding errors, differences in compilation, optimization?), but this line was indeed dangerous
if (TotDepthC < CropZx) thenand is solved by adding a small value
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.
Yes, we had the problem earlier. It's because the user defines round values for the max root zone depth like 1.5. Such round values are directly at the edge when the compartment thickness calculation decides to add or remove a 0.05 increment to the total depth of the simulated soil depth. We struggled with that difference in how the Delphi and Pascal code eventually discretizes. Adding a very small value solved the problem. The epsilon was not enough, so I used the ac_zero_threshold which worked.
@gdelannoy It is correct that the meaning of ac_zero_threshold is a bit different. I could also hardcode the small number instead of using this parameter. Should I?