private void HandleRestocking(double aEtoBuy, string paddockName, Ruminant exampleRuminant) { if (aEtoBuy <= 0) { return; } // we won't remove individuals from the sale pool as we can't assume we can keep them in the herd // as management has already decided they need to be sold. // buy steers to fatten up and take advantage of the good season growth. // ensure min pasture for restocking if ((foodStore == null) || ((foodStore.TonnesPerHectare * 1000) > MinimumFeedBeforeRestock)) { double weight = exampleRuminant.StandardReferenceWeight - ((1 - exampleRuminant.BreedParams.SRWBirth) * exampleRuminant.StandardReferenceWeight) * Math.Exp(-(exampleRuminant.BreedParams.AgeGrowthRateCoefficient * (exampleRuminant.Age * 30.4)) / (Math.Pow(exampleRuminant.StandardReferenceWeight, exampleRuminant.BreedParams.SRWGrowthScalar))); double numberToBuy = aEtoBuy * Math.Pow(weight, 0.75) / Math.Pow(exampleRuminant.BreedParams.BaseAnimalEquivalent, 0.75); // convert to AE for (int i = 0; i < Convert.ToInt32(numberToBuy, CultureInfo.InvariantCulture); i++) { Resources.RuminantHerd().PurchaseIndividuals.Add(new RuminantMale(192, Sex.Male, weight, exampleRuminant.BreedParams) { // Age = 192, or 16 months HerdName = exampleRuminant.HerdName, Number = 1, SaleFlag = HerdChangeReason.RestockPurchase, Breed = exampleRuminant.Breed, BreedingSire = false, Draught = false, Location = paddockName, } ); } } }
/// <summary> /// A method to add the male attributes to the female attribute store at mating /// </summary> /// <param name="female">The female breeder successfully mated</param> /// <param name="male">The mated male</param> private void AddMalesAttributeDetails(RuminantFemale female, Ruminant male) { if (male != null) { foreach (var attribute in female.Attributes) { var maleAttribute = male.GetAttributeValue(attribute.Key); if (maleAttribute != null) { if (attribute.Value.InheritanceStyle != maleAttribute.InheritanceStyle) { throw new ApsimXException(this, $"The inheritance style for attribute [{attribute.Key}] differs between the breeder and breeding male from the herd in [a={this.Name}]"); } attribute.Value.storedMateValue = maleAttribute.storedValue; } else { attribute.Value.storedMateValue = null; if (female.BreedParams.IsMandatoryAttribute(attribute.Key)) { throw new ApsimXException(this, $"The attributes provided with the breeding male from the herd does not include the madatory attribute [{attribute.Key}] in [a={this.Name}]"); } } } } }
private double HandleRestocking(double animalEquivalentsToBuy, string paddockName, Ruminant exampleRuminant) { if (animalEquivalentsToBuy <= 0) { return(0); } GrazeFoodStoreType foodStore = Resources.GetResourceItem(this, typeof(GrazeFoodStore), paddockName, OnMissingResourceActionTypes.Ignore, OnMissingResourceActionTypes.ReportErrorAndStop) as GrazeFoodStoreType; // ensure min pasture for restocking if ((foodStore == null) || ((foodStore.TonnesPerHectare * 1000) > MinimumFeedBeforeRestock)) { var specifyComponents = FindAllChildren <SpecifyRuminant>(); if (specifyComponents.Count() == 0) { string warn = $"No [f=SpecifyRuminant]s were provided in [a={this.Name}]\r\nNo restocking will be performed."; this.Status = ActivityStatus.Warning; if (!Warnings.Exists(warn)) { Summary.WriteWarning(this, warn); Warnings.Add(warn); } } // buy animals specified in restock ruminant groups foreach (SpecifyRuminant item in specifyComponents) { double sumAE = 0; double limitAE = animalEquivalentsToBuy * item.Proportion; while (sumAE < limitAE && animalEquivalentsToBuy > 0) { Ruminant newIndividual = item.Details.CreateIndividuals(1, null).FirstOrDefault(); newIndividual.Location = paddockName; newIndividual.BreedParams = item.BreedParams; newIndividual.HerdName = item.BreedParams.Name; newIndividual.PurchaseAge = newIndividual.Age; newIndividual.SaleFlag = HerdChangeReason.RestockPurchase; if (newIndividual.Weight == 0) { throw new ApsimXException(this, $"Specified individual added during restock cannot have no weight in [{this.Name}]"); } Resources.RuminantHerd().PurchaseIndividuals.Add(newIndividual); double indAE = newIndividual.AdultEquivalent; animalEquivalentsToBuy -= indAE; sumAE += indAE; } } return(Math.Max(0, animalEquivalentsToBuy)); } return(animalEquivalentsToBuy); }
/// <summary> /// A method to add the male attributes to the female attribute store at mating /// </summary> /// <param name="female">The female breeder successfully mated</param> /// <param name="male">The mated male</param> private void AddMalesAttributeDetails(RuminantFemale female, Ruminant male) { if (male is null) { return; } foreach (var attribute in female.Attributes.Items) { var maleAttribute = male.Attributes.GetValue(attribute.Key); SetFemaleMateAttributes(female, attribute, maleAttribute); } }
private void CalculatePotentialIntake(Ruminant ind) { // calculate daily potential intake for the selected individual/cohort double standardReferenceWeight = ind.StandardReferenceWeight; // now calculated in Ruminant // ind.NormalisedAnimalWeight = standardReferenceWeight - ((1 - ind.BreedParams.SRWBirth) * standardReferenceWeight) * Math.Exp(-(ind.BreedParams.AgeGrowthRateCoefficient * (ind.Age * 30.4)) / (Math.Pow(standardReferenceWeight, ind.BreedParams.SRWGrowthScalar))); double liveWeightForIntake = ind.NormalisedAnimalWeight; // now performed at allocation of weight in Ruminant if (ind.HighWeight < ind.NormalisedAnimalWeight) { liveWeightForIntake = ind.HighWeight; } // Calculate potential intake based on current weight compared to SRW and previous highest weight double potentialIntake = 0; ind.MilkIntakePotential = 0; // calculate milk intake shortfall for sucklings if (!ind.Weaned) { // potential milk intake/animal/day ind.MilkIntakePotential = ind.BreedParams.MilkIntakeIntercept + ind.BreedParams.MilkIntakeCoefficient * ind.Weight; // get estimated milk available // this will be updated to the corrected milk available in the calculate energy section. ind.MilkIntake = Math.Min(ind.MilkIntakePotential, ind.MothersMilkProductionAvailable); // if milk supply low, calf will subsitute forage up to a specified % of bodyweight (R_C60) if (ind.MilkIntake < ind.Weight * ind.BreedParams.MilkLWTFodderSubstitutionProportion) { potentialIntake = Math.Max(0.0, ind.Weight * ind.BreedParams.MaxJuvenileIntake - ind.MilkIntake * ind.BreedParams.ProportionalDiscountDueToMilk); } } else { if (ind.Weaner) { // Reference: SCA Metabolic LWTs // restored in v112 of NABSA for weaner animals potentialIntake = ind.BreedParams.IntakeCoefficient * standardReferenceWeight * (Math.Pow(liveWeightForIntake, 0.75) / Math.Pow(standardReferenceWeight, 0.75)) * (ind.BreedParams.IntakeIntercept - (Math.Pow(liveWeightForIntake, 0.75) / Math.Pow(standardReferenceWeight, 0.75))); // older individual check. previous method before adding calulation for weaners after discussions with Cam McD //double prevint = ind.BreedParams.IntakeCoefficient * liveWeightForIntake * (ind.BreedParams.IntakeIntercept - liveWeightForIntake / standardReferenceWeight); } else // 12month+ individuals { // Reference: SCA based actual LWTs potentialIntake = ind.BreedParams.IntakeCoefficient * liveWeightForIntake * (ind.BreedParams.IntakeIntercept - liveWeightForIntake / standardReferenceWeight); } if (ind.Gender == Sex.Female) { RuminantFemale femaleind = ind as RuminantFemale; // Increase potential intake for lactating breeder if (femaleind.IsLactating) { // move to half way through timestep double dayOfLactation = femaleind.DaysLactating; // Reference: Intake multiplier for lactating cow (M.Freer) // double intakeMilkMultiplier = 1 + 0.57 * Math.Pow((dayOfLactation / 81.0), 0.7) * Math.Exp(0.7 * (1 - (dayOfLactation / 81.0))); double intakeMilkMultiplier = 1 + ind.BreedParams.LactatingPotentialModifierConstantA * Math.Pow((dayOfLactation / ind.BreedParams.LactatingPotentialModifierConstantB), ind.BreedParams.LactatingPotentialModifierConstantC) * Math.Exp(ind.BreedParams.LactatingPotentialModifierConstantC * (1 - (dayOfLactation / ind.BreedParams.LactatingPotentialModifierConstantB))) * (1 - 0.5 + 0.5 * (ind.Weight / ind.NormalisedAnimalWeight)); // To make this flexible for sheep and goats, added three new Ruminant Coeffs // Feeding standard values for Beef, Dairy suck, Dairy non-suck and sheep are: // For 0.57 (A) use .42, .58, .85 and .69; for 0.7 (B) use 1.7, 0.7, 0.7 and 1.4, for 81 (C) use 62, 81, 81, 28 // added LactatingPotentialModifierConstantA, LactatingPotentialModifierConstantB and LactatingPotentialModifierConstantC // replaces (A), (B) and (C) potentialIntake *= intakeMilkMultiplier; // calculate estimated milk production for time step here // assuming average feed quality if no previous diet values // This need to happen before suckling potential intake can be determined. CalculateMilkProduction(femaleind); femaleind.MilkProducedThisTimeStep = femaleind.MilkProduction * 30.4; } else { femaleind.MilkProduction = 0; } } //TODO: option to restrict potential further due to stress (e.g. heat, cold, rain) } // get monthly intake potentialIntake *= 30.4; ind.PotentialIntake = potentialIntake; }
private void OnCLEMAnimalManage(object sender, EventArgs e) { // purchase details only on timer if (TimingOK) { // remove any old potential sales from list as these will be updated here Resources.RuminantHerd().PurchaseIndividuals.RemoveAll(a => a.Breed == this.PredictedHerdBreed & a.SaleFlag == HerdChangeReason.TradePurchase); foreach (RuminantTypeCohort purchasetype in this.Children.Where(a => a.GetType() == typeof(RuminantTypeCohort)).Cast <RuminantTypeCohort>()) { for (int i = 0; i < purchasetype.Number; i++) { object ruminantBase = null; if (purchasetype.Gender == Sex.Male) { ruminantBase = new RuminantMale(); } else { ruminantBase = new RuminantFemale(); } Ruminant ruminant = ruminantBase as Ruminant; ruminant.ID = 0; ruminant.BreedParams = herdToUse; ruminant.Breed = this.PredictedHerdBreed; ruminant.HerdName = this.PredictedHerdName; ruminant.Gender = purchasetype.Gender; ruminant.Age = purchasetype.Age; ruminant.PurchaseAge = purchasetype.Age; ruminant.SaleFlag = HerdChangeReason.TradePurchase; ruminant.Location = ""; double u1 = ZoneCLEM.RandomGenerator.NextDouble(); double u2 = ZoneCLEM.RandomGenerator.NextDouble(); double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); ruminant.Weight = purchasetype.Weight + purchasetype.WeightSD * randStdNormal; ruminant.PreviousWeight = ruminant.Weight; switch (purchasetype.Gender) { case Sex.Male: RuminantMale ruminantMale = ruminantBase as RuminantMale; ruminantMale.BreedingSire = false; break; case Sex.Female: RuminantFemale ruminantFemale = ruminantBase as RuminantFemale; ruminantFemale.DryBreeder = true; ruminantFemale.WeightAtConception = ruminant.Weight; ruminantFemale.NumberOfBirths = 0; break; default: break; } Resources.RuminantHerd().PurchaseIndividuals.Add(ruminantBase as Ruminant); } } //this.TriggerOnActivityPerformed(); } // sale details any timestep when conditions are met. foreach (Ruminant ind in this.CurrentHerd(true)) { if (ind.Age - ind.PurchaseAge >= MinMonthsKept & ind.Weight >= TradeWeight) { ind.SaleFlag = HerdChangeReason.TradeSale; } } }
private void OnCLEMAnimalManage(object sender, EventArgs e) { // purchase details only on timer if (TimingOK) { this.Status = ActivityStatus.NotNeeded; // remove any old potential sales from list as these will be updated here HerdResource.PurchaseIndividuals.RemoveAll(a => a.Breed == this.PredictedHerdBreed && a.SaleFlag == HerdChangeReason.TradePurchase); foreach (SpecifyRuminant purchaseSpecific in this.FindAllChildren <SpecifyRuminant>()) { RuminantTypeCohort purchasetype = purchaseSpecific.FindChild <RuminantTypeCohort>(); double number = purchasetype.Number; if (numberToStock != null && foodStore != null) { //NOTE: ensure calculation method in relationship is fixed values number = Convert.ToInt32(numberToStock.SolveY(foodStore.TonnesPerHectare), CultureInfo.InvariantCulture); } number *= purchaseSpecific.Proportion; for (int i = 0; i < Math.Ceiling(number); i++) { double u1 = RandomNumberGenerator.Generator.NextDouble(); double u2 = RandomNumberGenerator.Generator.NextDouble(); double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); double weight = purchasetype.Weight + purchasetype.WeightSD * randStdNormal; var ruminant = Ruminant.Create(purchasetype.Sex, herdToUse, purchasetype.Age, weight); ruminant.ID = 0; ruminant.Breed = purchaseSpecific.BreedParams.Name; ruminant.HerdName = purchaseSpecific.BreedParams.Breed; ruminant.PurchaseAge = purchasetype.Age; ruminant.SaleFlag = HerdChangeReason.TradePurchase; ruminant.Location = grazeStore; ruminant.PreviousWeight = ruminant.Weight; if (ruminant is RuminantFemale female) { female.WeightAtConception = ruminant.Weight; female.NumberOfBirths = 0; } HerdResource.PurchaseIndividuals.Add(ruminant); this.Status = ActivityStatus.Success; } } } // sale details any timestep when conditions are met. foreach (Ruminant ind in this.CurrentHerd(true)) { if (ind.Age - ind.PurchaseAge >= MinMonthsKept) { ind.SaleFlag = HerdChangeReason.TradeSale; this.Status = ActivityStatus.Success; } if (TradeWeight > 0 && ind.Weight >= TradeWeight) { ind.SaleFlag = HerdChangeReason.TradeSale; this.Status = ActivityStatus.Success; } } }
private void OnCLEMAnimalManage(object sender, EventArgs e) { // purchase details only on timer if (TimingOK) { this.Status = ActivityStatus.NotNeeded; // remove any old potential sales from list as these will be updated here Resources.RuminantHerd().PurchaseIndividuals.RemoveAll(a => a.Breed == this.PredictedHerdBreed && a.SaleFlag == HerdChangeReason.TradePurchase); foreach (RuminantTypeCohort purchasetype in this.Children.Where(a => a.GetType() == typeof(RuminantTypeCohort)).Cast <RuminantTypeCohort>()) { double number = purchasetype.Number; if (numberToStock != null && foodStore != null) { //NOTE: ensure calculation method in relationship is fixed values number = Convert.ToInt32(numberToStock.SolveY(foodStore.TonnesPerHectare), CultureInfo.InvariantCulture); } for (int i = 0; i < number; i++) { object ruminantBase = null; double u1 = RandomNumberGenerator.Generator.NextDouble(); double u2 = RandomNumberGenerator.Generator.NextDouble(); double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); double weight = purchasetype.Weight + purchasetype.WeightSD * randStdNormal; if (purchasetype.Gender == Sex.Male) { ruminantBase = new RuminantMale(purchasetype.Age, purchasetype.Gender, weight, herdToUse); } else { ruminantBase = new RuminantFemale(purchasetype.Age, purchasetype.Gender, weight, herdToUse); } Ruminant ruminant = ruminantBase as Ruminant; ruminant.ID = 0; ruminant.Breed = this.PredictedHerdBreed; ruminant.HerdName = this.PredictedHerdName; ruminant.PurchaseAge = purchasetype.Age; ruminant.SaleFlag = HerdChangeReason.TradePurchase; ruminant.Location = grazeStore; ruminant.PreviousWeight = ruminant.Weight; switch (purchasetype.Gender) { case Sex.Male: RuminantMale ruminantMale = ruminantBase as RuminantMale; ruminantMale.BreedingSire = false; break; case Sex.Female: RuminantFemale ruminantFemale = ruminantBase as RuminantFemale; ruminantFemale.DryBreeder = true; ruminantFemale.WeightAtConception = ruminant.Weight; ruminantFemale.NumberOfBirths = 0; break; default: break; } Resources.RuminantHerd().PurchaseIndividuals.Add(ruminantBase as Ruminant); this.Status = ActivityStatus.Success; } } } // sale details any timestep when conditions are met. foreach (Ruminant ind in this.CurrentHerd(true)) { if (ind.Age - ind.PurchaseAge >= MinMonthsKept) { ind.SaleFlag = HerdChangeReason.TradeSale; this.Status = ActivityStatus.Success; } if (TradeWeight > 0 && ind.Weight >= TradeWeight) { ind.SaleFlag = HerdChangeReason.TradeSale; this.Status = ActivityStatus.Success; } } }
/// <summary> /// Function to calculate energy from intake and subsequent growth /// </summary> /// <param name="ind">Ruminant individual class</param> /// <param name="methaneProduced">Sets output variable to value of methane produced</param> /// <returns></returns> private void CalculateEnergy(Ruminant ind, out double methaneProduced) { double intakeDaily = ind.Intake / 30.4; double potentialIntakeDaily = ind.PotentialIntake / 30.4; // Sme 1 for females and castrates // TODO: castrates not implemented double Sme = 1; // Sme 1.15 for all males. if (ind.Gender == Sex.Male) { Sme = 1.15; } double energyDiet = EnergyGross * ind.DietDryMatterDigestibility / 100.0; // Reference: Nutrient Requirements of domesticated ruminants (p7) double energyMetabolic = energyDiet * 0.81; double energyMetablicFromIntake = energyMetabolic * intakeDaily; double km = ind.BreedParams.EMaintCoefficient * energyMetabolic / EnergyGross + ind.BreedParams.EMaintIntercept; // Reference: SCA p.49 double kg = ind.BreedParams.EGrowthCoefficient * energyMetabolic / EnergyGross + ind.BreedParams.EGrowthIntercept; double energyPredictedBodyMassChange = 0; double energyMaintenance = 0; if (!ind.Weaned) { // calculate engergy and growth from milk intake // old code // dum = potential milk intake daily // dumshort = potential intake. check that it isnt monthly // Below now uses actual intake received rather than assume all potential intake is eaten double kml = 1; double kgl = 1; if ((ind.Intake + ind.MilkIntake) > 0) { // average energy efficiency for maintenance kml = ((ind.MilkIntake * 0.7) + (intakeDaily * km)) / (ind.MilkIntake + intakeDaily); // average energy efficiency for growth kgl = ((ind.MilkIntake * 0.7) + (intakeDaily * kg)) / (ind.MilkIntake + intakeDaily); } double energyMilkConsumed = ind.MilkIntake * 3.2; // limit calf intake of milk per day energyMilkConsumed = Math.Min(ind.BreedParams.MilkIntakeMaximum * 3.2, energyMilkConsumed); energyMaintenance = (ind.BreedParams.EMaintCoefficient * Math.Pow(ind.Weight, 0.75) / kml) * Math.Exp(-ind.BreedParams.EMaintExponent * ind.AgeZeroCorrected); ind.EnergyBalance = energyMilkConsumed - energyMaintenance + energyMetablicFromIntake; double feedingValue = 0; if (ind.EnergyBalance > 0) { feedingValue = 2 * 0.7 * ind.EnergyBalance / (kgl * energyMaintenance) - 1; } else { //(from Hirata model) feedingValue = 2 * ind.EnergyBalance / (0.85 * energyMaintenance) - 1; } double energyEmptyBodyGain = ind.BreedParams.GrowthEnergyIntercept1 + feedingValue + (ind.BreedParams.GrowthEnergyIntercept2 - feedingValue) / (1 + Math.Exp(-6 * (ind.Weight / ind.NormalisedAnimalWeight - 0.4))); energyPredictedBodyMassChange = ind.BreedParams.GrowthEfficiency * 0.7 * ind.EnergyBalance / energyEmptyBodyGain; } else { double energyMilk = 0; double energyFoetus = 0; if (ind.Gender == Sex.Female) { RuminantFemale femaleind = ind as RuminantFemale; // calculate energy for lactation if (femaleind.IsLactating) { // Reference: SCA p. double kl = ind.BreedParams.ELactationCoefficient * energyMetabolic / EnergyGross + ind.BreedParams.ELactationIntercept; double milkTime = Math.Max(0.0, (ind.Age - femaleind.AgeAtLastBirth + 1) * 30.4); if (milkTime <= ind.BreedParams.MilkingDays) { double milkCurve = 0; if (femaleind.DryBreeder) // no suckling calf { milkCurve = ind.BreedParams.MilkCurveNonSuckling; } else // suckling calf { milkCurve = ind.BreedParams.MilkCurveSuckling; } //TODO: check this equation that I redefined it correctly. double milkProduction = ind.BreedParams.MilkPeakYield * ind.Weight / ind.NormalisedAnimalWeight * (Math.Pow(((milkTime + ind.BreedParams.MilkOffsetDay) / ind.BreedParams.MilkPeakDay), milkCurve)) * Math.Exp(milkCurve * (1 - (milkTime + ind.BreedParams.MilkOffsetDay) / ind.BreedParams.MilkPeakDay)); milkProduction = Math.Max(milkProduction, 0.0); // Reference: Potential milk prodn, 3.2 MJ/kg milk - Jouven et al 2008 energyMilk = milkProduction * 3.2 / kl; if (ind.EnergyBalance < (-0.5936 / 0.322 * energyMilk)) { ind.EnergyBalance = (-0.5936 / 0.322 * energyMilk); } milkProduction = Math.Max(0.0, milkProduction * (0.5936 + 0.322 * ind.EnergyBalance / energyMilk)); // Reference: Adjusted milk prodn, 3.2 MJ/kg milk - Jouven et al 2008 energyMilk = milkProduction * 3.2 / kl; } } // Determine energy required for foetal development if (femaleind.IsPregnant) { double standardReferenceWeight = ind.StandardReferenceWeight; // Potential birth weight // Reference: Freer double potentialBirthWeight = ind.BreedParams.SRWBirth * standardReferenceWeight * (1 - 0.33 * (1 - ind.Weight / standardReferenceWeight)); double foetusAge = (femaleind.Age - femaleind.AgeAtLastConception + 1) * 30.4; //TODO: Check foetus gage correct energyFoetus = potentialBirthWeight * 349.16 * 0.000058 * Math.Exp(345.67 - 0.000058 * foetusAge - 349.16 * Math.Exp(-0.000058 * foetusAge)) / 0.13; } } //TODO: add draft energy requirement // set maintenance age to maximum of 6 years double maintenanceAge = Math.Min(ind.Age * 30.4, 2190); // Reference: SCA p.24 // Regference p19 (1.20). Does not include MEgraze or Ecold, also skips M, // 0.000082 is -0.03 Age in Years/365 for days energyMaintenance = ind.BreedParams.Kme * Sme * (0.26 * Math.Pow(ind.Weight, 0.75) / km) * Math.Exp(-0.000082 * maintenanceAge) + (0.09 * energyMetablicFromIntake); ind.EnergyBalance = energyMetablicFromIntake - energyMaintenance - energyMilk - energyFoetus; // milk will be zero for non lactating individuals. // Reference: Feeding_value = Ajustment for rate of loss or gain (SCA p.43, ? different from Hirata model) double feedingValue = 0; if (ind.EnergyBalance > 0) { feedingValue = 2 * ((kg * ind.EnergyBalance) / (km * energyMaintenance) - 1); } else { feedingValue = 2 * (ind.EnergyBalance / (0.8 * energyMaintenance) - 1); //(from Hirata model) } double weightToReferenceRatio = Math.Min(1.0, ind.Weight / ind.StandardReferenceWeight); // Reference: MJ of Energy required per kg Empty body gain (SCA p.43) double energyEmptyBodyGain = ind.BreedParams.GrowthEnergyIntercept1 + feedingValue + (ind.BreedParams.GrowthEnergyIntercept1 - feedingValue) / (1 + Math.Exp(-6 * (weightToReferenceRatio - 0.4))); // Determine Empty body change from Eebg and Ebal, and increase by 9% for LW change energyPredictedBodyMassChange = 0; if (ind.EnergyBalance > 0) { energyPredictedBodyMassChange = ind.BreedParams.GrowthEfficiency * kg * ind.EnergyBalance / energyEmptyBodyGain; } else { // Reference: from Hirata model energyPredictedBodyMassChange = ind.BreedParams.GrowthEfficiency * km * ind.EnergyBalance / (0.8 * energyEmptyBodyGain); } } energyPredictedBodyMassChange *= 30.4; // Convert to monthly ind.PreviousWeight = ind.Weight; ind.Weight += energyPredictedBodyMassChange; ind.Weight = Math.Max(0.0, ind.Weight); ind.Weight = Math.Min(ind.Weight, ind.StandardReferenceWeight * ind.BreedParams.MaximumSizeOfIndividual); // Function to calculate approximate methane produced by animal, based on feed intake // Function based on Freer spreadsheet methaneProduced = 0.02 * intakeDaily * ((13 + 7.52 * energyMetabolic) + energyMetablicFromIntake / energyMaintenance * (23.7 - 3.36 * energyMetabolic)); // MJ per day methaneProduced /= 55.28 * 1000; // grams per day }
private void OnCLEMAnimalBreeding(object sender, EventArgs e) { // RuminantHerd ruminantHerd = Resources.RuminantHerd(); List <Ruminant> herd = CurrentHerd(true); //ruminantHerd.Herd.Where(a => a.BreedParams.Name == HerdName).ToList(); // get list of all individuals of breeding age and condition // grouped by location var breeders = from ind in herd where (ind.Gender == Sex.Male & ind.Age >= ind.BreedParams.MinimumAge1stMating) || (ind.Gender == Sex.Female & ind.Age >= ind.BreedParams.MinimumAge1stMating & ind.Weight >= (ind.BreedParams.MinimumSize1stMating * ind.StandardReferenceWeight) ) group ind by ind.Location into grp select grp; // calculate labour and finance limitations if needed when doing AI int breedersCount = breeders.Count(); int numberPossible = breedersCount; int numberServiced = 1; if (UseAI & TimingOK) { // attempt to get required resources List <ResourceRequest> resourcesneeded = GetResourcesNeededForActivityLocal(); bool tookRequestedResources = TakeResources(resourcesneeded, true); // get all shortfalls if (tookRequestedResources & (ResourceRequestList != null)) { //TODO: fix this to account for perHead payments and labour and not fixed expenses double amountCashNeeded = resourcesneeded.Where(a => a.ResourceType == typeof(Finance)).Sum(a => a.Required); double amountCashProvided = resourcesneeded.Where(a => a.ResourceType == typeof(Finance)).Sum(a => a.Provided); double amountLabourNeeded = resourcesneeded.Where(a => a.ResourceType == typeof(Labour)).Sum(a => a.Required); double amountLabourProvided = resourcesneeded.Where(a => a.ResourceType == typeof(Labour)).Sum(a => a.Provided); double cashlimit = 1; if (amountCashNeeded > 0) { if (amountCashProvided == 0) { cashlimit = 0; } else { cashlimit = amountCashNeeded / amountCashProvided; } } double labourlimit = 1; if (amountLabourNeeded > 0) { if (amountLabourProvided == 0) { labourlimit = 0; } else { labourlimit = amountLabourNeeded / amountLabourProvided; } } double limiter = Math.Min(cashlimit, labourlimit); numberPossible = Convert.ToInt32(limiter * breedersCount); // TODO: determine if fixed payments were not possible // TODO: determine limits by insufficient labour or cash for per head payments } // report that this activity was performed as it does not use base GetResourcesRequired this.TriggerOnActivityPerformed(); } if (!UseAI) { // report that this activity was performed as it does not use base GetResourcesRequired this.TriggerOnActivityPerformed(); } // for each location where parts of this herd are located foreach (var location in breeders) { // determine all foetus and newborn mortality. foreach (RuminantFemale female in location.Where(a => a.Gender == Sex.Female).Cast <RuminantFemale>().ToList()) { if (female.IsPregnant) { // calculate foetus and newborn mortality // total mortality / (gestation months + 1) to get monthly mortality // done here before births to account for post birth motality as well.. double rnd = ZoneCLEM.RandomGenerator.NextDouble(); if (rnd < (female.BreedParams.PrenatalMortality / (female.BreedParams.GestationLength + 1))) { female.OneOffspringDies(); } } } // check for births of all pregnant females. foreach (RuminantFemale female in location.Where(a => a.Gender == Sex.Female).Cast <RuminantFemale>().ToList()) { if (female.BirthDue) { female.WeightLossDueToCalf = 0; int numberOfNewborn = (female.CarryingTwins) ? 2 : 1; for (int i = 0; i < numberOfNewborn; i++) { // Foetal mortality is now performed each timestep at base of this method object newCalf = null; bool isMale = (ZoneCLEM.RandomGenerator.NextDouble() > 0.5); if (isMale) { newCalf = new RuminantMale(); } else { newCalf = new RuminantFemale(); } Ruminant newCalfRuminant = newCalf as Ruminant; newCalfRuminant.Age = 0; newCalfRuminant.HerdName = female.HerdName; newCalfRuminant.BreedParams = female.BreedParams; newCalfRuminant.Breed = female.BreedParams.Breed; newCalfRuminant.Gender = (isMale) ? Sex.Male : Sex.Female; newCalfRuminant.ID = Resources.RuminantHerd().NextUniqueID; newCalfRuminant.Location = female.Location; newCalfRuminant.Mother = female; newCalfRuminant.Number = 1; newCalfRuminant.SetUnweaned(); // calf weight from Freer newCalfRuminant.Weight = female.BreedParams.SRWBirth * female.StandardReferenceWeight * (1 - 0.33 * (1 - female.Weight / female.StandardReferenceWeight)); newCalfRuminant.HighWeight = newCalfRuminant.Weight; newCalfRuminant.SaleFlag = HerdChangeReason.Born; Resources.RuminantHerd().AddRuminant(newCalfRuminant); // add to sucklings female.SucklingOffspring.Add(newCalfRuminant); // remove calf weight from female female.WeightLossDueToCalf += newCalfRuminant.Weight; } female.UpdateBirthDetails(); } } // uncontrolled conception if (!UseAI) { // check if males and females of breeding condition are together if (location.GroupBy(a => a.Gender).Count() == 2) { // servicing rate int maleCount = location.Where(a => a.Gender == Sex.Male).Count(); int femaleCount = location.Where(a => a.Gender == Sex.Female).Count(); double matingsPossible = maleCount * location.FirstOrDefault().BreedParams.MaximumMaleMatingsPerDay * 30; double maleLimiter = Math.Min(1.0, matingsPossible / femaleCount); foreach (RuminantFemale female in location.Where(a => a.Gender == Sex.Female).Cast <RuminantFemale>().ToList()) { if (!female.IsPregnant && !female.IsLactating && (female.Age - female.AgeAtLastBirth) * 30.4 >= female.BreedParams.MinimumDaysBirthToConception) { // calculate conception double conceptionRate = ConceptionRate(female) * maleLimiter; conceptionRate = Math.Min(conceptionRate, MaximumConceptionRateUncontrolled); if (ZoneCLEM.RandomGenerator.NextDouble() <= conceptionRate) { female.UpdateConceptionDetails(ZoneCLEM.RandomGenerator.NextDouble() < female.BreedParams.TwinRate, conceptionRate); } } } } } // controlled conception else { if (this.TimingOK) { foreach (RuminantFemale female in location.Where(a => a.Gender == Sex.Female).Cast <RuminantFemale>().ToList()) { if (!female.IsPregnant && !female.IsLactating && (female.Age - female.AgeAtLastBirth) * 30.4 >= female.BreedParams.MinimumDaysBirthToConception) { // calculate conception double conceptionRate = ConceptionRate(female); if (numberServiced <= numberPossible) // labour/finance limited number { if (ZoneCLEM.RandomGenerator.NextDouble() <= conceptionRate) { female.UpdateConceptionDetails(ZoneCLEM.RandomGenerator.NextDouble() < female.BreedParams.TwinRate, conceptionRate); } numberServiced++; } } } } } } }
private void OnCLEMAnimalBreeding(object sender, EventArgs e) { this.Status = ActivityStatus.NotNeeded; NumberConceived = 0; // get list of all pregnant females List <RuminantFemale> pregnantherd = CurrentHerd(true).Where(a => a.Gender == Sex.Female).Cast <RuminantFemale>().Where(a => a.IsPregnant).ToList(); // determine all fetus and newborn mortality of all pregnant females. foreach (RuminantFemale female in pregnantherd) { // calculate fetus and newborn mortality // total mortality / (gestation months + 1) to get monthly mortality // done here before births to account for post birth motality as well.. // IsPregnant status does not change until births occur in next section so will include mortality in month of birth // needs to be calculated for each offspring carried. for (int i = 0; i < female.CarryingCount; i++) { if (RandomNumberGenerator.Generator.NextDouble() < (female.BreedParams.PrenatalMortality / (female.BreedParams.GestationLength + 1))) { female.OneOffspringDies(); if (female.NumberOfOffspring == 0) { // report conception status changed when last multiple birth dies. female.BreedParams.OnConceptionStatusChanged(new Reporting.ConceptionStatusChangedEventArgs(Reporting.ConceptionStatus.Failed, female, Clock.Today)); } } } if (female.BirthDue) { int numberOfNewborn = female.CarryingCount; for (int i = 0; i < numberOfNewborn; i++) { object newCalf = null; bool isMale = (RandomNumberGenerator.Generator.NextDouble() <= female.BreedParams.ProportionOffspringMale); double weight = female.BreedParams.SRWBirth * female.StandardReferenceWeight * (1 - 0.33 * (1 - female.Weight / female.StandardReferenceWeight)); if (isMale) { newCalf = new RuminantMale(0, Sex.Male, weight, female.BreedParams); } else { newCalf = new RuminantFemale(0, Sex.Female, weight, female.BreedParams); } Ruminant newCalfRuminant = newCalf as Ruminant; newCalfRuminant.HerdName = female.HerdName; newCalfRuminant.Breed = female.BreedParams.Breed; newCalfRuminant.ID = Resources.RuminantHerd().NextUniqueID; newCalfRuminant.Location = female.Location; newCalfRuminant.Mother = female; newCalfRuminant.Number = 1; newCalfRuminant.SetUnweaned(); // calf weight from Freer newCalfRuminant.PreviousWeight = newCalfRuminant.Weight; newCalfRuminant.SaleFlag = HerdChangeReason.Born; // add attributes inherited from mother foreach (var attribute in female.Attributes) { newCalfRuminant.AddAttribute(attribute.Key, attribute.Value.GetInheritedAttribute() as ICLEMAttribute); } Resources.RuminantHerd().AddRuminant(newCalfRuminant, this); // add to sucklings female.SucklingOffspringList.Add(newCalfRuminant); // this now reports for each individual born not a birth event as individual wean events are reported female.BreedParams.OnConceptionStatusChanged(new Reporting.ConceptionStatusChangedEventArgs(Reporting.ConceptionStatus.Birth, female, Clock.Today)); } female.UpdateBirthDetails(); this.Status = ActivityStatus.Success; } } // Perform breeding IEnumerable <Ruminant> herd = null; if (useControlledMating && controlledMating.TimingOK) { // determined by controlled mating and subsequent timer (e.g. smart milking) herd = controlledMating.BreedersToMate(); this.TriggerOnActivityPerformed(); } else if (!useControlledMating && TimingOK) { // whole herd for activity herd = CurrentHerd(true); // report that this activity was performed as it does not use base GetResourcesRequired this.TriggerOnActivityPerformed(); } if (herd != null && herd.Count() > 0) { // group by location var breeders = from ind in herd where ind.IsAbleToBreed group ind by ind.Location into grp select grp; int breedersCount = breeders.Count(); int numberPossible = breedersCount; int numberServiced = 1; List <Ruminant> maleBreeders = new List <Ruminant>(); // for each location where parts of this herd are located foreach (var location in breeders) { numberPossible = -1; if (useControlledMating) { numberPossible = Convert.ToInt32(location.Where(a => a.Gender == Sex.Female).Count(), CultureInfo.InvariantCulture); } else { numberPossible = 0; // uncontrolled conception if (location.GroupBy(a => a.Gender).Count() == 2) { int maleCount = location.Where(a => a.Gender == Sex.Male).Count(); // get a list of males to provide attributes when incontrolled mating. if (maleCount > 0 && location.FirstOrDefault().BreedParams.IncludedAttributeInheritanceWhenMating) { maleBreeders = location.Where(a => a.Gender == Sex.Male).ToList(); } int femaleCount = location.Where(a => a.Gender == Sex.Female).Count(); numberPossible = Convert.ToInt32(Math.Ceiling(maleCount * location.FirstOrDefault().BreedParams.MaximumMaleMatingsPerDay * 30), CultureInfo.InvariantCulture); } } numberServiced = 1; foreach (RuminantFemale female in location.Where(a => a.Gender == Sex.Female).Cast <RuminantFemale>().Where(a => !a.IsPregnant & a.Age <= a.BreedParams.MaximumAgeMating).ToList()) { Reporting.ConceptionStatus status = Reporting.ConceptionStatus.NotMated; if (numberServiced <= numberPossible) { // calculate conception double conceptionRate = ConceptionRate(female, out status); if (conceptionRate > 0) { if (RandomNumberGenerator.Generator.NextDouble() <= conceptionRate) { female.UpdateConceptionDetails(female.CalulateNumberOfOffspringThisPregnancy(), conceptionRate, 0); // if mandatory attributes are present in the herd, save male value with female details. if (female.BreedParams.IncludedAttributeInheritanceWhenMating) { if (useControlledMating) { // save all male attributes AddMalesAttributeDetails(female, controlledMating.SireAttributes); } else { // randomly select male AddMalesAttributeDetails(female, maleBreeders[RandomNumberGenerator.Generator.Next(0, maleBreeders.Count() - 1)]); } } status = Reporting.ConceptionStatus.Conceived; NumberConceived++; } } numberServiced++; this.Status = ActivityStatus.Success; } // report change in breeding status // do not report for -1 (controlled mating outside timing) if (numberPossible >= 0 && status != Reporting.ConceptionStatus.NotAvailable) { female.BreedParams.OnConceptionStatusChanged(new Reporting.ConceptionStatusChangedEventArgs(status, female, Clock.Today)); } } // report a natural mating locations for transparency via a message if (numberServiced > 0 & !useControlledMating) { string warning = "Natural (uncontrolled) mating ocurred in [r=" + location.Key + "]"; if (!Warnings.Exists(warning)) { Warnings.Add(warning); Summary.WriteMessage(this, warning); } } } } }
private void OnCLEMAnimalBreeding(object sender, EventArgs e) { List <Ruminant> herd = CurrentHerd(true); int aDay = Clock.Today.Year; // get list of all individuals of breeding age and condition // grouped by location var breeders = from ind in herd where ind.IsBreedingCondition group ind by ind.Location into grp select grp; // calculate labour and finance limitations if needed when doing AI int breedersCount = breeders.Count(); int numberPossible = breedersCount; int numberServiced = 1; double limiter = 1; if (UseAI && TimingOK) { // attempt to get required resources List <ResourceRequest> resourcesneeded = GetResourcesNeededForActivityLocal(); CheckResources(resourcesneeded, Guid.NewGuid()); bool tookRequestedResources = TakeResources(resourcesneeded, true); // get all shortfalls if (tookRequestedResources && (ResourceRequestList != null)) { //TODO: fix this to account for perHead payments and labour and not fixed expenses double amountCashNeeded = resourcesneeded.Where(a => a.ResourceType == typeof(Finance)).Sum(a => a.Required); double amountCashProvided = resourcesneeded.Where(a => a.ResourceType == typeof(Finance)).Sum(a => a.Provided); double amountLabourNeeded = resourcesneeded.Where(a => a.ResourceType == typeof(Labour)).Sum(a => a.Required); double amountLabourProvided = resourcesneeded.Where(a => a.ResourceType == typeof(Labour)).Sum(a => a.Provided); double cashlimit = 1; if (amountCashNeeded > 0) { cashlimit = amountCashProvided == 0 ? 0 : amountCashNeeded / amountCashProvided; } double labourlimit = 1; if (amountLabourNeeded > 0) { labourlimit = amountLabourProvided == 0 ? 0 : amountLabourNeeded / amountLabourProvided; } limiter = Math.Min(cashlimit, labourlimit); // TODO: determine if fixed payments were not possible // TODO: determine limits by insufficient labour or cash for per head payments } // report that this activity was performed as it does not use base GetResourcesRequired this.TriggerOnActivityPerformed(); } if (!UseAI) { // report that this activity was performed as it does not use base GetResourcesRequired this.TriggerOnActivityPerformed(); this.Status = ActivityStatus.NotNeeded; } // for each location where parts of this herd are located foreach (var location in breeders) { // determine all fetus and newborn mortality of all pregnant females. foreach (RuminantFemale female in location.Where(a => a.Gender == Sex.Female).Cast <RuminantFemale>().Where(a => a.IsPregnant).ToList()) { // calculate fetus and newborn mortality // total mortality / (gestation months + 1) to get monthly mortality // done here before births to account for post birth motality as well.. // IsPregnant status does not change until births occur in next section so will include mortality in month of birth // needs to be caclulated for each offspring carried. for (int i = 0; i < female.CarryingCount; i++) { if (RandomNumberGenerator.Generator.NextDouble() < (female.BreedParams.PrenatalMortality / (female.BreedParams.GestationLength + 1))) { female.OneOffspringDies(); if (female.NumberOfOffspring == 0) { // report conception status changed when last multiple birth dies. female.BreedParams.OnConceptionStatusChanged(new Reporting.ConceptionStatusChangedEventArgs(Reporting.ConceptionStatus.Failed, female, Clock.Today)); } } } } // check for births of all pregnant females. int month = Clock.Today.Month; foreach (RuminantFemale female in location.Where(a => a.Gender == Sex.Female).Cast <RuminantFemale>().ToList()) { if (female.BirthDue) { int numberOfNewborn = female.CarryingCount; for (int i = 0; i < numberOfNewborn; i++) { // Foetal mortality is now performed each timestep at base of this method object newCalf = null; bool isMale = (RandomNumberGenerator.Generator.NextDouble() <= female.BreedParams.ProportionOffspringMale); double weight = female.BreedParams.SRWBirth * female.StandardReferenceWeight * (1 - 0.33 * (1 - female.Weight / female.StandardReferenceWeight)); if (isMale) { newCalf = new RuminantMale(0, Sex.Male, weight, female.BreedParams); } else { newCalf = new RuminantFemale(0, Sex.Female, weight, female.BreedParams); } Ruminant newCalfRuminant = newCalf as Ruminant; newCalfRuminant.HerdName = female.HerdName; newCalfRuminant.Breed = female.BreedParams.Breed; newCalfRuminant.ID = Resources.RuminantHerd().NextUniqueID; newCalfRuminant.Location = female.Location; newCalfRuminant.Mother = female; newCalfRuminant.Number = 1; newCalfRuminant.SetUnweaned(); // calf weight from Freer newCalfRuminant.PreviousWeight = newCalfRuminant.Weight; newCalfRuminant.SaleFlag = HerdChangeReason.Born; Resources.RuminantHerd().AddRuminant(newCalfRuminant, this); // add to sucklings female.SucklingOffspringList.Add(newCalfRuminant); // this now reports for each individual born not a birth event as individual wean events are reported female.BreedParams.OnConceptionStatusChanged(new Reporting.ConceptionStatusChangedEventArgs(Reporting.ConceptionStatus.Birth, female, Clock.Today)); } female.UpdateBirthDetails(); this.Status = ActivityStatus.Success; } } numberPossible = -1; if (!UseAI) { numberPossible = 0; // uncontrolled conception if (location.GroupBy(a => a.Gender).Count() == 2) { int maleCount = location.Where(a => a.Gender == Sex.Male).Count(); int femaleCount = location.Where(a => a.Gender == Sex.Female).Count(); numberPossible = Convert.ToInt32(Math.Ceiling(maleCount * location.FirstOrDefault().BreedParams.MaximumMaleMatingsPerDay * 30), CultureInfo.InvariantCulture); } } else { // controlled mating (AI) if (this.TimingOK) { numberPossible = Convert.ToInt32(limiter * location.Where(a => a.Gender == Sex.Female).Count(), CultureInfo.InvariantCulture); } } numberServiced = 1; foreach (RuminantFemale female in location.Where(a => a.Gender == Sex.Female).Cast <RuminantFemale>().Where(a => !a.IsPregnant & a.Age <= a.BreedParams.MaximumAgeMating).ToList()) { Reporting.ConceptionStatus status = Reporting.ConceptionStatus.NotMated; if (numberServiced <= numberPossible) { // calculate conception double conceptionRate = ConceptionRate(female, out status); if (conceptionRate > 0) { if (RandomNumberGenerator.Generator.NextDouble() <= conceptionRate) { female.UpdateConceptionDetails(female.CalulateNumberOfOffspringThisPregnancy(), conceptionRate, 0); status = Reporting.ConceptionStatus.Conceived; } } numberServiced++; this.Status = ActivityStatus.Success; } // report change in breeding status // do not report for -1 (controlled mating outside timing) if (numberPossible >= 0 && status != Reporting.ConceptionStatus.NotAvailable) { female.BreedParams.OnConceptionStatusChanged(new Reporting.ConceptionStatusChangedEventArgs(status, female, Clock.Today)); } } // report a natural mating locations for transparency via a message if (this.Status == ActivityStatus.Success && !UseAI) { string warning = "Natural (uncontrolled) mating ocurred in [r=" + location.Key + "]"; if (!Warnings.Exists(warning)) { Warnings.Add(warning); Summary.WriteMessage(this, warning); } } } }
/// <summary> /// Constructor /// </summary> /// <param name="status">Change status</param> /// <param name="female">Individual being reported</param> /// <param name="dateTime">Current clock</param> /// <param name="offspring">The offspring related to</param> public ConceptionStatusChangedEventArgs(ConceptionStatus status, RuminantFemale female, DateTime dateTime, Ruminant offspring = null) { Status = status; Female = female; // Calculate conception date switch (Status) { case ConceptionStatus.Conceived: case ConceptionStatus.Failed: case ConceptionStatus.Birth: ConceptionDate = dateTime.AddMonths(-1 * Convert.ToInt32(female.Age - female.AgeAtLastConception, CultureInfo.InvariantCulture)); ConceptionDate = new DateTime(ConceptionDate.Year, ConceptionDate.Month, DateTime.DaysInMonth(ConceptionDate.Year, ConceptionDate.Month)); break; case ConceptionStatus.Weaned: if (offspring is null) { throw new ArgumentException("Code logice error: An offspring must be supplied in ConceptionStatusChangedEventArgs when status is Weaned"); } ConceptionDate = dateTime.AddMonths(-1 * Convert.ToInt32(offspring.Age + female.BreedParams.GestationLength, CultureInfo.InvariantCulture)); ConceptionDate = new DateTime(ConceptionDate.Year, ConceptionDate.Month, DateTime.DaysInMonth(ConceptionDate.Year, ConceptionDate.Month)); break; case ConceptionStatus.Unsuccessful: case ConceptionStatus.NotMated: case ConceptionStatus.NotReady: ConceptionDate = dateTime; break; default: break; } }
private void CalculatePotentialIntake(Ruminant ind) { // calculate daily potential intake for the selected individual/cohort double standardReferenceWeight = ind.StandardReferenceWeight; ind.NormalisedAnimalWeight = standardReferenceWeight - ((1 - ind.BreedParams.SRWBirth) * standardReferenceWeight) * Math.Exp(-(ind.BreedParams.AgeGrowthRateCoefficient * (ind.Age * 30.4)) / (Math.Pow(standardReferenceWeight, ind.BreedParams.SRWGrowthScalar))); double liveWeightForIntake = ind.NormalisedAnimalWeight; ind.HighWeight = Math.Max(ind.HighWeight, ind.Weight); if (ind.HighWeight < ind.NormalisedAnimalWeight) { liveWeightForIntake = ind.HighWeight; } // Calculate potential intake based on current weight compared to SRW and previous highest weight double potentialIntake = 0; // calculate milk intake shortfall for sucklings if (!ind.Weaned) { // potential milk intake/animal/day double potentialMilkIntake = ind.BreedParams.MilkIntakeIntercept + ind.BreedParams.IntakeCoefficient * ind.Weight; // get mother ind.MilkIntake = Math.Min(potentialMilkIntake, ind.MothersMilkAvailable); // if milk supply low, calf will subsitute forage up to a specified % of bodyweight (R_C60) if (ind.MilkIntake < ind.Weight * ind.BreedParams.MilkLWTFodderSubstitutionProportion) { potentialIntake = Math.Max(0.0, ind.Weight * ind.BreedParams.MaxJuvenileIntake - ind.MilkIntake * ind.BreedParams.ProportionalDiscountDueToMilk); } // This has been removed and replaced with prop of LWT based on milk supply. // Reference: SCA Metabolic LWTs //potentialIntake = ind.BreedParams.IntakeCoefficient * standardReferenceWeight * (Math.Pow(liveWeightForIntake, 0.75) / Math.Pow(standardReferenceWeight, 0.75)) * (ind.BreedParams.IntakeIntercept - (Math.Pow(liveWeightForIntake, 0.75) / Math.Pow(standardReferenceWeight, 0.75))); } else { // Reference: SCA based actual LWTs potentialIntake = ind.BreedParams.IntakeCoefficient * liveWeightForIntake * (ind.BreedParams.IntakeIntercept - liveWeightForIntake / standardReferenceWeight); if (ind.Gender == Sex.Female) { RuminantFemale femaleind = ind as RuminantFemale; // Increase potential intake for lactating breeder if (femaleind.IsLactating) { double dayOfLactation = Math.Max((ind.Age - femaleind.AgeAtLastBirth) * 30.4, 0); if (dayOfLactation > ind.BreedParams.MilkingDays) { // Reference: Intake multiplier for lactating cow (M.Freer) // TODO: Need to look at equation to fix Math.Pow() ^ issue // double intakeMilkMultiplier = 1 + 0.57 * Math.Pow((dayOfLactation / 81.0), 0.7) * Math.Exp(0.7 * (1 - (dayOfLactation / 81.0))); double intakeMilkMultiplier = 1 + ind.BreedParams.LactatingPotentialModifierConstantA * Math.Pow((dayOfLactation / ind.BreedParams.LactatingPotentialModifierConstantB), ind.BreedParams.LactatingPotentialModifierConstantC) * Math.Exp(ind.BreedParams.LactatingPotentialModifierConstantC * (1 - (dayOfLactation / ind.BreedParams.LactatingPotentialModifierConstantB))); // To make this flexible for sheep and goats, added three new Ruminant Coeffs // Feeding standard values for Beef, Dairy suck, Dairy non-suck and sheep are: // For 0.57 (A) use .42, .58, .85 and .69; for 0.7 (B) use 1.7, 0.7, 0.7 and 1.4, for 81 (C) use 62, 81, 81, 28 // added LactatingPotentialModifierConstantA, LactatingPotentialModifierConstantB and LactatingPotentialModifierConstantC potentialIntake *= intakeMilkMultiplier; } } } //TODO: option to restrict potential further due to stress (e.g. heat, cold, rain) // get monthly intake potentialIntake *= 30.4; } ind.PotentialIntake = potentialIntake; }
/// <summary> /// Function to calculate energy from intake and subsequent growth /// </summary> /// <param name="ind">Ruminant individual class</param> /// <param name="methaneProduced">Sets output variable to value of methane produced</param> /// <returns></returns> private void CalculateEnergy(Ruminant ind, out double methaneProduced) { double intakeDaily = ind.MetabilicIntake / 30.4; // Sme 1 for females and castrates // TODO: castrates not implemented double sme = 1; // Sme 1.15 for all males. if (ind.Gender == Sex.Male) { sme = 1.15; } double energyDiet = EnergyGross * ind.DietDryMatterDigestibility / 100.0; // Reference: Nutrient Requirements of domesticated ruminants (p7) double energyMetabolic = energyDiet * 0.81; double energyMetablicFromIntake = energyMetabolic * intakeDaily; double km = ind.BreedParams.EMaintEfficiencyCoefficient * energyMetabolic / EnergyGross + ind.BreedParams.EMaintEfficiencyIntercept; // Reference: SCA p.49 double kg = ind.BreedParams.EGrowthEfficiencyCoefficient * energyMetabolic / EnergyGross + ind.BreedParams.EGrowthEfficiencyIntercept; double energyPredictedBodyMassChange; double energyMaintenance; if (!ind.Weaned) { // calculate engergy and growth from milk intake // recalculate milk intake based on mothers updated milk production for the time step double potentialMilkIntake = ind.BreedParams.MilkIntakeIntercept + ind.BreedParams.MilkIntakeCoefficient * ind.Weight; ind.MilkIntake = Math.Min(potentialMilkIntake, ind.MothersMilkProductionAvailable); if (ind.Mother != null) { ind.Mother.TakeMilk(ind.MilkIntake * 30.4, MilkUseReason.Suckling); } // Below now uses actual intake received rather than assume all potential intake is eaten double kml = 1; double kgl = 1; if ((ind.MetabilicIntake + ind.MilkIntake) > 0) { // average energy efficiency for maintenance kml = ((ind.MilkIntake * 0.7) + (intakeDaily * km)) / (ind.MilkIntake + intakeDaily); // average energy efficiency for growth kgl = ((ind.MilkIntake * 0.7) + (intakeDaily * kg)) / (ind.MilkIntake + intakeDaily); } double energyMilkConsumed = ind.MilkIntake * 3.2; // limit calf intake of milk per day energyMilkConsumed = Math.Min(ind.BreedParams.MilkIntakeMaximum * 3.2, energyMilkConsumed); energyMaintenance = (ind.BreedParams.EMaintCoefficient * Math.Pow(ind.Weight, 0.75) / kml) * Math.Exp(-ind.BreedParams.EMaintExponent * ind.AgeZeroCorrected); ind.EnergyBalance = energyMilkConsumed - energyMaintenance + energyMetablicFromIntake; double feedingValue; if (ind.EnergyBalance > 0) { feedingValue = 2 * 0.7 * ind.EnergyBalance / (kgl * energyMaintenance) - 1; } else { //(from Hirata model) feedingValue = 2 * ind.EnergyBalance / (0.85 * energyMaintenance) - 1; } double energyEmptyBodyGain = ind.BreedParams.GrowthEnergyIntercept1 + feedingValue + (ind.BreedParams.GrowthEnergyIntercept2 - feedingValue) / (1 + Math.Exp(-6 * (ind.Weight / ind.NormalisedAnimalWeight - 0.4))); energyPredictedBodyMassChange = ind.BreedParams.GrowthEfficiency * 0.7 * ind.EnergyBalance / energyEmptyBodyGain; } else { double energyMilk = 0; double energyFoetus = 0; if (ind.Gender == Sex.Female) { RuminantFemale femaleind = ind as RuminantFemale; // calculate energy for lactation if (femaleind.IsLactating) { // recalculate milk production based on DMD of food provided energyMilk = CalculateMilkProduction(femaleind); } else { femaleind.MilkProductionPotential = 0; } // Determine energy required for foetal development if (femaleind.IsPregnant) { double standardReferenceWeight = ind.StandardReferenceWeight; // Potential birth weight // Reference: Freer double potentialBirthWeight = ind.BreedParams.SRWBirth * standardReferenceWeight * (1 - 0.33 * (1 - ind.Weight / standardReferenceWeight)); double foetusAge = (femaleind.Age - femaleind.AgeAtLastConception) * 30.4; //TODO: Check foetus gage correct energyFoetus = potentialBirthWeight * 349.16 * 0.000058 * Math.Exp(345.67 - 0.000058 * foetusAge - 349.16 * Math.Exp(-0.000058 * foetusAge)) / 0.13; } } //TODO: add draft individual energy requirement // set maintenance age to maximum of 6 years (2190 days). Now uses EnergeyMaintenanceMaximumAge double maintenanceAge = Math.Min(ind.Age * 30.4, ind.BreedParams.EnergyMaintenanceMaximumAge * 365); // Reference: SCA p.24 // Reference p19 (1.20). Does not include MEgraze or Ecold, also skips M, // 0.000082 is -0.03 Age in Years/365 for days energyMaintenance = ind.BreedParams.Kme * sme * (ind.BreedParams.EMaintCoefficient * Math.Pow(ind.Weight, 0.75) / km) * Math.Exp(-ind.BreedParams.EMaintExponent * maintenanceAge) + (ind.BreedParams.EMaintIntercept * energyMetablicFromIntake); ind.EnergyBalance = energyMetablicFromIntake - energyMaintenance - energyMilk - energyFoetus; // milk will be zero for non lactating individuals. double feedingValue; // Reference: Feeding_value = Ajustment for rate of loss or gain (SCA p.43, ? different from Hirata model) if (ind.EnergyBalance > 0) { feedingValue = 2 * ((kg * ind.EnergyBalance) / (km * energyMaintenance) - 1); } else { feedingValue = 2 * (ind.EnergyBalance / (0.8 * energyMaintenance) - 1); //(from Hirata model) } double weightToReferenceRatio = Math.Min(1.0, ind.Weight / ind.StandardReferenceWeight); // Reference: MJ of Energy required per kg Empty body gain (SCA p.43) double energyEmptyBodyGain = ind.BreedParams.GrowthEnergyIntercept1 + feedingValue + (ind.BreedParams.GrowthEnergyIntercept1 - feedingValue) / (1 + Math.Exp(-6 * (weightToReferenceRatio - 0.4))); // Determine Empty body change from Eebg and Ebal, and increase by 9% for LW change if (ind.EnergyBalance > 0) { energyPredictedBodyMassChange = ind.BreedParams.GrowthEfficiency * kg * ind.EnergyBalance / energyEmptyBodyGain; } else { // Reference: from Hirata model energyPredictedBodyMassChange = ind.BreedParams.GrowthEfficiency * km * ind.EnergyBalance / (0.8 * energyEmptyBodyGain); } } energyPredictedBodyMassChange *= 30.4; // Convert to monthly ind.PreviousWeight = ind.Weight; if (ind.Gender == Sex.Female && (ind as RuminantFemale).BirthDue) { ind.Weight -= (ind as RuminantFemale).WeightLossDueToCalf; } ind.Weight += energyPredictedBodyMassChange; ind.Weight = Math.Max(0.0, ind.Weight); ind.Weight = Math.Min(ind.Weight, ind.StandardReferenceWeight * ind.BreedParams.MaximumSizeOfIndividual); // Function to calculate approximate methane produced by animal, based on feed intake // Function based on Freer spreadsheet // methane is 0.02 * intakeDaily * ((13 + 7.52 * energyMetabolic) + energyMetablicFromIntake / energyMaintenance * (23.7 - 3.36 * energyMetabolic)); // MJ per day // methane is methaneProduced / 55.28 * 1000; // grams per day // Charmely et al 2016 can be substituted by intercept = 0 and coefficient = 20.7 methaneProduced = ind.BreedParams.MethaneProductionCoefficient * intakeDaily; }
private void OnCLEMAnimalBreeding(object sender, EventArgs e) { this.Status = ActivityStatus.NotNeeded; NumberConceived = 0; // get list of all pregnant females List <RuminantFemale> pregnantherd = CurrentHerd(true).OfType <RuminantFemale>().Where(a => a.IsPregnant).ToList(); // determine all fetus and newborn mortality of all pregnant females. foreach (RuminantFemale female in pregnantherd) { // calculate fetus and newborn mortality // total mortality / (gestation months + 1) to get monthly mortality // done here before births to account for post birth motality as well.. // IsPregnant status does not change until births occur in next section so will include mortality in month of birth // needs to be calculated for each offspring carried. for (int i = 0; i < female.CarryingCount; i++) { var rnd = RandomNumberGenerator.Generator.NextDouble(); if (rnd < (female.BreedParams.PrenatalMortality / (female.BreedParams.GestationLength + 1))) { female.OneOffspringDies(); if (female.NumberOfOffspring == 0) { // report conception status changed when last multiple birth dies. female.BreedParams.OnConceptionStatusChanged(new Reporting.ConceptionStatusChangedEventArgs(Reporting.ConceptionStatus.Failed, female, clock.Today)); } } } if (female.BirthDue) { int numberOfNewborn = female.CarryingCount; for (int i = 0; i < numberOfNewborn; i++) { bool isMale = RandomNumberGenerator.Generator.NextDouble() <= female.BreedParams.ProportionOffspringMale; Sex sex = isMale ? Sex.Male : Sex.Female; double weight = female.BreedParams.SRWBirth * female.StandardReferenceWeight * (1 - 0.33 * (1 - female.Weight / female.StandardReferenceWeight)); Ruminant newSucklingRuminant = Ruminant.Create(sex, female.BreedParams, 0, weight); newSucklingRuminant.HerdName = female.HerdName; newSucklingRuminant.Breed = female.BreedParams.Breed; newSucklingRuminant.ID = HerdResource.NextUniqueID; newSucklingRuminant.Location = female.Location; newSucklingRuminant.Mother = female; newSucklingRuminant.Number = 1; newSucklingRuminant.SetUnweaned(); // suckling/calf weight from Freer newSucklingRuminant.PreviousWeight = newSucklingRuminant.Weight; newSucklingRuminant.SaleFlag = HerdChangeReason.Born; // add attributes inherited from mother foreach (var attribute in female.Attributes.Items) { if (attribute.Value != null) { newSucklingRuminant.Attributes.Add(attribute.Key, attribute.Value.GetInheritedAttribute() as IIndividualAttribute); } } HerdResource.AddRuminant(newSucklingRuminant, this); // add to sucklings female.SucklingOffspringList.Add(newSucklingRuminant); // this now reports for each individual born not a birth event as individual wean events are reported female.BreedParams.OnConceptionStatusChanged(new Reporting.ConceptionStatusChangedEventArgs(Reporting.ConceptionStatus.Birth, female, clock.Today)); } female.UpdateBirthDetails(); this.Status = ActivityStatus.Success; } } // Perform breeding IEnumerable <Ruminant> herd = null; if (useControlledMating && controlledMating.TimingOK) { // determined by controlled mating and subsequent timer (e.g. smart milking) herd = controlledMating.BreedersToMate(); } else if (!useControlledMating && TimingOK) { // whole herd for activity including males herd = CurrentHerd(true); } if (herd != null && herd.Any()) { // group by location var breeders = from ind in herd where ind.IsAbleToBreed group ind by ind.Location into grp select grp; // identify not ready for reporting and tracking var notReadyBreeders = herd.Where(a => a.Sex == Sex.Female).Cast <RuminantFemale>().Where(a => a.IsBreeder && !a.IsAbleToBreed && !a.IsPregnant); foreach (RuminantFemale female in notReadyBreeders) { female.BreedParams.OnConceptionStatusChanged(new Reporting.ConceptionStatusChangedEventArgs(Reporting.ConceptionStatus.NotReady, female, clock.Today)); } int numberPossible = breeders.Sum(a => a.Count()); int numberServiced = 1; List <Ruminant> maleBreeders = new List <Ruminant>(); // for each location where parts of this herd are located foreach (var location in breeders) { numberPossible = -1; if (useControlledMating) { numberPossible = Convert.ToInt32(location.OfType <RuminantFemale>().Count(), CultureInfo.InvariantCulture); } else { numberPossible = 0; // uncontrolled conception if (location.GroupBy(a => a.Sex).Count() == 2) { int maleCount = location.OfType <RuminantMale>().Count(); // get a list of males to provide attributes when incontrolled mating. if (maleCount > 0 && location.FirstOrDefault().BreedParams.IncludedAttributeInheritanceWhenMating) { maleBreeders = location.Where(a => a.Sex == Sex.Male).ToList(); } int femaleCount = location.Where(a => a.Sex == Sex.Female).Count(); numberPossible = Convert.ToInt32(Math.Ceiling(maleCount * location.FirstOrDefault().BreedParams.MaximumMaleMatingsPerDay * 30), CultureInfo.InvariantCulture); } } numberServiced = 0; lastJoinIndex = -1; int cnt = 0; // shuffle the not pregnant females when obtained to avoid any inherant order by creation of individuals affecting which individuals are available first var notPregnantFemales = location.OfType <RuminantFemale>().Where(a => !a.IsPregnant).OrderBy(a => RandomNumberGenerator.Generator.Next()).ToList(); int totalToBreed = notPregnantFemales.Count; while (cnt < totalToBreed) { RuminantFemale female = notPregnantFemales.ElementAt(cnt); Reporting.ConceptionStatus status = Reporting.ConceptionStatus.NotMated; if (numberServiced < numberPossible) { // calculate conception double conceptionRate = ConceptionRate(female, out status); // if mandatory attributes are present in the herd, save male value with female details. // update male for both successful and failed matings (next if statement if (female.BreedParams.IncludedAttributeInheritanceWhenMating) { object male = null; if (useControlledMating) { bool newJoining = needsNewJoiningMale(controlledMating.JoiningsPerMale, numberServiced); // save all male attributes AddMalesAttributeDetails(female, controlledMating.SireAttributes, newJoining); } else { male = maleBreeders[RandomNumberGenerator.Generator.Next(0, maleBreeders.Count() - 1)]; female.LastMatingStyle = ((male as RuminantMale).IsWildBreeder ? MatingStyle.WildBreeder : MatingStyle.Natural); // randomly select male AddMalesAttributeDetails(female, male as Ruminant); } } if (conceptionRate > 0) { if (RandomNumberGenerator.Generator.NextDouble() <= conceptionRate) { female.UpdateConceptionDetails(female.CalulateNumberOfOffspringThisPregnancy(), conceptionRate, 0); if (useControlledMating) { female.LastMatingStyle = MatingStyle.Controlled; } status = Reporting.ConceptionStatus.Conceived; NumberConceived++; } else { status = Reporting.ConceptionStatus.Unsuccessful; } } numberServiced++; this.Status = ActivityStatus.Success; } // report change in breeding status // do not report for -1 (controlled mating outside timing) if (numberPossible >= 0 && status != Reporting.ConceptionStatus.NotAvailable) { female.BreedParams.OnConceptionStatusChanged(new Reporting.ConceptionStatusChangedEventArgs(status, female, clock.Today)); } cnt++; } // report a natural mating locations for transparency via a message if (numberServiced > 0 & !useControlledMating) { string warning = $"Natural (uncontrolled) mating ocurred in [r={(location.Key ?? "Not specified - general yards")}]"; Warnings.CheckAndWrite(warning, Summary, this, MessageType.Information); } } } // report that this activity was performed as it does not use base GetResourcesRequired this.TriggerOnActivityPerformed(); }
private void OnWFAnimalBreeding(object sender, EventArgs e) { RuminantHerd ruminantHerd = Resources.RuminantHerd(); List <Ruminant> herd = ruminantHerd.Herd.Where(a => a.BreedParams.Name == HerdName).ToList(); // get list of all individuals of breeding age and condition // grouped by location var breeders = from ind in herd where (ind.Gender == Sex.Male & ind.Age >= ind.BreedParams.MinimumAge1stMating) ^ (ind.Gender == Sex.Female & ind.Age >= ind.BreedParams.MinimumAge1stMating & ind.Weight >= (ind.BreedParams.MinimumSize1stMating * ind.StandardReferenceWeight) ) group ind by ind.Location into grp select grp; // for each location where parts of this herd are located foreach (var location in breeders) { // check for births of all pregnant females. foreach (RuminantFemale female in location.Where(a => a.Gender == Sex.Female).Cast <RuminantFemale>().ToList()) { if (female.BirthDue) { int numberOfNewborn = (female.CarryingTwins) ? 2 : 1; for (int i = 0; i < numberOfNewborn; i++) { // Determine if the offspring died during pregancy from conception to after birth // This is currently only performed at time of birth rather than monthly during pregnancy // and so does not reflect changes in female intake etc after dead of foetus. object newCalf = null; bool isMale = (WholeFarm.RandomGenerator.NextDouble() > 0.5); if (isMale) { newCalf = new RuminantMale(); } else { newCalf = new RuminantFemale(); } Ruminant newCalfRuminant = newCalf as Ruminant; newCalfRuminant.Age = 0; newCalfRuminant.HerdName = female.HerdName; newCalfRuminant.BreedParams = female.BreedParams; newCalfRuminant.Gender = (isMale) ? Sex.Male : Sex.Female; newCalfRuminant.ID = ruminantHerd.NextUniqueID; newCalfRuminant.Location = female.Location; newCalfRuminant.Mother = female; // newCalfRuminant.Number = 1; newCalfRuminant.SetUnweaned(); // calf weight from Freer newCalfRuminant.Weight = female.BreedParams.SRWBirth * female.StandardReferenceWeight * (1 - 0.33 * (1 - female.Weight / female.StandardReferenceWeight)); newCalfRuminant.HighWeight = newCalfRuminant.Weight; newCalfRuminant.SaleFlag = Common.HerdChangeReason.Born; ruminantHerd.AddRuminant(newCalfRuminant); // add to sucklings female.SucklingOffspring.Add(newCalfRuminant); } female.UpdateBirthDetails(); } } // uncontrolled conception if (ControlledMatings == null) { // check if males and females of breeding condition are together if (location.GroupBy(a => a.Gender).Count() == 2) { // servicing rate int maleCount = location.Where(a => a.Gender == Sex.Male).Count(); int femaleCount = location.Where(a => a.Gender == Sex.Female).Count(); double matingsPossible = maleCount * location.FirstOrDefault().BreedParams.MaximumMaleMatingsPerDay * 30; double maleLimiter = Math.Max(1.0, matingsPossible / femaleCount); foreach (RuminantFemale female in location.Where(a => a.Gender == Sex.Female).Cast <RuminantFemale>().ToList()) { //TODO: ensure enough time since last calf if (!female.IsPregnant & !female.IsLactating) { // calculate conception double conceptionRate = ConceptionRate(female) * maleLimiter; conceptionRate = Math.Min(conceptionRate, MaximumConceptionRateUncontrolled); if (WholeFarm.RandomGenerator.NextDouble() <= conceptionRate) { female.UpdateConceptionDetails(WholeFarm.RandomGenerator.NextDouble() > female.BreedParams.TwinRate, conceptionRate); } } } } } // controlled conception else if (ControlledMatings != null && ControlledMatings.IsDueDate()) { foreach (RuminantFemale female in location.Where(a => a.Gender == Sex.Female).Cast <RuminantFemale>().ToList()) { //TODO: ensure enough time since last calf if (!female.IsPregnant & !female.IsLactating) { // calculate conception double conceptionRate = ConceptionRate(female); if (WholeFarm.RandomGenerator.NextDouble() <= conceptionRate) { female.UpdateConceptionDetails(WholeFarm.RandomGenerator.NextDouble() > female.BreedParams.TwinRate, conceptionRate); } } } } // determine all foetus and newborn mortality. foreach (RuminantFemale female in location.Where(a => a.Gender == Sex.Female).Cast <RuminantFemale>().ToList()) { if (female.IsPregnant) { // calculate foetus and newborn mortality // total mortality / gestation months to get monthly mortality // TODO: check if need to be done before births to get last month mortality if (WholeFarm.RandomGenerator.NextDouble() > female.BreedParams.PrenatalMortality / female.BreedParams.GestationLength) { female.OneOffspringDies(); } } } } if (ControlledMatings != null && ControlledMatings.IsDueDate()) { } }