/// <summary>Create all necessary YP files (.apsim and .met) from a YieldProphet spec.</summary> /// <param name="simulation">The simulation to write.</param> /// <param name="workingFolder">The folder where files shoud be created.</param> /// <param name="fileNameToWrite">The name of a file to write.</param> /// <returns>The name of the created .apsim file.</returns> public static string Create(APSIMSpecification simulation, string workingFolder, string fileNameToWrite) { return(Create(new List <APSIMSpecification>() { simulation }, workingFolder, fileNameToWrite)); }
/// <summary> /// Correct the CONA / U parameters depending on LAT/LONG /// </summary> /// <param name="simulation"></param> /// <param name="soil"></param> /// <param name="workingFolder">The folder where files shoud be created.</param> private static void CorrectCONAU(APSIMSpecification simulation, Soil soil, string workingFolder) { // Get lat/long from weather file. ApsimTextFile weather = new ApsimTextFile(); try { weather.Open(Path.Combine(workingFolder, simulation.WeatherFileName)); double latitude = Convert.ToDouble(weather.Constant("Latitude").Value); double longitude = Convert.ToDouble(weather.Constant("Longitude").Value); // Dubbo latitude = -32.24 // NSW western border longitude = 141.0 if (longitude >= 141.0 && latitude > -32.24) { // Northern values. soil.SoilWater.SummerU = 6.0; soil.SoilWater.SummerCona = 3.5; soil.SoilWater.WinterU = 4.0; soil.SoilWater.WinterCona = 2.5; } else { // Southern values. soil.SoilWater.SummerU = 6.0; soil.SoilWater.SummerCona = 3.5; soil.SoilWater.WinterU = 2.0; soil.SoilWater.WinterCona = 2.0; } } finally { weather.Close(); } }
/// <summary>Create a .apsim file node for the job and append it to the parentNode</summary> /// <param name="simulation">The specification to use</param> /// <param name="workingFolder">The folder where files shoud be created.</param> /// <param name="parentNode">Parent XmlNode to append the simulation to.</param> /// <param name="usingAPSIMx">Write APSIMx files?</param> private static void CreateApsimFile(APSIMSpecification simulation, string workingFolder, XmlNode parentNode, bool usingAPSIMx) { if (simulation.ErrorMessage == null) { XmlNode simulationXML = CreateSimulationXML(simulation, workingFolder, usingAPSIMx); if (simulationXML != null) { parentNode.AppendChild(parentNode.OwnerDocument.ImportNode(simulationXML, true)); } } }
/// <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>Converts a Yield Prophet specification to an APSIM one.</summary> /// <param name="yieldProphet">The yield prophet spec.</param> /// <returns>The list of APSIM simulations that will need to be run.</returns> public static List <APSIMSpecification> ToAPSIM(YieldProphet yieldProphet) { List <APSIMSpecification> apsimSpecs = new List <APSIMSpecification>(); foreach (Paddock paddock in yieldProphet.Paddock) { APSIMSpecification simulation; try { simulation = CreateBaseSimulation(paddock); } catch (Exception err) { simulation = new APSIMSpecification(); simulation.ErrorMessage = err.Message; } simulation.Name = paddock.Name; apsimSpecs.Add(simulation); } return(apsimSpecs); }
/// <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> /// Create a one year APSIM simulation for the specified yield prophet specification /// and paddock /// </summary> /// <param name="simulation">The specification to use</param> /// <param name="workingFolder">The folder where files shoud be created.</param> /// <param name="usingAPSIMx">Write APSIMx files?</param> /// <returns>The XML node of the APSIM simulation.</returns> private static XmlNode CreateSimulationXML(APSIMSpecification simulation, string workingFolder, bool usingAPSIMx) { IAPSIMFileWriter apsimWriter; if (usingAPSIMx) { apsimWriter = new APSIMxFileWriter(); } else { apsimWriter = new APSIMFileWriter(); } // Name the paddock. apsimWriter.NameSimulation(simulation.Name); // Set the clock start and end dates. apsimWriter.SetStartEndDate(simulation.StartDate, simulation.EndDate); // Set the report date. apsimWriter.SetReportDate(simulation.NowDate); // Set the weather file apsimWriter.SetWeatherFile(simulation.WeatherFileName); // Set the stubble apsimWriter.SetStubble(simulation.StubbleType, simulation.StubbleMass, YieldProphetUtility.GetStubbleCNRatio(simulation.StubbleType)); // Set NUnlimited if (simulation.NUnlimited) { apsimWriter.SetNUnlimited(); } // Set NUnlimited from today if (simulation.NUnlimitedFromToday) { apsimWriter.SetNUnlimitedFromToday(); } if (simulation.WriteDepthFile) { apsimWriter.WriteDepthFile(); } if (simulation.Next10DaysDry) { apsimWriter.Next10DaysDry(); } apsimWriter.SetErosion(simulation.Slope, simulation.SlopeLength); // Do soil stuff. DoSoil(simulation, workingFolder); apsimWriter.SetSoil(simulation.Soil); // Loop through all management actions and create an operations list foreach (Management management in simulation.Management) { if (management is Sow) { apsimWriter.AddSowingOperation(management as Sow, simulation.UseEC); } else if (management is Fertilise) { apsimWriter.AddFertilseOperation(management as Fertilise); } else if (management is Irrigate) { apsimWriter.AddIrrigateOperation(management as Irrigate); } else if (management is Tillage) { apsimWriter.AddTillageOperation(management as Tillage); } else if (management is StubbleRemoved) { apsimWriter.AddStubbleRemovedOperation(management as StubbleRemoved); } else if (management is ResetWater) { apsimWriter.AddResetWaterOperation(management as ResetWater); } else if (management is ResetNitrogen) { apsimWriter.AddResetNitrogenOperation(management as ResetNitrogen); } else if (management is ResetSurfaceOrganicMatter) { apsimWriter.AddSurfaceOrganicMatterOperation(management as ResetSurfaceOrganicMatter); } } // Set Daily output if (simulation.DailyOutput) { apsimWriter.SetDailyOutput(); } // Set Monthly output if (simulation.MonthlyOutput) { apsimWriter.SetMonthlyOutput(); } // Set Yearly output if (simulation.YearlyOutput) { apsimWriter.SetYearlyOutput(); } return(apsimWriter.ToXML()); }
/// <summary>Creates the weather files for all simulations.</summary> /// <param name="simulations">The simulations.</param> /// <param name="workingFolder">The working folder to create the files in.</param> /// <param name="allSimulationsAreSingleSeason">All simulations are short season?</param> private static void CreateWeatherFilesForSimulations(APSIMSpecification simulation, string workingFolder, WeatherFileCache weatherCache, bool allSimulationsAreSingleSeason) { if (simulation.ErrorMessage == null) { string rainFileName = Path.Combine(workingFolder, simulation.Name + ".met"); string[] filesCreated = null; // Make sure the observed data has a codes column. if (simulation.ObservedData != null) { Weather.AddCodesColumn(simulation.ObservedData, 'O'); } if (simulation.RunType == APSIMSpecification.RunTypeEnum.LongTermPatched) { // long term. DateTime longTermStartDate = new DateTime(simulation.LongtermStartYear, 1, 1); int numYears = simulation.StartDate.Year - longTermStartDate.Year + 1; // Check to see if in cache. filesCreated = weatherCache.GetWeatherFiles(simulation.StationNumber, simulation.StartDate, simulation.NowDate, simulation.ObservedData.TableName, numYears); if (filesCreated == null) { // Create a long term weather file. filesCreated = Weather.CreateLongTerm(rainFileName, simulation.StationNumber, simulation.StartDate, simulation.NowDate, simulation.ObservedData, simulation.DecileDate, numYears); weatherCache.AddWeatherFiles(simulation.StationNumber, simulation.StartDate, simulation.NowDate, simulation.ObservedData.TableName, numYears, filesCreated); } } else { // short term. // Create a short term weather file. Weather.Data weatherFile = Weather.ExtractDataFromSILO(simulation.StationNumber, simulation.StartDate, simulation.EndDate); Weather.OverlayData(simulation.ObservedData, weatherFile.Table); Weather.WriteWeatherFile(weatherFile.Table, rainFileName, weatherFile.Latitude, weatherFile.Longitude, weatherFile.TAV, weatherFile.AMP); filesCreated = new string[] { rainFileName }; } if (filesCreated.Length > 0) { simulation.WeatherFileName = Path.GetFileName(filesCreated[0]); // Set the simulation end date to the end date of the weather file. This will avoid // problems where SILO hasn't been updated for a while. ApsimTextFile weatherFile = new ApsimTextFile(); try { weatherFile.Open(filesCreated[0]); simulation.EndDate = weatherFile.LastDate; } finally { weatherFile.Close(); } } if (!allSimulationsAreSingleSeason) { APSIMSpecification.Factor factor = new APSIMSpecification.Factor(); factor.Name = "Met"; factor.ComponentPath = "/Simulations/" + simulation.Name + "/Met"; factor.ComponentVariableName = "filename"; factor.ComponentVariableValues = filesCreated; if (simulation.Factors == null) { simulation.Factors = new List <APSIMSpecification.Factor>(); } simulation.Factors.Add(factor); } } }
/// <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); }