Skip to content

Commit

Permalink
Merge pull request #6986 from jbrider/SorghumLeaf
Browse files Browse the repository at this point in the history
Sorghum leaf
  • Loading branch information
hol353 authored Dec 12, 2021
2 parents e773daf + bbdbebc commit c8740fb
Show file tree
Hide file tree
Showing 22 changed files with 28,073 additions and 43,747 deletions.
77 changes: 77 additions & 0 deletions Models/Functions/Senescence/AgeSenescenceFunction.cs
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);
}
}
}
103 changes: 103 additions & 0 deletions Models/Functions/Senescence/FrostSenescenceFunction.cs
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();
}

}

}


108 changes: 108 additions & 0 deletions Models/Functions/Senescence/LightSenescenceFunction.cs
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);
}
}

}


Loading

0 comments on commit c8740fb

Please sign in to comment.