private void SetupStartingPasturePools(double startingGrowth) { // Initial biomass double amountToAdd = Area * startingGrowth; if (amountToAdd <= 0) { return; } // Set up pasture pools to start run based on month and user defined pasture properties // Locates the previous five months where growth occurred (Nov-Mar) and applies decomposition to current month // This months growth will not be included. int month = Clock.Today.Month; int monthCount = 0; int includedMonthCount = 0; double propBiomass = 1.0; double currentN = LinkedNativeFoodType.GreenNitrogen; // NABSA changes N by 0.8 for particular months. Not needed here as decay included. double currentDMD = currentN * LinkedNativeFoodType.NToDMDCoefficient + LinkedNativeFoodType.NToDMDIntercept; currentDMD = Math.Max(LinkedNativeFoodType.MinimumDMD, currentDMD); LinkedNativeFoodType.Pools.Clear(); List <GrazeFoodStorePool> newPools = new List <GrazeFoodStorePool>(); while (includedMonthCount < 5) { if (month == 0) { month = 12; } if (month <= 3 | month >= 11) { // add new pool newPools.Add(new GrazeFoodStorePool() { Age = monthCount, Nitrogen = currentN, DMD = currentDMD, StartingAmount = propBiomass }); includedMonthCount++; } propBiomass *= 1 - LinkedNativeFoodType.DetachRate; currentN -= LinkedNativeFoodType.DecayNitrogen; currentN = Math.Max(currentN, LinkedNativeFoodType.MinimumNitrogen); currentDMD *= 1 - LinkedNativeFoodType.DecayDMD; currentDMD = Math.Max(currentDMD, LinkedNativeFoodType.MinimumDMD); monthCount++; month--; } // assign pasture biomass to pools based on proportion of total double total = newPools.Sum(a => a.StartingAmount); foreach (var pool in newPools) { pool.Set(amountToAdd * (pool.StartingAmount / total)); pool.Growth = amountToAdd * (pool.StartingAmount / total); } // Previously: remove this months growth from pool age 0 to keep biomass at approximately setup. // But as updates happen at the end of the month, the fist months biomass is never added so stay with 0 or delete following section // Get this months growth // Get this months pasture data from the pasture data list if (PastureDataList != null) { PastureDataType pasturedata = PastureDataList.Where(a => a.Year == Clock.StartDate.Year && a.Month == Clock.StartDate.Month).FirstOrDefault(); double thisMonthsGrowth = pasturedata.Growth * Area; if (thisMonthsGrowth > 0) { GrazeFoodStorePool thisMonth = newPools.Where(a => a.Age == 0).FirstOrDefault() as GrazeFoodStorePool; if (thisMonth != null) { thisMonth.Set(Math.Max(0, thisMonth.Amount - thisMonthsGrowth)); } } } // Add to pasture. This will add pool to pasture available store. foreach (var pool in newPools) { string reason = "Initialise"; if (newPools.Count() > 1) { reason = "Initialise pool " + pool.Age.ToString(); } LinkedNativeFoodType.Add(pool, this, reason); } }
private void OnCLEMInitialiseResource(object sender, EventArgs e) { // activity is performed in CLEMUpdatePasture not CLEMGetResources and has no labour this.AllocationStyle = ResourceAllocationStyle.Manual; // locate Land Type resource for this forage. LinkedLandItem = Resources.FindResourceType <Land, LandType>(this, LandTypeNameToUse, OnMissingResourceActionTypes.ReportErrorAndStop, OnMissingResourceActionTypes.ReportErrorAndStop); LandConditionIndex = FindAllDescendants <RelationshipRunningValue>().Where(a => (new string[] { "lc", "landcondition", "landcon", "landconditionindex" }).Contains(a.Name.ToLower())).FirstOrDefault() as RelationshipRunningValue; GrassBasalArea = FindAllDescendants <RelationshipRunningValue>().Where(a => (new string[] { "gba", "basalarea", "grassbasalarea" }).Contains(a.Name.ToLower())).FirstOrDefault() as RelationshipRunningValue; filePasture = zoneCLEM.Parent.FindAllDescendants().Where(a => a.Name == PastureDataReader).FirstOrDefault() as IFilePasture; if (LandConditionIndex is null || GrassBasalArea is null || filePasture is null) { return; } LandType land = null; if (filePasture != null) { // check that database has region id and land id ZoneCLEM clem = FindAncestor <ZoneCLEM>(); int recs = filePasture.RecordsFound((filePasture as FileSQLitePasture).RegionColumnName, clem.ClimateRegion); if (recs == 0) { throw new ApsimXException(this, $"No pasture production records were located by [x={(filePasture as Model).Name}] for [a={this.Name}] given [Region id] = [{clem.ClimateRegion}] as specified in [{clem.Name}]"); } land = Resources.FindResourceType <Land, LandType>(this, LandTypeNameToUse, OnMissingResourceActionTypes.ReportErrorAndStop, OnMissingResourceActionTypes.ReportErrorAndStop); if (land != null) { recs = filePasture.RecordsFound((filePasture as FileSQLitePasture).LandIdColumnName, land.SoilType); if (recs == 0) { throw new ApsimXException(this, $"No pasture production records were located by [x={(filePasture as Model).Name}] for [a={this.Name}] given [Land id] = [{land.SoilType}] as specified in [{land.Name}] used to manage the pasture"); } } } if (UseAreaAvailable) { LinkedLandItem.TransactionOccurred += LinkedLandItem_TransactionOccurred; } ResourceRequestList = new List <ResourceRequest> { new ResourceRequest() { Resource = land, AllowTransmutation = false, Required = UseAreaAvailable ? LinkedLandItem.AreaAvailable : AreaRequested, ResourceType = typeof(Land), ResourceTypeName = LandTypeNameToUse.Split('.').Last(), ActivityModel = this, Category = TransactionCategory, FilterDetails = null } }; CheckResources(ResourceRequestList, Guid.NewGuid()); gotLandRequested = TakeResources(ResourceRequestList, false); //Now the Land has been allocated we have an Area if (gotLandRequested) { //get the units of area for this run from the Land resource parent. unitsOfArea2Ha = Resources.FindResourceGroup <Land>().UnitsOfAreaToHaConversion; // locate Pasture Type resource LinkedNativeFoodType = Resources.FindResourceType <GrazeFoodStore, GrazeFoodStoreType>(this, FeedTypeName, OnMissingResourceActionTypes.ReportErrorAndStop, OnMissingResourceActionTypes.ReportErrorAndStop); //Assign the area actually got after taking it. It might be less than AreaRequested (if partial) Area = ResourceRequestList.FirstOrDefault().Provided; // ensure no other activity has set the area of this GrazeFoodStore LinkedNativeFoodType.Manager = this as IPastureManager; soilIndex = ((LandType)ResourceRequestList.FirstOrDefault().Resource).SoilType; if (!(LandConditionIndex is null)) { LinkedNativeFoodType.CurrentEcologicalIndicators.LandConditionIndex = LandConditionIndex.StartingValue; } if (!(GrassBasalArea is null)) { LinkedNativeFoodType.CurrentEcologicalIndicators.GrassBasalArea = GrassBasalArea.StartingValue; } LinkedNativeFoodType.CurrentEcologicalIndicators.StockingRate = StartingStockingRate; stockingRateSummed = StartingStockingRate; //Now we have a stocking rate and we have starting values for Land Condition and Grass Basal Area //get the starting pasture data list from Pasture reader if (filePasture != null & LinkedNativeFoodType != null) { GetPastureDataList_TodayToNextEcolCalculation(); double firstMonthsGrowth = 0; if (pastureDataList != null) { PastureDataType pasturedata = pastureDataList.Where(a => a.Year == clock.StartDate.Year && a.Month == clock.StartDate.Month).FirstOrDefault(); firstMonthsGrowth = pasturedata.Growth; } LinkedNativeFoodType.SetupStartingPasturePools(Area * unitsOfArea2Ha, firstMonthsGrowth); } } }