/// <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>Sets the sample thickness.</summary> /// <param name="sample">The sample.</param> /// <param name="thickness">The thickness to change the sample to.</param> /// <param name="soil">The soil</param> private static void SetSampleThickness(Sample sample, double[] thickness, Soil soil) { if (!MathUtilities.AreEqual(thickness, sample.Thickness)) { sample.Name = sample.Name; sample.Date = sample.Date; if (sample.SW != null) sample.SW = MapSW(sample.SW, sample.Thickness, thickness, soil); if (sample.NH4 != null) sample.NH4 = MapConcentration(sample.NH4, sample.Thickness, thickness, 0.01); if (sample.NO3 != null) sample.NO3 = MapConcentration(sample.NO3, sample.Thickness, thickness, 0.01); // The elements below will be overlaid over other arrays of values so we want // to have missing values (double.NaN) used at the bottom of the profile. if (sample.CL != null) sample.CL = MapConcentration(sample.CL, sample.Thickness, thickness, double.NaN, allowMissingValues:true); if (sample.EC != null) sample.EC = MapConcentration(sample.EC, sample.Thickness, thickness, double.NaN, allowMissingValues: true); if (sample.ESP != null) sample.ESP = MapConcentration(sample.ESP, sample.Thickness, thickness, double.NaN, allowMissingValues: true); if (sample.OC != null) sample.OC = MapConcentration(sample.OC, sample.Thickness, thickness, double.NaN, allowMissingValues: true); if (sample.PH != null) sample.PH = MapConcentration(sample.PH, sample.Thickness, thickness, double.NaN, allowMissingValues: true); sample.Thickness = thickness; } }
/// <summary>Converts the paddock XML.</summary> /// <param name="paddock">The paddock.</param> /// <exception cref="System.Exception">Bad paddock name: + name</exception> private static Paddock CreateSimulationSpec(XmlNode paddock, string baseFolder) { Paddock simulation = new Paddock(); string name = XmlUtilities.NameAttr(paddock); string delimiter = "^"; int posCaret = name.IndexOf(delimiter); if (posCaret == -1) throw new Exception("Bad paddock name: " + name); string remainder = StringUtilities.SplitOffAfterDelimiter(ref name, delimiter); string growerName; string paddockName = StringUtilities.SplitOffAfterDelimiter(ref remainder, delimiter); if (paddockName == string.Empty) { growerName = name; paddockName = remainder; } else growerName = remainder; simulation.StartSeasonDate = GetDate(paddock, "StartSeasonDateFull"); // Give the paddock a name. string fullName = string.Format("{0}^{1}^{2}", simulation.StartSeasonDate.Year, growerName, paddockName); simulation.Name = fullName; // Set the report date. simulation.NowDate = GetDate(paddock.ParentNode, "TodayDateFull"); if (simulation.NowDate == DateTime.MinValue) simulation.NowDate = DateTime.Now; // Store any rainfall data in the simulation. simulation.RainfallSource = GetString(paddock, "RainfallSource"); if (simulation.RainfallSource != "Weather station") { string rainFileName = GetString(paddock, "RainfallFilename"); if (rainFileName != string.Empty) { string fullFileName = Path.Combine(baseFolder, rainFileName); if (!File.Exists(fullFileName)) throw new Exception("Cannot find file: " + fullFileName); simulation.ObservedData = ApsimTextFile.ToTable(fullFileName); if (simulation.ObservedData.Rows.Count == 0) simulation.ObservedData = null; } } // Set the reset dates simulation.SoilWaterSampleDate = GetDate(paddock, "ResetDateFull"); simulation.SoilNitrogenSampleDate = GetDate(paddock, "SoilNitrogenSampleDateFull"); simulation.StationNumber = GetInteger(paddock, "StationNumber"); simulation.StationName = GetString(paddock, "StationName"); // Create a sowing management Sow sowing = new Sow(); simulation.Management.Add(sowing); sowing.Date = GetDate(paddock, "SowDateFull"); sowing.EmergenceDate = GetDate(paddock, "EmergenceDateFull"); sowing.Crop = GetString(paddock, "Crop"); sowing.Cultivar = GetString(paddock, "Cultivar"); sowing.SkipRow = GetString(paddock, "SkipRow"); sowing.SowingDensity = GetInteger(paddock, "SowingDensity"); sowing.MaxRootDepth = GetInteger(paddock, "MaxRootDepth") * 10; // cm to mm sowing.BedWidth = GetInteger(paddock, "BedWidth"); sowing.BedRowSpacing = GetDouble(paddock, "BedRowSpacing"); // Make sure we have a stubbletype simulation.StubbleType = GetString(paddock, "StubbleType"); if (simulation.StubbleType == string.Empty || simulation.StubbleType == "None") simulation.StubbleType = "Wheat"; simulation.StubbleMass = GetDouble(paddock, "StubbleMass"); simulation.Slope = GetDouble(paddock, "Slope"); simulation.SlopeLength = GetDouble(paddock, "SlopeLength"); simulation.UseEC = GetBoolean(paddock, "UseEC"); // Fertilise nodes. List<XmlNode> fertiliserNodes = XmlUtilities.ChildNodes(paddock, "Fertilise"); for (int f = 0; f < fertiliserNodes.Count; f++) { Fertilise fertilise = new Fertilise(); simulation.Management.Add(fertilise); fertilise.Date = GetDate(fertiliserNodes[f], "FertDateFull"); fertilise.Amount = GetDouble(fertiliserNodes[f], "FertAmount"); fertilise.Scenario = GetBoolean(fertiliserNodes[f], "Scenario"); } // Irrigate nodes. List<XmlNode> irrigateNodes = XmlUtilities.ChildNodes(paddock, "Irrigate"); for (int i = 0; i < irrigateNodes.Count; i++) { Irrigate irrigate = new Irrigate(); simulation.Management.Add(irrigate); irrigate.Date = GetDate(irrigateNodes[i], "IrrigateDateFull"); irrigate.Amount = GetDouble(irrigateNodes[i], "IrrigateAmount"); irrigate.Efficiency = GetDouble(irrigateNodes[i], "IrrigateEfficiency"); irrigate.Scenario = GetBoolean(irrigateNodes[i], "Scenario"); } // Tillage nodes. foreach (XmlNode tillageNode in XmlUtilities.ChildNodes(paddock, "Tillage")) { Tillage tillage = new Tillage(); simulation.Management.Add(tillage); tillage.Date = GetDate(tillageNode, "TillageDateFull"); string disturbance = GetString(tillageNode, "Disturbance"); if (disturbance == "Low") tillage.Disturbance = Tillage.DisturbanceEnum.Low; else if (disturbance == "Medium") tillage.Disturbance = Tillage.DisturbanceEnum.Medium; else tillage.Disturbance = Tillage.DisturbanceEnum.High; tillage.Scenario = GetBoolean(tillageNode, "Scenario"); } // Stubble removed nodes. foreach (XmlNode stubbleRemovedNode in XmlUtilities.ChildNodes(paddock, "StubbleRemoved")) { StubbleRemoved stubbleRemoved = new StubbleRemoved(); simulation.Management.Add(stubbleRemoved); stubbleRemoved.Date = GetDate(stubbleRemovedNode, "StubbleRemovedDateFull"); stubbleRemoved.Percent = GetDouble(stubbleRemovedNode, "StubbleRemovedAmount"); } // Look for a soil node. XmlNode soilNode = XmlUtilities.FindByType(paddock, "Soil"); if (soilNode != null) { string testValue = XmlUtilities.Value(soilNode, "Water/Layer/Thickness"); if (testValue != string.Empty) { // old format. soilNode = ConvertSoilNode.Upgrade(soilNode); } } // Fix up soil sample variables. Sample sample1 = new Sample(); sample1.Name = "Sample1"; sample1.Thickness = GetArray(paddock, "Sample1Thickness"); sample1.SW = GetArray(paddock, "SW"); if (sample1.SW == null) { // Really old way of doing samples - they are stored under soil. List<XmlNode> sampleNodes = XmlUtilities.ChildNodes(soilNode, "Sample"); if (sampleNodes.Count > 0) sample1 = XmlUtilities.Deserialise(sampleNodes[0], typeof(Sample)) as Sample; } else { sample1.NO3 = GetArray(paddock, "NO3"); sample1.NH4 = GetArray(paddock, "NH4"); } Sample sample2 = null; double[] sample2Thickness = GetArray(paddock, "Sample2Thickness"); if (sample2Thickness == null) { // Really old way of doing samples - they are stored under soil. List<XmlNode> sampleNodes = XmlUtilities.ChildNodes(soilNode, "Sample"); if (sampleNodes.Count > 1) sample2 = XmlUtilities.Deserialise(sampleNodes[1], typeof(Sample)) as Sample; } else { sample2 = sample1; if (!MathUtilities.AreEqual(sample2Thickness, sample1.Thickness)) { sample2 = new Sample(); sample2.Name = "Sample2"; } sample2.OC = GetArray(paddock, "OC"); sample2.EC = GetArray(paddock, "EC"); sample2.PH = GetArray(paddock, "PH"); sample2.CL = GetArray(paddock, "CL"); sample2.OCUnits = SoilOrganicMatter.OCUnitsEnum.WalkleyBlack; sample2.PHUnits = Analysis.PHUnitsEnum.CaCl2; } // Make sure we have NH4 values. if (sample1.NH4 == null && sample1.NO3 != null) { string[] defaultValues = StringUtilities.CreateStringArray("0.1", sample1.NO3.Length); sample1.NH4 = MathUtilities.StringsToDoubles(defaultValues); } RemoveNullFieldsFromSample(sample1); if (sample2 != null) RemoveNullFieldsFromSample(sample2); // Fix up <WaterFormat> string waterFormatString = GetString(paddock, "WaterFormat"); if (waterFormatString.Contains("Gravimetric")) sample1.SWUnits = Sample.SWUnitsEnum.Gravimetric; else sample1.SWUnits = Sample.SWUnitsEnum.Volumetric; if (MathUtilities.ValuesInArray(sample1.Thickness)) simulation.Samples.Add(sample1); if (sample2 != null && MathUtilities.ValuesInArray(sample2.Thickness) && sample2 != sample1) simulation.Samples.Add(sample2); // Check for InitTotalWater & InitTotalNitrogen simulation.InitTotalWater = GetDouble(paddock, "InitTotalWater"); simulation.InitTotalNitrogen = GetDouble(paddock, "InitTotalNitrogen"); // Check to see if we need to convert the soil structure. simulation.SoilPath = GetString(paddock, "SoilName"); if (soilNode != null) { // See if there is a 'SWUnits' value. If found then copy it into // <WaterFormat> string waterFormat = XmlUtilities.Value(paddock, "WaterFormat"); if (waterFormat == string.Empty) { int sampleNumber = 0; foreach (XmlNode soilSample in XmlUtilities.ChildNodes(soilNode, "Sample")) { string swUnits = XmlUtilities.Value(soilSample, "SWUnits"); if (swUnits != string.Empty) XmlUtilities.SetValue(paddock, "WaterFormat", swUnits); // Also make sure we don't have 2 samples with the same name. string sampleName = "Sample" + (sampleNumber + 1).ToString(); XmlUtilities.SetAttribute(soilSample, "name", sampleName); sampleNumber++; } } simulation.Soil = SoilUtilities.FromXML(soilNode.OuterXml); if (simulation.Samples != null) simulation.Soil.Samples = simulation.Samples; } return simulation; }
private static void RemoveNullFieldsFromSample(Sample sample) { if (sample.CL == null) sample.CL = new double[0]; if (sample.EC == null) sample.EC = new double[0]; if (sample.ESP == null) sample.ESP = new double[0]; if (sample.NH4 == null) sample.NH4 = new double[0]; if (sample.NO3 == null) sample.NO3 = new double[0]; if (sample.OC == null) sample.OC = new double[0]; if (sample.PH == null) sample.PH = new double[0]; if (sample.SW == null) sample.SW = new double[0]; }
/// <summary>Checks the sample for missing values.</summary> /// <param name="sample">The sample.</param> /// <param name="soil">The soil.</param> private static void CheckSampleForMissingValues(Sample sample, Soil soil) { if (!MathUtilities.ValuesInArray(sample.SW)) sample.SW = null; if (!MathUtilities.ValuesInArray(sample.NO3)) sample.NO3 = null; if (!MathUtilities.ValuesInArray(sample.CL)) sample.CL = null; if (!MathUtilities.ValuesInArray(sample.EC)) sample.EC = null; if (!MathUtilities.ValuesInArray(sample.ESP)) sample.ESP = null; if (!MathUtilities.ValuesInArray(sample.PH)) sample.PH = null; if (!MathUtilities.ValuesInArray(sample.OC)) sample.OC = null; if (sample.SW != null) sample.SW = MathUtilities.FixArrayLength(sample.SW, sample.Thickness.Length); if (sample.NO3 != null) sample.NO3 = MathUtilities.FixArrayLength(sample.NO3, sample.Thickness.Length); if (sample.NH4 != null) sample.NH4 = MathUtilities.FixArrayLength(sample.NH4, sample.Thickness.Length); if (sample.CL != null) sample.CL = MathUtilities.FixArrayLength(sample.CL, sample.Thickness.Length); if (sample.EC != null) sample.EC = MathUtilities.FixArrayLength(sample.EC, sample.Thickness.Length); if (sample.ESP != null) sample.ESP = MathUtilities.FixArrayLength(sample.ESP, sample.Thickness.Length); if (sample.PH != null) sample.PH = MathUtilities.FixArrayLength(sample.PH, sample.Thickness.Length); if (sample.OC != null) sample.OC = MathUtilities.FixArrayLength(sample.OC, sample.Thickness.Length); double[] ll15 = LayerStructure.LL15Mapped(soil, sample.Thickness); for (int i = 0; i < sample.Thickness.Length; i++) { if (sample.SW != null && double.IsNaN(sample.SW[i])) sample.SW[i] = ll15[i]; if (sample.NO3 != null && double.IsNaN(sample.NO3[i])) sample.NO3[i] = 1.0; if (sample.NH4 != null && double.IsNaN(sample.NH4[i])) sample.NH4[i] = 0.1; if (sample.CL != null && double.IsNaN(sample.CL[i])) sample.CL[i] = 0; if (sample.EC != null && double.IsNaN(sample.EC[i])) sample.EC[i] = 0; if (sample.ESP != null && double.IsNaN(sample.ESP[i])) sample.ESP[i] = 0; if (sample.PH != null && (double.IsNaN(sample.PH[i]) || sample.PH[i] == 0.0)) sample.PH[i] = 7.0; if (sample.OC != null && (double.IsNaN(sample.OC[i]) || sample.OC[i] == 0.0)) sample.OC[i] = 0.5; } }
/// <summary>Calculates volumetric soil water for the given sample.</summary> /// <param name="sample">The sample.</param> /// <param name="soil">The soil.</param> /// <returns>Volumetric water (mm/mm)</returns> private static double[] SWVolumetric(Sample sample, Soil soil) { if (sample.SWUnits == Sample.SWUnitsEnum.Volumetric || sample.SW == null) return sample.SW; else { // convert the numbers if (sample.SWUnits == Sample.SWUnitsEnum.Gravimetric) { double[] bd = LayerStructure.BDMapped(soil, sample.Thickness); return MathUtilities.Multiply(sample.SW, bd); } else return MathUtilities.Divide(sample.SW, sample.Thickness); // from mm to mm/mm } }
/// <summary>Checks the soil sample.</summary> /// <param name="parentSoil">The parent soil.</param> /// <param name="sample">The sample.</param> private static void CheckSample(Soil parentSoil, Sample sample) { // Do some checking of NO3 / NH4 CheckMissingValuesAreNaN(sample.NO3); CheckMissingValuesAreNaN(sample.NH4); CheckMissingValuesAreNaN(sample.OC); CheckMissingValuesAreNaN(sample.EC); CheckMissingValuesAreNaN(sample.PH); if (sample.NO3 != null) sample.NO3 = FixArrayLength(sample.NO3, sample.Thickness.Length); if (sample.NH4 != null) sample.NH4 = FixArrayLength(sample.NH4, sample.Thickness.Length); // NH4 can be null so give default values if that is the case. for (int i = 0; i < sample.NH4.Length; i++) if (double.IsNaN(sample.NH4[i])) sample.NH4[i] = 0.1; sample.OCUnits = SoilOrganicMatter.OCUnitsEnum.WalkleyBlack; if (sample.OC != null) sample.OC = FixArrayLength(sample.OC, sample.Thickness.Length); if (sample.EC != null) sample.EC = FixArrayLength(sample.EC, sample.Thickness.Length); if (sample.PH != null) sample.PH = FixArrayLength(sample.PH, sample.Thickness.Length); }