/// <summary>Return the plant available water CAPACITY for the specified crop. Units: mm/mm</summary> /// <param name="soil">The soil to calculate PAWC for.</param> /// <param name="crop">The crop.</param> /// <returns></returns> public static double[] OfCrop(Soil soil, SoilCrop crop) { return(PAWC.PAWCInternal(soil.Water.Thickness, crop.LL, soil.Water.DUL, crop.XF)); }
/// <summary>Convert the crop to the specified thickness. Ensures LL is between AirDry and DUL.</summary> /// <param name="crop">The crop to convert</param> /// <param name="thickness">The thicknesses to convert the crop to.</param> /// <param name="soil">The soil the crop belongs to.</param> private static void SetCropThickness(SoilCrop crop, double[] thickness, Soil soil) { if (!MathUtilities.AreEqual(thickness, crop.Thickness)) { crop.LL = MapConcentration(crop.LL, crop.Thickness, thickness, MathUtilities.LastValue(crop.LL)); crop.KL = MapConcentration(crop.KL, crop.Thickness, thickness, MathUtilities.LastValue(crop.KL)); crop.XF = MapConcentration(crop.XF, crop.Thickness, thickness, MathUtilities.LastValue(crop.XF)); crop.Thickness = thickness; crop.LL = MathUtilities.Constrain(crop.LL, AirDryMapped(soil, thickness), DULMapped(soil, thickness)); } }
/// <summary>Checks the crop for missing values.</summary> /// <param name="crop">The crop.</param> /// <param name="soil">The soil.</param> private static void CheckCropForMissingValues(SoilCrop crop, Soil soil) { for (int i = 0; i < crop.Thickness.Length; i++) { if (crop.LL != null && double.IsNaN(crop.LL[i])) { crop.LL[i] = soil.Water.LL15[i]; } if (crop.KL != null && double.IsNaN(crop.KL[i])) { crop.KL[i] = 0; } if (crop.XF != null && double.IsNaN(crop.XF[i])) { crop.XF[i] = 0; } } }
/// <summary>Fills in KL for crop.</summary> /// <param name="crop">The crop.</param> private static void FillInKLForCrop(SoilCrop crop) { if (crop.Name == null) throw new Exception("Crop has no name"); int i = StringUtilities.IndexOfCaseInsensitive(cropNames, crop.Name); if (i != -1) { double[] KLs = GetRowOfArray(defaultKLs, i); double[] cumThickness = SoilUtilities.ToCumThickness(crop.Thickness); crop.KL = new double[crop.Thickness.Length]; for (int l = 0; l < crop.Thickness.Length; l++) { bool didInterpolate; crop.KL[l] = MathUtilities.LinearInterpReal(cumThickness[l], defaultKLThickness, KLs, out didInterpolate); } } }
/// <summary>Calculate a layered soil water. Units: mm/mm</summary> public double[] SW(Soil soil) { string[] cropNames = soil.Water.Crops.Select(c => c.Name).ToArray(); // Get the correct LL and XF int cropIndex = -1; if (RelativeTo != null) { cropIndex = StringUtilities.IndexOfCaseInsensitive(cropNames, RelativeTo); } double[] ll; double[] xf = null; double[] PAWCmm; if (cropIndex == -1) { ll = soil.Water.LL15; PAWCmm = PAWC.OfSoilmm(soil); } else { SoilCrop crop = soil.Water.Crops[cropIndex]; ll = crop.LL; xf = crop.XF; PAWCmm = PAWC.OfCropmm(soil, crop); } if (double.IsNaN(DepthWetSoil)) { if (PercentMethod == InitialWater.PercentMethodEnum.FilledFromTop) { return(SWFilledFromTop(PAWCmm, ll, soil.Water.DUL, xf)); } else { return(SWEvenlyDistributed(ll, soil.Water.DUL)); } } else { return(SWDepthWetSoil(soil.Water.Thickness, ll, soil.Water.DUL)); } }
/// <summary>Test setup routine. Returns a soil properties that can be used for testing.</summary> public static APSIM.Shared.Soils.Soil Setup() { APSIM.Shared.Soils.Soil soil = new APSIM.Shared.Soils.Soil(); soil.Water = new Water(); soil.Water.Thickness = new double[] { 100, 300, 300, 300, 300, 300 }; soil.Water.BD = new double[] { 1.36, 1.216, 1.24, 1.32, 1.372, 1.368 }; soil.Water.AirDry = new double[] { 0.135, 0.214, 0.261, 0.261, 0.261, 0.261 }; soil.Water.LL15 = new double[] { 0.27, 0.267, 0.261, 0.261, 0.261, 0.261 }; soil.Water.DUL = new double[] { 0.365, 0.461, 0.43, 0.412, 0.402, 0.404 }; soil.Water.SAT = new double[] { 0.400, 0.481, 0.45, 0.432, 0.422, 0.424 }; // Add a wheat crop. SoilCrop crop = new SoilCrop(); crop.Thickness = soil.Water.Thickness; crop.Name = "Wheat"; crop.KL = new double[] { 0.06, 0.060, 0.060, 0.060, 0.060, 0.060 }; crop.LL = new double[] { 0.27, 0.267, 0.261, 0.315, 0.402, 0.402 }; soil.Water.Crops = new List<SoilCrop>(); soil.Water.Crops.Add(crop); // Add OC values into SoilOrganicMatter. soil.SoilOrganicMatter = new SoilOrganicMatter(); soil.SoilOrganicMatter.Thickness = soil.Water.Thickness; soil.SoilOrganicMatter.OC = new double[] { 2, 1, 0.5, 0.4, 0.3, 0.2 }; // Add in CL into analysis. soil.Analysis = new Analysis(); soil.Analysis.Thickness = soil.Water.Thickness; soil.Analysis.CL = new double[] { 38, double.NaN, 500, 490, 500, 500 }; // Add a sample. Sample sample = new Sample(); sample.Thickness = new double[] { 100, 300, 300, 300 }; sample.SW = new double[] { 0.103, 0.238, 0.253, 0.247 }; sample.NO3 = new double[] { 23, 7, 2, 1 }; sample.OC = new double[] { 1.35, double.NaN, double.NaN, double.NaN }; sample.SWUnits = Sample.SWUnitsEnum.Gravimetric; soil.Samples = new List<Sample>(); soil.Samples.Add(sample); return soil; }
/// <summary>Fills in KL for crop.</summary> /// <param name="crop">The crop.</param> private static void FillInKLForCrop(SoilCrop crop) { if (crop.Name == null) { throw new Exception("Crop has no name"); } int i = StringUtilities.IndexOfCaseInsensitive(cropNames, crop.Name); if (i != -1) { double[] KLs = GetRowOfArray(defaultKLs, i); double[] cumThickness = SoilUtilities.ToCumThickness(crop.Thickness); crop.KL = new double[crop.Thickness.Length]; for (int l = 0; l < crop.Thickness.Length; l++) { bool didInterpolate; crop.KL[l] = MathUtilities.LinearInterpReal(cumThickness[l], defaultKLThickness, KLs, out didInterpolate); } } }
/// <summary>Crop lower limit - mapped to the specified layer structure. Units: mm/mm /// </summary> /// <param name="crop">The crop.</param> /// <param name="ToThickness">To thickness.</param> /// <returns></returns> private static double[] LLMapped(SoilCrop crop, double[] ToThickness) { return(MapConcentration(crop.LL, crop.Thickness, ToThickness, MathUtilities.LastValue(crop.LL))); }
/// <summary>Crop lower limit - mapped to the specified layer structure. Units: mm/mm /// </summary> /// <param name="crop">The crop.</param> /// <param name="ToThickness">To thickness.</param> /// <returns></returns> private static double[] LLMapped(SoilCrop crop, double[] ToThickness) { return MapConcentration(crop.LL, crop.Thickness, ToThickness, MathUtilities.LastValue(crop.LL)); }
/// <summary>Checks the crop for missing values.</summary> /// <param name="crop">The crop.</param> /// <param name="soil">The soil.</param> private static void CheckCropForMissingValues(SoilCrop crop, Soil soil) { for (int i = 0; i < crop.Thickness.Length; i++) { if (crop.LL != null && double.IsNaN(crop.LL[i])) crop.LL[i] = soil.Water.LL15[i]; if (crop.KL != null && double.IsNaN(crop.KL[i])) crop.KL[i] = 0; if (crop.XF != null && double.IsNaN(crop.XF[i])) crop.XF[i] = 0; } }
private static string ReplaceSoilMacros(XmlNode SoilNode, string ApsimToSimContents) { // Get rid of nodes under <soil> that are disabled. RemoveDisabledNodes(SoilNode); APSIM.Shared.Soils.Soil mySoil = APSIM.Shared.Soils.SoilUtilities.FromXML(SoilNode.OuterXml); mySoil = APSIM.Shared.Soils.APSIMReadySoil.Create(mySoil); if (mySoil.Name == null) { mySoil.Name = "Soil"; } // Loop through all soil macros. int PosMacro = 0; PosMacro = ApsimToSimContents.IndexOf("[soil.", PosMacro); while (PosMacro != -1) { int PosEndMacro = ApsimToSimContents.IndexOf(']', PosMacro); if (PosEndMacro == -1) { throw new Exception("Invalid soil macro found: " + ApsimToSimContents.Substring(PosMacro)); } // Get macro name e.g. soil.thickness string MacroName = ApsimToSimContents.Substring(PosMacro + 1, PosEndMacro - PosMacro - 1); //if (MacroName.Contains("soil.")) { // remove the soil. prefix from the MacroName. MacroName = MacroName.Substring(MacroName.IndexOf('.') + 1); // Is it a crop macro i.e. wheat ll object Obj = null; if (MacroName.Contains(" ")) { string CropName = MacroName.Substring(0, MacroName.IndexOf(' ')); string VariableName = MacroName.Substring(MacroName.IndexOf(' ') + 1); APSIM.Shared.Soils.SoilCrop crop = mySoil.Water.Crops.Find(c => c.Name.Equals(CropName, StringComparison.InvariantCultureIgnoreCase)); if (crop == null) { throw new Exception("Cannot find soil water information for crop '" + CropName + "'. " + "You likely need to \"Manage Crops\" in the Water node of your soil, and set the water properties of this crop"); } if (VariableName.Equals("ll", StringComparison.CurrentCultureIgnoreCase)) { Obj = crop.LL; } else if (VariableName.Equals("kl", StringComparison.CurrentCultureIgnoreCase)) { Obj = crop.KL; } else if (VariableName.Equals("xf", StringComparison.CurrentCultureIgnoreCase)) { Obj = crop.XF; } } else { Obj = Utility.GetValueOfFieldOrProperty(MacroName, mySoil); } if (Obj != null) { string MacroValue = ""; if (Obj is IList) { foreach (object I in Obj as IList) { if (I is double) { MacroValue += ((double)I).ToString("f3", CultureInfo.CreateSpecificCulture("en-AU")) + " "; } else { MacroValue += I.ToString() + " "; } } } else if (Obj is double) { if (double.IsNaN(Convert.ToDouble(Obj))) { MacroValue = ""; } else { MacroValue = ((Double)Obj).ToString("f3", CultureInfo.CreateSpecificCulture("en-AU")); } } else { MacroValue = Obj.ToString(); } ApsimToSimContents = ApsimToSimContents.Remove(PosMacro, PosEndMacro - PosMacro + 1); ApsimToSimContents = ApsimToSimContents.Insert(PosMacro, MacroValue); } PosMacro = ApsimToSimContents.IndexOf("[soil.", PosMacro + 1); } } return(ApsimToSimContents); }
/// <summary>Return the plant available water CAPACITY for the specified crop. Units: mm</summary> /// <param name="soil">The soil to calculate PAWC for.</param> /// <param name="crop">The crop.</param> /// <returns></returns> public static double[] OfCropmm(Soil soil, SoilCrop crop) { double[] pawc = PAWC.OfCrop(soil, crop); return(MathUtilities.Multiply(pawc, soil.Water.Thickness)); }