private void OnCLEMAnimalManage(object sender, EventArgs e) { // if management month if (this.TimingOK) { double labourlimit = this.LabourLimitProportion; int weanedCount = 0; ResourceRequest labour = ResourceRequestList.Where(a => a.ResourceType == typeof(LabourType)).FirstOrDefault <ResourceRequest>(); // Perform weaning int count = this.CurrentHerd(true).Where(a => a.Weaned == false).Count(); foreach (var ind in this.CurrentHerd(true).Where(a => a.Weaned == false)) { if (ind.Age >= WeaningAge || ind.Weight >= WeaningWeight) { string reason = (ind.Age >= WeaningAge)? "Age" : "Weight"; ind.Wean(true, reason); ind.Location = grazeStore; weanedCount++; Status = ActivityStatus.Success; } // stop if labour limited individuals reached and LabourShortfallAffectsActivity if (weanedCount > Convert.ToInt32(count * labourlimit)) { break; } } } }
private void OnCLEMCollectManure(object sender, EventArgs e) { Status = ActivityStatus.Critical; // is manure in resources if (manureStore != null) { if (this.TimingOK) { List <ResourceRequest> resourcesneeded = GetResourcesNeededForActivityLocal(); bool tookRequestedResources = TakeResources(resourcesneeded, true); // get all shortfalls double labourNeeded = 0; double labourLimit = 1; if (tookRequestedResources & (ResourceRequestList != null)) { labourNeeded = ResourceRequestList.Where(a => a.ResourceType == typeof(Labour)).Sum(a => a.Required); double labourProvided = ResourceRequestList.Where(a => a.ResourceType == typeof(Labour)).Sum(a => a.Provided); labourLimit = labourProvided / labourNeeded; } if (labourLimit == 1 || this.OnPartialResourcesAvailableAction == OnPartialResourcesAvailableActionTypes.UseResourcesAvailable) { foreach (ManureStoreUncollected msu in manureStore.UncollectedStores) { manureStore.Collect(msu.Name, labourLimit, this.Name); SetStatusSuccess(); } } } } }
/// <summary> /// The method allows the activity to adjust resources requested based on shortfalls (e.g. labour) before they are taken from the pools /// </summary> public override void AdjustResourcesNeededForActivity() { // adjust amount needed by labour shortfall. double labprop = this.LabourLimitProportion; // get additional reduction based on labour cost shortfall as cost has already been accounted for double priceprop = 1; if (labprop < 1) { if (unitsCanAfford < units) { priceprop = unitsCanAfford / units; } if (labprop < priceprop) { unitsCanAfford = units * labprop; if (price.UseWholePackets) { unitsCanAfford = Math.Truncate(unitsCanAfford); } } } if (unitsCanAfford > 0 & (resourceToBuy as CLEMResourceTypeBase).MarketStoreExists) { // find resource entry in market if present and reduce ResourceRequest rr = ResourceRequestList.Where(a => a.Resource == (resourceToBuy as CLEMResourceTypeBase).EquivalentMarketStore).FirstOrDefault(); if (rr.Required != unitsCanAfford * price.PacketSize * this.FarmMultiplier) { rr.Required = unitsCanAfford * price.PacketSize * this.FarmMultiplier; } } return; }
/// <summary> /// Method to provide the proportional limit based on specified resource type /// A proportion less than 1 will only be returned if LabourShortfallAffectsActivity is true in the LabourRequirement /// </summary> /// <returns></returns> public double LimitProportion(Type resourceType) { double proportion = 1.0; if (ResourceRequestList == null) { return(proportion); } double totalNeeded = ResourceRequestList.Where(a => a.ResourceType == resourceType).Sum(a => a.Required); foreach (ResourceRequest item in ResourceRequestList.Where(a => a.ResourceType == resourceType).ToList()) { if (resourceType == typeof(LabourType)) { if (item.FilterDetails != null && ((item.FilterDetails.First() as LabourFilterGroup).Parent as LabourRequirement).LabourShortfallAffectsActivity) { proportion *= item.Provided / item.Required; } } else // all other types { proportion *= item.Provided / item.Required; } } return(proportion); }
private void OnCLEMAnimalMilking(object sender, EventArgs e) { if (this.TimingOK & this.Status != ActivityStatus.Ignored) { if (ProportionToRemove > 0) { // get labour shortfall double labourLimiter = 1.0; if (this.Status == ActivityStatus.Partial & this.OnPartialResourcesAvailableAction == OnPartialResourcesAvailableActionTypes.UseResourcesAvailable) { double labourLimit = 1; double labourNeeded = ResourceRequestList.Where(a => a.ResourceType == typeof(Labour)).Sum(a => a.Required); double labourProvided = ResourceRequestList.Where(a => a.ResourceType == typeof(Labour)).Sum(a => a.Provided); if (labourNeeded > 0) { labourLimit = labourProvided / labourNeeded; } } // get dry breeders RuminantHerd ruminantHerd = Resources.RuminantHerd(); List <RuminantFemale> herd = this.CurrentHerd(true).Where(a => a.Gender == Sex.Female).Cast <RuminantFemale>().ToList(); // get dry breeders from females foreach (RuminantFemale female in herd.Where(a => a.Age - a.AgeAtLastBirth >= MonthsSinceBirth & a.PreviousConceptionRate >= MinimumConceptionBeforeSell & a.AgeAtLastBirth > 0)) { if (ZoneCLEM.RandomGenerator.NextDouble() <= ProportionToRemove * labourLimiter) { // flag female ready to transport. female.SaleFlag = HerdChangeReason.DryBreederSale; } } } } }
/// <inheritdoc/> public override void DoActivity() { if (PaymentCalculationStyle == PayHiredLabourCalculationStyle.ByAvailableLabour) { Status = ActivityStatus.Warning; // get amount of finance needed and provided double financeRequired = 0; double financeProvided = 0; foreach (ResourceRequest item in ResourceRequestList.Where(a => a.ResourceType == typeof(Finance))) { financeRequired += item.Required; financeProvided += item.Provided; Status = ActivityStatus.NotNeeded; } if (financeRequired > 0) { Status = ActivityStatus.Success; } // reduce limiters based on financial shortfall if (financeProvided < financeRequired) { if (this.OnPartialResourcesAvailableAction == OnPartialResourcesAvailableActionTypes.UseResourcesAvailable) { Status = ActivityStatus.Partial; int currentmonth = clock.Today.Month; double currentCost = 0; // step through all hired labour in order and set limiter where needed foreach (LabourType item in labour.Items.Where(a => a.Hired)) { // get days needed double daysNeeded = item.LabourAvailability.GetAvailability(currentmonth - 1); // calculate rate and amount needed double rate = item.PayRate(); double cost = daysNeeded * rate; if (currentCost == financeProvided) { item.AvailabilityLimiter = 0; cost = 0; } else if (currentCost + cost > financeProvided) { // reduce limit double excess = currentCost + cost - financeProvided; item.AvailabilityLimiter = (cost - excess) / cost; cost = financeProvided - currentCost; } currentCost += cost; } } } } return; }
/// <summary> /// Method used to perform activity if it can occur as soon as resources are available. /// </summary> public override void DoActivity() { List <LabourType> group = Resources.Labour().Items.Where(a => a.Hired != true).ToList(); if (group != null && group.Count > 0) { // calculate feed limit double feedLimit = 0.0; ResourceRequest feedRequest = ResourceRequestList.Where(a => a.ResourceType == typeof(HumanFoodStore)).FirstOrDefault(); if (feedRequest != null) { feedLimit = Math.Min(1.0, feedRequest.Provided / feedRequest.Required); } if (feedRequest == null || (feedRequest.Required == 0 | feedRequest.Available == 0)) { Status = ActivityStatus.NotNeeded; return; } foreach (Model child in Apsim.Children(this, typeof(LabourFeedGroup))) { double value = (child as LabourFeedGroup).Value; foreach (LabourType ind in Resources.Labour().Items.Filter(child)) { switch (FeedStyle) { case LabourFeedActivityTypes.SpecifiedDailyAmountPerIndividual: feedRequest.Provided = Math.Min(value * 30.4, FeedType.MaximumDailyIntakePerAE * ind.AdultEquivalent * 30.4); feedRequest.Provided *= feedLimit; ind.AddIntake(feedRequest); break; case LabourFeedActivityTypes.SpecifiedDailyAmountPerAE: feedRequest.Provided = Math.Min(value, FeedType.MaximumDailyIntakePerAE) * ind.AdultEquivalent * 30.4; feedRequest.Provided *= feedLimit; ind.AddIntake(feedRequest); break; default: throw new Exception(String.Format("FeedStyle {0} is not supported in {1}", FeedStyle, this.Name)); } } } SetStatusSuccess(); } }
/// <summary> /// Method used to perform activity if it can occur as soon as resources are available. /// </summary> public override void DoActivity() { if (this.TimingOK) { // reduce if labour limiting double labourlimit = 1; if (ResourceRequestList != null && ResourceRequestList.Where(a => a.ResourceType == typeof(Labour)).Count() > 0) { double amountLabourNeeded = ResourceRequestList.Where(a => a.ResourceType == typeof(Labour)).Sum(a => a.Required); double amountLabourProvided = ResourceRequestList.Where(a => a.ResourceType == typeof(Labour)).Sum(a => a.Provided); if (amountLabourNeeded > 0) { if (amountLabourProvided == 0) { labourlimit = 0; } else { labourlimit = amountLabourNeeded / amountLabourProvided; } } } double units = 0; if (labourlimit == 1 || this.OnPartialResourcesAvailableAction == OnPartialResourcesAvailableActionTypes.UseResourcesAvailable) { units = UnitsAvailableForSale * labourlimit; if (SellWholeUnitsOnly) { units = Math.Truncate(units); } } if (units > 0) { // remove resource ResourceRequest purchaseRequest = new ResourceRequest(); purchaseRequest.ActivityModel = this; purchaseRequest.Required = units * UnitSize; purchaseRequest.AllowTransmutation = false; purchaseRequest.Reason = "Sales"; resourceToSell.Remove(purchaseRequest); // transfer money earned bankAccount.Add(units * UnitPrice, this.Name, "Sales"); SetStatusSuccess(); } } }
/// <inheritdoc/> public override void DoActivity() { // processed resource should already be taken Status = ActivityStatus.NotNeeded; // add created resources ResourceRequest rr = ResourceRequestList.Where(a => (a.Resource != null && a.Resource.GetType() == resourceTypeProcessModel.GetType())).FirstOrDefault(); if (rr != null) { resourceTypeCreatedModel.Add(rr.Provided * ConversionRate, this, (resourceTypeCreatedModel as CLEMModel).NameWithParent, "Created"); if (rr.Provided > 0) { Status = ActivityStatus.Success; } } }
private void OnCLEMAnimalManage(object sender, EventArgs e) { // Weaning is performed in the Management event to ensure weaned individuals are treated as unweaned for their intake calculations // and the mother is considered lactating for lactation energy demands otherwise IsLactating stops as soon as ind.wean() is performed. // if wean month if (this.TimingOK) { double labourlimit = this.LabourLimitProportion; int weanedCount = 0; ResourceRequest labour = ResourceRequestList.Where(a => a.ResourceType == typeof(LabourType)).FirstOrDefault <ResourceRequest>(); // Perform weaning int count = this.CurrentHerd(false).Where(a => a.Weaned == false).Count(); foreach (var ind in this.CurrentHerd(false).Where(a => a.Weaned == false)) { if (ind.Age >= WeaningAge || ind.Weight >= WeaningWeight) { string reason = (ind.Age >= WeaningAge)? "Age" : "Weight"; ind.Wean(true, reason); ind.Location = grazeStore; weanedCount++; if (ind.Mother != null) { // report conception status changed when offspring weaned. ind.Mother.BreedParams.OnConceptionStatusChanged(new Reporting.ConceptionStatusChangedEventArgs(Reporting.ConceptionStatus.Weaned, ind.Mother, Clock.Today)); } } // stop if labour limited individuals reached and LabourShortfallAffectsActivity if (weanedCount > Convert.ToInt32(count * labourlimit, CultureInfo.InvariantCulture)) { this.Status = ActivityStatus.Partial; break; } } if (weanedCount > 0) { SetStatusSuccess(); } else { this.Status = ActivityStatus.NotNeeded; } } }
/// <inheritdoc/> public override void DoActivity() { // add all provided requests to the individuals intake pools. List <LabourType> group = people?.Items.Where(a => IncludeHiredLabour | a.Hired != true).ToList(); Status = ActivityStatus.NotNeeded; if (group != null && group.Count > 0) { var requests = ResourceRequestList.Where(a => a.ResourceType == typeof(HumanFoodStore)); if (requests.Any()) { double aE = group.Sum(a => a.TotalAdultEquivalents); foreach (ResourceRequest request in requests.Where(a => a.Provided > 0)) { // add to individual intake foreach (LabourType labour in group) { double amount = request.Provided * (labour.TotalAdultEquivalents / aE); labour.AddIntake(new LabourDietComponent() { AmountConsumed = amount, FoodStore = request.Resource as HumanFoodStoreType }); labour.FeedToTargetIntake += amount; } } } if (this.FindAllChildren <LabourActivityFeedTarget>().Where(a => !a.TargetAchieved).Any()) { this.Status = ActivityStatus.Partial; } else { this.Status = ActivityStatus.Success; } } // finished eating, so this household is now free to sell the resources // assumes all households above in the tree supply this level. // if sibling above relies on food from this household it own't work // selling is perfomed in the next method called in this same event }
/// <summary> /// Method used to perform activity if it can occur as soon as resources are available. /// </summary> public override void DoActivity() { Status = ActivityStatus.NotNeeded; // take local equivalent of market from resource double provided = 0; if ((resourceToBuy as CLEMResourceTypeBase).MarketStoreExists) { // find resource entry in market if present and reduce ResourceRequest rr = ResourceRequestList.Where(a => a.Resource == (resourceToBuy as CLEMResourceTypeBase).EquivalentMarketStore).FirstOrDefault(); provided = rr.Provided / this.FarmMultiplier; } else { provided = unitsCanAfford * price.PacketSize; } if (provided > 0) { resourceToBuy.Add(provided, this, "", "Purchase"); Status = ActivityStatus.Success; } // make financial transactions if (bankAccount != null) { ResourceRequest payment = new ResourceRequest() { AllowTransmutation = false, MarketTransactionMultiplier = this.FarmMultiplier, Required = provided / price.PacketSize * price.PricePerPacket, ResourceType = typeof(Finance), ResourceTypeName = bankAccount.Name, Category = "Purchase", RelatesToResource = (resourceToBuy as CLEMModel).NameWithParent, ActivityModel = this }; bankAccount.Remove(payment); } }
/// <inheritdoc/> public override void AdjustResourcesNeededForActivity() { // get labour shortfall double labprop = this.LimitProportion(typeof(LabourType)); // get finance shortfall double finprop = this.LimitProportion(typeof(FinanceType)); // reduce amount used double limit = Math.Min(labprop, finprop); if (limit < 1) { // find process resource entry in resource list ResourceRequest rr = ResourceRequestList.Where(a => a.ResourceType == resourceTypeProcessModel.GetType()).FirstOrDefault(); if (rr != null) { // reduce amount required rr.Required *= limit; } } }
/// <summary> /// Method used to perform activity if it can occur as soon as resources are available. /// </summary> public override void DoActivity() { // add all provided requests to the individuals intake pools. List <LabourType> group = Resources.Labour().Items.Where(a => IncludeHiredLabour | a.Hired != true).ToList(); double aE = group.Sum(a => a.AdultEquivalent); Status = ActivityStatus.NotNeeded; if (group != null && group.Count > 0) { var requests = ResourceRequestList.Where(a => a.ResourceType == typeof(HumanFoodStore)); if (requests.Count() > 0) { foreach (ResourceRequest request in requests) { if (request.Provided > 0) { // add to individual intake foreach (LabourType labour in group) { labour.AddIntake(new LabourDietComponent() { AmountConsumed = request.Provided * (labour.AdultEquivalent / aE), FoodStore = request.Resource as HumanFoodStoreType }); } } } } List <LabourActivityFeedTarget> labourActivityFeedTargets = this.FindAllChildren <LabourActivityFeedTarget>().Cast <LabourActivityFeedTarget>().ToList(); if (labourActivityFeedTargets.Where(a => !a.TargetMet).Count() > 0) { this.Status = ActivityStatus.Partial; } else { this.Status = ActivityStatus.Success; } } }
private void OnCLEMMilking(object sender, EventArgs e) { // take all milk List <RuminantFemale> herd = this.CurrentHerd(true).Where(a => a.Gender == Sex.Female).Cast <RuminantFemale>().Where(a => a.IsLactating == true & a.SucklingOffspring.Count() == 0).ToList(); double milkTotal = herd.Sum(a => a.MilkAmount); if (milkTotal > 0) { // set these females to state milking perfomred so they switch to the non-suckling milk production curves. herd.Select(a => a.MilkingPerformed == true); // only provide what labour would allow // calculate labour limit double labourLimit = 1; double labourNeeded = ResourceRequestList.Where(a => a.ResourceType == typeof(Labour)).Sum(a => a.Required); double labourProvided = ResourceRequestList.Where(a => a.ResourceType == typeof(Labour)).Sum(a => a.Provided); if (labourNeeded > 0) { labourLimit = labourProvided / labourNeeded; } milkStore.Add(milkTotal * labourLimit, this.Name, this.PredictedHerdName); } }
/// <summary> /// The method allows the activity to adjust resources requested based on shortfalls (e.g. labour) before they are taken from the pools /// </summary> public override void AdjustResourcesNeededForActivity() { // labour limiter var labourRequests = ResourceRequestList.Where(a => a.ResourceType == typeof(Labour)).ToList(); if (labourRequests.Count > 0) { double required = labourRequests.Sum(a => a.Required); double provided = labourRequests.Sum(a => a.Provided); double limiter = Math.Min(1.0, provided / required); // TODO add ability to turn off labour influence if (limiter < 1) { // find pasture and reduce required based on labour limit. ResourceRequest pastureRequest = ResourceRequestList.Where(a => a.ResourceType == typeof(GrazeFoodStoreType)).FirstOrDefault(); if (pastureRequest != null) { AmountHarvested *= limiter; pastureRequest.Required = AmountHarvested; } } } }
/// <summary> /// Method used to perform activity if it can occur as soon as resources are available. /// </summary> public override void PerformActivity() { RuminantHerd ruminantHerd = Resources.RuminantHerd(); List <Ruminant> herd = ruminantHerd.Herd; if (herd != null && herd.Count > 0) { // calculate labour limit double labourLimit = 1.0; if (LabourShortfallLimitsFeeding) { ResourceRequest labourRequest = ResourceRequestList.Where(a => a.ResourceName == "Labour").FirstOrDefault(); if (labourRequest != null) { labourLimit = Math.Min(1.0, labourRequest.Required / labourRequest.Provided); } } // calculate feed limit double feedLimit = 0.0; ResourceRequest feedRequest = ResourceRequestList.Where(a => a.ResourceName == "AnimalFoodStore").FirstOrDefault(); if (feedRequest != null) { feedLimit = Math.Min(1.0, feedRequest.Required / feedRequest.Provided); } // feed animals int month = Clock.Today.Month - 1; // get list from filters foreach (RuminantFilterGroup child in this.Children.Where(a => a.GetType() == typeof(RuminantFilterGroup))) { foreach (Ruminant ind in herd.Filter(child as RuminantFilterGroup)) { double feedRequired = 0; switch (FeedStyle) { case RuminantFeedActivityTypes.SpecifiedDailyAmount: feedRequired += (child as RuminantFilterGroup).MonthlyValues[month] * 30.4; // * ind.Number; ind.Intake += feedRequired * feedLimit * labourLimit; break; case RuminantFeedActivityTypes.ProportionOfWeight: feedRequired += (child as RuminantFilterGroup).MonthlyValues[month] * ind.Weight * 30.4; // * ind.Number; ind.Intake += feedRequired * feedLimit * labourLimit; break; case RuminantFeedActivityTypes.ProportionOfPotentialIntake: feedRequired += (child as RuminantFilterGroup).MonthlyValues[month] * ind.PotentialIntake; // * ind.Number; ind.Intake += feedRequired * feedLimit * labourLimit; break; case RuminantFeedActivityTypes.ProportionOfRemainingIntakeRequired: feedRequired += (child as RuminantFilterGroup).MonthlyValues[month] * (ind.PotentialIntake - ind.Intake); // * ind.Number; ind.Intake += feedRequired * feedLimit * labourLimit; break; default: break; } } } } }
/// <inheritdoc/> public override void DoActivity() { IEnumerable <LabourType> group = labour?.Items.Where(a => a.Hired != true); if (group != null && group.Any()) { // calculate feed limit double feedLimit = 0.0; ResourceRequest feedRequest = ResourceRequestList.Where(a => a.ResourceType == typeof(HumanFoodStore)).FirstOrDefault(); if (feedRequest != null) { feedLimit = Math.Min(1.0, feedRequest.Provided / feedRequest.Required); } if (feedRequest == null || (feedRequest.Required == 0 | feedRequest.Available == 0)) { Status = ActivityStatus.NotNeeded; return; } foreach (LabourFeedGroup child in this.FindAllChildren <LabourFeedGroup>()) { double value = child.Value; foreach (LabourType ind in child.Filter(labour?.Items)) { switch (FeedStyle) { case LabourFeedActivityTypes.SpecifiedDailyAmountPerIndividual: feedRequest.Provided = value * 30.4; feedRequest.Provided *= feedLimit; feedRequest.Provided *= (feedRequest.Resource as HumanFoodStoreType).EdibleProportion; ind.AddIntake(new LabourDietComponent() { AmountConsumed = feedRequest.Provided, FoodStore = feedRequest.Resource as HumanFoodStoreType } ); break; case LabourFeedActivityTypes.SpecifiedDailyAmountPerAE: feedRequest.Provided = value * ind.AdultEquivalent * 30.4; feedRequest.Provided *= feedLimit; feedRequest.Provided *= (feedRequest.Resource as HumanFoodStoreType).EdibleProportion; ind.AddIntake(new LabourDietComponent() { AmountConsumed = feedRequest.Provided, FoodStore = feedRequest.Resource as HumanFoodStoreType } ); break; default: throw new Exception(String.Format("FeedStyle {0} is not supported in {1}", FeedStyle, this.Name)); } } } SetStatusSuccess(); } }
/// <summary> /// Method used to perform activity if it can occur as soon as resources are available. /// </summary> public override void DoActivity() { List <Ruminant> herd = CurrentHerd(false); if (herd != null && herd.Count > 0) { // calculate feed limit double feedLimit = 0.0; double wastage = 1.0 - this.ProportionTramplingWastage; double dailyAmountShortfall = 1.0; ResourceRequest feedRequest = ResourceRequestList.Where(a => a.ResourceType == typeof(AnimalFoodStore)).FirstOrDefault(); FoodResourcePacket details = new FoodResourcePacket(); if (feedRequest != null) { details = feedRequest.AdditionalDetails as FoodResourcePacket; feedLimit = Math.Min(1.0, feedRequest.Provided / feedRequest.Required); } // feed animals int month = Clock.Today.Month - 1; if (feedRequest == null || (feedRequest.Required == 0 | feedRequest.Available == 0)) { Status = ActivityStatus.NotNeeded; return; } // if feed style is fixed daily amount compare amount received against herd requirement. // this produces a reduction from potential intake for each individual. if (FeedStyle == RuminantFeedActivityTypes.SpecifiedDailyAmount) { double herdRequirement = 0; foreach (Model child in this.Children.Where(a => a.GetType().ToString().Contains("RuminantFeedGroup"))) { herdRequirement += herd.Filter(child).Sum(a => a.PotentialIntake - a.Intake); } dailyAmountShortfall = Math.Min(1.0, (feedRequest.Provided * wastage) / herdRequirement); } // get list from filters foreach (Model child in this.Children.Where(a => a.GetType().ToString().Contains("RuminantFeedGroup"))) { double value = 0; if (child is RuminantFeedGroup) { value = (child as RuminantFeedGroup).Value; } else { value = (child as RuminantFeedGroupMonthly).MonthlyValues[month]; } foreach (Ruminant ind in herd.Filter(child)) { switch (FeedStyle) { case RuminantFeedActivityTypes.SpecifiedDailyAmount: details.Amount = (ind.PotentialIntake - ind.Intake); details.Amount *= dailyAmountShortfall; ind.AddIntake(details); break; case RuminantFeedActivityTypes.SpecifiedDailyAmountPerIndividual: details.Amount = value * 30.4; // * ind.Number; details.Amount *= feedLimit * wastage; ind.AddIntake(details); break; case RuminantFeedActivityTypes.ProportionOfWeight: details.Amount = value * ind.Weight * 30.4; // * ind.Number; details.Amount *= feedLimit * wastage; ind.AddIntake(details); break; case RuminantFeedActivityTypes.ProportionOfPotentialIntake: details.Amount = value * ind.PotentialIntake; // * ind.Number; details.Amount *= feedLimit * wastage; ind.AddIntake(details); break; case RuminantFeedActivityTypes.ProportionOfRemainingIntakeRequired: details.Amount = value * (ind.PotentialIntake - ind.Intake); // * ind.Number; details.Amount *= feedLimit * wastage; ind.AddIntake(details); break; default: throw new Exception("Feed style used [" + FeedStyle + "] not implemented in [" + this.Name + "]"); } } } SetStatusSuccess(); } }
/// <summary> /// Method used to perform activity if it can occur as soon as resources are available. /// </summary> public override void DoActivity() { List <Ruminant> herd = CurrentHerd(false); if (herd != null && herd.Count > 0) { // calculate labour limit double labourLimit = 1; double labourNeeded = ResourceRequestList.Where(a => a.ResourceType == typeof(Labour)).Sum(a => a.Required); double labourProvided = ResourceRequestList.Where(a => a.ResourceType == typeof(Labour)).Sum(a => a.Provided); if (labourNeeded > 0) { labourLimit = labourProvided / labourNeeded; } // calculate feed limit double feedLimit = 0.0; double wastage = 1.0 - this.ProportionTramplingWastage; ResourceRequest feedRequest = ResourceRequestList.Where(a => a.ResourceType == typeof(AnimalFoodStore)).FirstOrDefault(); FoodResourcePacket details = new FoodResourcePacket(); if (feedRequest != null) { details = feedRequest.AdditionalDetails as FoodResourcePacket; feedLimit = Math.Min(1.0, feedRequest.Provided / feedRequest.Required); } // feed animals int month = Clock.Today.Month - 1; SetStatusSuccess(); // get list from filters foreach (RuminantFeedGroup child in Apsim.Children(this, typeof(RuminantFeedGroup))) { foreach (Ruminant ind in herd.Filter(child as RuminantFeedGroup)) { switch (FeedStyle) { case RuminantFeedActivityTypes.SpecifiedDailyAmount: details.Amount = (child as RuminantFeedGroup).MonthlyValues[month] * 30.4; details.Amount *= feedLimit * labourLimit * wastage; ind.AddIntake(details); break; case RuminantFeedActivityTypes.ProportionOfWeight: details.Amount = (child as RuminantFeedGroup).MonthlyValues[month] * ind.Weight * 30.4; // * ind.Number; details.Amount *= feedLimit * labourLimit * wastage; ind.AddIntake(details); break; case RuminantFeedActivityTypes.ProportionOfPotentialIntake: details.Amount = (child as RuminantFeedGroup).MonthlyValues[month] * ind.PotentialIntake; // * ind.Number; details.Amount *= feedLimit * labourLimit * wastage; ind.AddIntake(details); break; case RuminantFeedActivityTypes.ProportionOfRemainingIntakeRequired: details.Amount = (child as RuminantFeedGroup).MonthlyValues[month] * (ind.PotentialIntake - ind.Intake); // * ind.Number; details.Amount *= feedLimit * labourLimit * wastage; ind.AddIntake(details); break; default: break; } } } } }
/// <inheritdoc/> public override void DoActivity() { IEnumerable <Ruminant> herd = CurrentHerd(false); if (herd != null && herd.Any()) { double feedLimit = 0.0; ResourceRequest feedRequest = ResourceRequestList.Where(a => a.ResourceType == typeof(AnimalFoodStore)).LastOrDefault(); FoodResourcePacket details = new FoodResourcePacket(); if (feedRequest != null) { details = feedRequest.AdditionalDetails as FoodResourcePacket; feedLimit = Math.Min(1.0, feedRequest.Provided / feedRequest.Required); } // feed animals if (feedRequest == null | (feedRequest?.Required == 0 | feedRequest?.Available == 0) | APSIM.Shared.Utilities.MathUtilities.FloatsAreEqual(feedLimit, 0.0)) { Status = ActivityStatus.NotNeeded; return; } // get list from filters foreach (var child in Children.OfType <FilterGroup <Ruminant> >()) { double value = 0; if (child is RuminantFeedGroup rfg) { value = rfg.Value; } else if (child is RuminantFeedGroupMonthly rfgm) { value = rfgm.MonthlyValues[clock.Today.Month - 1]; } else { continue; } foreach (Ruminant ind in child.Filter(herd)) { switch (FeedStyle) { case RuminantFeedActivityTypes.SpecifiedDailyAmount: case RuminantFeedActivityTypes.ProportionOfFeedAvailable: details.Amount = ((ind.PotentialIntake * (usingPotentialIntakeMultiplier ? ind.BreedParams.OverfeedPotentialIntakeModifier : 1)) - ind.Intake); details.Amount *= feedLimit; break; case RuminantFeedActivityTypes.SpecifiedDailyAmountPerIndividual: details.Amount = value * 30.4; details.Amount *= feedLimit; break; case RuminantFeedActivityTypes.ProportionOfWeight: details.Amount = value * ind.Weight * 30.4; details.Amount *= feedLimit; break; case RuminantFeedActivityTypes.ProportionOfPotentialIntake: details.Amount = value * ind.PotentialIntake; details.Amount *= feedLimit; break; case RuminantFeedActivityTypes.ProportionOfRemainingIntakeRequired: details.Amount = value * (ind.PotentialIntake - ind.Intake); details.Amount *= feedLimit; break; default: throw new Exception($"FeedStyle [{FeedStyle}] is not supported in [a={Name}]"); } // check amount meets intake limits if (usingPotentialIntakeMultiplier) { if (details.Amount > (ind.PotentialIntake + (Math.Max(0, ind.BreedParams.OverfeedPotentialIntakeModifier - 1) * overfeedProportion * ind.PotentialIntake)) - ind.Intake) { details.Amount = (ind.PotentialIntake + (Math.Max(0, ind.BreedParams.OverfeedPotentialIntakeModifier - 1) * overfeedProportion * ind.PotentialIntake)) - ind.Intake; } } ind.AddIntake(details); } } SetStatusSuccess(); } else { Status = ActivityStatus.NotNeeded; } }
/// <summary> /// Method to determine resources required for this activity in the current month /// </summary> /// <returns>List of required resource requests</returns> public override List <ResourceRequest> GetResourcesNeededForActivity() { if (people is null | food is null) { return(null); } List <LabourType> peopleList = people.Items.Where(a => IncludeHiredLabour || a.Hired == true).ToList(); peopleList.Select(a => a.FeedToTargetIntake == 0); List <ResourceRequest> requests = new List <ResourceRequest>(); // determine AEs to be fed double aE = peopleList.Sum(a => a.AdultEquivalent); // determine feed limits (max kg per AE per day * AEs * days) double intakeLimit = DailyIntakeLimit * aE * 30.4; // remove previous consumption intakeLimit -= this.DailyIntakeOtherSources * aE * 30.4; intakeLimit -= peopleList.Sum(a => a.GetAmountConsumed()); List <LabourActivityFeedTarget> labourActivityFeedTargets = this.FindAllChildren <LabourActivityFeedTarget>().Cast <LabourActivityFeedTarget>().ToList(); int feedTargetIndex = 0; // determine targets foreach (LabourActivityFeedTarget target in labourActivityFeedTargets) { // calculate target target.Target = target.TargetValue * aE * 30.4; // set initial level based on off store inputs target.CurrentAchieved = target.OtherSourcesValue * aE * 30.4; // calculate current level from previous intake this month (LabourActivityFeed) target.CurrentAchieved += people.GetDietaryValue(target.Metric, IncludeHiredLabour, true) * aE * 30.4; } // order food to achieve best returns for first criteria conversion factor decreasing List <HumanFoodStoreType> foodStoreTypes = food.FindAllChildren <HumanFoodStoreType>().Cast <HumanFoodStoreType>().OrderBy(a => a.ConversionFactor(labourActivityFeedTargets[feedTargetIndex].Metric)).ToList(); // check availability to take food based on order in simulation tree while (foodStoreTypes.Count() > 0 & intakeLimit > 0) { // get next food store type HumanFoodStoreType foodtype = foodStoreTypes[0]; // get amount people can still eat based on limits and previous consumption double amountNeededRaw = 0; foreach (LabourType labourType in peopleList) { double indLimit = (labourType.AdultEquivalent * DailyIntakeLimit * 30.4); double alreadyEatenThis = labourType.GetAmountConsumed(foodtype.Name); double alreadyEaten = labourType.GetAmountConsumed() + labourType.FeedToTargetIntake; double canStillEat = Math.Max(0, indLimit - alreadyEaten); double amountOfThisFood = canStillEat; amountNeededRaw += amountOfThisFood / foodtype.EdibleProportion; } // update targets based on amount available (will update excess if transmutated later) double amountNeededEdible = Math.Min(amountNeededRaw, foodtype.Amount) * foodtype.EdibleProportion; foreach (LabourActivityFeedTarget target in labourActivityFeedTargets) { target.CurrentAchieved += amountNeededEdible * foodtype.ConversionFactor(target.Metric); } if (amountNeededRaw > 0) { // create request requests.Add(new ResourceRequest() { AllowTransmutation = false, Required = amountNeededRaw, Available = foodtype.Amount, ResourceType = typeof(HumanFoodStore), ResourceTypeName = foodtype.Name, ActivityModel = this, Category = "Consumption" } ); } foodStoreTypes.RemoveAt(0); // check if target has been met (allows slight overrun) if (labourActivityFeedTargets[feedTargetIndex].CurrentAchieved >= labourActivityFeedTargets[feedTargetIndex].Target) { feedTargetIndex++; if (feedTargetIndex > labourActivityFeedTargets.Count()) { // all feed targets have been met. Preserve remaining food for next time. //TODO: eat food that will go off if not eaten and still below limits. break; } // reorder remaining food types to next feed target if available foodStoreTypes = foodStoreTypes.OrderBy(a => a.ConversionFactor(labourActivityFeedTargets[feedTargetIndex].Metric)).ToList(); } } // We have now been through all food types or all targets have been achieved. // Any unused food will not be consumed even if it is about to spoil. // The food requests ready to send contain excesses that may need to be purchased but haven't been accounted for towards targets yet // Next we go through and check all requests that exceed available to see if we can and there is need to buy resources. foreach (ResourceRequest request in ResourceRequestList.Where(a => a.Required > a.Available)) { // all targets have not been met if (feedTargetIndex <= labourActivityFeedTargets.Count()) { // allow if transmutation possible if ((request.Resource as HumanFoodStoreType).TransmutationDefined) { // allow if still below threshold if (labourActivityFeedTargets[feedTargetIndex].CurrentAchieved < labourActivityFeedTargets[feedTargetIndex].Target) { HumanFoodStoreType foodStore = request.Resource as HumanFoodStoreType; // if this food type provides towards the target if (foodStore.ConversionFactor(labourActivityFeedTargets[feedTargetIndex].Metric) > 0) { // work out what the extra is worth double excess = request.Required - request.Available; // get target needed double remainingToTarget = labourActivityFeedTargets[feedTargetIndex].Target - labourActivityFeedTargets[feedTargetIndex].CurrentAchieved; double excessConverted = excess * foodStore.EdibleProportion * foodStore.ConversionFactor(labourActivityFeedTargets[feedTargetIndex].Metric); // reduce if less than needed double prop = Math.Max(excessConverted / remainingToTarget, 1.0); double newExcess = excess * prop; request.Required = request.Available + newExcess; request.AllowTransmutation = true; // update targets based on new amount eaten foreach (LabourActivityFeedTarget target in labourActivityFeedTargets) { target.CurrentAchieved += newExcess * foodStore.EdibleProportion * foodStore.ConversionFactor(target.Metric); } // move to next target if achieved. if (labourActivityFeedTargets[feedTargetIndex].CurrentAchieved >= labourActivityFeedTargets[feedTargetIndex].Target) { feedTargetIndex++; } } } } } // transmutation not allowed so only get what was available. if (request.AllowTransmutation == false) { request.Required = request.Available; } } return(requests); }
private void OnCLEMAnimalManage(object sender, EventArgs e) { // Weaning is performed in the Management event to ensure weaned individuals are treated as unweaned for their intake calculations // and the mother is considered lactating for lactation energy demands otherwise IsLactating stops as soon as ind.wean() is performed. // if wean month if (this.TimingOK) { double labourlimit = this.LabourLimitProportion; int weanedCount = 0; ResourceRequest labour = ResourceRequestList.Where(a => a.ResourceType == typeof(LabourType)).FirstOrDefault <ResourceRequest>(); // Perform weaning int count = 0; foreach (RuminantGroup item in filterGroups) { count += this.GetIndividuals <Ruminant>(GetRuminantHerdSelectionStyle.NotMarkedForSale, null, false).Where(a => a.Weaned == false).Count(); } foreach (RuminantGroup item in filterGroups) { foreach (Ruminant ind in item.Filter(this.GetIndividuals <Ruminant>(GetRuminantHerdSelectionStyle.NotMarkedForSale, null, false).Where(a => a.Weaned == false)).ToList()) { bool readyToWean = false; string reason = ""; switch (Style) { case WeaningStyle.AgeOrWeight: readyToWean = (ind.Age >= WeaningAge || ind.Weight >= WeaningWeight); reason = (ind.Age >= WeaningAge) ? ((ind.Weight >= WeaningWeight) ? "AgeAndWeight": "Age") : "Weight"; break; case WeaningStyle.AgeOnly: readyToWean = (ind.Age >= WeaningAge); reason = "Age"; break; case WeaningStyle.WeightOnly: readyToWean = (ind.Weight >= WeaningWeight); reason = "Weight"; break; } if (readyToWean) { this.Status = ActivityStatus.Success; ind.Wean(true, reason); ind.Location = grazeStore; weanedCount++; // report wean. If mother has died create temp female with the mother's ID for reporting only ind.BreedParams.OnConceptionStatusChanged(new Reporting.ConceptionStatusChangedEventArgs(Reporting.ConceptionStatus.Weaned, ind.Mother ?? new RuminantFemale(ind.BreedParams, -1, 999) { ID = ind.MotherID }, clock.Today, ind)); } // stop if labour limited individuals reached and LabourShortfallAffectsActivity if (weanedCount > Convert.ToInt32(count * labourlimit, CultureInfo.InvariantCulture)) { this.Status = ActivityStatus.Partial; break; } } } if (weanedCount > 0) { SetStatusSuccess(); } else { this.Status = ActivityStatus.NotNeeded; } } }
/// <summary> /// Method used to perform activity if it can occur as soon as resources are available. /// </summary> public override void DoActivity() { List <Ruminant> herd = CurrentHerd(false); if (herd != null && herd.Count > 0) { double feedLimit = 0.0; ResourceRequest feedRequest = ResourceRequestList.Where(a => a.ResourceType == typeof(AnimalFoodStore)).LastOrDefault(); FoodResourcePacket details = new FoodResourcePacket(); if (feedRequest != null) { details = feedRequest.AdditionalDetails as FoodResourcePacket; feedLimit = Math.Min(1.0, feedRequest.Provided / feedRequest.Required); } // feed animals if (feedRequest == null || (feedRequest.Required == 0 | feedRequest.Available == 0)) { Status = ActivityStatus.NotNeeded; return; } // get list from filters foreach (IFilterGroup child in Children.Where(a => a.GetType().ToString().Contains("RuminantFeedGroup"))) { double value = 0; if (child is RuminantFeedGroup) { value = (child as RuminantFeedGroup).Value; } else { value = (child as RuminantFeedGroupMonthly).MonthlyValues[Clock.Today.Month - 1]; } foreach (Ruminant ind in herd.FilterRuminants(child)) { switch (FeedStyle) { case RuminantFeedActivityTypes.SpecifiedDailyAmount: case RuminantFeedActivityTypes.ProportionOfFeedAvailable: details.Amount = ((ind.PotentialIntake * (usingPotentialintakeMultiplier ? ind.BreedParams.OverfeedPotentialIntakeModifier : 1)) - ind.Intake); details.Amount *= feedLimit; break; case RuminantFeedActivityTypes.SpecifiedDailyAmountPerIndividual: details.Amount = value * 30.4; details.Amount *= feedLimit; break; case RuminantFeedActivityTypes.ProportionOfWeight: details.Amount = value * ind.Weight * 30.4; details.Amount *= feedLimit; break; case RuminantFeedActivityTypes.ProportionOfPotentialIntake: details.Amount = value * ind.PotentialIntake; details.Amount *= feedLimit; break; case RuminantFeedActivityTypes.ProportionOfRemainingIntakeRequired: details.Amount = value * (ind.PotentialIntake - ind.Intake); details.Amount *= feedLimit; break; default: throw new Exception("Feed style used [" + FeedStyle + "] not implemented in [" + this.Name + "]"); } // check amount meets intake limits if (usingPotentialintakeMultiplier) { if (details.Amount > (ind.PotentialIntake + (Math.Max(0, ind.BreedParams.OverfeedPotentialIntakeModifier - 1) * overfeedProportion * ind.PotentialIntake)) - ind.Intake) { details.Amount = (ind.PotentialIntake + (Math.Max(0, ind.BreedParams.OverfeedPotentialIntakeModifier - 1) * overfeedProportion * ind.PotentialIntake)) - ind.Intake; } } ind.AddIntake(details); } } SetStatusSuccess(); } else { Status = ActivityStatus.NotNeeded; } }
/// <summary> /// The method allows the activity to adjust resources requested based on shortfalls (e.g. labour) before they are taken from the pools /// </summary> public override void AdjustResourcesNeededForActivity() { // labour shortfall if any double labourLimit = this.LabourLimitProportion; overfeedProportion = 0; // TODO: adjust if multiple animal food stores included in future. // FirstOrDefault() is still known to be food store request. After this call it will be last in list with wasted and excess at start of list ResourceRequest item = ResourceRequestList.Where(a => a.ResourceType == typeof(AnimalFoodStore)).FirstOrDefault(); if (item != null) { //add limits to amout collected based on labour shortfall item.Required *= labourLimit; // account for any wastage // removed from food resource provided and then will be handled if required if less than provided in next section (DoActivity). if (ProportionTramplingWastage > 0) { double wasted = Math.Min(item.Available, item.Required) * ProportionTramplingWastage; if (wasted > 0) { ResourceRequest wastedRequest = new ResourceRequest() { AllowTransmutation = false, Required = wasted, Available = wasted, ResourceType = typeof(AnimalFoodStore), ResourceTypeName = item.ResourceTypeName, ActivityModel = this, Category = "Wastage", RelatesToResource = this.PredictedHerdName }; ResourceRequestList.Insert(0, wastedRequest); item.Required -= wasted; // adjust the food known available for the actual feed item.Available -= wasted; } } // report any excess fed above feed needed to fill animals itake (including potential multiplier if required for overfeeding) double excess = 0; if (Math.Min(item.Available, item.Required) >= feedToOverSatisfy) { excess = Math.Min(item.Available, item.Required) - feedToOverSatisfy; if (feedToOverSatisfy > feedToSatisfy) { overfeedProportion = 1; } } else if (feedToOverSatisfy > feedToSatisfy && Math.Min(item.Available, item.Required) > feedToSatisfy) { overfeedProportion = (Math.Min(item.Available, item.Required) - feedToSatisfy) / (feedToOverSatisfy - feedToSatisfy); } if (excess > 0) { ResourceRequest excessRequest = new ResourceRequest() { AllowTransmutation = false, Required = excess, Available = excess, ResourceType = typeof(AnimalFoodStore), ResourceTypeName = item.ResourceTypeName, ActivityModel = this, Category = "Overfed wastage", RelatesToResource = this.PredictedHerdName }; ResourceRequestList.Insert(0, excessRequest); item.Required -= excess; item.Available -= excess; } } return; }