Closed BrianCollinss closed 5 years ago
I think it might be easy to confuse users if we start to always show events in the report's completion popup - it would imply that these events are reportable properties. Perhaps we need some mechanism (a keyboard shortcut?) to ask for a list of events, or perhaps it would be possible to show events instead of properties based on the context.
e.g. after typing Sum of [Wheat].
we would get a list of properties, but after typing Sum of [Wheat].Leaf.Transpiration from [Clock].
we would get a list of events.
Agree with your point. Maybe a new column like the type of the item (variable/event) or (printed?) or a different color/icon could be useful and easier to implement in the popup list.
I am trying to generate the actual evapotranspiration
variable in a manager-script. I cannot call the Wheat.Leaf.Transpiration
variable as a part of the script. However, it can be called from the DailyReport.
This is the error:
System.Exception: Errors found in manager model Volatilisation1
Line 99: 'Models.Interfaces.ICanopy' does not contain a definition for 'Transpiration' and no extension method 'Transpiration' accepting a first argument of type 'Models.Interfaces.ICanopy' could be found (are you missing a using directive or an assembly reference?)
at Models.Manager.RebuildScriptModel()
at UserInterface.Presenters.ManagerPresenter.BuildScript()
and this is the script:
using Models.Climate;
using Models.Interfaces;
using System;
using Models.Core;
using Models.PMF;
using Models.Soils;
using Models.Utilities;
using Models.Surface;
using APSIM.Shared.Utilities;
using System.Xml.Serialization;
using Models.Soils.Nutrients;
namespace Models
{
[Serializable]
public class Script : Model
{
public double FASW { get; set; }
public double WaterDeficit { get; set; }
[Link] private Clock Clock;
[Link] private Zone myZone;
[Link] private Summary Summary;
[Link] private Fertiliser Fertiliser;
[Link] private SoilNitrogen SoilN;
[Link] private Irrigation irrigation;
[Link] private Weather Met;
[Link] private Soil soil;
[Link] private SoilNitrogenNH4 SoilNH4;
[Link] private Weather Weather;
[Link] private Plant Wheat;
[Link(ByName = true)] private ISolute Urea;
[Link(ByName = true)] private ISolute NO3;
[Link(ByName = true)] private ISolute NH4;
[XmlIgnore] public double NH4N { get; set; }
[Description("Reset SoilNH4 due to volatilisation (Yes or No)")]
public string ResetN {get;set;}
[Description("Reset Method (Schwenke or Johnson)")]
public string ResetMethod {get;set;}
[Description("Fertilisation date (dd-mmm)")]
public string FertDate { get; set; }
[Description("End of volatilisation period (dd-mmm)")]
public string EndDate { get; set; }
[Description("Volatilisation period (days)")]
public double Days { get; set; }
[Description("Average coarse sand in the soil profile (%)")]
public double CoarseSand { get; set; }
// Variables available for reporting
public double ESW {get; set;}
public double LL15 {get; set;}
public double SW {get; set;}
public double Wind {get; set;}
public double vol_red {get; set;}
public double Eo {get; set;}
public double Ea {get; set;}
public double rain {get; set;}
public double currentNH4 {get; set;}
public double newNH4 {get; set;}
public double[] myNewNH4 {get; set;}
[EventSubscribe("DoManagement")]
private void OnDoManagement(object sender, EventArgs e)
{
if (ResetN == "Yes" && ResetMethod == "Schwenke")
{
// Calculate the extractable soil water for layer 1
LL15 = soil.LL15[0];
SW = soil.SoilWater.SW[0];
ESW = SW - LL15;
Wind = Weather.Wind;
if (DateUtilities.WithinDates(FertDate, Clock.Today, EndDate))
{
Summary.WriteMessage(this, "Reset Nitrogen");
// SoilN.Reset();
// SoilNH4.AddKgHaDelta(SoluteSetterType.Other,myNH4); //delta values cannot be negative
CalcNewNH4();
Summary.WriteMessage(this, "About to set myNewNH4[0] to: " + myNewNH4[0].ToString());
SoilNH4.SetKgHa(SoluteSetterType.Other, myNewNH4);
}
}
if (ResetN == "Yes" && ResetMethod == "Johnson")
{
// Calculate the extractable soil water for layer 1
Eo = soil.SoilWater.Eo;
Ea = soil.SoilWater.Es + Wheat.Leaf.Transpiration;
rain = Weather.Rain;
if (DateUtilities.WithinDates(FertDate, Clock.Today, EndDate))
{
Summary.WriteMessage(this, "Reset Nitrogen");
// SoilN.Reset();
// SoilNH4.AddKgHaDelta(SoluteSetterType.Other,myNH4); //delta values cannot be negative
CalcNewNH4();
Summary.WriteMessage(this, "About to set myNewNH4[0] to: " + myNewNH4[0].ToString());
SoilNH4.SetKgHa(SoluteSetterType.Other, myNewNH4);
}
}
}
// Calculate the adjusted values of the NH4 by soil layer
private void CalcNewNH4()
{
// Calculate daily reduction (%) of SoilNH4
vol_red = (Ea-rain)/Eo;
// Create reports about variables and reset function
currentNH4 = SoilNH4.kgha[0];
Summary.WriteMessage(this, string.Format("SoilNH4.kgha[0] = {0}, SoilNH4.kgha[1] = {1}, SoilNH4.kgha[2] = {2}", SoilNH4.kgha[0], SoilNH4.kgha[1], SoilNH4.kgha[2]));
newNH4 = currentNH4 - (currentNH4 * 10000 * vol_red *0.001);
myNewNH4 = new double[] {newNH4};
Summary.WriteMessage(this, string.Format("newNH4 = {0}, vol_red = {1}, currentNH4 = {2}, ESW = {3}", newNH4, vol_red, currentNH4, ESW));
}
}
}
Yeah manager scripts don't integrate with the simulation tree structure very well. I think there is another issue which tracks that problem but we haven't come up with a solution yet. In the meantime you should be able to replicate report functionality using the FindByPath()
function.
Ea = soil.SoilWater.Es + (double)this.FindByPath("[Wheat].Leaf.Transpiration").Value;
thanks @hol430 . Now I just want to restrict the following equation vol_red = (Ea-rain)/Eo
to positive values, i.e. vol_red should range from 0 to any positive value. How could I do that? If the result is negative the model should consider vol_red=0.
You could do:
vol_red = Math.Max(0, (Ea - rain) / Eo);
You could do:
vol_red = Math.Max(0, (Ea - rain) / Eo);
I applied that and I got this error:
System.Exception: Errors found in manager model Volatilisation
Line 104: Cannot implicitly convert type 'double' to 'System.EventArgs'
at Models.Manager.RebuildScriptModel()
at UserInterface.Presenters.ManagerPresenter.BuildScript()
Hmm that is a strange error...did you put the line of code inside a function? If so, can you paste your script contents?
It is in a function but at the same time I want to report it as e
in the summary report.
using Models.Climate;
using Models.Interfaces;
using System;
using Models.Core;
using Models.PMF;
using Models.Soils;
using Models.Utilities;
using Models.Surface;
using APSIM.Shared.Utilities;
using System.Xml.Serialization;
using Models.Soils.Nutrients;
namespace Models
{
[Serializable]
public class Script : Model
{
public double FASW { get; set; }
public double WaterDeficit { get; set; }
[Link] private Clock Clock;
[Link] private Zone myZone;
[Link] private Summary Summary;
[Link] private Fertiliser Fertiliser;
[Link] private SoilNitrogen SoilN;
[Link] private Irrigation irrigation;
[Link] private Weather Met;
[Link] private Soil soil;
[Link] private SoilNitrogenNH4 SoilNH4;
[Link] private Weather Weather;
[Link] private Plant Wheat;
[Link(ByName = true)] private ISolute Urea;
[Link(ByName = true)] private ISolute NO3;
[Link(ByName = true)] private ISolute NH4;
[XmlIgnore] public double NH4N { get; set; }
[Description("Reset SoilNH4 due to volatilisation (Yes or No)")]
public string ResetN {get;set;}
[Description("Reset Method (Schwenke or Johnson)")]
public string ResetMethod {get;set;}
[Description("Fertilisation date (dd-mmm)")]
public string FertDate { get; set; }
[Description("End of volatilisation period (dd-mmm)")]
public string EndDate { get; set; }
[Description("Volatilisation period (days)")]
public double Days { get; set; }
[Description("Average coarse sand in the soil profile (%)")]
public double CoarseSand { get; set; }
// Variables available for reporting
public double ESW {get; set;}
public double LL15 {get; set;}
public double SW {get; set;}
public double Wind {get; set;}
public double vol_red {get; set;}
public double NH4reduction {get; set;}
public double e {get; set;}
public double Eo {get; set;}
public double Ea {get; set;}
public double rain {get; set;}
public double currentNH4 {get; set;}
public double newNH4 {get; set;}
public double[] myNewNH4 {get; set;}
[EventSubscribe("DoManagement")]
private void OnDoManagement(object sender, EventArgs e)
{
//Set up the Schwenke method to reduce SoilNH4
if (ResetN == "Yes" && ResetMethod == "Schwenke")
{
// Calculate the extractable soil water for layer 1
LL15 = soil.LL15[0];
SW = soil.SoilWater.SW[0];
ESW = SW - LL15;
Wind = Weather.Wind;
if (DateUtilities.WithinDates(FertDate, Clock.Today, EndDate))
{
Summary.WriteMessage(this, "Reset Nitrogen by Schwenke's method");
// SoilN.Reset();
// SoilNH4.AddKgHaDelta(SoluteSetterType.Other,myNH4); //delta values cannot be negative
CalcNewNH4();
Summary.WriteMessage(this, "About to set myNewNH4[0] to: " + myNewNH4[0].ToString());
SoilNH4.SetKgHa(SoluteSetterType.Other, myNewNH4);
}
}
//Set up the Schwenke method to reduce SoilNH4
if (ResetN == "Yes" && ResetMethod == "Johnson")
{
// Calculate the extractable soil water for layer 1
Eo = soil.SoilWater.Eo;
Ea = soil.SoilWater.Es + (double)this.FindByPath("[Wheat].Leaf.Transpiration").Value;
rain = Weather.Rain;
e = Math.Max(0, (Ea - rain) / Eo);
if (DateUtilities.WithinDates(FertDate, Clock.Today, EndDate))
{
Summary.WriteMessage(this, "Reset Nitrogen by Johnson's method");
// SoilN.Reset();
// SoilNH4.AddKgHaDelta(SoluteSetterType.Other,myNH4); //delta values cannot be negative
CalcNewNH4();
Summary.WriteMessage(this, "About to set myNewNH4[0] to: " + myNewNH4[0].ToString());
SoilNH4.SetKgHa(SoluteSetterType.Other, myNewNH4);
}
}
}
// Calculate the adjusted values of the NH4 in the first layer
private void CalcNewNH4()
{
currentNH4 = SoilNH4.kgha[0];
// Create reports about variables and reset function
Summary.WriteMessage(this, string.Format("SoilNH4.kgha[0] = {0}", SoilNH4.kgha[0]));
NH4reduction = (Math.Max(0, (Ea - rain) / Eo)) * currentNH4 * 0.01;
newNH4 = currentNH4 - NH4reduction;
myNewNH4 = new double[] {newNH4};
// Create reports about variables and reset function
Summary.WriteMessage(this, string.Format("currentNH4 = {0}, newNH4 = {1}, e = {2}, Ea = {3}, Eo = {4}, rain = {5}", currentNH4, newNH4, e, Ea, Eo, rain));
}
}
}
here is the problem: e = Math.Max(0, (Ea - rain) / Eo);
Ah, ok I see. One of the arguments in the OnDoManagement function is called e
, and its type is EventArgs
. So when you try to assign a double to an object of type EventArgs, you get an error. An easy fix would be to rename the EventArgs argument to something else (e.g. args):
...
private void OnDoManagement(object sender, EventArgs args)
...
perfect I did not realize that. Thanks @hol430 !
@hol430 just a single question, is the variable SoilNH4.kgha
in kgN/ha or in kgNH4/ha.
If the description in the code is accurate, then it's in kgN/ha
I want to create a variable hosting the currentUrea (kgHa) in the surface soil layer to calculates the N volatilisation in this pool. Which will be a similar variable than SoilNH4.kgha[0] but for Urea? I found the following variable [Soil].SoilNitrogen.ureappm
but I need it in kgHa. Below is part of the script which was described below in the discussion:
currentNH4 = SoilNH4.kgha[0];
// Create reports about variables and reset function
Summary.WriteMessage(this, string.Format("SoilNH4.kgha[0] = {0}", SoilNH4.kgha[0]));
NH4reduction = (Math.Max(0, (Ea - rain) / Eo)) * currentNH4 * 0.01;
newNH4 = currentNH4 - NH4reduction;
[Link(ByName = true)] private ISolute Urea;
Urea.kgha[0]?
Urea.kgha[0]
It's working well but, is it kgUreaN/ha or kgUrea/ha? I need kgUreaN/ha.
It is urea N. The only place in APSIM that allows the user to specify urea is in Fertiliser. Everywhere else it is the N in urea.
Do the conversion from urea to NH4 happen at the start of day or at the end of day?
The conversion only happens when fertiliser is applied. It happens immediately the Fertiliser.Apply method is called.
The conversion only happens when fertiliser is applied. It happens immediately the Fertiliser.Apply method is called.
I think you are referring to the conversion of fertiliser Urea to the Urea-N pool in the soil. I was asking about the conversion of the soil Urea-N pool to the soil NH4 pool
Ahh ok. That happens between start of day and end of day.
Ahh ok. That happens between start of day and end of day.
@hol353 thanks. @Keith-Pembleton
@hol353 We implemented a reset function to estimate volatilisation. This is calculated by the “DoManagement” event , which will be done early in the day. We think that by resetting the urea pool each day we are no then allowing the urea pool to convert to the NH4. We need a way of depleting the urea pool through volatilisation and also allowing the conversion to NH4 to happen at the same time. Could you please check if we can implement this to happen at the end of the day?
using Models.Climate;
using Models.Interfaces;
using System;
using Models.Core;
using Models.PMF;
using Models.Soils;
using Models.Utilities;
using Models.Surface;
using APSIM.Shared.Utilities;
using System.Xml.Serialization;
using Models.Soils.Nutrients;
namespace Models
{
[Serializable]
public class Script : Model
{
[Link] private Clock Clock;
[Link] private Zone myZone;
[Link] private Summary Summary;
[Link] private Fertiliser Fertiliser;
[Link] private SoilNitrogen SoilN;
[Link] private Irrigation irrigation;
[Link] private Weather Met;
[Link] private Soil soil;
[Link] private SoilNitrogenNH4 SoilNH4;
[Link] private Weather Weather;
[Link(ByName = true)] private ISolute Urea;
[Link(ByName = true)] private ISolute NO3;
[Link(ByName = true)] private ISolute NH4;
[XmlIgnore] public double NH4N { get; set; }
[Separator("Both volatilisation methods (Schwenke or Johnson)")]
[Description("Reset SoilNH4 due to volatilisation (Yes or No)")]
public string ResetN {get;set;}
[Description("Reset Method (Schwenke or Johnson)")]
public string ResetMethod {get;set;}
[Description("Fertilisation date (dd-mmm)")]
public string FertDate { get; set; }
[Description("End of volatilisation period (dd-mmm)")]
public string EndDate { get; set; }
[Separator("Only for Schwenke's method")]
[Description("Coarse sand in the surface soil layer (%)")]
public double CoarseSand { get; set; }
[Description("Is WindSpeed the average for the period? If variable leave 0 the next field (FixedWind or VariableWind)")]
public string Wind { get; set; }
[Description("Average Wind Speed? (m/s)")]
public double WindSpeed { get; set; }
// Variables available for reporting
public double ESW {get; set;}
public double LL15 {get; set;}
public double SW {get; set;}
public double VariableWind {get; set;}
public double FixedWind {get; set;}
public double vol_red {get; set;}
public double epsilon {get; set;}
public double Eo {get; set;}
public double Ea {get; set;}
public double rain {get; set;}
public double currentNH4 {get; set;}
public double NH4reduction {get; set;}
public double newNH4 {get; set;}
public double[] myNewNH4 {get; set;}
public double currentUrea {get; set;}
public double UreaReduction {get; set;}
public double newUrea {get; set;}
public double[] myNewUrea {get; set;}
[EventSubscribe("DoManagement")]
private void OnDoManagement(object sender, EventArgs e)
{
//Set up the Schwenke method to reduce SoilNH4
if (ResetN == "Yes" && ResetMethod == "Schwenke")
{
// Calculate the extractable soil water for layer 1
LL15 = soil.LL15[0];
SW = soil.SoilWater.SW[0];
ESW = SW - LL15;
VariableWind = Weather.Wind;
FixedWind = WindSpeed;
if (Wind == "VariableWind" && (DateUtilities.WithinDates(FertDate, Clock.Today, EndDate)))
{
Summary.WriteMessage(this, "Reset Nitrogen by Schwenke's method");
// SoilN.Reset();
// SoilNH4.AddKgHaDelta(SoluteSetterType.Other,myNH4); //delta values cannot be negative
CalcNewNH4SchwenkeVariableWind();
Summary.WriteMessage(this, "About to set myNewNH4[0] to: " + myNewNH4[0].ToString());
SoilNH4.SetKgHa(SoluteSetterType.Other, myNewNH4);
}
else
{
Summary.WriteMessage(this, "Reset Nitrogen by Schwenke's method");
// SoilN.Reset();
// SoilNH4.AddKgHaDelta(SoluteSetterType.Other,myNH4); //delta values cannot be negative
CalcNewNH4SchwenkeFixedWind();
Summary.WriteMessage(this, "About to set myNewNH4[0] to: " + myNewNH4[0].ToString());
SoilNH4.SetKgHa(SoluteSetterType.Other, myNewNH4);
}
}
//Set up the Schwenke method to reduce SoilNH4
if (ResetN == "Yes" && ResetMethod == "Johnson")
{
// Calculate the extractable soil water for layer 1
Eo = soil.SoilWater.Eo;
Ea = soil.SoilWater.Es;
rain = Weather.Rain;
epsilon = Math.Max(0, (Ea - rain) / Eo);
if (DateUtilities.WithinDates(FertDate, Clock.Today, EndDate))
{
Summary.WriteMessage(this, "Reset Nitrogen by Johnson's method");
// SoilN.Reset();
// SoilNH4.AddKgHaDelta(SoluteSetterType.Other,myNH4); //delta values cannot be negative
CalcNewNH4Johnson();
Summary.WriteMessage(this, "About to set myNewNH4[0] to: " + myNewNH4[0].ToString());
SoilNH4.SetKgHa(SoluteSetterType.Other, myNewNH4);
}
}
}
// Calculate the adjusted values of the NH4 by Schwenke's equation in the first layer
private void CalcNewNH4SchwenkeVariableWind()
{
currentNH4 = SoilNH4.kgha[0];
// Create reports about variables and reset function
Summary.WriteMessage(this, string.Format("SoilNH4.kgha[0] = {0}", SoilNH4.kgha[0]));
// Calculate daily reduction (%) of SoilNH4
vol_red = (Math.Max(0, (11.29 + (0.31 * CoarseSand) - (2.78 * VariableWind) + (ESW * 18.65))))/30;
newNH4 = currentNH4 - (currentNH4 * (vol_red/100));
myNewNH4 = new double[] {newNH4};
// Create reports about variables and reset function
Summary.WriteMessage(this, string.Format("newNH4 = {0}, vol_red = {1}, currentNH4 = {2}, ESW = {3}", newNH4, vol_red, currentNH4, ESW));
}
// Calculate the adjusted values of the NH4 by Schwenke's equation in the first layer
private void CalcNewNH4SchwenkeFixedWind()
{
currentNH4 = SoilNH4.kgha[0];
// Create reports about variables and reset function
Summary.WriteMessage(this, string.Format("SoilNH4.kgha[0] = {0}", SoilNH4.kgha[0]));
// Calculate daily reduction (%) of SoilNH4
vol_red = (Math.Max(0, (11.29 + (0.31 * CoarseSand) - (2.78 * WindSpeed) + (ESW * 18.65))))/30;
newNH4 = currentNH4 - (currentNH4 * (vol_red/100));
myNewNH4 = new double[] {newNH4};
// Create reports about variables and reset function
Summary.WriteMessage(this, string.Format("newNH4 = {0}, vol_red = {1}, currentNH4 = {2}, ESW = {3}", newNH4, vol_red, currentNH4, ESW));
}
// Calculate the adjusted values of the NH4 by Johnson's equation in the first layer
private void CalcNewNH4Johnson()
{
currentNH4 = SoilNH4.kgha[0];
currentUrea = Urea.kgha[0];
// Create reports about variables and reset function
Summary.WriteMessage(this, string.Format("SoilNH4.kgha[0] = {0}", SoilNH4.kgha[0]));
//Calculates the NH4 reduction (kg/ha)
NH4reduction = epsilon * currentNH4 * 0.01;
newNH4 = currentNH4 - NH4reduction;
//Calculates the Urea reduction (kg/ha)
UreaReduction = epsilon * currentUrea * 0.2;
newUrea = currentUrea - UreaReduction;
myNewNH4 = new double[] {newNH4};
myNewUrea = new double[] {newUrea};
// Create reports about variables and reset function
Summary.WriteMessage(this, string.Format("currentNH4 = {0}, newNH4 = {1}, currentUrea = {2}, newUrea = {3}, epsilon = {4}, Ea = {5}, Eo = {6}, rain = {7}", currentNH4, newNH4, currentUrea, newUrea, epsilon, Ea, Eo, rain));
}
}
}
If you want this to happen after the nutrient model has done it's thing then change
[EventSubscribe("DoManagement")]
to
[EventSubscribe("DoManagementCalculations")]
I suspect this won't make any difference though because essentially you're doing the same thing. You would just be resetting the NH4 pool before the next days nutrient calculations instead of the current days calculations.
I'm unsure why you're not seeing the behaviour you expect. I'm not an expert in the intricacies of nutrient modelling.
Thanks @hol353 it's really appreciated.
It would be handy if we could choose events like [Wheat].Sowing from a pop-up list (like other objects or traits) when we need to calculate an aggregated value using arithmetic functions. Users are not usually familiar with all the events available. Currently events can only be chosen that way in the Reporting frequency panel in Report objects, not in the Reporting variables panel.