private void OnWFAnimalDeath(object sender, EventArgs e)
        {
            // remove individuals that died
            // currently performed in the month after weight has been adjusted
            // and before breeding, trading, culling etc (See Clock event order)

            // Calculated by
            // critical weight &
            // juvenile (unweaned) death based on mothers weight &
            // adult weight adjusted base mortality.

            RuminantHerd    ruminantHerd = Resources.RuminantHerd();
            List <Ruminant> herd         = ruminantHerd.Herd;

            // weight based mortality
            List <Ruminant> died = herd.Where(a => a.Weight < (a.HighWeight * (1.0 - a.BreedParams.ProportionOfMaxWeightToSurvive))).ToList();

            // set died flag
            died.Select(a => { a.SaleFlag = Common.HerdChangeReason.Died; return(a); }).ToList();
            ruminantHerd.RemoveRuminant(died);

            // base mortality adjusted for condition
            foreach (var ind in ruminantHerd.Herd)
            {
                double mortalityRate = 0;
                if (!ind.Weaned)
                {
                    mortalityRate = 0;
                    if ((ind.Mother == null) || (ind.Mother.Weight < ind.BreedParams.CriticalCowWeight * ind.StandardReferenceWeight))
                    {
                        // if no mohter assigned or mother's weight is < CriticalCowWeight * SFR
                        mortalityRate = ind.BreedParams.JuvenileMortalityMaximum;
                    }
                    else
                    {
                        // if mother's weight >= criticalCowWeight * SFR
                        mortalityRate = Math.Exp(-Math.Pow(ind.BreedParams.JuvenileMortalityCoefficient * (ind.Mother.Weight / ind.Mother.NormalisedAnimalWeight), ind.BreedParams.JuvenileMortalityExponent));
                    }
                    mortalityRate = mortalityRate + ind.BreedParams.MortalityBase;
                    mortalityRate = Math.Max(mortalityRate, ind.BreedParams.JuvenileMortalityMaximum);
                }
                else
                {
                    mortalityRate = 1 - (1 - ind.BreedParams.MortalityBase) * (1 - Math.Exp(Math.Pow(-(ind.BreedParams.MortalityCoefficient * (ind.Weight / ind.NormalisedAnimalWeight - ind.BreedParams.MortalityIntercept)), ind.BreedParams.MortalityExponent)));
                }
                if (WholeFarm.RandomGenerator.NextDouble() <= mortalityRate)
                {
                    ind.Died = true;
                }
            }

            died = herd.Where(a => a.Died).ToList();
            died.Select(a => { a.SaleFlag = Common.HerdChangeReason.Died; return(a); }).ToList();
            ruminantHerd.RemoveRuminant(died);
        }
        private void OnCLEMAnimalDeath(object sender, EventArgs e)
        {
            // remove individuals that died
            // currently performed in the month after weight has been adjusted
            // and before breeding, trading, culling etc (See Clock event order)

            // Calculated by
            // critical weight &&
            // juvenile (unweaned) death based on mothers weight &&
            // adult weight adjusted base mortality.

            RuminantHerd    ruminantHerd = Resources.RuminantHerd();
            List <Ruminant> herd         = ruminantHerd.Herd;

            // weight based mortality
            List <Ruminant> died = herd.Where(a => a.Weight < (a.HighWeight * a.BreedParams.ProportionOfMaxWeightToSurvive)).ToList();

            // set died flag
            died.Select(a => { a.SaleFlag = HerdChangeReason.DiedUnderweight; return(a); }).ToList();
            ruminantHerd.RemoveRuminant(died, this);

            // base mortality adjusted for condition
            foreach (var ind in ruminantHerd.Herd)
            {
                double mortalityRate = 0;
                if (!ind.Weaned)
                {
                    mortalityRate = 0;
                    if ((ind.Mother == null) || (ind.Mother.Weight < ind.BreedParams.CriticalCowWeight * ind.StandardReferenceWeight))
                    {
                        // if no mother assigned or mother's weight is < CriticalCowWeight * SFR
                        mortalityRate = ind.BreedParams.JuvenileMortalityMaximum;
                    }
                    else
                    {
                        // if mother's weight >= criticalCowWeight * SFR
                        mortalityRate = Math.Exp(-Math.Pow(ind.BreedParams.JuvenileMortalityCoefficient * (ind.Mother.Weight / ind.Mother.NormalisedAnimalWeight), ind.BreedParams.JuvenileMortalityExponent));
                    }
                    mortalityRate += ind.BreedParams.MortalityBase;
                    mortalityRate  = Math.Min(mortalityRate, ind.BreedParams.JuvenileMortalityMaximum);
                }
                else
                {
                    mortalityRate = 1 - (1 - ind.BreedParams.MortalityBase) * (1 - Math.Exp(Math.Pow(-(ind.BreedParams.MortalityCoefficient * (ind.Weight / ind.NormalisedAnimalWeight - ind.BreedParams.MortalityIntercept)), ind.BreedParams.MortalityExponent)));
                }
                // convert mortality from annual (calculated) to monthly (applied).
                if (ZoneCLEM.RandomGenerator.NextDouble() <= (mortalityRate / 12))
                {
                    ind.Died = true;
                }
            }

            died = herd.Where(a => a.Died).ToList();
            died.Select(a => { a.SaleFlag = HerdChangeReason.DiedMortality; return(a); }).ToList();

            // TODO: separate foster from real mother for genetics
            // check for death of mother with sucklings and try foster sucklings
            List <RuminantFemale> mothersWithCalf     = died.Where(a => a.Gender == Sex.Female).Cast <RuminantFemale>().Where(a => a.SucklingOffspringList.Count() > 0).ToList();
            List <RuminantFemale> wetMothersAvailable = died.Where(a => a.Gender == Sex.Female).Cast <RuminantFemale>().Where(a => a.IsLactating & a.SucklingOffspringList.Count() == 0).OrderBy(a => a.DaysLactating).ToList();
            int wetMothersAssigned = 0;

            if (wetMothersAvailable.Count() > 0)
            {
                if (mothersWithCalf.Count() > 0)
                {
                    foreach (var deadMother in mothersWithCalf)
                    {
                        foreach (var calf in deadMother.SucklingOffspringList)
                        {
                            if (wetMothersAssigned < wetMothersAvailable.Count())
                            {
                                calf.Mother = wetMothersAvailable[wetMothersAssigned];
                                wetMothersAssigned++;
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                }
            }

            ruminantHerd.RemoveRuminant(died, this);
        }
        private void OnCLEMAnimalSell(object sender, EventArgs e)
        {
            RuminantHerd ruminantHerd = Resources.RuminantHerd();

            int    trucks     = 0;
            double saleValue  = 0;
            double saleWeight = 0;
            int    head       = 0;
            double aESum      = 0;

            // get current untrucked list of animals flagged for sale
            List <Ruminant> herd = this.CurrentHerd(false).Where(a => a.SaleFlag != HerdChangeReason.None).OrderByDescending(a => a.Weight).ToList();

            if (trucking == null)
            {
                // no trucking just sell
                head = herd.Count();
                if (herd.Count() > 0)
                {
                    SetStatusSuccess();
                }
                foreach (var ind in herd)
                {
                    aESum      += ind.AdultEquivalent;
                    saleValue  += ind.BreedParams.ValueofIndividual(ind, PurchaseOrSalePricingStyleType.Sale);
                    saleWeight += ind.Weight;
                    ruminantHerd.RemoveRuminant(ind, this);
                }
            }
            else
            {
                // if sale herd > min loads before allowing sale
                if (herd.Select(a => a.Weight / 450.0).Sum() / trucking.Number450kgPerTruck >= trucking.MinimumTrucksBeforeSelling)
                {
                    // while truck to fill
                    while (herd.Select(a => a.Weight / 450.0).Sum() / trucking.Number450kgPerTruck > trucking.MinimumLoadBeforeSelling)
                    {
                        bool nonloaded = true;
                        trucks++;
                        double load450kgs = 0;
                        // while truck below carrying capacity load individuals
                        foreach (var ind in herd)
                        {
                            if (load450kgs + (ind.Weight / 450.0) <= trucking.Number450kgPerTruck)
                            {
                                nonloaded = false;
                                head++;
                                aESum      += ind.AdultEquivalent;
                                load450kgs += ind.Weight / 450.0;
                                saleValue  += ind.BreedParams.ValueofIndividual(ind, PurchaseOrSalePricingStyleType.Sale);
                                saleWeight += ind.Weight;
                                ruminantHerd.RemoveRuminant(ind, this);

                                //TODO: work out what to do with suckling calves still with mothers if mother sold.
                            }
                        }
                        if (nonloaded)
                        {
                            Summary.WriteWarning(this, String.Format("There was a problem loading the sale truck as sale individuals did not meet the loading criteria for breed [r={0}]", this.PredictedHerdBreed));
                            break;
                        }
                        herd = this.CurrentHerd(false).Where(a => a.SaleFlag != HerdChangeReason.None).OrderByDescending(a => a.Weight).ToList();
                    }
                    // create trucking emissions
                    if (trucks > 0)
                    {
                        SetStatusSuccess();
                    }
                    trucking.ReportEmissions(trucks, true);
                }
            }
            if (bankAccount != null && head > 0) //(trucks > 0 || trucking == null)
            {
                ResourceRequest expenseRequest = new ResourceRequest
                {
                    ActivityModel      = this,
                    AllowTransmutation = false
                };

                // calculate transport costs
                if (trucking != null)
                {
                    expenseRequest.Required = trucks * trucking.DistanceToMarket * trucking.CostPerKmTrucking;
                    expenseRequest.Reason   = "Transport sales";
                    bankAccount.Remove(expenseRequest);
                }

                foreach (RuminantActivityFee item in Apsim.Children(this, typeof(RuminantActivityFee)))
                {
                    switch (item.PaymentStyle)
                    {
                    case AnimalPaymentStyleType.Fixed:
                        expenseRequest.Required = item.Amount;
                        break;

                    case AnimalPaymentStyleType.perHead:
                        expenseRequest.Required = head * item.Amount;
                        break;

                    case AnimalPaymentStyleType.perAE:
                        expenseRequest.Required = aESum * item.Amount;
                        break;

                    case AnimalPaymentStyleType.ProportionOfTotalSales:
                        expenseRequest.Required = saleValue * item.Amount;
                        break;

                    default:
                        throw new Exception(String.Format("PaymentStyle ({0}) is not supported for ({1}) in ({2})", item.PaymentStyle, item.Name, this.Name));
                    }
                    expenseRequest.Reason = item.Name;
                    // uses bank account specified in the RuminantActivityFee
                    item.BankAccount.Remove(expenseRequest);
                }

                // add and remove from bank
                if (saleValue > 0)
                {
                    bankAccount.Add(saleValue, this, this.PredictedHerdName + " sales");
                }
            }
        }
        private void OnWFAnimalSell(object sender, EventArgs e)
        {
            RuminantHerd ruminantHerd = Resources.RuminantHerd();

            Finance     Accounts    = Resources.FinanceResource() as Finance;
            FinanceType bankAccount = Accounts.GetFirst() as FinanceType;

            int    trucks     = 0;
            double saleValue  = 0;
            double saleWeight = 0;
            int    head       = 0;

            // get current untrucked list of animals flagged for sale
            List <Ruminant> herd = ruminantHerd.Herd.Where(a => a.SaleFlag != Common.HerdChangeReason.None & a.Breed == BreedName).OrderByDescending(a => a.Weight).ToList();

            // if sale herd > min loads before allowing sale
            if (herd.Select(a => a.Weight / 450.0).Sum() / Number450kgPerTruck >= MinimumTrucksBeforeSelling)
            {
                // while truck to fill
                while (herd.Select(a => a.Weight / 450.0).Sum() / Number450kgPerTruck > MinimumLoadBeforeSelling)
                {
                    bool nonloaded = true;
                    trucks++;
                    double load450kgs = 0;
                    // while truck below carrying capacity load individuals
                    foreach (var ind in herd)
                    {
                        if (load450kgs + (ind.Weight / 450.0) <= Number450kgPerTruck)
                        {
                            nonloaded = false;
                            head++;
                            load450kgs += ind.Weight / 450.0;
                            RuminantValue getvalue = PriceList.Where(a => a.Age < ind.Age).OrderBy(a => a.Age).LastOrDefault();
                            saleValue  += getvalue.SellValue * ((getvalue.Style == Common.PricingStyleType.perKg) ? ind.Weight : 1.0);
                            saleWeight += ind.Weight;
                            ruminantHerd.RemoveRuminant(ind);
                        }
                    }
                    if (nonloaded)
                    {
                        Summary.WriteWarning(this, String.Format("There was a problem loading the sale truck as sale individuals did not meet the loading criteria for breed {0}", BreedName));
                        break;
                    }
                    herd = ruminantHerd.Herd.Where(a => a.SaleFlag != Common.HerdChangeReason.None & a.Breed == BreedName).OrderByDescending(a => a.Weight).ToList();
                }

                if (trucks > 0 & bankAccount != null)
                {
                    // calculate transport costs
                    double transportCost = trucks * DistanceToMarket * CostPerKmTrucking;
                    bankAccount.Remove(transportCost, this.Name, "Transport");
                    // calculate MLA fees
                    double mlaCost = head * MLAFees;
                    bankAccount.Remove(mlaCost, this.Name, "R&DFee");
                    // calculate yard fees
                    double yardCost = head * YardFees;
                    bankAccount.Remove(yardCost, this.Name, "YardCosts");
                    // calculate commission
                    double commissionCost = saleValue * SalesCommission;
                    bankAccount.Remove(commissionCost, this.Name, "SalesCommission");

                    // add and remove from bank
                    bankAccount.Add(saleValue, this.Name, "Sales");
                }
            }
        }
        private void OnCLEMInitialiseActivity(object sender, EventArgs e)
        {
            // create local version of max breeders so we can modify without affecting user set value
            maxBreeders = Math.Max(this.MaximumBreedersKept, this.MinimumBreedersKept);

            this.InitialiseHerd(false, true);
            breedParams = Resources.GetResourceItem(this, typeof(RuminantHerd), this.PredictedHerdName, OnMissingResourceActionTypes.ReportErrorAndStop, OnMissingResourceActionTypes.ReportErrorAndStop) as RuminantType;

            decimal breederHerdSize = 0;

            if (AdjustBreedingFemalesAtStartup)
            {
                RuminantHerd    herd    = Resources.RuminantHerd();
                List <Ruminant> rumHerd = this.CurrentHerd(false);
                if (rumHerd != null && rumHerd.Count() > 0)
                {
                    int                    numberAdded = 0;
                    RuminantType           breedParams = rumHerd.FirstOrDefault().BreedParams;
                    RuminantInitialCohorts cohorts     = Apsim.Children(rumHerd.FirstOrDefault().BreedParams, typeof(RuminantInitialCohorts)).FirstOrDefault() as RuminantInitialCohorts;

                    if (cohorts != null)
                    {
                        List <RuminantTypeCohort> cohortList = Apsim.Children(cohorts, typeof(RuminantTypeCohort)).Cast <RuminantTypeCohort>().Where(a => a.Gender == Sex.Female && (a.Age >= breedParams.MinimumAge1stMating & a.Age <= this.MaximumBreederAge)).ToList();
                        int initialBreeders = Convert.ToInt32(cohortList.Sum(a => a.Number));
                        if (initialBreeders < this.MinimumBreedersKept)
                        {
                            double scaleFactor = this.MinimumBreedersKept / Convert.ToDouble(initialBreeders);
                            // add new individuals
                            foreach (var item in cohortList)
                            {
                                int numberToAdd = Convert.ToInt32(Math.Round(item.Number * scaleFactor) - item.Number);
                                foreach (var newind in item.CreateIndividuals(numberToAdd))
                                {
                                    newind.SaleFlag = HerdChangeReason.FillInitialHerd;
                                    herd.AddRuminant(newind, this);
                                    numberAdded++;
                                }
                            }
                            if (numberAdded == 0)
                            {
                                throw new ApsimXException(this, $"Unable to scale breeding female population up to the maximum breeders kept at startup\nNo cohorts representing breeders were found in the initial herd structure [r=InitialCohorts] for [r={breedParams.Name}]\nAdd at least one initial cohort that meets the breeder criteria of age at first mating and max age kept");
                            }
                            breederHerdSize = initialBreeders + numberAdded;
                        }
                        else if (initialBreeders > maxBreeders)
                        {
                            int reduceBy = Math.Max(0, initialBreeders - maxBreeders);
                            // reduce initial herd size
                            // randomly select the individuals to remove form the breeder herd
                            List <Ruminant> breeders = rumHerd.Where(a => a.Gender == Sex.Female && a.Age > breedParams.MinimumAge1stMating && a.Age < this.MaximumBreederAge).OrderBy(x => Guid.NewGuid()).Take(reduceBy).ToList();
                            foreach (var item in breeders)
                            {
                                item.SaleFlag = HerdChangeReason.ReduceInitialHerd;
                                herd.RemoveRuminant(item, this);
                                reduceBy--;
                            }

                            if (reduceBy > 0)
                            {
                                // add warning
                                string warn = $"Unable to reduce breeders at the start of the simulation to number required [{maxBreeders}] using [a={this.Name}]";
                                if (!Warnings.Exists(warn))
                                {
                                    Summary.WriteWarning(this, warn);
                                    Warnings.Add(warn);
                                }
                            }
                            breederHerdSize = maxBreeders;
                        }
                    }
                    else
                    {
                        throw new ApsimXException(this, $"Unable to adjust breeding female population to the maximum breeders kept at startup\nNo initial herd structure [r=InitialCohorts] has been provided in [r={breedParams.Name}]");
                    }
                }
            }

            // max sires
            if (MaximumSiresKept < 1 & MaximumSiresKept > 0)
            {
                SiresKept = Convert.ToInt32(Math.Ceiling(maxBreeders * breederHerdSize), CultureInfo.InvariantCulture);
            }
            else
            {
                SiresKept = Convert.ToInt32(Math.Truncate(MaximumSiresKept), CultureInfo.InvariantCulture);
            }

            if (AdjustBreedingMalesAtStartup)
            {
                RuminantHerd herd = Resources.RuminantHerd();
                if (herd != null)
                {
                    // get number in herd
                    List <Ruminant> rumHerd       = this.CurrentHerd(false);
                    int             numberPresent = rumHerd.Where(a => a.Gender == Sex.Male).Cast <RuminantMale>().Where(a => a.BreedingSire).Count();
                    if (numberPresent < SiresKept)
                    {
                        // fill to number needed
                        for (int i = numberPresent; i < SiresKept; i++)
                        {
                            RuminantMale newSire = new RuminantMale(SireAgeAtPurchase, Sex.Male, 0, breedParams)
                            {
                                Breed        = this.PredictedHerdBreed,
                                HerdName     = this.PredictedHerdName,
                                BreedingSire = true,
                                ID           = herd.NextUniqueID,
                                SaleFlag     = HerdChangeReason.FillInitialHerd
                            };
                            herd.AddRuminant(newSire, this);
                        }
                    }
                    else if (numberPresent > SiresKept)
                    {
                        // reduce initial herd.
                        int reduceBy = Math.Max(0, numberPresent - SiresKept);
                        // reduce initial sire herd size
                        // randomly select the individuals to remove form the breeder herd
                        List <RuminantMale> sires = rumHerd.Where(a => a.Gender == Sex.Male).Cast <RuminantMale>().Where(a => a.BreedingSire).OrderBy(x => Guid.NewGuid()).Take(reduceBy).ToList();
                        foreach (var item in sires)
                        {
                            item.SaleFlag = HerdChangeReason.ReduceInitialHerd;
                            herd.RemoveRuminant(item, this);
                            reduceBy--;
                        }

                        if (reduceBy > 0)
                        {
                            // add warning
                            string warn = $"Unable to reduce breeding sires at the start of the simulation to number required [{SiresKept}] using [a={this.Name}]";
                            if (!Warnings.Exists(warn))
                            {
                                Summary.WriteWarning(this, warn);
                                Warnings.Add(warn);
                            }
                        }
                    }
                }
            }

            // check GrazeFoodStoreExists for breeders
            grazeStoreBreeders = "";
            if (GrazeFoodStoreNameBreeders != null && !GrazeFoodStoreNameBreeders.StartsWith("Not specified"))
            {
                grazeStoreBreeders = GrazeFoodStoreNameBreeders.Split('.').Last();
                foodStoreBreeders  = Resources.GetResourceItem(this, GrazeFoodStoreNameBreeders, OnMissingResourceActionTypes.ReportErrorAndStop, OnMissingResourceActionTypes.ReportErrorAndStop) as GrazeFoodStoreType;
            }

            // check for managed paddocks and warn if breeders placed in yards.
            if (grazeStoreBreeders == "" && this.MaximumProportionBreedersPerPurchase > 0)
            {
                var ah = Apsim.Find(this, typeof(ActivitiesHolder));
                if (Apsim.ChildrenRecursively(ah, typeof(PastureActivityManage)).Count() != 0)
                {
                    Summary.WriteWarning(this, String.Format("Breeders purchased by [a={0}] are currently placed in [Not specified - general yards] while a managed pasture is available. These animals will not graze until mustered and will require feeding while in yards.\nSolution: Set the [GrazeFoodStore to place purchase in] located in the properties [General].[PastureDetails]", this.Name));
                }
            }

            // check GrazeFoodStoreExists for sires
            grazeStoreSires = "";
            if (GrazeFoodStoreNameSires != null && !GrazeFoodStoreNameSires.StartsWith("Not specified"))
            {
                grazeStoreSires = GrazeFoodStoreNameSires.Split('.').Last();
                foodStoreSires  = Resources.GetResourceItem(this, GrazeFoodStoreNameSires, OnMissingResourceActionTypes.ReportErrorAndStop, OnMissingResourceActionTypes.ReportErrorAndStop) as GrazeFoodStoreType;
            }

            // check for managed paddocks and warn if sires placed in yards.
            if (grazeStoreBreeders == "" && this.SiresKept > 0)
            {
                var ah = Apsim.Find(this, typeof(ActivitiesHolder));
                if (Apsim.ChildrenRecursively(ah, typeof(PastureActivityManage)).Count() != 0)
                {
                    Summary.WriteWarning(this, String.Format("Sires purchased by [a={0}] are currently placed in [Not specified - general yards] while a managed pasture is available. These animals will not graze until mustered and will require feeding while in yards.\nSolution: Set the [GrazeFoodStore to place purchase in] located in the properties [General].[PastureDetails]", this.Name));
                }
            }
        }