/// <summary>Add in reset management events to the APSIM spec for the specified paddock.</summary> /// <param name="paddock">The paddock.</param> /// <param name="simulation">The simulation to add the management operations to.</param> /// <exception cref="System.Exception">Cannot find soil water reset date</exception> /// <exception cref="Exception">Cannot find soil water reset date</exception> private static void AddResetDatesToManagement(Paddock paddock, APSIMSpecification simulation) { // Reset if (paddock.SoilWaterSampleDate != DateTime.MinValue) { if (paddock.SoilNitrogenSampleDate == DateTime.MinValue) { paddock.SoilNitrogenSampleDate = paddock.SoilWaterSampleDate; } Sow sowing = YieldProphetUtility.GetCropBeingSown(paddock.Management); if (sowing != null && sowing.Date != DateTime.MinValue) { List <Management> resetActions = new List <Management>(); // reset at sowing if the sample dates are after sowing. if (paddock.SoilWaterSampleDate > sowing.Date) { resetActions.Add(new ResetWater() { Date = sowing.Date }); resetActions.Add(new ResetSurfaceOrganicMatter() { Date = sowing.Date }); } if (paddock.SoilNitrogenSampleDate > sowing.Date) { resetActions.Add(new ResetNitrogen() { Date = sowing.Date }); } // reset on the sample dates. resetActions.Add(new ResetWater() { Date = paddock.SoilWaterSampleDate }); resetActions.Add(new ResetSurfaceOrganicMatter() { Date = paddock.SoilWaterSampleDate }); resetActions.Add(new ResetNitrogen() { Date = paddock.SoilNitrogenSampleDate }); simulation.Management.InsertRange(0, resetActions); } } }
/// <summary>Checks the soil sample.</summary> /// <param name="parentSoil">The parent soil.</param> /// <param name="sample">The sample.</param> /// <param name="sowing">The sowing rule</param> private static void CheckSample(Soil parentSoil, Sample sample, Sow sowing) { // Do some checking of NO3 / NH4 CheckMissingValuesAreNaN(sample.NO3); CheckMissingValuesAreNaN(sample.NH4); CheckMissingValuesAreNaN(sample.OC); CheckMissingValuesAreNaN(sample.EC); CheckMissingValuesAreNaN(sample.PH); if (sample.SW != null && sample.SW.Length > 0) { // Check to make sure SW is >= CLL for layers below top layer int cropIndex = parentSoil.Water.Crops.FindIndex(crop => crop.Name.Equals(sowing.Crop, StringComparison.CurrentCultureIgnoreCase)); if (cropIndex != -1) { double[] CLLVol = parentSoil.Water.Crops[cropIndex].LL; double[] CLLVolMapped = LayerStructure.MapConcentration(CLLVol, parentSoil.Water.Thickness, sample.Thickness, 0); if (sample.SWUnits == Sample.SWUnitsEnum.Gravimetric) { // Sample SW is in gravimetric double[] BDMapped = LayerStructure.MapConcentration(parentSoil.Water.BD, parentSoil.Water.Thickness, sample.Thickness, parentSoil.Water.BD.Last()); double[] CLLGrav = MathUtilities.Divide(CLLVolMapped, BDMapped); // vol to grav double[] SWVol = MathUtilities.Multiply(sample.SW, BDMapped); // grav to vol for (int i = 1; i < sample.Thickness.Length; i++) { if (SWVol[i] < CLLVolMapped[i]) { sample.SW[i] = CLLGrav[i]; } } } else { // Sample SW is in volumetric for (int i = 1; i < sample.Thickness.Length; i++) { if (sample.SW[i] < CLLVolMapped[i]) { sample.SW[i] = CLLVolMapped[i]; } } } } } }
/// <summary>Do all soil related settings.</summary> /// <param name="simulation">The specification to use</param> /// <param name="workingFolder">The folder where files shoud be created.</param> private static void DoSoil(APSIMSpecification simulation, string workingFolder) { Soil soil; if (simulation.Soil == null) { if (simulation.SoilPath.StartsWith("<Soil")) { // Soil and Landscape grid XmlDocument doc = new XmlDocument(); doc.LoadXml(simulation.SoilPath); soil = XmlUtilities.Deserialise(doc.DocumentElement, typeof(Soil)) as Soil; } else if (simulation.SoilPath.StartsWith("http")) { // Soil and Landscape grid string xml; using (var client = new WebClient()) { xml = client.DownloadString(simulation.SoilPath); } XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); List <XmlNode> soils = XmlUtilities.ChildNodes(doc.DocumentElement, "Soil"); if (soils.Count == 0) { throw new Exception("Cannot find soil in Soil and Landscape Grid"); } soil = XmlUtilities.Deserialise(soils[0], typeof(Soil)) as Soil; } else { // Apsoil web service. APSOIL.Service apsoilService = new APSOIL.Service(); string soilXml = apsoilService.SoilXML(simulation.SoilPath.Replace("\r\n", "")); if (soilXml == string.Empty) { throw new Exception("Cannot find soil: " + simulation.SoilPath); } soil = SoilUtilities.FromXML(soilXml); } } else { // Just use the soil we already have soil = simulation.Soil; } // Make sure we have a soil crop parameterisation. If not then try creating one // based on wheat. Sow sowing = YieldProphetUtility.GetCropBeingSown(simulation.Management); string[] cropNames = soil.Water.Crops.Select(c => c.Name).ToArray(); if (cropNames.Length == 0) { throw new Exception("Cannot find any crop parameterisations in soil: " + simulation.SoilPath); } if (sowing != null && !StringUtilities.Contains(cropNames, sowing.Crop)) { SoilCrop wheat = soil.Water.Crops.Find(c => c.Name.Equals("wheat", StringComparison.InvariantCultureIgnoreCase)); if (wheat == null) { // Use the first crop instead. wheat = soil.Water.Crops[0]; } SoilCrop newSoilCrop = new SoilCrop(); newSoilCrop.Name = sowing.Crop; newSoilCrop.Thickness = wheat.Thickness; newSoilCrop.LL = wheat.LL; newSoilCrop.KL = wheat.KL; newSoilCrop.XF = wheat.XF; soil.Water.Crops.Add(newSoilCrop); } // Remove any initwater nodes. soil.InitialWater = null; // Transfer the simulation samples to the soil if (simulation.Samples != null) { soil.Samples = simulation.Samples; } if (simulation.InitTotalWater != 0) { soil.InitialWater = new InitialWater(); soil.InitialWater.PercentMethod = InitialWater.PercentMethodEnum.FilledFromTop; double pawc; if (sowing == null || sowing.Crop == null) { pawc = MathUtilities.Sum(PAWC.OfSoilmm(soil)); soil.InitialWater.RelativeTo = "LL15"; } else { SoilCrop crop = soil.Water.Crops.Find(c => c.Name.Equals(sowing.Crop, StringComparison.InvariantCultureIgnoreCase)); pawc = MathUtilities.Sum(PAWC.OfCropmm(soil, crop)); soil.InitialWater.RelativeTo = crop.Name; } soil.InitialWater.FractionFull = Convert.ToDouble(simulation.InitTotalWater) / pawc; } if (simulation.InitTotalNitrogen != 0) { // Add in a sample. Sample nitrogenSample = new Sample(); nitrogenSample.Name = "NitrogenSample"; soil.Samples.Add(nitrogenSample); nitrogenSample.Thickness = new double[] { 150, 150, 3000 }; nitrogenSample.NO3Units = Nitrogen.NUnitsEnum.kgha; nitrogenSample.NH4Units = Nitrogen.NUnitsEnum.kgha; nitrogenSample.NO3 = new double[] { 6.0, 2.1, 0.1 }; nitrogenSample.NH4 = new double[] { 0.5, 0.1, 0.1 }; nitrogenSample.OC = new double[] { double.NaN, double.NaN, double.NaN }; nitrogenSample.EC = new double[] { double.NaN, double.NaN, double.NaN }; nitrogenSample.PH = new double[] { double.NaN, double.NaN, double.NaN }; double Scale = Convert.ToDouble(simulation.InitTotalNitrogen) / MathUtilities.Sum(nitrogenSample.NO3); nitrogenSample.NO3 = MathUtilities.Multiply_Value(nitrogenSample.NO3, Scale); } // Add in soil temperature. Needed for Aflatoxin risk. soil.SoilTemperature = new SoilTemperature(); soil.SoilTemperature.BoundaryLayerConductance = 15; soil.SoilTemperature.Thickness = new double[] { 2000 }; soil.SoilTemperature.InitialSoilTemperature = new double[] { 22 }; if (soil.Analysis.ParticleSizeClay == null) { soil.Analysis.ParticleSizeClay = MathUtilities.CreateArrayOfValues(60, soil.Analysis.Thickness.Length); } InFillMissingValues(soil.Analysis.ParticleSizeClay); foreach (Sample sample in soil.Samples) { CheckSample(soil, sample, sowing); } Defaults.FillInMissingValues(soil); // Correct the CONA / U parameters depending on LAT/LONG CorrectCONAU(simulation, soil, workingFolder); // get rid of <soiltype> from the soil // this is necessary because NPD uses this field and puts in really long // descriptive classifications. Soiln2 bombs with an FString internal error. soil.SoilType = null; // Set the soil name to 'soil' soil.Name = "Soil"; simulation.Soil = soil; }
/// <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; } // Give the paddock a name. string fullName = string.Format("{0}^{1}^{2}", GetDate(paddock, "SowDateFull").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. string rainfallSource = GetString(paddock, "RainfallSource"); if (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); simulation.ObservedData.TableName = rainfallSource; 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"); } // 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"); } // 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; } } // 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 { Name = "Sample1", Thickness = GetArray(paddock, "Sample1Thickness"), 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 { 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); }
/// <summary>Write a sowing operation to the specified xml.</summary> /// <param name="sowing">The sowing.</param> /// <returns></returns> /// <exception cref="Exception"> /// Maximum root depth should be specified in mm, not cm /// or /// Invalid bed width found: + sowing.BedWidth.ToString() /// </exception> public string AddSowingOperation(Sow sowing, bool useEC) { string operationsXML = string.Empty; if (sowing.Date != DateTime.MinValue) { XmlUtilities.SetValue(simulationXML, "Paddock/Management/ui/CropName", sowing.Crop); cropBeingSown = sowing.Crop; string cropNodePath = "Paddock/" + sowing.Crop; string useECValue = "no"; if (useEC) useECValue = "yes"; XmlUtilities.SetValue(simulationXML, cropNodePath + "/ModifyKL", useECValue); // Maximum rooting depth. if (sowing.MaxRootDepth > 0 && sowing.MaxRootDepth < 20) throw new Exception("Maximum root depth should be specified in mm, not cm"); if (sowing.MaxRootDepth > 0) XmlUtilities.SetValue(simulationXML, cropNodePath + "/MaxRootDepth", sowing.MaxRootDepth.ToString()); // Make sure we have a row spacing. if (sowing.RowSpacing == 0) sowing.RowSpacing = 250; if (sowing.SeedDepth == 0) sowing.SeedDepth = 50; string sowAction = sowing.Crop + " sow plants = " + sowing.SowingDensity.ToString() + ", sowing_depth = " + sowing.SeedDepth + ", cultivar = " + sowing.Cultivar + ", row_spacing = " + sowing.RowSpacing.ToString() + ", crop_class = plant" + ", skip = solid"; // Allan's furrow irrigation hack. if (sowing.BedWidth > 0) { double skiprow; if (sowing.BedWidth == 1) skiprow = 0.44; else if (sowing.BedWidth == 2) skiprow = 0.2; else throw new Exception("Invalid bed width found: " + sowing.BedWidth.ToString()); sowAction += ", skiprow = " + skiprow.ToString(); } AddOperation(sowing.Date, sowAction); // Add a sowing tillage operation string tillageAction = "SurfaceOM tillage type = planter, f_incorp = 0.1, tillage_depth = 50"; AddOperation(sowing.Date, tillageAction); // see if an emergence date was specified. If so then write some operations to // specify it and the germination date. if (sowing.EmergenceDate != DateTime.MinValue) { TimeSpan FiveDays = new TimeSpan(5, 0, 0, 0); TimeSpan OneDay = new TimeSpan(1, 0, 0, 0); DateTime GerminationDate = sowing.EmergenceDate - FiveDays; if (GerminationDate <= sowing.Date) GerminationDate = sowing.Date + OneDay; if (sowing.EmergenceDate <= GerminationDate) sowing.EmergenceDate = GerminationDate + OneDay; int DaysToGermination = (GerminationDate - sowing.Date).Days; int DaysToEmergence = (sowing.EmergenceDate - sowing.Date).Days; AddOperation(sowing.Date, sowing.Crop + " set DaysToGermination = " + DaysToGermination.ToString()); AddOperation(sowing.Date, sowing.Crop + " set DaysToEmergence = " + DaysToEmergence.ToString()); } if (sowing.IrrigationAmount > 0) AddOperation(sowing.Date, "Irrigation apply amount = " + sowing.IrrigationAmount.ToString()); if (sowing.Crop == "Wheat") XmlUtilities.SetAttribute(simulationXML, "Paddock/WheatFrostHeat/enabled", "yes"); if (sowing.Crop == "Canola") XmlUtilities.SetAttribute(simulationXML, "Paddock/CanolaFrostHeat/enabled", "yes"); } return operationsXML; }
/// <summary>Write a sowing operation to the specified xml.</summary> /// <param name="sowing">The sowing.</param> /// <returns></returns> /// <exception cref="Exception"> /// Maximum root depth should be specified in mm, not cm /// or /// Invalid bed width found: + sowing.BedWidth.ToString() /// </exception> public string AddSowingOperation(Sow sowing, bool useEC) { string operationsXML = string.Empty; if (sowing.Date != DateTime.MinValue) { XmlUtilities.SetValue(simulationXML, "Paddock/Management/ui/CropName", sowing.Crop); cropBeingSown = sowing.Crop; string cropNodePath = "Paddock/" + sowing.Crop; string useECValue = "no"; if (useEC) { useECValue = "yes"; } XmlUtilities.SetValue(simulationXML, cropNodePath + "/ModifyKL", useECValue); // Maximum rooting depth. if (sowing.MaxRootDepth > 0 && sowing.MaxRootDepth < 20) { throw new Exception("Maximum root depth should be specified in mm, not cm"); } if (sowing.MaxRootDepth > 0) { XmlUtilities.SetValue(simulationXML, cropNodePath + "/MaxRootDepth", sowing.MaxRootDepth.ToString()); } // Make sure we have a row spacing. if (sowing.RowSpacing == 0) { sowing.RowSpacing = 250; } if (sowing.SeedDepth == 0) { sowing.SeedDepth = 50; } string sowAction = sowing.Crop + " sow plants = " + sowing.SowingDensity.ToString() + ", sowing_depth = " + sowing.SeedDepth + ", cultivar = " + sowing.Cultivar + ", row_spacing = " + sowing.RowSpacing.ToString() + ", crop_class = plant" + ", skip = solid"; // Allan's furrow irrigation hack. if (sowing.BedWidth > 0) { double skiprow; if (sowing.BedWidth == 1) { skiprow = 0.44; } else if (sowing.BedWidth == 2) { skiprow = 0.2; } else { throw new Exception("Invalid bed width found: " + sowing.BedWidth.ToString()); } sowAction += ", skiprow = " + skiprow.ToString(); } AddOperation(sowing.Date, sowAction, sowing.IsEveryYear); // Add a sowing tillage operation string tillageAction = "SurfaceOM tillage type = planter, f_incorp = 0.1, tillage_depth = 50"; AddOperation(sowing.Date, tillageAction, sowing.IsEveryYear); // see if an emergence date was specified. If so then write some operations to // specify it and the germination date. if (sowing.EmergenceDate != DateTime.MinValue) { TimeSpan FiveDays = new TimeSpan(5, 0, 0, 0); TimeSpan OneDay = new TimeSpan(1, 0, 0, 0); DateTime GerminationDate = sowing.EmergenceDate - FiveDays; if (GerminationDate <= sowing.Date) { GerminationDate = sowing.Date + OneDay; } if (sowing.EmergenceDate <= GerminationDate) { sowing.EmergenceDate = GerminationDate + OneDay; } int DaysToGermination = (GerminationDate - sowing.Date).Days; int DaysToEmergence = (sowing.EmergenceDate - sowing.Date).Days; AddOperation(sowing.Date, sowing.Crop + " set DaysToGermination = " + DaysToGermination.ToString(), sowing.IsEveryYear); AddOperation(sowing.Date, sowing.Crop + " set DaysToEmergence = " + DaysToEmergence.ToString(), sowing.IsEveryYear); } if (sowing.IrrigationAmount > 0) { AddOperation(sowing.Date, "Irrigation apply amount = " + sowing.IrrigationAmount.ToString(), sowing.IsEveryYear); } if (sowing.Crop == "Wheat") { XmlUtilities.SetAttribute(simulationXML, "Paddock/WheatFrostHeat/enabled", "yes"); } if (sowing.Crop == "Canola") { XmlUtilities.SetAttribute(simulationXML, "Paddock/CanolaFrostHeat/enabled", "yes"); } } return(operationsXML); }
/// <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; }
/// <summary>Creates a base APSIM simulation spec for the Yield Prophet spec.</summary> /// <param name="yieldProphet">The yield prophet specification.</param> /// <returns>The created APSIM simulation spec.</returns> private static APSIMSpecification CreateBaseSimulation(Paddock paddock) { Paddock copyOfPaddock = paddock; // XmlUtilities.Clone(paddock) as JobsService.Paddock; copyOfPaddock.ObservedData = paddock.ObservedData; APSIMSpecification shortSimulation = new APSIMSpecification(); shortSimulation.Name = "Base"; shortSimulation.WeatherFileName = shortSimulation.Name + ".met"; // Start date of simulation should be the earliest of ResetDate, SowDate and StartSeasonDate Sow sow = YieldProphetUtility.GetCropBeingSown(paddock.Management); if (sow == null) { throw new Exception("No sowing specified for paddock: " + paddock.Name); } if (sow.Date == DateTime.MinValue) { throw new Exception("No sowing DATE specified for paddock: " + paddock.Name); } if (sow.Crop == null || sow.Crop == "" || sow.Crop == "None") { throw new Exception("No sowing CROP specified for paddock: " + paddock.Name); } shortSimulation.StartDate = DateTime.MaxValue; if (paddock.SoilWaterSampleDate != DateTime.MinValue && paddock.SoilWaterSampleDate < shortSimulation.StartDate) { shortSimulation.StartDate = paddock.SoilWaterSampleDate; } if (paddock.SoilNitrogenSampleDate != DateTime.MinValue && paddock.SoilNitrogenSampleDate < shortSimulation.StartDate) { shortSimulation.StartDate = paddock.SoilNitrogenSampleDate; } if (sow != null && sow.Date < shortSimulation.StartDate && sow.Date != DateTime.MinValue) { shortSimulation.StartDate = sow.Date; } if (paddock.StartSeasonDate < shortSimulation.StartDate) { shortSimulation.StartDate = paddock.StartSeasonDate; } if (paddock.LongtermStartYear == 0) { shortSimulation.LongtermStartYear = 1957; } else { shortSimulation.LongtermStartYear = paddock.LongtermStartYear; } if (paddock.RunType == Paddock.RunTypeEnum.SingleSeason) { shortSimulation.EndDate = copyOfPaddock.NowDate.AddDays(-1); if ((shortSimulation.EndDate - shortSimulation.StartDate).Days > 360) { shortSimulation.EndDate = shortSimulation.StartDate.AddDays(360); } } else if (paddock.RunType == Paddock.RunTypeEnum.LongTermPatched) { shortSimulation.EndDate = shortSimulation.StartDate.AddDays(360); } else if (paddock.RunType == Paddock.RunTypeEnum.LongTerm) { // Sow opp reports. shortSimulation.StartDate = new DateTime(shortSimulation.LongtermStartYear, 1, 1); shortSimulation.EndDate = copyOfPaddock.NowDate.AddDays(-1); } shortSimulation.NowDate = copyOfPaddock.NowDate.AddDays(-1); if (shortSimulation.NowDate == DateTime.MinValue) { shortSimulation.NowDate = DateTime.Now; } shortSimulation.DailyOutput = paddock.DailyOutput; shortSimulation.MonthlyOutput = paddock.MonthlyOutput; shortSimulation.YearlyOutput = paddock.YearlyOutput; shortSimulation.ObservedData = copyOfPaddock.ObservedData; shortSimulation.Soil = copyOfPaddock.Soil; shortSimulation.SoilPath = copyOfPaddock.SoilPath; shortSimulation.Samples = new List <APSIM.Shared.Soils.Sample>(); shortSimulation.Samples.AddRange(copyOfPaddock.Samples); shortSimulation.InitTotalWater = copyOfPaddock.InitTotalWater; shortSimulation.InitTotalNitrogen = copyOfPaddock.InitTotalNitrogen; shortSimulation.StationNumber = copyOfPaddock.StationNumber; shortSimulation.StubbleMass = copyOfPaddock.StubbleMass; shortSimulation.StubbleType = copyOfPaddock.StubbleType; shortSimulation.Management = new List <Management>(); shortSimulation.Management.AddRange(copyOfPaddock.Management); shortSimulation.UseEC = paddock.UseEC; shortSimulation.WriteDepthFile = false; if (paddock.RunType == Paddock.RunTypeEnum.LongTermPatched) { shortSimulation.RunType = APSIMSpecification.RunTypeEnum.LongTermPatched; } else { shortSimulation.RunType = APSIMSpecification.RunTypeEnum.Normal; } shortSimulation.DecileDate = paddock.StartSeasonDate; shortSimulation.NUnlimited = paddock.NUnlimited; shortSimulation.NUnlimitedFromToday = paddock.NUnlimitedFromToday; shortSimulation.WriteDepthFile = paddock.WriteDepthFile; shortSimulation.Next10DaysDry = paddock.Next10DaysDry; AddResetDatesToManagement(copyOfPaddock, shortSimulation); // Do a stable sort on management actions. shortSimulation.Management = shortSimulation.Management.OrderBy(m => m.Date).ToList(); if (paddock.RunType == Paddock.RunTypeEnum.LongTerm) { foreach (Management man in shortSimulation.Management) { man.IsEveryYear = true; } } return(shortSimulation); }
/// <summary>Write a sowing operation to the specified xml.</summary> /// <param name="sowing">The sowing.</param> public string AddSowingOperation(Sow sowing, bool useEC) { string operationsXML = string.Empty; if (sowing.Date != DateTime.MinValue) { cropBeingSown = sowing.Crop; string cropNodePath = "Zone/" + sowing.Crop; //string useECValue = "no"; //if (useEC) // useECValue = "yes"; //XmlUtilities.SetValue(simulationXML, cropNodePath + "/ModifyKL", useECValue); // Maximum rooting depth. if (sowing.MaxRootDepth > 0 && sowing.MaxRootDepth < 20) { throw new Exception("Maximum root depth should be specified in mm, not cm"); } if (sowing.MaxRootDepth > 0) { AddOperation(sowing.Date, "[" + sowing.Crop + "].Root.MaximumRootDepth.FixedValue = " + sowing.MaxRootDepth); } // Make sure we have a row spacing. if (sowing.RowSpacing == 0) { sowing.RowSpacing = 250; } if (sowing.SeedDepth == 0) { sowing.SeedDepth = 50; } string sowAction = "[" + sowing.Crop + "].Sow(population: " + sowing.SowingDensity.ToString() + ", cultivar: " + sowing.Cultivar + ", depth: " + sowing.SeedDepth + ", rowSpacing: " + sowing.RowSpacing.ToString() + ")"; // Allan's furrow irrigation hack. //if (sowing.BedWidth > 0) //{ // double skiprow; // if (sowing.BedWidth == 1) // skiprow = 0.44; // else if (sowing.BedWidth == 2) // skiprow = 0.2; // else // throw new Exception("Invalid bed width found: " + sowing.BedWidth.ToString()); // sowAction += ", skiprow = " + skiprow.ToString(); //} AddOperation(sowing.Date, sowAction); // Add a sowing tillage operation string tillageAction = "[SurfaceOrganicMatter].Incorporate(fraction: 0.1, depth: 50)"; AddOperation(sowing.Date, tillageAction); // see if an emergence date was specified. If so then write some operations to // specify it and the germination date. if (sowing.EmergenceDate != DateTime.MinValue) { DateTime GerminationDate = sowing.EmergenceDate.AddDays(-5); if (GerminationDate <= sowing.Date) { GerminationDate = sowing.Date.AddDays(1); } if (sowing.EmergenceDate <= GerminationDate) { sowing.EmergenceDate = GerminationDate.AddDays(1); } int DaysToGermination = (GerminationDate - sowing.Date).Days; int DaysToEmergence = (sowing.EmergenceDate - sowing.Date).Days; AddOperation(sowing.Date, "[" + sowing.Crop + "].Phenology.Germinating.DaysFromSowingToEndPhase = " + DaysToGermination); AddOperation(sowing.Date, "[" + sowing.Crop + "].Phenology.Emerging.DaysFromSowingToEndPhase = " + DaysToEmergence); } if (sowing.IrrigationAmount > 0) { AddOperation(sowing.Date, "[Irrigation].Apply(amount: " + sowing.IrrigationAmount.ToString() + ")"); } // Add a crop node. XmlNode cropNode = XmlUtilities.EnsureNodeExists(simulationXML, "Zone/Plant"); XmlUtilities.SetValue(cropNode, "ResourceName", sowing.Crop); XmlUtilities.SetValue(cropNode, "Name", sowing.Crop); XmlUtilities.SetValue(cropNode, "CropType", sowing.Crop); XmlUtilities.SetValue(simulationXML, "Zone/Manager/Script/CropName", sowing.Crop); //if (sowing.Crop == "Wheat") // XmlUtilities.SetAttribute(simulationXML, "Paddock/WheatFrostHeat/enabled", "yes"); //if (sowing.Crop == "Canola") // XmlUtilities.SetAttribute(simulationXML, "Paddock/CanolaFrostHeat/enabled", "yes"); } return(operationsXML); }