-
Notifications
You must be signed in to change notification settings - Fork 167
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6986 from jbrider/SorghumLeaf
Sorghum leaf
- Loading branch information
Showing
22 changed files
with
28,073 additions
and
43,747 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
using Models.Core; | ||
using Models.PMF.Organs; | ||
using Models.PMF.Phen; | ||
using System; | ||
using System.Linq; | ||
using APSIM.Shared.Utilities; | ||
using Models.PMF; | ||
using System.Collections.Generic; | ||
|
||
namespace Models.Functions | ||
{ | ||
/// <summary>Calculate Senescence due to age for C4Maize</summary> | ||
[Serializable] | ||
[Description("Calculate AgeSenescence")] | ||
public class AgeSenescenceFunction : Model, IFunction | ||
{ | ||
[Link(Type = LinkType.Ancestor)] | ||
private SorghumLeaf leaf = null; | ||
|
||
/// <summary>Phenology</summary> | ||
[Link] | ||
private Phenology phenology = null; | ||
|
||
[Link(Type = LinkType.Child, ByName = true)] | ||
private IFunction leafNoDeadIntercept = null; | ||
|
||
[Link(Type = LinkType.Child, ByName = true)] | ||
private IFunction leafNoDeadSlope = null; | ||
|
||
private double nDeadLeaves; | ||
private double dltDeadLeaves; | ||
private const double squareMM2squareM = 1.0 / 1000000.0; //! conversion factor of mm^2 to m^2 | ||
|
||
/// <summary>Called when crop is ending</summary> | ||
/// <param name="sender">The sender.</param> | ||
/// <param name="data">The <see cref="EventArgs"/> instance containing the event data.</param> | ||
[EventSubscribe("PlantSowing")] | ||
virtual protected void OnPlantSowing(object sender, SowingParameters data) | ||
{ | ||
nDeadLeaves = 0.0; | ||
dltDeadLeaves = 0.0; | ||
} | ||
|
||
/// <summary>Called when [EndCrop].</summary> | ||
/// <param name="sender">The sender.</param> | ||
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> | ||
[EventSubscribe("PlantEnding")] | ||
private void OnPlantEnding(object sender, EventArgs e) | ||
{ | ||
nDeadLeaves = 0.0; | ||
dltDeadLeaves = 0.0; | ||
} | ||
|
||
private double CalcDltDeadLeaves() | ||
{ | ||
double nDeadYesterday = nDeadLeaves; | ||
double nDeadToday = leaf.FinalLeafNo * (leafNoDeadIntercept.Value() + leafNoDeadSlope.Value() * phenology.AccumulatedEmergedTT); | ||
nDeadToday = MathUtilities.Bound(nDeadToday, nDeadYesterday, leaf.FinalLeafNo); | ||
return nDeadToday - nDeadYesterday; | ||
} | ||
|
||
/// <summary>Calculate Senescence due to age for C4Maize</summary> | ||
public double Value(int arrayIndex = -1) | ||
{ | ||
dltDeadLeaves = CalcDltDeadLeaves(); | ||
double deadLeaves = nDeadLeaves + dltDeadLeaves; | ||
double laiSenescenceAge = 0; | ||
if (MathUtilities.IsPositive(deadLeaves)) | ||
{ | ||
int leafDying = (int)Math.Ceiling(deadLeaves); | ||
double areaDying = (deadLeaves % 1.0) * leaf.culms.LeafSizes[leafDying - 1]; | ||
laiSenescenceAge = (leaf.culms.LeafSizes.Take(leafDying - 1).Sum() + areaDying) * squareMM2squareM * leaf.SowingDensity; | ||
} | ||
return Math.Max(laiSenescenceAge - leaf.SenescedLai, 0); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
using System; | ||
using System.Text; | ||
using Models.Core; | ||
using APSIM.Shared.Utilities; | ||
using Models.PMF.Organs; | ||
using Models.Interfaces; | ||
|
||
namespace Models.Functions | ||
{ | ||
/// <summary> | ||
/// [DocumentMathFunction /] | ||
/// </summary> | ||
[Serializable] | ||
[Description("Calculate LightSenescence")] | ||
public class FrostSenescenceFunction : Model, IFunction | ||
{ | ||
[Link(Type = LinkType.Ancestor)] | ||
private SorghumLeaf leaf = null; | ||
|
||
/// <summary> | ||
/// Linke to weather, used for frost senescence calcs. | ||
/// </summary> | ||
[Link] | ||
private IWeather weather = null; | ||
|
||
/// <summary>The met data</summary> | ||
[Link] | ||
public IWeather metData = null; | ||
|
||
[Link] | ||
private ISummary summary = null; | ||
|
||
/// <summary> Temperature threshold for leaf death, when plant is between floral init and flowering. </summary> | ||
[Link(Type = LinkType.Child, ByName = true)] | ||
private IFunction frostKill = null; | ||
|
||
/// <summary>Temperature threshold for leaf death.</summary> | ||
[Link(Type = LinkType.Child, ByName = true)] | ||
private IFunction frostKillSevere = null; | ||
|
||
/// <summary>Gets the value.</summary> | ||
/// <value>The value.</value> | ||
public double Value(int arrayIndex = -1) | ||
{ | ||
var frostEventThreshold = frostKill.Value(); | ||
|
||
if (weather.MinT > frostEventThreshold) | ||
return 0; | ||
|
||
var frostKillThreshold = frostKillSevere.Value(); | ||
|
||
if (MathUtilities.IsLessThanOrEqual(weather.MinT, frostKillSevere.Value())) | ||
{ | ||
// Temperature is below frostKillSevere parameter, senesce all LAI. | ||
summary.WriteMessage(this, FrostSenescenceMessage(fatal: true), MessageType.Diagnostic); | ||
return leaf.LAI; | ||
} | ||
|
||
// Temperature is warmer than frostKillSevere, but cooler than frostKill. | ||
// So the plant will only die if between floral init - flowering. | ||
if (leaf.phenology.Between("Germination", "FloralInitiation")) | ||
{ | ||
// The plant will survive but all of the leaf area is removed except a fraction. | ||
// 3 degrees is a default for now - extract to a parameter to customise it. | ||
summary.WriteMessage(this, FrostSenescenceMessage(fatal: false), MessageType.Diagnostic); | ||
return Math.Max(0, leaf.LAI - 0.1); | ||
} | ||
|
||
if (leaf.phenology.Between("FloralInitiation", "Flowering")) | ||
{ | ||
// Plant is between floral init and flowering - time to die. | ||
summary.WriteMessage(this, FrostSenescenceMessage(fatal: true), MessageType.Diagnostic); | ||
return leaf.LAI; // rip | ||
} | ||
|
||
// After flowering it takes a severe frost to kill the plant | ||
// (which didn't happen today). | ||
//there should probably be some leaf damage? | ||
return 0; | ||
} | ||
|
||
/// <summary> | ||
/// Generates a message to be displayed when senescence due to frost | ||
/// occurs. Putting this in a method for now so we don't have the same | ||
/// code twice, but if frost senescence is tweaked in the future it might | ||
/// just be easier to do away with the method and hardcode similar | ||
/// messages multiple times. | ||
/// </summary> | ||
/// <param name="fatal">Was the frost event fatal?</param> | ||
private string FrostSenescenceMessage(bool fatal) | ||
{ | ||
StringBuilder message = new StringBuilder(); | ||
message.AppendLine($"Frost Event: ({(fatal ? "Fatal" : "Non Fatal")})"); | ||
message.AppendLine($"\tMin Temp = {weather.MinT}"); | ||
message.AppendLine($"\tSenesced LAI = {leaf.LAI - 0.01}"); | ||
return message.ToString(); | ||
} | ||
|
||
} | ||
|
||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
using System.Reflection; | ||
using Models.Core; | ||
using System.Linq; | ||
using APSIM.Shared.Utilities; | ||
using Models.PMF.Interfaces; | ||
using Models.PMF.Organs; | ||
using Models.PMF; | ||
using Models.Interfaces; | ||
|
||
namespace Models.Functions | ||
{ | ||
/// <summary> | ||
/// [DocumentMathFunction /] | ||
/// </summary> | ||
[Serializable] | ||
[Description("Calculate LightSenescence")] | ||
public class LightSenescenceFunction : Model, IFunction | ||
{ | ||
[Link(Type = LinkType.Ancestor)] | ||
private SorghumLeaf Leaf = null; | ||
|
||
/// <summary>The met data</summary> | ||
[Link] | ||
private IWeather metData = null; | ||
|
||
/// <summary>Radiation level for onset of light senescence.</summary> | ||
[Link(Type = LinkType.Child, ByName = true)] | ||
[Units("Mj/m^2")] | ||
private IFunction senRadnCrit = null; | ||
|
||
[Link(Type = LinkType.Child, ByName = true)] | ||
private IFunction senLightTimeConst = null; | ||
|
||
/// <summary>Called when crop is ending</summary> | ||
/// <param name="sender">The sender.</param> | ||
/// <param name="data">The <see cref="EventArgs"/> instance containing the event data.</param> | ||
[EventSubscribe("PlantSowing")] | ||
virtual protected void OnPlantSowing(object sender, SowingParameters data) | ||
{ | ||
totalLaiEqlbLight = 0; | ||
avgLaiEquilibLight = 0; | ||
laiEqlbLightTodayQ = new Queue<double>(); | ||
|
||
} | ||
|
||
/// <summary>Called when [EndCrop].</summary> | ||
/// <param name="sender">The sender.</param> | ||
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> | ||
[EventSubscribe("PlantEnding")] | ||
private void OnPlantEnding(object sender, EventArgs e) | ||
{ | ||
totalLaiEqlbLight = 0; | ||
avgLaiEquilibLight = 0; | ||
laiEqlbLightTodayQ.Clear(); | ||
} | ||
|
||
/// <summary>Gets the value.</summary> | ||
/// <value>The value.</value> | ||
public double Value(int arrayIndex = -1) | ||
{ | ||
var senRadiationCrit = senRadnCrit.Value(); | ||
double critTransmission = MathUtilities.Divide(senRadiationCrit, metData.Radn, 1); | ||
/* TODO : Direct translation - needs cleanup */ | ||
// ! needs rework for row spacing | ||
double laiEqlbLightToday; | ||
if (critTransmission > 0.0) | ||
{ | ||
laiEqlbLightToday = -Math.Log(critTransmission) / Leaf.extinctionCoefficientFunction.Value(); | ||
} | ||
else | ||
{ | ||
laiEqlbLightToday = Leaf.LAI; | ||
} | ||
// average of the last 10 days of laiEquilibLight | ||
avgLaiEquilibLight = UpdateAvLaiEquilibLight(laiEqlbLightToday, 10);//senLightTimeConst? | ||
|
||
// dh - In old apsim, we had another variable frIntcRadn which is always set to 0. | ||
// Set Plant::radnInt(void) in Plant.cpp. | ||
double radnInt = metData.Radn * Leaf.CoverGreen; | ||
double radnTransmitted = metData.Radn - radnInt; | ||
double dltSlaiLight = 0.0; | ||
if (radnTransmitted < senRadiationCrit) | ||
dltSlaiLight = Math.Max(0.0, MathUtilities.Divide(Leaf.LAI - avgLaiEquilibLight, senLightTimeConst.Value(), 0.0)); | ||
dltSlaiLight = Math.Min(dltSlaiLight, Leaf.LAI); | ||
return dltSlaiLight; | ||
} | ||
|
||
private double totalLaiEqlbLight; | ||
private double avgLaiEquilibLight; | ||
private Queue<double> laiEqlbLightTodayQ; | ||
private double UpdateAvLaiEquilibLight(double laiEqlbLightToday, int days) | ||
{ | ||
totalLaiEqlbLight += laiEqlbLightToday; | ||
laiEqlbLightTodayQ.Enqueue(laiEqlbLightToday); | ||
if (laiEqlbLightTodayQ.Count > days) | ||
{ | ||
totalLaiEqlbLight -= laiEqlbLightTodayQ.Dequeue(); | ||
} | ||
return MathUtilities.Divide(totalLaiEqlbLight, laiEqlbLightTodayQ.Count, 0); | ||
} | ||
} | ||
|
||
} | ||
|
||
|
Oops, something went wrong.