private void Muster() { foreach (Ruminant ind in this.CurrentHerd(false)) { // set new location ID ind.Location = Pasture.Name; this.SetStatusSuccess(); // check if sucklings are to be moved with mother if (MoveSucklings) { // if female if (ind.GetType() == typeof(RuminantFemale)) { RuminantFemale female = ind as RuminantFemale; // check if mother with sucklings if (female.SucklingOffspring.Count > 0) { foreach (var suckling in female.SucklingOffspring) { suckling.Location = Pasture.Name; } } } } } }
/// <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}]"); } } } } }
/// <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 Move() { Status = ActivityStatus.NotNeeded; // allow multiple filter groups for moving. var filterGroups = FindAllChildren <RuminantGroup>(); foreach (RuminantGroup item in filterGroups) { foreach (Ruminant ind in item.Filter(CurrentHerd(false))) { // set new location ID if (ind.Location != pastureName) { this.Status = ActivityStatus.Success; ind.Location = pastureName; // check if sucklings are to be moved with mother if (MoveSucklings) { // if female if (ind is RuminantFemale) { RuminantFemale female = ind as RuminantFemale; // check if mother with sucklings foreach (var suckling in female.SucklingOffspringList) { suckling.Location = pastureName; } } } } } } }
/// <summary> /// Set the milk production of the selected female given diet drymatter digesibility /// </summary> /// <param name="ind">Female individual</param> /// <returns>energy of milk</returns> private double CalculateMilkProduction(RuminantFemale ind) { double energyMetabolic = EnergyGross * (((ind.DietDryMatterDigestibility == 0)?50:ind.DietDryMatterDigestibility) / 100.0) * 0.81; // Reference: SCA p. double kl = ind.BreedParams.ELactationEfficiencyCoefficient * energyMetabolic / EnergyGross + ind.BreedParams.ELactationEfficiencyIntercept; double milkTime = ind.DaysLactating; double milkCurve = 0; if (ind.SucklingOffspring.Count == 0 && ind.MilkingPerformed) // no suckling calf and milking has been performed { milkCurve = ind.BreedParams.MilkCurveNonSuckling; } else // suckling calf { milkCurve = ind.BreedParams.MilkCurveSuckling; } 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 double energyMilk = milkProduction * 3.2 / kl; // adjust last time step's energy balance 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)); // set milk production in lactating females for consumption. ind.MilkProduction = milkProduction; ind.MilkAmount = ind.MilkProduction * 30.4; return(milkProduction * 3.2 / kl); }
/// <summary> /// Calculate conception rate for a female /// </summary> /// <param name="female">Female to calculate conception rate for</param> /// <returns></returns> private double ConceptionRate(RuminantFemale female) { double rate = 0; bool isConceptionReady = false; if (female.Age >= female.BreedParams.MinimumAge1stMating && female.NumberOfBirths == 0) { isConceptionReady = true; } else { double currentIPI = female.BreedParams.InterParturitionIntervalIntercept * Math.Pow((female.Weight / female.StandardReferenceWeight), female.BreedParams.InterParturitionIntervalCoefficient) * 30.64; // calculate inter-parturition interval currentIPI = Math.Max(currentIPI, female.BreedParams.GestationLength * 30.4 + female.BreedParams.MinimumDaysBirthToConception); // 2nd param was 61 double ageNextConception = female.AgeAtLastConception + (currentIPI / 30.4); isConceptionReady = (female.Age >= ageNextConception); } // if first mating and of age or suffcient time since last birth/conception if (isConceptionReady) { // Get conception rate from conception model associated with the Ruminant Type parameters if (female.BreedParams.ConceptionModel == null) { throw new ApsimXException(this, String.Format("No conception details were found for [r={0}]\nPlease add a conception component below the [r=RuminantType]", female.BreedParams.Name)); } return(female.BreedParams.ConceptionModel.ConceptionRate(female)); } return(rate / 100); }
/// <summary> /// Constructor /// </summary> /// <param name="status">Change status</param> /// <param name="female">Individual being reported</param> /// <param name="dateTime">Current clock</param> public ConceptionStatusChangedEventArgs(ConceptionStatus status, RuminantFemale female, DateTime dateTime) { Status = status; Female = female; // Calculate conception date switch (Status) { case ConceptionStatus.Conceived: case ConceptionStatus.Failed: case ConceptionStatus.Birth: case ConceptionStatus.Weaned: 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.Unsuccessful: case ConceptionStatus.NotMated: case ConceptionStatus.NotReady: ConceptionDate = dateTime; break; default: break; } }
private void Muster() { Status = ActivityStatus.NotNeeded; foreach (Ruminant ind in this.CurrentHerd(false)) { // set new location ID if (ind.Location != pastureName) { this.Status = ActivityStatus.Success; } ind.Location = pastureName; // check if sucklings are to be moved with mother if (MoveSucklings) { // if female if (ind.GetType() == typeof(RuminantFemale)) { RuminantFemale female = ind as RuminantFemale; // check if mother with sucklings if (female.SucklingOffspringList.Count > 0) { foreach (var suckling in female.SucklingOffspringList) { suckling.Location = pastureName; } } } } } }
/// <summary> /// A method to add the available male attributes to the female store at mating using attributes supplied by controlled mating /// </summary> /// <param name="female">The female breeder successfully mated</param> /// <param name="maleAttributes">a list of available male attributes setters</param> /// <param name="newMale">Create new instance (T) or use last created (F)</param> private void AddMalesAttributeDetails(RuminantFemale female, List <ISetAttribute> maleAttributes, bool newMale = true) { foreach (var attribute in female.Attributes.Items) { var maleAttribute = maleAttributes.FirstOrDefault(a => a.AttributeName == attribute.Key); SetFemaleMateAttributes(female, attribute, maleAttribute?.GetAttribute(newMale)); } }
/// <summary> /// A method to add the available male attributes to the female store at mating using attributes supplied by controlled mating /// </summary> /// <param name="female">The female breeder successfully mated</param> /// <param name="maleAttributes">a list of available male attributes setters</param> /// <param name="newMale">Create new instance (T) or use last created (F)</param> private void AddMalesAttributeDetails(RuminantFemale female, List <SetAttributeWithValue> maleAttributes, bool newMale = true) { foreach (var attribute in female.Attributes.Items) { var maleAttribute = maleAttributes.Where(a => a.AttributeName == attribute.Key).FirstOrDefault(); if (maleAttribute != null) { var calculatedAttribute = maleAttribute.GetAttribute(newMale); if (attribute.Value != null && attribute.Value.InheritanceStyle != calculatedAttribute.InheritanceStyle) { throw new ApsimXException(this, $"The inheritance style for attribute [{attribute.Key}] differs between the breeder and attributes supplied by controlled mating in [a={this.Name}]"); } if (attribute.Value != null) { attribute.Value.StoredMateValue = calculatedAttribute.StoredValue; } } else { // if there are random herd attributes available if (randomHerdAttributes.Any()) { if (!randomHerdAttributes.TryGetValue(attribute.Key, out IIndividualAttribute randomAttribute)) { throw new ApsimXException(this, $"Unable to assign mandatory attribute from random herd selection for [a={this.Name}] and madatory attribute [{attribute.Key}]"); } else { if (attribute.Value != null && attribute.Value.InheritanceStyle != randomAttribute.InheritanceStyle) { throw new ApsimXException(this, $"The inheritance style for attribute [{attribute.Key}] differs between the breeder and attributes supplied by random herd selection in [a={this.Name}]"); } if (attribute.Value != null) { attribute.Value.StoredMateValue = randomAttribute.StoredValue; } } } else { if (attribute.Value != null) { attribute.Value.StoredMateValue = null; } if (female.BreedParams.IsMandatoryAttribute(attribute.Key)) { throw new ApsimXException(this, $"The sire attributes provided for [a={this.Name}] do not include the madatory attribute [{attribute.Key}]"); } } } } }
/// <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); } }
/// <summary> /// Calculate conception rate for a female /// </summary> /// <param name="female">Female to calculate conception rate for</param> /// <param name="status">Returns conception status</param> /// <returns></returns> private double ConceptionRate(RuminantFemale female, out Reporting.ConceptionStatus status) { bool isConceptionReady = false; status = Reporting.ConceptionStatus.NotAvailable; if (!female.IsPregnant) { status = Reporting.ConceptionStatus.NotReady; if (female.Age >= female.BreedParams.MinimumAge1stMating && female.NumberOfBirths == 0) { isConceptionReady = true; } else { // add one to age to ensure that conception is due this timestep if ((female.Age + 1 - female.AgeAtLastBirth) * 30.4 > female.BreedParams.MinimumDaysBirthToConception) { // only based upon period since birth isConceptionReady = true; // DEVELOPMENT NOTE: // The following IPI calculation and check present in NABSA has been removed for testing // It is assumed that the individual based model with weight influences will handle the old IPI calculation // These parameters can now be removed form the RuminantType list //double currentIPI = female.BreedParams.InterParturitionIntervalIntercept * Math.Pow(female.ProportionOfNormalisedWeight, female.BreedParams.InterParturitionIntervalCoefficient) * 30.4; //double ageNextConception = female.AgeAtLastConception + (currentIPI / 30.4); //isConceptionReady = (female.Age+1 >= ageNextConception); } } } // if first mating and of age or sufficient time since last birth if (isConceptionReady) { status = Reporting.ConceptionStatus.Unsuccessful; // Get conception rate from conception model associated with the Ruminant Type parameters if (female.BreedParams.ConceptionModel == null) { throw new ApsimXException(this, String.Format("No conception details were found for [r={0}]\r\nPlease add a conception component below the [r=RuminantType]", female.BreedParams.Name)); } return(female.BreedParams.ConceptionModel.ConceptionRate(female)); } return(0); }
private void SetFemaleMateAttributes(RuminantFemale female, KeyValuePair <string, IIndividualAttribute> femaleAttribute, IIndividualAttribute maleAttribute) { if (maleAttribute != null) { if (femaleAttribute.Value != null && femaleAttribute.Value.InheritanceStyle != maleAttribute.InheritanceStyle) { string errorMsg; if (useControlledMating) { errorMsg = $"provided from [a={controlledMating.NameWithParent}]"; } else { errorMsg = $"from the herd in [a={NameWithParent}]"; } throw new ApsimXException(this, $"The inheritance style for attribute [{femaleAttribute.Key}] differs between the breeder [{femaleAttribute.Value.InheritanceStyle}] and breeding male [{maleAttribute.InheritanceStyle}] {errorMsg}"); } if (femaleAttribute.Value != null) { femaleAttribute.Value.StoredMateValue = maleAttribute.StoredValue; } } else { if (femaleAttribute.Value != null) { femaleAttribute.Value.StoredMateValue = null; } if (female.BreedParams.IsMandatoryAttribute(femaleAttribute.Key)) { string errorMsg; if (useControlledMating) { errorMsg = $"Cannot locate the madatory attribute [{femaleAttribute.Key}] in [a={controlledMating.NameWithParent}]{Environment.NewLine}Add a [SetAttribute] component below the [a=RuminantnActivityControlledMating]"; } else { errorMsg = $"Cannot locate the madatory attribute [{femaleAttribute.Key}] in from the breeding male selected from the herd in [a={NameWithParent}]{Environment.NewLine}Ensure all sires in initial herd or purchased provide the appropriate [SetAttribute] component"; } throw new ApsimXException(this, errorMsg); } } }
/// <summary> /// Set the milk production of the selected female given diet drymatter digesibility /// </summary> /// <param name="ind">Female individual</param> /// <returns>energy of milk</returns> private double CalculateMilkProduction(RuminantFemale ind) { ind.MilkMilkedThisTimeStep = 0; ind.MilkSuckledThisTimeStep = 0; double energyMetabolic = EnergyGross * (((ind.DietDryMatterDigestibility == 0)?50:ind.DietDryMatterDigestibility) / 100.0) * 0.81; // Reference: SCA p. double kl = ind.BreedParams.ELactationEfficiencyCoefficient * energyMetabolic / EnergyGross + ind.BreedParams.ELactationEfficiencyIntercept; double milkTime = ind.DaysLactating; double milkCurve; // determine milk production curve to use // if milking is taking place use the non-suckling curve for duration of lactation // otherwise use the suckling curve where there is a larger drop off in milk production if (ind.MilkingPerformed) { milkCurve = ind.BreedParams.MilkCurveNonSuckling; } else // no milking { milkCurve = ind.BreedParams.MilkCurveSuckling; } ind.MilkProductionPotential = 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)); ind.MilkProductionPotential = Math.Max(ind.MilkProductionPotential, 0.0); // Reference: Potential milk prodn, 3.2 MJ/kg milk - Jouven et al 2008 double energyMilk = ind.MilkProductionPotential * 3.2 / kl; // adjust last time step's energy balance double adjustedEnergyBalance = ind.EnergyBalance; if (adjustedEnergyBalance < (-0.5936 / 0.322 * energyMilk)) { adjustedEnergyBalance = (-0.5936 / 0.322 * energyMilk); } // set milk production in lactating females for consumption. ind.MilkProduction = Math.Max(0.0, ind.MilkProductionPotential * (0.5936 + 0.322 * adjustedEnergyBalance / energyMilk)); ind.MilkCurrentlyAvailable = ind.MilkProduction * 30.4; // returns the energy required for milk production return(ind.MilkProduction * 3.2 / kl); }
/// <summary> /// Calculate conception rate for a female /// </summary> /// <param name="female">Female to calculate conception rate for</param> /// <returns></returns> private double ConceptionRate(RuminantFemale female) { double rate = 0; switch (female.NumberOfBirths) { case 0: // first mating if (female.BreedParams.MinimumAge1stMating >= 24) { // 1st mated at 24 months or older rate = female.BreedParams.ConceptionRateAsymptote[1] / (1 + Math.Exp(female.BreedParams.ConceptionRateCoefficent[1] * female.WeightAtConception / female.StandardReferenceWeight + female.BreedParams.ConceptionRateIntercept[1])); } else if (female.BreedParams.MinimumAge1stMating >= 12) { // 1st mated between 12 and 24 months double rate24 = female.BreedParams.ConceptionRateAsymptote[1] / (1 + Math.Exp(female.BreedParams.ConceptionRateCoefficent[1] * female.WeightAtConception / female.StandardReferenceWeight + female.BreedParams.ConceptionRateIntercept[1])); double rate12 = female.BreedParams.ConceptionRateAsymptote[0] / (1 + Math.Exp(female.BreedParams.ConceptionRateCoefficent[0] * female.WeightAtConception / female.StandardReferenceWeight + female.BreedParams.ConceptionRateIntercept[0])); rate = (rate12 + rate24) / 2; // Not sure what the next code was doing in old version //Concep_rate = ((730 - Anim_concep(rumcat)) * temp1 + (Anim_concep(rumcat) - 365) * temp2) / 365 ' interpolate between 12 & 24 months } break; case 1: // second offspring mother rate = female.BreedParams.ConceptionRateAsymptote[2] / (1 + Math.Exp(female.BreedParams.ConceptionRateCoefficent[2] * female.WeightAtConception / female.StandardReferenceWeight + female.BreedParams.ConceptionRateIntercept[2])); break; default: // females who have had more then one birth (twins should could as one birth) if (female.WeightAtConception > female.BreedParams.CriticalCowWeight * female.StandardReferenceWeight) { rate = female.BreedParams.ConceptionRateAsymptote[3] / (1 + Math.Exp(female.BreedParams.ConceptionRateCoefficent[3] * female.WeightAtConception / female.StandardReferenceWeight + female.BreedParams.ConceptionRateIntercept[3])); } break; } return(rate); }
/// <summary> /// A method to add the available male attributes to the female store at mating using attributes supplied by controlled mating /// </summary> /// <param name="female">The female breeder successfully mated</param> /// <param name="maleAttributes">a list of available male attributes setters</param> private void AddMalesAttributeDetails(RuminantFemale female, List <SetAttributeWithValue> maleAttributes) { foreach (var attribute in female.Attributes) { var maleAttribute = maleAttributes.Where(a => a.AttributeName == attribute.Key).FirstOrDefault(); if (maleAttribute != null) { var calculatedAttribute = maleAttribute.GetRandomSetAttribute(); if (attribute.Value.InheritanceStyle != calculatedAttribute.InheritanceStyle) { throw new ApsimXException(this, $"The inheritance style for attribute [{attribute.Key}] differs between the breeder and attributes supplied by controlled mating in [a={this.Name}]"); } attribute.Value.storedMateValue = calculatedAttribute.storedValue; } else { attribute.Value.storedMateValue = null; if (female.BreedParams.IsMandatoryAttribute(attribute.Key)) { throw new ApsimXException(this, $"The sire attributes provided for [a={this.Name}] do not include the madatory attribute [{attribute.Key}]"); } } } }
private void Muster() { // get herd to muster RuminantHerd ruminantHerd = Resources.RuminantHerd(); List <Ruminant> herd = ruminantHerd.Herd; if (herd == null && herd.Count == 0) { return; } // get list from filters foreach (Ruminant ind in herd.Filter(this)) { // set new location ID ind.Location = pasture.Name; // check if sucklings are to be moved with mother if (MoveSucklings) { // if female if (ind.GetType() == typeof(RuminantFemale)) { RuminantFemale female = ind as RuminantFemale; // check if mother with sucklings if (female.SucklingOffspring.Count > 0) { foreach (var suckling in female.SucklingOffspring) { suckling.Location = pasture.Name; } } } } } }
/// <summary> /// Calculate conception rate for a female /// </summary> /// <param name="female">Female to calculate conception rate for</param> /// <returns></returns> private double ConceptionRate(RuminantFemale female) { double rate = 0; bool isConceptionReady = false; if (female.Age >= female.BreedParams.MinimumAge1stMating && female.NumberOfBirths == 0) { isConceptionReady = true; } else { double IPIcurrent = female.BreedParams.InterParturitionIntervalIntercept * Math.Pow((female.Weight / female.StandardReferenceWeight), female.BreedParams.InterParturitionIntervalCoefficient) * 30.64; // calculate inter-parturition interval IPIcurrent = Math.Max(IPIcurrent, female.BreedParams.GestationLength * 30.4 + female.BreedParams.MinimumDaysBirthToConception); // 2nd param was 61 double ageNextConception = female.AgeAtLastConception + (IPIcurrent / 30.4); isConceptionReady = (female.Age >= ageNextConception); } // if first mating and of age or suffcient time since last birth/conception if (isConceptionReady) { // get advanced conception rate if available otherwise use defaults. if (female.BreedParams.AdvancedConceptionParameters != null) { // generalised curve switch (female.NumberOfBirths) { case 0: // first mating if (female.BreedParams.MinimumAge1stMating >= 24) { // 1st mated at 24 months or older rate = female.BreedParams.AdvancedConceptionParameters.ConceptionRateAsymptote[1] / (1 + Math.Exp(female.BreedParams.AdvancedConceptionParameters.ConceptionRateCoefficent[1] * female.WeightAtConception / female.StandardReferenceWeight + female.BreedParams.AdvancedConceptionParameters.ConceptionRateIntercept[1])); } else if (female.BreedParams.MinimumAge1stMating >= 12) { // 1st mated between 12 and 24 months double rate24 = female.BreedParams.AdvancedConceptionParameters.ConceptionRateAsymptote[1] / (1 + Math.Exp(female.BreedParams.AdvancedConceptionParameters.ConceptionRateCoefficent[1] * female.WeightAtConception / female.StandardReferenceWeight + female.BreedParams.AdvancedConceptionParameters.ConceptionRateIntercept[1])); double rate12 = female.BreedParams.AdvancedConceptionParameters.ConceptionRateAsymptote[0] / (1 + Math.Exp(female.BreedParams.AdvancedConceptionParameters.ConceptionRateCoefficent[0] * female.WeightAtConception / female.StandardReferenceWeight + female.BreedParams.AdvancedConceptionParameters.ConceptionRateIntercept[0])); rate = (rate12 + rate24) / 2; // Not sure what the next code was doing in old version //Concep_rate = ((730 - Anim_concep(rumcat)) * temp1 + (Anim_concep(rumcat) - 365) * temp2) / 365 ' interpolate between 12 & 24 months } else { // first mating < 12 months old rate = female.BreedParams.AdvancedConceptionParameters.ConceptionRateAsymptote[0] / (1 + Math.Exp(female.BreedParams.AdvancedConceptionParameters.ConceptionRateCoefficent[0] * female.WeightAtConception / female.StandardReferenceWeight + female.BreedParams.AdvancedConceptionParameters.ConceptionRateIntercept[0])); } break; case 1: // second offspring mother rate = female.BreedParams.AdvancedConceptionParameters.ConceptionRateAsymptote[2] / (1 + Math.Exp(female.BreedParams.AdvancedConceptionParameters.ConceptionRateCoefficent[2] * female.WeightAtConception / female.StandardReferenceWeight + female.BreedParams.AdvancedConceptionParameters.ConceptionRateIntercept[2])); break; default: // females who have had more than two births (twins should count as one birth) if (female.WeightAtConception > female.BreedParams.CriticalCowWeight * female.StandardReferenceWeight) { rate = female.BreedParams.AdvancedConceptionParameters.ConceptionRateAsymptote[3] / (1 + Math.Exp(female.BreedParams.AdvancedConceptionParameters.ConceptionRateCoefficent[3] * female.WeightAtConception / female.StandardReferenceWeight + female.BreedParams.AdvancedConceptionParameters.ConceptionRateIntercept[3])); } break; } } else { // use default values rate = female.BreedParams.ConceptionRateAsymptote / (1 + Math.Exp(female.BreedParams.ConceptionRateCoefficent * female.WeightAtConception / female.StandardReferenceWeight + female.BreedParams.ConceptionRateIntercept)); } } return(rate / 100); }
/// <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 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; } } }
private void OnCLEMAnimalManage(object sender, EventArgs e) { RuminantHerd ruminantHerd = Resources.RuminantHerd(); // remove only the individuals that are affected by this activity. // these are old purchases that were not made. This list will be regenerated in this method. ruminantHerd.PurchaseIndividuals.RemoveAll(a => a.Breed == this.PredictedHerdBreed); List <Ruminant> herd = this.CurrentHerd(true); // can sell off males any month as per NABSA // if we don't need this monthly, then it goes into next if statement with herd declaration // NABSA MALES - weaners, 1-2, 2-3 and 3-4 yo, we check for any male weaned and not a breeding sire. // check for sell age/weight of young males // if SellYoungFemalesLikeMales then all apply to both sexes else only males. // SellFemalesLikeMales will grow out excess heifers until age/weight rather than sell immediately. if (this.TimingOK || ContinuousMaleSales) { foreach (var ind in herd.Where(a => a.Weaned && (SellFemalesLikeMales ? true : (a.Gender == Sex.Male)) && (a.Age >= MaleSellingAge || a.Weight >= MaleSellingWeight))) { bool sell = true; if (ind.GetType() == typeof(RuminantMale)) { // don't sell breeding sires. sell = !((ind as RuminantMale).BreedingSire); } else { // only sell females that were marked as excess sell = ind.Tags.Contains("GrowHeifer"); } if (sell) { ind.SaleFlag = HerdChangeReason.AgeWeightSale; } } } // if management month if (this.TimingOK) { // ensure pasture limits are ok before purchases bool sufficientFood = true; if (foodStore != null) { sufficientFood = (foodStore.TonnesPerHectare * 1000) >= MinimumPastureBeforeRestock; } // check for maximum age (females and males have different cutoffs) foreach (var ind in herd.Where(a => a.Age >= ((a.Gender == Sex.Female) ? MaximumBreederAge : MaximumBullAge))) { ind.SaleFlag = HerdChangeReason.MaxAgeSale; // ensure females are not pregnant and add warning if pregnant old females found. if (ind.Gender == Sex.Female && (ind as RuminantFemale).IsPregnant) { string warning = "Some females sold at maximum age in [a=" + this.Name + "] were pregnant.\nConsider changing the MaximumBreederAge in [a=RuminantActivityManage] or ensure [r=RuminantType.MaxAgeMating] is less than or equal to the MaximumBreederAge to avoid selling pregnant individuals."; if (!Warnings.Exists(warning)) { Warnings.Add(warning); Summary.WriteWarning(this, warning); } } } // MALES // check for breeder bulls after sale of old individuals and buy/sell int numberMaleSiresInHerd = herd.Where(a => a.Gender == Sex.Male && a.SaleFlag == HerdChangeReason.None).Cast <RuminantMale>().Where(a => a.BreedingSire).Count(); // Number of females int numberFemaleBreedingInHerd = herd.Where(a => a.Gender == Sex.Female && a.Age >= a.BreedParams.MinimumAge1stMating && a.SaleFlag == HerdChangeReason.None).Count(); int numberFemaleTotalInHerd = herd.Where(a => a.Gender == Sex.Female && a.SaleFlag == HerdChangeReason.None).Count(); // these are females that will exceed max age and be sold in next 12 months int numberFemaleOldInHerd = herd.Where(a => a.Gender == Sex.Female && (a.Age + 12 >= MaximumBreederAge) && a.SaleFlag == HerdChangeReason.None).Count(); // defined heifers here as weaned and will be a breeder in the next year int numberFemaleHeifersInHerd = herd.Where(a => a.Gender == Sex.Female && a.Weaned && ((a.Age - a.BreedParams.MinimumAge1stMating < 0) && (a.Age - a.BreedParams.MinimumAge1stMating > -12)) && a.SaleFlag == HerdChangeReason.None).Count(); if (numberMaleSiresInHerd > SiresKept) { // sell bulls // What rule? oldest first as they may be lost soonest? int numberToRemove = numberMaleSiresInHerd - SiresKept; if (numberToRemove > 0) { foreach (var male in herd.Where(a => a.Gender == Sex.Male).Cast <RuminantMale>().Where(a => a.BreedingSire).OrderByDescending(a => a.Age).Take(numberToRemove)) { male.SaleFlag = HerdChangeReason.ExcessBullSale; numberToRemove--; if (numberToRemove == 0) { break; } } } } else if (numberMaleSiresInHerd < SiresKept) { if ((foodStore == null) || (sufficientFood)) { if (AllowSireReplacement) { // remove young bulls from sale herd to replace breed bulls (not those sold because too old) foreach (RuminantMale male in herd.Where(a => a.Gender == Sex.Male && a.SaleFlag == HerdChangeReason.AgeWeightSale).OrderByDescending(a => a.Weight)) { male.SaleFlag = HerdChangeReason.None; male.BreedingSire = true; numberMaleSiresInHerd++; if (numberMaleSiresInHerd >= SiresKept) { break; } } // if still insufficent, look into current herd for replacement // remaining males assumed to be too small, so await next time-step } // if still insufficient buy bulls. if (numberMaleSiresInHerd < SiresKept && (MaximumSiresPerPurchase > 0)) { // limit by breeders as proportion of max breeders so we don't spend alot on sires when building the herd and females more valuable double propOfBreeders = (double)numberFemaleBreedingInHerd / (double)MaximumBreedersKept; propOfBreeders = 1; int sires = Convert.ToInt32(Math.Ceiling(Math.Ceiling(SiresKept * propOfBreeders))); int numberToBuy = Math.Min(MaximumSiresPerPurchase, Math.Max(0, sires - numberMaleSiresInHerd)); for (int i = 0; i < numberToBuy; i++) { if (i < MaximumSiresPerPurchase) { RuminantMale newbull = new RuminantMale(48, Sex.Male, 450, breedParams) { Location = grazeStore, Breed = this.PredictedHerdBreed, HerdName = this.PredictedHerdName, BreedingSire = true, Gender = Sex.Male, ID = 0, // Next unique ide will be assigned when added PreviousWeight = 450, SaleFlag = HerdChangeReason.SirePurchase }; // add to purchase request list and await purchase in Buy/Sell ruminantHerd.PurchaseIndividuals.Add(newbull); } } } } } // FEMALES // Breeding herd sold as heifers only, purchased as breeders (>= minAge1stMating) int excessBreeders = 0; // get the mortality rate for the herd if available or assume zero double mortalityRate = breedParams.MortalityBase; // shortfall between actual and desired numbers of breeders (-ve for shortfall) excessBreeders = numberFemaleBreedingInHerd - MaximumBreedersKept; // IAT-NABSA removes adjusts to account for the old animals that will be sold in the next year // This is not required in CLEM as they have been sold in this method, and it wont be until this method is called again that the next lot are sold. // Like IAT-NABSA we will account for mortality losses in the next year in our breeder purchases // Account for whole individuals only. int numberDyingInNextYear = Convert.ToInt32(Math.Floor(numberFemaleBreedingInHerd * mortalityRate), CultureInfo.InvariantCulture); // adjust for future mortality excessBreeders -= numberDyingInNextYear; // account for heifers already in the herd // These are the next cohort that will become breeders in the next 12 months (before this method is called again) excessBreeders += numberFemaleHeifersInHerd; if (excessBreeders > 0) // surplus heifers to sell { foreach (var female in herd.Where(a => a.Gender == Sex.Female && (a as RuminantFemale).IsHeifer).Take(excessBreeders)) { // if sell like males tag for grow out otherwise mark for sale if (SellFemalesLikeMales) { if (!female.Tags.Contains("GrowHeifer")) { female.Tags.Add("GrowHeifer"); } } else { // tag for sale. female.SaleFlag = HerdChangeReason.ExcessHeiferSale; } excessBreeders--; if (excessBreeders == 0) { break; } } } else if (excessBreeders < 0) // shortfall heifers to buy { double minBreedAge = breedParams.MinimumAge1stMating; excessBreeders *= -1; if ((foodStore == null) || (sufficientFood)) { // remove grow out heifers from grow out herd to replace breeders if (SellFemalesLikeMales) { foreach (Ruminant female in herd.Where(a => a.Tags.Contains("GrowHeifer")).OrderByDescending(a => a.Age)) { female.Tags.Remove("GrowHeifer"); excessBreeders--; if (excessBreeders == 0) { break; } } } // remove young females from sale herd to replace breeders (not those sold because too old) foreach (RuminantFemale female in herd.Where(a => a.Gender == Sex.Female && (a.SaleFlag == HerdChangeReason.AgeWeightSale || a.SaleFlag == HerdChangeReason.ExcessHeiferSale)).OrderByDescending(a => a.Age)) { female.SaleFlag = HerdChangeReason.None; excessBreeders--; if (excessBreeders == 0) { break; } } // if still insufficient buy breeders. if (excessBreeders > 0 && (MaximumProportionBreedersPerPurchase > 0)) { int ageOfBreeder = 0; // IAT-NABSA had buy mortality base% more to account for deaths before these individuals grow to breeding age // These individuals are already of breeding age so we will ignore this in CLEM // minimum of (max kept x prop in single purchase) and (the number needed + annual mortality) int numberToBuy = Math.Min(excessBreeders, Convert.ToInt32(Math.Ceiling(MaximumProportionBreedersPerPurchase * MaximumBreedersKept), CultureInfo.InvariantCulture)); int numberPerPurchaseCohort = Convert.ToInt32(Math.Ceiling(numberToBuy / Convert.ToDouble(NumberOfBreederPurchaseAgeClasses, CultureInfo.InvariantCulture)), CultureInfo.InvariantCulture); int numberBought = 0; while (numberBought < numberToBuy) { int breederClass = Convert.ToInt32(numberBought / numberPerPurchaseCohort, CultureInfo.InvariantCulture); ageOfBreeder = Convert.ToInt32(minBreedAge + (breederClass * 12), CultureInfo.InvariantCulture); RuminantFemale newBreeder = new RuminantFemale(ageOfBreeder, Sex.Female, 0, breedParams) { Location = grazeStore, Breed = this.PredictedHerdBreed, HerdName = this.PredictedHerdName, BreedParams = breedParams, Gender = Sex.Female, ID = 0, SaleFlag = HerdChangeReason.BreederPurchase }; // weight will be set to normalised weight as it was assigned 0 at initialisation newBreeder.PreviousWeight = newBreeder.Weight; // this individual must be weaned to be permitted to start breeding. newBreeder.Wean(false, "Initial"); // add to purchase request list and await purchase in Buy/Sell ruminantHerd.PurchaseIndividuals.Add(newBreeder); numberBought++; } } } } // Breeders themselves don't get sold. Trading is with Heifers // Breeders can be sold in seasonal and ENSO destocking. // sell breeders // What rule? oldest first as they may be lost soonest // should keep pregnant females... and young... // this will currently remove pregnant females and females with suckling calf } }
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; }
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); } } } }
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 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; } } }
/// <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 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 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 OnCLEMAnimalManage(object sender, EventArgs e) { //List<Ruminant> localHerd = this.CurrentHerd(); RuminantHerd ruminantHerd = Resources.RuminantHerd(); // clear store of individuals to try and purchase // ruminantHerd.PurchaseIndividuals.Clear(); // remove only the individuals that are affected by this activity. ruminantHerd.PurchaseIndividuals.RemoveAll(a => a.Breed == this.PredictedHerdBreed); List <Ruminant> herd = this.CurrentHerd(true); // List<Ruminant> herd = ruminantHerd.Herd.Where(a => a.HerdName == HerdName).ToList(); // can sell off males any month as per NABSA // if we don't need this monthly, then it goes into next if statement with herd declaration // NABSA MALES - weaners, 1-2, 2-3 and 3-4 yo, we check for any male weaned and not a breeding sire. // check for sell age/weight of young males // if SellYoungFemalesLikeMales then all apply to both sexes else only males. if (this.TimingOK || ContinuousMaleSales) { foreach (var ind in herd.Where(a => a.Weaned & (SellFemalesLikeMales ? true : (a.Gender == Sex.Male)) & (a.Age >= MaleSellingAge || a.Weight >= MaleSellingWeight))) { bool sell = true; if (ind.GetType() == typeof(RuminantMale)) { // don't sell breeding sires. sell = !((ind as RuminantMale).BreedingSire); } if (sell) { ind.SaleFlag = HerdChangeReason.AgeWeightSale; } } } // if management month if (this.TimingOK) { bool sufficientFood = true; if (foodStore != null) { sufficientFood = (foodStore.TonnesPerHectare * 1000) > MinimumPastureBeforeRestock; } // check for maximum age (females and males have different cutoffs) foreach (var ind in herd.Where(a => a.Age >= ((a.Gender == Sex.Female) ? MaximumBreederAge : MaximumBullAge))) { ind.SaleFlag = HerdChangeReason.MaxAgeSale; } // MALES // check for breeder bulls after sale of old individuals and buy/sell int numberMaleSiresInHerd = herd.Where(a => a.Gender == Sex.Male & a.SaleFlag == HerdChangeReason.None).Cast <RuminantMale>().Where(a => a.BreedingSire).Count(); // Number of females int numberFemaleBreedingInHerd = herd.Where(a => a.Gender == Sex.Female & a.Age >= a.BreedParams.MinimumAge1stMating & a.SaleFlag == HerdChangeReason.None).Count(); int numberFemaleTotalInHerd = herd.Where(a => a.Gender == Sex.Female & a.SaleFlag == HerdChangeReason.None).Count(); int numberFemaleOldInHerd = herd.Where(a => a.Gender == Sex.Female & MaximumBreederAge - a.Age <= 12 & a.SaleFlag == HerdChangeReason.None).Count(); if (numberMaleSiresInHerd > MaximumSiresKept) { // sell bulls // What rule? oldest first as they may be lost soonest int numberToRemove = MaximumSiresKept - numberMaleSiresInHerd; foreach (var male in herd.Where(a => a.Gender == Sex.Male).Cast <RuminantMale>().Where(a => a.BreedingSire).OrderByDescending(a => a.Age).Take(numberToRemove)) { male.SaleFlag = HerdChangeReason.ExcessBullSale; numberToRemove--; if (numberToRemove == 0) { break; } } } else if (numberMaleSiresInHerd < MaximumSiresKept) { if ((foodStore == null) || (sufficientFood)) { if (AllowSireReplacement) { // remove young bulls from sale herd to replace breed bulls (not those sold because too old) foreach (RuminantMale male in herd.Where(a => a.Gender == Sex.Male & a.SaleFlag == HerdChangeReason.AgeWeightSale).OrderByDescending(a => a.Weight)) { male.SaleFlag = HerdChangeReason.None; male.BreedingSire = true; numberMaleSiresInHerd++; if (numberMaleSiresInHerd >= MaximumSiresKept) { break; } } // if still insufficent, look into current herd for replacement // remaining males assumed to be too small, so await next time-step } // if still insufficient buy bulls. if (numberMaleSiresInHerd < MaximumSiresKept && (MaximumSiresPerPurchase > 0)) { // limit by breeders as proportion of max breeders so we don't spend alot on sires when building the herd and females more valuable double propOfBreeders = (double)numberFemaleBreedingInHerd / (double)MaximumBreedersKept; int sires = Convert.ToInt32(Math.Ceiling(Math.Ceiling(MaximumSiresKept * propOfBreeders))); int numberToBuy = Math.Min(MaximumSiresPerPurchase, Math.Max(0, sires - numberMaleSiresInHerd)); for (int i = 0; i < numberToBuy; i++) { RuminantMale newbull = new RuminantMale { Location = GrazeFoodStoreName, Age = 48, Breed = this.PredictedHerdBreed,// breedParams.Breed; HerdName = this.PredictedHerdName, BreedingSire = true, BreedParams = breedParams, Gender = Sex.Male, ID = 0, // ruminantHerd.NextUniqueID; Weight = 450, HighWeight = 450, SaleFlag = HerdChangeReason.SirePurchase }; // add to purchase request list and await purchase in Buy/Sell ruminantHerd.PurchaseIndividuals.Add(newbull); } } } } // FEMALES // Breeding herd traded as heifers only int excessHeifers = 0; // check for maximum number of breeders remaining after sale and buy/sell if (numberFemaleBreedingInHerd > MaximumBreedersKept) { // herd mortality of 5% plus those that will be culled in next 12 months excessHeifers = Convert.ToInt32((numberFemaleTotalInHerd - numberFemaleOldInHerd) * 0.05) + numberFemaleOldInHerd; // shortfall + (number of young - replacement heifers) excessHeifers = (numberFemaleTotalInHerd - MaximumBreedersKept) + ((numberFemaleTotalInHerd - numberFemaleBreedingInHerd) - excessHeifers); } else { // shortfall between actual and desired numbers excessHeifers = MaximumBreedersKept - numberFemaleBreedingInHerd; // add future cull for age + 5% excessHeifers += Convert.ToInt32((numberFemaleTotalInHerd - numberFemaleOldInHerd) * 0.05) + numberFemaleOldInHerd; excessHeifers = (numberFemaleTotalInHerd - numberFemaleBreedingInHerd) - excessHeifers; } // surplus heifers to sell if (excessHeifers > 0) { foreach (var female in herd.Where(a => a.Gender == Sex.Female & a.Age < a.BreedParams.MinimumAge1stMating & a.Weaned).OrderByDescending(a => a.Age).Take(excessHeifers)) { // tag fo sale. female.SaleFlag = HerdChangeReason.ExcessHeiferSale; excessHeifers--; if (excessHeifers == 0) { break; } } } else if (excessHeifers < 0) { excessHeifers *= -1; if ((foodStore == null) || (sufficientFood)) { // remove young females from sale herd to replace breeders (not those sold because too old) foreach (RuminantFemale female in herd.Where(a => a.Gender == Sex.Female & a.SaleFlag == HerdChangeReason.AgeWeightSale).OrderByDescending(a => a.Age)) { female.SaleFlag = HerdChangeReason.None; excessHeifers--; if (excessHeifers == 0) { break; } } // if still insufficient buy heifers. if (excessHeifers > 0 & (MaximumBreedersPerPurchase > 0)) { int ageOfHeifer = 12; double weightOfHeifer = 260; // buy 5% more to account for deaths before these individuals grow to breeding age int numberToBuy = Math.Min(MaximumBreedersPerPurchase, Math.Max(0, Convert.ToInt32(excessHeifers * 1.05))); for (int i = 0; i < numberToBuy; i++) { RuminantFemale newheifer = new RuminantFemale { Location = GrazeFoodStoreName, Age = ageOfHeifer, Breed = this.PredictedHerdBreed, HerdName = this.PredictedHerdName, BreedParams = breedParams, Gender = Sex.Female, ID = 0, Weight = weightOfHeifer, HighWeight = weightOfHeifer, SaleFlag = HerdChangeReason.HeiferPurchase }; // add to purchase request list and await purchase in Buy/Sell ruminantHerd.PurchaseIndividuals.Add(newheifer); } } } } // report that this activity was performed as it does not use base GetResourcesRequired //this.TriggerOnActivityPerformed(); // Breeders themselves don't get sold. Trading is with Heifers // Breeders can be sold in seasonal and ENSO destocking. // sell breeders // What rule? oldest first as they may be lost soonest // should keep pregnant females... and young... // this will currently remove pregnant females and females with suckling calf // int numberToRemove = Convert.ToInt32((numberFemaleInHerd-MaximumBreedersKept)); // foreach (var female in herd.Where(a => a.Gender == Sex.Female & a.Age >= a.BreedParams.MinimumAge1stMating).OrderByDescending(a => a.Age).Take(numberToRemove)) // { // female.SaleFlag = HerdChangeReason.ExcessBreederSale; // numberToRemove--; // if (numberToRemove == 0) break; // } // } //else //{ //} } }
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(); }