Пример #1
0
        /// <summary>
        /// Get value of a specific individual
        /// </summary>
        /// <returns>value</returns>
        public AnimalPriceGroup ValueofIndividual(Ruminant ind, PurchaseOrSalePricingStyleType purchaseStyle, string warningMessage = "")
        {
            if (PricingAvailable())
            {
                if (ind.CurrentPrice == null || !ind.CurrentPrice.Filter(ind))
                {
                    // search through RuminantPriceGroups for first match with desired purchase or sale flag
                    foreach (AnimalPriceGroup priceGroup in priceGroups.Where(a => a.PurchaseOrSale == purchaseStyle || a.PurchaseOrSale == PurchaseOrSalePricingStyleType.Both))
                    {
                        if (priceGroup.Filter(ind))
                        {
                            ind.CurrentPrice = priceGroup;
                            return(priceGroup);
                        }
                    }

                    // no price match found.
                    string warningString = warningMessage;
                    if (warningString == "")
                    {
                        warningString = $"No [{purchaseStyle}] price entry was found for [r={ind.Breed}] meeting the required criteria [f=age: {ind.Age}] [f=sex: {ind.Sex}] [f=weight: {ind.Weight:##0}]";
                    }
                    Warnings.CheckAndWrite(warningString, Summary, this);
                }
                return(ind.CurrentPrice);
            }
            return(null);
        }
Пример #2
0
        /// <summary>
        /// Do simple error checking to make sure the data retrieved is usable
        /// </summary>
        /// <param name="filtered"></param>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <param name="region"></param>
        /// <param name="soil"></param>
        /// <param name="grassBasalArea"></param>
        /// <param name="landCondition"></param>
        /// <param name="stockingRate"></param>
        private void CheckAllMonthsWereRetrieved(List <PastureDataType> filtered, DateTime startDate, DateTime endDate,
                                                 int region, string soil, double grassBasalArea, double landCondition, double stockingRate)
        {
            if (clock.EndDate == clock.Today)
            {
                return;
            }

            //Check no gaps in the months
            DateTime tempdate = startDate;

            if (filtered.Count() > 0 && MissingDataAction != OnMissingResourceActionTypes.Ignore)
            {
                foreach (PastureDataType month in filtered)
                {
                    if ((tempdate.Year != month.Year) || (tempdate.Month != month.Month))
                    {
                        // missing month entry
                        string warn     = $"Missing pasture production entry for [{tempdate.Month}/{tempdate.Year}] in [x={this.Name}]";
                        string warnfull = warn + $"\r\nGiven Region id: [{region}], Land id: [{soil}], Grass Basal Area: [{grassBasalArea}], Land Condition: [{landCondition}] & Stocking Rate: [{stockingRate}]\r\nAssume [0] for pasture production and all associated values such as rainfall";
                        if (MissingDataAction == OnMissingResourceActionTypes.ReportWarning)
                        {
                            Warnings.CheckAndWrite(warn, Summary, this, MessageType.Warning, warnfull);
                        }
                        else if (MissingDataAction == OnMissingResourceActionTypes.ReportErrorAndStop)
                        {
                            throw new ApsimXException(this, warnfull);
                        }
                    }
                    tempdate = tempdate.AddMonths(1);
                }
            }
        }
Пример #3
0
        private void OnCLEMInitialiseActivity(object sender, EventArgs e)
        {
            this.InitialiseHerd(false, true);
            IEnumerable <Ruminant> testherd = this.CurrentHerd(true);

            // check if finance is available and warn if not supplying bank account.
            if (Resources.ResourceItemsExist <Finance>())
            {
                if (BankAccountName == "")
                {
                    Summary.WriteMessage(this, $"No bank account has been specified in [a={this.Name}] while Finances are available in the simulation. No financial transactions will be recorded for the purchase and sale of animals.", MessageType.Warning);
                }
            }
            if (BankAccountName != "")
            {
                bankAccount = Resources.FindResourceType <Finance, FinanceType>(this, BankAccountName, OnMissingResourceActionTypes.Ignore, OnMissingResourceActionTypes.ReportErrorAndStop);
            }

            // get trucking settings
            trucking = this.FindAllChildren <TruckingSettings>().FirstOrDefault() as TruckingSettings;

            // check if pricing is present
            if (bankAccount != null)
            {
                var breeds = HerdResource.Herd.Where(a => a.BreedParams.Breed == this.PredictedHerdBreed).GroupBy(a => a.HerdName);
                foreach (var herd in breeds)
                {
                    if (!herd.FirstOrDefault().BreedParams.PricingAvailable())
                    {
                        string warn = $"No pricing schedule has been provided for herd [r={herd.Key}]. No financial transactions will be recorded for activity [a={this.Name}]";
                        Warnings.CheckAndWrite(warn, Summary, this, MessageType.Warning);
                    }
                }
            }
        }
Пример #4
0
        private void UpdatePricingToDate(int year, int month)
        {
            DateTime checkDate = new DateTime(year, month, DateTime.DaysInMonth(year, month));

            // work through to start date
            while (priceFileAsRows.Count > 0 && (DateTime.Parse(priceFileAsRows[0][DateColumnName].ToString()) <= checkDate))
            {
                int cnt = 0;
                foreach (var column in priceFileAsRows[0].Table.Columns)
                {
                    if (!column.ToString().Equals(DateColumnName, StringComparison.OrdinalIgnoreCase) && double.TryParse(priceFileAsRows[0][cnt].ToString(), out double res))
                    {
                        // update
                        var components = pricingComonentsFound.Where(a => (a as IModel).Name == column.ToString());
                        if (components.Count() > 1)
                        {
                            string warn = $"Multiple resource [r=PricingComponents] named [{column}] were found when applying pricing by [a={this.Name}]. \r\n Ensure input price applies to all these components or provide unique component names";
                            Warnings.CheckAndWrite(warn, Summary, this);
                        }
                        foreach (IResourcePricing resourcePricing in components)
                        {
                            resourcePricing.SetPrice(res, this);
                        }
                    }
                    cnt++;
                }
                // remove row
                priceFileAsRows.RemoveAt(0);
            }
        }
Пример #5
0
        /// <summary>
        /// Finds the closest Value of categorised lookup values form the database
        /// This applies to Stocking rates, Grass Basal Area (or use GBA) and Land Condition
        /// The Pasture database does not have every stocking rate, grass basal area or land condition.
        /// It will find the category with the next largest value to the actual value supplied.
        /// So if the value is 0 the category with the next largest value will normally be the first entry
        /// </summary>
        /// <param name="category">The name of the distict categories to use</param>
        /// <param name="value">The value to search for</param>
        /// <returns></returns>
        private double FindClosestCategory(string category, double value)
        {
            double[] valuesToUse;
            switch (category)
            {
            case "StockingRate":
                valuesToUse = distinctStkRates;
                if (valuesToUse.Max() > 100 | valuesToUse.Min() < 0)
                {
                    // add warning
                    string warn     = $"Suspicious values for [{category}] found in pasture database [x={this.Name}]";
                    string warnfull = $"Suspicious values for [{category}] found in pasture database [x={this.Name}]\r\nValues in database: [{string.Join("],[", valuesToUse.Select(a => a.ToString()).ToArray())}]\r\nExpecting values between 1 and 100";
                    Warnings.CheckAndWrite(warn, Summary, this, MessageType.Warning, warnfull);
                }
                break;

            case "GrassBasalArea":
            case "GBA":
                valuesToUse = distinctGBAs;
                if (valuesToUse.Max() > 10 | valuesToUse.Min() < 0)
                {
                    // add warning
                    string warn     = $"Suspicious values for [{category}] found in pasture database [x={this.Name}]";
                    string warnfull = $"Suspicious values for [{category}] found in pasture database [x={this.Name}]\r\nValues in database: [{string.Join("],[", valuesToUse.Select(a => a.ToString()).ToArray())}]\r\nExpecting values between 0 and 10";
                    Warnings.CheckAndWrite(warn, Summary, this, MessageType.Warning, warnfull);
                }
                break;

            case "LandCondition":
                valuesToUse = distinctLandConditions;
                if (valuesToUse.Max() > 11 | valuesToUse.Min() < 0)
                {
                    // add warning
                    string warn     = $"Suspicious values for [{category}] found in pasture database [x={this.Name}]";
                    string warnfull = $"Suspicious values for [{category}] found in pasture database [x={this.Name}]\r\nValues in database: [{string.Join("],[", valuesToUse.Select(a => a.ToString()).ToArray())}]\r\nExpecting values between 1 and 11";
                    Warnings.CheckAndWrite(warn, Summary, this, MessageType.Warning, warnfull);
                }
                break;

            default:
                throw new ApsimXException(this, $"Unknown pasture data category [{category}] used in code behind [x={this.Name}]");
            }

            if (valuesToUse.Count() == 0)
            {
                throw new ApsimXException(this, $"Unable to find any values for [{category}] in [x={this.Name}]");
            }

            int index = Array.BinarySearch(valuesToUse, value);

            if (~index >= valuesToUse.Count())
            {
                // add warning
                string warn     = $"Unable to find a [{category}] value greater than the specified value in pasture database [x={this.Name}]";
                string warnfull = $"Unable to find a [{category}] value greater than the specified [{value:0.##}] in pasture database [x={this.Name}]\r\nKnown values in database: [{string.Join("],[", valuesToUse.Select(a => a.ToString()).ToArray())}]\r\nUsed: [{valuesToUse.Last()}]\r\nFix: Ensure the pasture database includes a [{category}] greater than values produced in this simulation for optimal results.";
                Warnings.CheckAndWrite(warn, Summary, this, MessageType.Error, warnfull);
                index = valuesToUse.Count() - 1;
            }
            return((index < 0) ? valuesToUse[~index] : valuesToUse[index]);
        }
Пример #6
0
        private void OnStartOfSimulation(object sender, EventArgs e)
        {
            //if the SQLite Database can't be opened throw an exception.
            var validationContext = new ValidationContext(this, null, null);

            validationResults = new List <ValidationResult>();
            Validator.TryValidateObject(this, validationContext, validationResults, true);

            if (OpenSQLiteDB() == false)
            {
                throw new Exception(ErrorMessage);
            }
            else
            {
                // create warning if database is not optimised for CLEM
                var result = SQLiteReader.ExecuteQuery("SELECT count(*) FROM sqlite_master WHERE type='index' and name='CLEM_next_growth';");
                if (result.Rows.Count >= 0 && Convert.ToInt32(result.Rows[0][0]) != 1)
                {
                    // add warning
                    string warn = $"The database [x={this.FileName.Replace("_", "\\_")}] specified in [x={this.Name}] has not been optimised for best performance in CLEM. Add the following index to your database using your chosen database management software (e.g. DB Browser) to significantly improve your simulation speed:\r\nCREATE INDEX CLEM\\_next\\_growth ON Native\\_Inputs (Region, Soil, GrassBA, LandCon, StkRate, Year, Month);\r\nThis index must be named CLEM\\_next\\_growth and should include the table and column names appropriate to your database.";
                    Warnings.CheckAndWrite(warn, Summary, this, MessageType.Warning);
                }
            }

            // get list of distinct stocking rates available in database
            // database has already been opened and checked in Validate()
            this.distinctStkRates       = GetCategories(StkRateColumnName);
            this.distinctGBAs           = GetCategories(GrassBAColumnName);
            this.distinctLandConditions = GetCategories(LandConColumnName);
        }
Пример #7
0
 /// <summary>
 /// Check whether an individual has all mandotory attributes
 /// </summary>
 /// <param name="ind">Individual ruminant to check</param>
 /// <param name="model">Model adding individuals</param>
 public void CheckMandatoryAttributes(Ruminant ind, IModel model)
 {
     foreach (var attribute in mandatoryAttributes)
     {
         if (!ind.Attributes.Exists(attribute))
         {
             string warningString = $"No mandatory attribute [{attribute.ToUpper()}] present for individual added by [a={model.Name}]";
             Warnings.CheckAndWrite(warningString, Summary, this, MessageType.Error);
         }
     }
 }
        private double HandleRestocking(double animalEquivalentsToBuy, string paddockName, Ruminant exampleRuminant)
        {
            if (animalEquivalentsToBuy <= 0)
            {
                return(0);
            }

            GrazeFoodStoreType foodStore = Resources.FindResourceType <GrazeFoodStore, GrazeFoodStoreType>(this, paddockName, OnMissingResourceActionTypes.Ignore, OnMissingResourceActionTypes.ReportErrorAndStop);

            // ensure min pasture for restocking
            if ((foodStore == null) || ((foodStore.TonnesPerHectare * 1000) > MinimumFeedBeforeRestock))
            {
                var specifyComponents = FindAllChildren <SpecifyRuminant>();
                if (specifyComponents.Count() == 0)
                {
                    string warn = $"No [f=SpecifyRuminant]s were provided in [a={this.Name}]\r\nNo restocking will be performed.";
                    this.Status = ActivityStatus.Warning;
                    Warnings.CheckAndWrite(warn, Summary, this);
                }

                // buy animals specified in restock ruminant groups
                foreach (SpecifyRuminant item in specifyComponents)
                {
                    double sumAE   = 0;
                    double limitAE = animalEquivalentsToBuy * item.Proportion;

                    while (sumAE < limitAE && animalEquivalentsToBuy > 0)
                    {
                        Ruminant newIndividual = item.Details.CreateIndividuals(1, null).FirstOrDefault();
                        newIndividual.Location    = paddockName;
                        newIndividual.BreedParams = item.BreedParams;
                        newIndividual.HerdName    = item.BreedParams.Name;
                        newIndividual.PurchaseAge = newIndividual.Age;
                        newIndividual.SaleFlag    = HerdChangeReason.RestockPurchase;

                        if (newIndividual.Weight == 0)
                        {
                            throw new ApsimXException(this, $"Specified individual added during restock cannot have no weight in [{this.Name}]");
                        }

                        HerdResource.PurchaseIndividuals.Add(newIndividual);
                        double indAE = newIndividual.AdultEquivalent;
                        animalEquivalentsToBuy -= indAE;
                        sumAE += indAE;
                    }
                }
                return(Math.Max(0, animalEquivalentsToBuy));
            }
            return(animalEquivalentsToBuy);
        }
        /// <summary>
        /// Method to perform destocking
        /// </summary>
        /// <param name="animalEquivalentsForSale"></param>
        /// <param name="paddockName"></param>
        /// <returns>The AE that were not handled</returns>
        private double HandleDestocking(double animalEquivalentsForSale, string paddockName)
        {
            if (animalEquivalentsForSale <= 0)
            {
                return(0);
            }

            // move to underutilised paddocks
            // TODO: This can be added later as an activity including spelling

            // remove all potential purchases from list as they can't be supported.
            // This does not change the shortfall AE as they were not counted in TotalAE pressure.
            HerdResource.PurchaseIndividuals.RemoveAll(a => a.Location == paddockName);

            var destockGroups = FindAllChildren <RuminantGroup>().Where(a => a.Reason == RuminantStockGroupStyle.Destock);

            if (!destockGroups.Any())
            {
                string warn = $"No [f=FilterGroup]s with a [Destock] Reason were provided in [a={this.Name}]\r\nNo destocking will be performed.";
                this.Status = ActivityStatus.Warning;
                Warnings.CheckAndWrite(warn, Summary, this);
            }

            foreach (var item in destockGroups)
            {
                // works with current filtered herd to obey filtering.
                var herd = item.Filter(CurrentHerd(false))
                           .Where(a => a.Location == paddockName && !a.ReadyForSale);

                foreach (Ruminant ruminant in herd)
                {
                    if (ruminant.SaleFlag != HerdChangeReason.DestockSale)
                    {
                        animalEquivalentsForSale -= ruminant.AdultEquivalent;
                        ruminant.SaleFlag         = HerdChangeReason.DestockSale;
                    }

                    if (animalEquivalentsForSale <= 0)
                    {
                        this.Status = ActivityStatus.Success;
                        return(0);
                    }
                }
            }

            return(animalEquivalentsForSale);

            // handling of sucklings with sold female is in RuminantActivityBuySell
            // buy or sell is handled by the buy sell activity
        }
Пример #10
0
        /// <inheritdoc/>
        public override void DoActivity()
        {
            Status = ActivityStatus.NotNeeded;
            double labourlimit = this.LabourLimitProportion;
            double units       = 0;

            if (labourlimit == 1 || this.OnPartialResourcesAvailableAction == OnPartialResourcesAvailableActionTypes.UseResourcesAvailable)
            {
                units = unitsAvailableForSale * labourlimit;
                if (price.UseWholePackets)
                {
                    units = Math.Truncate(units);
                }
            }

            if (units > 0)
            {
                // remove resource
                ResourceRequest purchaseRequest = new ResourceRequest
                {
                    ActivityModel      = this,
                    Required           = units * price.PacketSize,
                    AllowTransmutation = true,
                    Category           = TransactionCategory,
                    RelatesToResource  = (resourceToSell as CLEMModel).NameWithParent
                };
                resourceToSell.Remove(purchaseRequest);

                // transfer money earned
                if (bankAccount != null)
                {
                    if (price.PricePerPacket == 0)
                    {
                        string warn = $"No price set [0] for [r={resourceToSell.Name}] at time of transaction for [a={this.Name}]{Environment.NewLine}No financial transactions will occur.{Environment.NewLine}Ensure price is set or resource pricing file contains entries before this transaction or start of simulation.";
                        Warnings.CheckAndWrite(warn, Summary, this, MessageType.Warning);
                    }

                    bankAccount.Add(units * price.PricePerPacket, this, (resourceToSell as CLEMModel).NameWithParent, TransactionCategory);
                    if (bankAccount.EquivalentMarketStore != null)
                    {
                        purchaseRequest.Required          = units * price.PricePerPacket;
                        purchaseRequest.Category          = TransactionCategory;
                        purchaseRequest.RelatesToResource = (resourceToSell as CLEMModel).NameWithParent;
                        (bankAccount.EquivalentMarketStore as FinanceType).Remove(purchaseRequest);
                    }
                }

                SetStatusSuccess();
            }
        }
Пример #11
0
        /// <summary>
        /// Returns the link to the matching resource in the market place if found or creates a new clone copy for future transactions
        /// This allows this action to be performed once to store the link rather than at every transaction
        /// This functionality allows resources not in the market at the start of the simulation to be traded.
        /// </summary>
        /// <param name="resourceType">The resource type to trade</param>
        /// <returns>Whether the search was successful</returns>
        public IResourceWithTransactionType LinkToMarketResourceType(CLEMResourceTypeBase resourceType)
        {
            if (!(this.Parent is Market))
            {
                throw new ApsimXException(this, $"Logic error in code. Trying to link a resource type [r={resourceType.Name}] from the market with the same market./nThis is a coding issue. Please contact the developers");
            }

            // find parent group type
            ResourceBaseWithTransactions parent = (resourceType as Model).Parent as ResourceBaseWithTransactions;

            if (!(FindResource(parent.GetType()) is ResourceBaseWithTransactions resourceGroupInMarket))
            {
                // add warning the market is not currently trading in this resource
                string zoneName = FindAncestor <Zone>().Name;
                string warn     = $"[{zoneName}] is currently not accepting resources of type [r={parent.GetType().Name}]\r\nOnly resources groups provided in the [r=ResourceHolder] in the simulation tree will be traded.";
                Warnings.CheckAndWrite(warn, Summary, this);
                return(null);
            }

            // TODO: do some group checks. land units, currency

            // TODO: if market and looking for finance only return or create "Bank"

            // find resource type in group
            object resType = resourceGroupInMarket.FindChild <IResourceWithTransactionType>(resourceType.Name);

            // clone resource: too many problems with linked events to clone these objects and setup again
            // it will be the responsibility of the user to ensure the resources and details are in the market
            if (resType is null)
            {
                // add warning the market does not have the resource
                string warn = $"The resource [r={resourceType.Parent.Name}.{resourceType.Name}] does not exist in [m={this.Parent.Name}].\r\nAdd resource and associated components to the market to permit trading.";
                Warnings.CheckAndWrite(warn, Summary, this);
                return(null);
            }

            // TODO: create a clone of the resource and put it in the market

            return(resType as IResourceWithTransactionType);
        }
Пример #12
0
        /// <inheritdoc/>
        public override List <ResourceRequest> GetResourcesNeededForActivity()
        {
            if (people is null | food is null)
            {
                return(null);
            }

            List <LabourType> peopleList = people.Items.Where(a => IncludeHiredLabour || a.Hired == false).ToList();

            peopleList.Select(a => { a.FeedToTargetIntake = 0; return(a); }).ToList();

            // determine AEs to be fed
            double aE = peopleList.Sum(a => a.TotalAdultEquivalents);

            if (aE <= 0)
            {
                return(null);
            }

            int daysInMonth = DateTime.DaysInMonth(clock.Today.Year, clock.Today.Month);

            // determine feed limits (max kg per AE per day * AEs * days)
            double intakeLimit = DailyIntakeLimit * aE * daysInMonth;

            // remove previous consumption
            double otherIntake = this.DailyIntakeOtherSources * aE * daysInMonth;

            otherIntake += peopleList.Sum(a => a.GetAmountConsumed());

            List <LabourActivityFeedTarget> labourActivityFeedTargets = this.FindAllChildren <LabourActivityFeedTarget>().ToList();

            // determine targets
            foreach (LabourActivityFeedTarget target in labourActivityFeedTargets)
            {
                // calculate target
                target.Target = target.TargetValue * aE * daysInMonth;

                // calculate target maximum
                target.TargetMaximum = target.TargetMaximumValue * aE * daysInMonth;

                // set initial level based on off store inputs
                target.CurrentAchieved = target.OtherSourcesValue * aE * daysInMonth;

                // calculate current level from previous intake this month (LabourActivityFeed)
                target.CurrentAchieved += people.GetDietaryValue(target.Metric, IncludeHiredLabour, false); // * aE; // * daysInMonth;

                // add sources outside of this activity to peoples' diets
                if (target.OtherSourcesValue > 0)
                {
                    foreach (var person in peopleList)
                    {
                        LabourDietComponent outsideEat = new LabourDietComponent();
                        // TODO: might need to add consumed here
                        outsideEat.AmountConsumed = this.DailyIntakeOtherSources * person.TotalAdultEquivalents * daysInMonth;
                        outsideEat.AddOtherSource(target.Metric, target.OtherSourcesValue * person.TotalAdultEquivalents * daysInMonth);
                        // track this consumption by people here.
                        person.AddIntake(outsideEat);
                        person.FeedToTargetIntake += outsideEat.AmountConsumed;
                    }
                }
            }

            // get max months before spoiling of all food stored (will be zero for non perishable food)
            int maxFoodAge = food.FindAllChildren <HumanFoodStoreType>().Max(a => a.Pools.Select(b => a.UseByAge - b.Age).DefaultIfEmpty(0).Max());

            // create list of all food parcels
            List <HumanFoodParcel> foodParcels = new List <HumanFoodParcel>();

            foreach (HumanFoodStoreType foodStore in food.FindAllChildren <HumanFoodStoreType>().ToList())
            {
                foreach (HumanFoodStorePool pool in foodStore.Pools.Where(a => a.Amount > 0))
                {
                    foodParcels.Add(new HumanFoodParcel()
                    {
                        FoodStore = foodStore,
                        Pool      = pool,
                        Expires   = ((foodStore.UseByAge == 0) ? maxFoodAge + 1: foodStore.UseByAge - pool.Age)
                    });
                }
            }

            foodParcels = foodParcels.OrderBy(a => a.Expires).ToList();

            // if a market exists add the available market produce to the list below that ordered above.
            // order market food by price ascending
            // this will include market available food in the decisions.
            // will need to purchase this food before taking it if cost associated.
            // We can check if the parent of the human food store used is a market and charge accordingly.

            // for each market
            List <HumanFoodParcel> marketFoodParcels = new List <HumanFoodParcel>();
            ResourcesHolder        resources         = Market?.Resources;

            if (resources != null)
            {
                HumanFoodStore food = resources.FindResourceGroup <HumanFoodStore>();
                if (food != null)
                {
                    foreach (HumanFoodStoreType foodStore in food.FindAllChildren <HumanFoodStoreType>())
                    {
                        foreach (HumanFoodStorePool pool in foodStore.Pools.Where(a => a.Amount > 0))
                        {
                            marketFoodParcels.Add(new HumanFoodParcel()
                            {
                                FoodStore = foodStore,
                                Pool      = pool,
                                Expires   = ((foodStore.UseByAge == 0) ? maxFoodAge + 1 : foodStore.UseByAge - pool.Age)
                            });
                        }
                    }
                }
            }
            foodParcels.AddRange(marketFoodParcels.OrderBy(a => a.FoodStore.Price(PurchaseOrSalePricingStyleType.Purchase).PricePerPacket));

            double fundsAvailable = double.PositiveInfinity;

            if (bankAccount != null)
            {
                fundsAvailable = bankAccount.FundsAvailable;
            }

            int    parcelIndex  = 0;
            double metricneeded = 0;
            double intake       = otherIntake;

            // start eating food from list from that about to expire first

            // food from household can be eaten up to target maximum
            // food from market can only be eaten up to target

            while (parcelIndex < foodParcels.Count)
            {
                foodParcels[parcelIndex].Proportion = 0;
                var isHousehold = foodParcels[parcelIndex].FoodStore.CLEMParentName == this.CLEMParentName;
                if (intake < intakeLimit & (labourActivityFeedTargets.Where(a => ((isHousehold)? !a.TargetMaximumAchieved: !a.TargetAchieved)).Count() > 0 | foodParcels[parcelIndex].Expires == 0))
                {
                    // still able to eat and target not met or food about to expire this timestep
                    // reduce by amout that can be eaten
                    double propCanBeEaten = Math.Min(1, (intakeLimit - intake) / (foodParcels[parcelIndex].FoodStore.EdibleProportion * foodParcels[parcelIndex].Pool.Amount));
                    // reduce to target limits
                    double propToTarget = 1;
                    if (foodParcels[parcelIndex].Expires != 0)
                    {
                        // if the food is not going to spoil
                        // then adjust what can be eaten up to target otherwise allow over target consumption to avoid waste

                        LabourActivityFeedTarget targetUnfilled = labourActivityFeedTargets.Where(a => ((isHousehold) ? !a.TargetMaximumAchieved : !a.TargetAchieved)).FirstOrDefault();
                        if (targetUnfilled != null)
                        {
                            // calculate reduction to metric target
                            metricneeded = Math.Max(0, (isHousehold ? targetUnfilled.TargetMaximum : targetUnfilled.Target) - targetUnfilled.CurrentAchieved);
                            double amountneeded = metricneeded / foodParcels[parcelIndex].FoodStore.ConversionFactor(targetUnfilled.Metric);

                            propToTarget = Math.Min(1, amountneeded / (foodParcels[parcelIndex].FoodStore.EdibleProportion * foodParcels[parcelIndex].Pool.Amount));
                        }
                    }

                    foodParcels[parcelIndex].Proportion = Math.Min(propCanBeEaten, propToTarget);

                    // work out if there will be a cost limitation, only if a price structure exists for the resource
                    // no charge for household consumption
                    double propToPrice = 1;
                    if (!isHousehold && foodParcels[parcelIndex].FoodStore.PricingExists(PurchaseOrSalePricingStyleType.Purchase))
                    {
                        ResourcePricing price = foodParcels[parcelIndex].FoodStore.Price(PurchaseOrSalePricingStyleType.Purchase);
                        double          cost  = (foodParcels[parcelIndex].Pool.Amount * foodParcels[parcelIndex].Proportion) / price.PacketSize * price.PricePerPacket;

                        // TODO: sell cattle based on selling groups till run out of cattle or meet shortfall
                        // adjust fundsAvailable with new money
                        // if cost > 0 and cost > funds available

                        if (cost > 0)
                        {
                            propToPrice = Math.Min(1, fundsAvailable / cost);
                            // remove cost from running check tally
                            fundsAvailable = Math.Max(0, fundsAvailable - (cost * propToPrice));

                            // real finance transactions will happen in the do activity as stuff is allocated
                            // there should not be shortfall as all the checks and reductions have happened here
                        }
                    }
                    foodParcels[parcelIndex].Proportion *= propToPrice;

                    // update intake
                    double newIntake = (foodParcels[parcelIndex].FoodStore.EdibleProportion * foodParcels[parcelIndex].Pool.Amount * foodParcels[parcelIndex].Proportion);
                    intake += newIntake;
                    // update metrics
                    foreach (LabourActivityFeedTarget target in labourActivityFeedTargets)
                    {
                        target.CurrentAchieved += newIntake * foodParcels[parcelIndex].FoodStore.ConversionFactor(target.Metric);
                    }
                }
                else if (intake >= intakeLimit && labourActivityFeedTargets.Where(a => ((isHousehold) ? !a.TargetMaximumAchieved : !a.TargetAchieved)).Count() > 1)
                {
                    // full but could still reach target with some substitution
                    // but can substitute to remove a previous target

                    // does the current parcel have better target values than any previous non age 0 pool of a different food type
                }
                else
                {
                    break;
                }
                parcelIndex++;
            }

            // fill resource requests
            List <ResourceRequest> requests = new List <ResourceRequest>();

            foreach (var item in foodParcels.GroupBy(a => a.FoodStore))
            {
                double amount = item.Sum(a => a.Pool.Amount * a.Proportion);
                if (amount > 0)
                {
                    double financeLimit = 1;
                    // if obtained from the market make financial transaction before taking
                    ResourcePricing price          = item.Key.Price(PurchaseOrSalePricingStyleType.Sale);
                    bool            marketIsSource = item.Key.Parent.Parent.Parent == Market;
                    if (bankAccount != null && marketIsSource && price.PricePerPacket > 0)
                    {
                        // finance transaction to buy food from market
                        ResourceRequest marketRequest = new ResourceRequest
                        {
                            ActivityModel               = this,
                            Required                    = amount / price.PacketSize * price.PricePerPacket,
                            AllowTransmutation          = false,
                            Category                    = $"{TransactionCategory}.PurchaseFood",
                            MarketTransactionMultiplier = 1,
                            RelatesToResource           = item.Key.NameWithParent
                        };
                        bankAccount.Remove(marketRequest);
                    }

                    // is this a market

                    requests.Add(new ResourceRequest()
                    {
                        Resource           = item.Key,
                        ResourceType       = typeof(HumanFoodStore),
                        AllowTransmutation = false,
                        Required           = amount * financeLimit,
                        ResourceTypeName   = item.Key.NameWithParent,
                        ActivityModel      = this,
                        Category           = $"{TransactionCategory}{(marketIsSource?".FromMarket":".FromHousehold")}"
                    });
                }
            }

            // if still hungry and funds available, try buy food in excess of what stores (private or market) offered using transmutation if present.
            // This will force the market or private sources to purchase more food to meet demand if transmutation available.
            // if no market is present it will look to transmutating from its own stores if possible.
            // this means that other than a purchase from market (above) this activity doesn't need to worry about financial tranactions.
            int testType = 0;

            // test is limited to 1 for now so only to metric target NOT intake limit as we use maximum and target values now
            while (testType < 1 && intake < intakeLimit && (labourActivityFeedTargets.Where(a => !a.TargetAchieved).Any()) && fundsAvailable > 0)
            {
                // don't worry about money anymore. The over request will be handled by the transmutation.
                // move through specified purchase list
                // 1. to assign based on energy
                // 2. if still need food assign based on intake still needed

                metricneeded = 0;
                LabourActivityFeedTarget targetUnfilled = labourActivityFeedTargets.Where(a => !a.TargetAchieved).FirstOrDefault();
                if (targetUnfilled != null)
                {
                    metricneeded = Math.Max(0, (targetUnfilled.Target - targetUnfilled.CurrentAchieved));
                    double amountToFull = intakeLimit - intake;

                    foreach (LabourActivityFeedTargetPurchase purchase in this.FindAllChildren <LabourActivityFeedTargetPurchase>())
                    {
                        HumanFoodStoreType foodtype = purchase.FoodStore;
                        if (purchase.ProportionToPurchase > 0 && foodtype != null && (foodtype.TransmutationDefined & intake < intakeLimit))
                        {
                            double amountEaten = 0;
                            if (testType == 0)
                            {
                                // metric target based on purchase proportion
                                amountEaten = metricneeded / foodtype.ConversionFactor(targetUnfilled.Metric) * purchase.ProportionToPurchase;
                            }
                            else
                            {
                                // amount to satisfy limited by proportion of purchases
                                amountEaten = amountToFull * purchase.ProportionToPurchase;
                            }

                            if (intake + amountEaten > intakeLimit)
                            {
                                amountEaten = intakeLimit - intake;
                            }

                            if (amountEaten > 0)
                            {
                                targetUnfilled.CurrentAchieved += amountEaten * foodtype.ConversionFactor(targetUnfilled.Metric);
                                double amountPurchased = amountEaten / foodtype.EdibleProportion;

                                // update intake.. needed is the amount edible, not the amount purchased.
                                intake += amountEaten;

                                // add financial transactions to purchase from market
                                // if obtained from the market make financial transaction before taking
                                ResourcePricing price = foodtype.Price(PurchaseOrSalePricingStyleType.Sale);
                                if (bankAccount != null)
                                {
                                    if (price.PricePerPacket > 0)
                                    {
                                        ResourceRequest marketRequest = new ResourceRequest
                                        {
                                            ActivityModel               = this,
                                            Required                    = amountPurchased / price.PacketSize * price.PricePerPacket,
                                            AllowTransmutation          = false,
                                            Category                    = "Import",
                                            MarketTransactionMultiplier = 1,
                                            RelatesToResource           = foodtype.NameWithParent
                                        };
                                        bankAccount.Remove(marketRequest);
                                    }
                                    else
                                    {
                                        string warn = $"No price set [{price.PricePerPacket}] for [r={foodtype.Name}] at time of transaction for [a={this.Name}]{Environment.NewLine}No financial transactions will occur.{Environment.NewLine}Ensure price is set or resource pricing file contains entries before this transaction or start of simulation.";
                                        Warnings.CheckAndWrite(warn, Summary, this, MessageType.Warning);
                                    }
                                }

                                // find in requests or create a new one
                                ResourceRequest foodRequestFound = requests.Find(a => a.Resource == foodtype);
                                if (foodRequestFound is null)
                                {
                                    requests.Add(new ResourceRequest()
                                    {
                                        Resource           = foodtype,
                                        ResourceType       = typeof(HumanFoodStore),
                                        AllowTransmutation = true,
                                        Required           = amountPurchased,
                                        ResourceTypeName   = purchase.FoodStoreName,
                                        ActivityModel      = this,
                                        Category           = $"{TransactionCategory}.FromImports"
                                    });
                                }
                                else
                                {
                                    foodRequestFound.Required          += amountPurchased;
                                    foodRequestFound.AllowTransmutation = true;
                                }
                            }
                        }
                    }
                    testType++;
                }
            }
            return(requests);
        }
Пример #13
0
        private void OnCLEMInitialiseActivity(object sender, EventArgs e)
        {
            people      = Resources.FindResourceGroup <Labour>();
            food        = Resources.FindResourceGroup <HumanFoodStore>();
            bankAccount = Resources.FindResourceType <Finance, FinanceType>(this, AccountName, OnMissingResourceActionTypes.Ignore, OnMissingResourceActionTypes.Ignore);

            Market = food.FindAncestor <ResourcesHolder>().FoundMarket;

            resourcesHolder = base.Resources;
            // if market is present point to market to find the resource
            if (Market != null)
            {
                resourcesHolder = Market.FindChild <ResourcesHolder>();
            }

            // set the food store linked in any TargetPurchase if target proportion set > 0
            // check that all purchase resources have transmutation or recalulate the proportion
            var targetPurchases = this.FindAllChildren <LabourActivityFeedTargetPurchase>().Where(a => a.TargetProportion > 0).ToList();

            if (targetPurchases.Any())
            {
                double checkPropAvailable = 0;
                double totPropAvailable   = 0;
                bool   adjusted           = false;
                foreach (var item in targetPurchases)
                {
                    checkPropAvailable += item.TargetProportion;
                    item.FoodStore      = resourcesHolder.FindResourceType <HumanFoodStore, HumanFoodStoreType>(this, item.FoodStoreName, OnMissingResourceActionTypes.Ignore, OnMissingResourceActionTypes.Ignore);
                    if (item.FoodStore.TransmutationDefined)
                    {
                        totPropAvailable         += item.TargetProportion;
                        item.ProportionToPurchase = item.TargetProportion;
                    }
                    else
                    {
                        string warn = $"The HumanFoodStoreType [r={item.FoodStore.FullPath}] does not have a Transmutation required to be a LabourActivityFeedTargetPurchase [a={item.FullPath}] of [a={this.FullPath}]{Environment.NewLine}This HumanFoodStore will not be allocated and the remaining purchase proportions have been adjusted";
                        Warnings.CheckAndWrite(warn, Summary, this, MessageType.Warning);
                        adjusted = true;
                        item.ProportionToPurchase = 0;
                    }
                }

                if (Math.Abs(1 - checkPropAvailable) < 0.0001)
                {
                    if (!adjusted)
                    {
                        string warn = $"The TargetProportions provided for [a=LabourActivityFeedTargetPurchase] provided for [a={this.FullPath}] do not sum to 1.{Environment.NewLine}These purchase proportions have been adjusted";
                        Warnings.CheckAndWrite(warn, Summary, this, MessageType.Warning);
                    }
                    adjusted = true;
                }

                // recalculate proportions to buy based on transmuation of resource allowed
                if (adjusted)
                {
                    foreach (var item in targetPurchases.Where(a => a.FoodStore.TransmutationDefined))
                    {
                        item.ProportionToPurchase = item.TargetProportion / totPropAvailable;
                    }
                }
            }
        }
        /// <inheritdoc/>
        public override List <ResourceRequest> GetResourcesNeededForActivity()
        {
            List <ResourceRequest> requests = new List <ResourceRequest>();

            earned = 0;
            spent  = 0;

            // get data
            currentEntries = fileResource.GetCurrentResourceData(clock.Today.Month, clock.Today.Year);
            resourceList   = new List <IResourceType>();
            if (currentEntries.Count > 0)
            {
                IResourceType resource = null;

                foreach (DataRowView item in currentEntries)
                {
                    // find resource
                    string resName = item[fileResource.ResourceNameColumnName].ToString();

                    if (resName.Contains("."))
                    {
                        resource = Resources.FindResourceType <ResourceBaseWithTransactions, IResourceType>(this, resName, OnMissingResourceActionTypes.ReportErrorAndStop, OnMissingResourceActionTypes.ReportErrorAndStop);
                    }
                    else
                    {
                        var found = Resources.FindAllDescendants <IResourceType>(resName);
                        if (found.Count() == 1)
                        {
                            resource = found.FirstOrDefault();

                            // highlight unsupported resource types
                            // TODO: add ability to include labour (days) and ruminants (number added/removed)
                            switch (resource.GetType().ToString())
                            {
                            case "Models.CLEM.Resources.LandType":
                            case "Models.CLEM.Resources.RuminantType":
                            case "Models.CLEM.Resources.LabourType":
                            case "Models.CLEM.Resources.GrazeFoodStoreType":
                            case "Models.CLEM.Resources.OtherAnimalsType":
                                string warn = $"[a={this.Name}] does not support [r={resource.GetType()}]\r\nThis resource will be ignored. Contact developers for more information";
                                Warnings.CheckAndWrite(warn, Summary, this);
                                resource = null;
                                break;

                            default:
                                break;
                            }

                            // if finances
                            if (resource != null && bankAccount != null)
                            {
                                double amount = Convert.ToDouble(item[fileResource.AmountColumnName], CultureInfo.InvariantCulture);

                                // get price of resource
                                ResourcePricing price = resource.Price((amount > 0 ? PurchaseOrSalePricingStyleType.Purchase : PurchaseOrSalePricingStyleType.Sale));

                                double amountAvailable = (amount < 0) ? Math.Min(Math.Abs(amount), resource.Amount) : amount;

                                double packets = amountAvailable / price.PacketSize;
                                if (price.UseWholePackets)
                                {
                                    packets = Math.Truncate(packets);
                                }

                                if (amount < 0)
                                {
                                    earned += packets * price.PricePerPacket;
                                }
                                else
                                {
                                    spent += packets * price.PricePerPacket;
                                }
                            }
                        }
                        else
                        {
                            string warn = "";
                            if (found.Count() == 0)
                            {
                                warn = $"[a={this.Name}] could not find a resource [r={resName}] provided by [x={fileResource.Name}] in the local [r=ResourcesHolder]\r\nExternal transactions with this resource will be ignored\r\nYou can either add this resource to your simulation or remove it from the input file to avoid this warning";
                            }
                            else
                            {
                                warn = $"[a={this.Name}] could not distinguish between multiple occurences of resource [r={resName}] provided by [x={fileResource.Name}] in the local [r=ResourcesHolder]\r\nEnsure all resource names are unique across stores, or use ResourceStore.ResourceType notation to specify resources in the input file";
                            }

                            Warnings.CheckAndWrite(warn, Summary, this);
                        }
                    }
                    if (resource != null)
                    {
                        resourceList.Add(resource);
                    }
                }
            }
            return(requests);
        }
Пример #15
0
        /// <summary>
        /// Create the individual ruminant animals using the Cohort parameterisations.
        /// </summary>
        /// <param name="number">The number of individuals to create</param>
        /// <param name="initialAttributes">The initial attributes found from parent and this cohort</param>
        /// <param name="ruminantType">The breed parameters if overwritten</param>
        /// <returns>List of ruminants</returns>
        public List <Ruminant> CreateIndividuals(int number, List <ISetAttribute> initialAttributes, RuminantType ruminantType = null)
        {
            List <Ruminant> individuals = new List <Ruminant>();

            if (number > 0)
            {
                RuminantType parent = ruminantType;
                if (parent is null)
                {
                    parent = FindAncestor <RuminantType>();
                }

                // get Ruminant Herd resource for unique ids
                RuminantHerd ruminantHerd = parent.Parent as RuminantHerd; // Resources.FindResourceGroup<RuminantHerd>();

                for (int i = 1; i <= number; i++)
                {
                    double weight = 0;
                    if (Weight > 0)
                    {
                        // avoid accidental small weight if SD provided but weight is 0
                        // if weight is 0 then the normalised weight will be applied in Ruminant constructor.
                        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);
                        weight = Weight + WeightSD * randStdNormal;
                    }

                    Ruminant ruminant = Ruminant.Create(Sex, parent, Age, weight);

                    ruminant.ID       = ruminantHerd.NextUniqueID;
                    ruminant.Breed    = parent.Breed;
                    ruminant.HerdName = parent.Name;
                    ruminant.SaleFlag = HerdChangeReason.None;

                    if (Suckling)
                    {
                        if (Age >= ((parent.NaturalWeaningAge == 0)?parent.GestationLength: parent.NaturalWeaningAge))
                        {
                            string limitstring = (parent.NaturalWeaningAge == 0) ? $"gestation length [{parent.GestationLength}]" : $"natural weaning age [{parent.NaturalWeaningAge}]";
                            string warn        = $"Individuals older than {limitstring} cannot be assigned as suckling [r={parent.Name}][r={this.Parent.Name}][r={this.Name}]{Environment.NewLine}These individuals have not been assigned suckling.";
                            Warnings.CheckAndWrite(warn, Summary, this, MessageType.Warning);
                        }
                        else
                        {
                            ruminant.SetUnweaned();
                        }
                    }

                    if (Sire)
                    {
                        if (this.Sex == Sex.Male)
                        {
                            RuminantMale ruminantMale = ruminant as RuminantMale;
                            ruminantMale.Attributes.Add("Sire");
                        }
                        else
                        {
                            string warn = $"Breeding sire switch is not valid for individual females [r={parent.Name}][r={this.Parent.Name}][r={this.Name}]{Environment.NewLine}These individuals have not been assigned sires. Change Sex to Male to create sires in initial herd.";
                            Warnings.CheckAndWrite(warn, Summary, this, MessageType.Warning);
                        }
                    }

                    // if weight not provided use normalised weight
                    ruminant.PreviousWeight = ruminant.Weight;

                    if (this.Sex == Sex.Female)
                    {
                        RuminantFemale ruminantFemale = ruminant as RuminantFemale;
                        ruminantFemale.WeightAtConception = ruminant.Weight;
                        ruminantFemale.NumberOfBirths     = 0;

                        if (setPreviousConception != null)
                        {
                            setPreviousConception.SetConceptionDetails(ruminantFemale);
                        }
                    }

                    // initialise attributes
                    foreach (ISetAttribute item in initialAttributes)
                    {
                        ruminant.Attributes.Add(item.AttributeName, item.GetAttribute(true));
                    }

                    individuals.Add(ruminant);
                }

                // add any mandatory attributes to the list on the ruminant type
                foreach (var mattrib in initialAttributes.Where(a => a.Mandatory))
                {
                    parent.AddMandatoryAttribute(mattrib.AttributeName);
                }
            }

            return(individuals);
        }
Пример #16
0
        /// <summary>
        /// Find a resource type from  type of resources and name of resource type component
        /// </summary>
        /// <typeparam name="T">Type of ResourceType to return</typeparam>
        /// <typeparam name="R">Type of Resource group containing resource type</typeparam>
        /// <param name="requestingModel">The model requesting this resource</param>
        /// <param name="resourceName">The name identifier NameOfResource.NameOfResourceType or simply NameOfResourceType</param>
        /// <param name="missingResourceAction">Action if resource group missing</param>
        /// <param name="missingResourceTypeAction">Action if resource type is missing</param>
        /// <returns>A resource type component</returns>
        public T FindResourceType <R, T>(IModel requestingModel, string resourceName, OnMissingResourceActionTypes missingResourceAction = OnMissingResourceActionTypes.Ignore, OnMissingResourceActionTypes missingResourceTypeAction = OnMissingResourceActionTypes.Ignore) where T : IResourceType where R : ResourceBaseWithTransactions
        {
            string[] nameParts = new string[] { "", resourceName };
            if (nameParts.Last().Contains('.'))
            {
                nameParts = nameParts.Last().Split('.');
                if (nameParts.Length > 2)
                {
                    throw new ApsimXException(requestingModel, $"Invalid resource name identifier for [{requestingModel.Name}], expecting 'ResourceName.ResourceTypeName' or 'ResourceTypeName'. Value provided [{resourceName}]");
                }
            }

            // not sure it's quickets to find the resource then look at it's children
            // or look through all descendents for the type and name
            // if we find children then we use R as a double check

            bool searchForAllIresourceType = false;
            bool resGroupNameMatch         = true;
            T    resType = default(T);
            ResourceBaseWithTransactions resGroup = null;

            if (!typeof(R).IsSubclassOf(typeof(ResourceBaseWithTransactions)))
            {
                if (nameParts.First() == "")
                {
                    searchForAllIresourceType = true;
                }
                else
                {
                    // find resource by name
                    resGroup = FindChild <R>(nameParts.First());
                }
            }
            else
            {
                resGroup = (nameParts.First() != "") ? FindResource <R>(nameParts.First()) : FindResource <R>();
                if (resGroup == null && nameParts.First() != "")
                {
                    // no resource name match so try with just the type
                    resGroupNameMatch = false;
                    resGroup          = FindResource <R>();
                }
            }

            if (searchForAllIresourceType)
            {
                resType = FindAllDescendants <T>(nameParts.Last()).FirstOrDefault();
            }
            else
            {
                if (resGroup != null)
                {
                    resType = (resGroup as IModel).FindChild <T>(nameParts.Last());
                }
            }

            string errorMsg;

            if (resGroup == null)
            {
                errorMsg = $"Unable to locate resource group [r={typeof(R).Name}] for [a={requestingModel.Name}]";

                switch (missingResourceAction)
                {
                case OnMissingResourceActionTypes.ReportErrorAndStop:
                    throw new ApsimXException(this, errorMsg);

                case OnMissingResourceActionTypes.ReportWarning:
                    Warnings.CheckAndWrite(errorMsg, Summary, this);
                    break;

                default:
                    break;
                }
                return(default(T));
            }
            else
            {
                if (!resGroupNameMatch)
                {
                    errorMsg = $"Unable to locate resource named [r={nameParts.First()}] for [a={requestingModel.Name}] but a [{typeof(R).Name}] resource was found and will be used.";
                    Warnings.CheckAndWrite(errorMsg, Summary, this);
                }
            }

            if (resType as IModel is null)
            {
                errorMsg = $"Unable to locate resource type [r={nameParts.Last()}] in [r={resGroup.Name}] for [a={requestingModel.Name}]";
                switch (missingResourceTypeAction)
                {
                case OnMissingResourceActionTypes.ReportErrorAndStop:
                    throw new ApsimXException(this, errorMsg);

                case OnMissingResourceActionTypes.ReportWarning:
                    Warnings.CheckAndWrite(errorMsg, Summary, this);
                    break;

                default:
                    break;
                }
            }
            return(resType);
        }
        private void OnCLEMAnimalStock(object sender, EventArgs e)
        {
            AeToDestock = 0;
            AeDestocked = 0;
            AeToRestock = 0;
            AeRestocked = 0;

            // this event happens after management has marked individuals for purchase or sale.
            if (this.TimingOK)
            {
                // Get ENSO forcase for current time
                ENSOState forecastEnsoState = GetENSOMeasure();

                this.Status = ActivityStatus.NotNeeded;

                // calculate dry season pasture available for each managed paddock holding stock
                foreach (var newgroup in HerdResource.Herd.Where(a => a.Location != "").GroupBy(a => a.Location))
                {
                    double aELocationNeeded = 0;

                    // total adult equivalents of all breeds on pasture for utilisation
                    double totalAE = newgroup.Sum(a => a.AdultEquivalent);
                    // determine AE marked for sale and purchase of managed herd
                    double markedForSaleAE = newgroup.Where(a => a.ReadyForSale).Sum(a => a.AdultEquivalent);
                    double purchaseAE      = HerdResource.PurchaseIndividuals.Where(a => a.Location == newgroup.Key).Sum(a => a.AdultEquivalent);

                    double herdChange        = 1.0;
                    bool   relationshipFound = false;
                    switch (forecastEnsoState)
                    {
                    case ENSOState.Neutral:
                        break;

                    case ENSOState.ElNino:
                        if (!(pastureToStockingChangeElNino is null))
                        {
                            GrazeFoodStoreType pasture = Resources.FindResourceType <GrazeFoodStore, GrazeFoodStoreType>(this, newgroup.Key, OnMissingResourceActionTypes.Ignore, OnMissingResourceActionTypes.Ignore);
                            double             kgha    = pasture.TonnesPerHectare * 1000;
                            herdChange        = pastureToStockingChangeElNino.SolveY(kgha);
                            relationshipFound = true;
                        }
                        break;

                    case ENSOState.LaNina:
                        if (!(pastureToStockingChangeLaNina is null))
                        {
                            GrazeFoodStoreType pasture = Resources.FindResourceType <GrazeFoodStore, GrazeFoodStoreType>(this, newgroup.Key, OnMissingResourceActionTypes.Ignore, OnMissingResourceActionTypes.Ignore);
                            double             kgha    = pasture.TonnesPerHectare * 1000;
                            herdChange        = pastureToStockingChangeLaNina.SolveY(kgha);
                            relationshipFound = true;
                        }
                        break;

                    default:
                        break;
                    }
                    if (!relationshipFound)
                    {
                        string warn = $"No pasture biomass to herd change proportion [Relationship] provided for {((forecastEnsoState== ENSOState.ElNino)? "El Niño":"La Niña")} phase in [a={this.Name}]\r\nNo stock management will be performed in this phase.";
                        this.Status = ActivityStatus.Warning;
                        Warnings.CheckAndWrite(warn, Summary, this);
                    }

                    if (herdChange > 1.0)
                    {
                        aELocationNeeded = Math.Max(0, (totalAE * herdChange) - purchaseAE);
                        AeToRestock     += aELocationNeeded;
                        double notHandled = HandleRestocking(aELocationNeeded, newgroup.Key, newgroup.FirstOrDefault());
                        AeRestocked += (aELocationNeeded - notHandled);
                    }
                    else if (herdChange < 1.0)
                    {
                        aELocationNeeded = Math.Max(0, (totalAE * (1 - herdChange)) - markedForSaleAE);
                        AeToDestock     += aELocationNeeded;
                        double notHandled = HandleDestocking(AeToDestock, newgroup.Key);
                        AeDestocked += (aELocationNeeded - notHandled);
                    }
                }

                if (this.Status != ActivityStatus.Warning & AeToDestock + AeToRestock > 0)
                {
                    if (Math.Max(0, AeToRestock - AeRestocked) + Math.Max(0, AeToDestock - AeDestocked) == 0)
                    {
                        this.Status = ActivityStatus.Success;
                    }
                    else
                    {
                        this.Status = ActivityStatus.Partial;
                    }
                }
            }
        }
Пример #18
0
        /// <summary>
        /// Queries the the Pasture SQLite database using the specified parameters.
        /// </summary>
        /// <param name="region"></param>
        /// <param name="soil"></param>
        /// <param name="grassBasalArea"></param>
        /// <param name="landCondition"></param>
        /// <param name="stockingRate"></param>
        /// <param name="ecolCalculationDate"></param>
        /// <param name="ecolCalculationInterval"></param>
        /// <returns></returns>
        public List <PastureDataType> GetIntervalsPastureData(int region, string soil, double grassBasalArea, double landCondition, double stockingRate,
                                                              DateTime ecolCalculationDate, int ecolCalculationInterval)
        {
            List <PastureDataType> pastureDetails = new List <PastureDataType>();

            if (validationResults.Count > 0 | ecolCalculationDate > clock.EndDate)
            {
                return(pastureDetails);
            }

            int      startYear  = ecolCalculationDate.Year;
            int      startMonth = ecolCalculationDate.Month;
            DateTime endDate    = ecolCalculationDate.AddMonths(ecolCalculationInterval + 1);

            if (endDate > clock.EndDate)
            {
                endDate = clock.EndDate;
            }
            int endYear  = endDate.Year;
            int endMonth = endDate.Month;

            double stkRateCategory        = FindClosestCategory("StockingRate", stockingRate);
            double grassBasalAreaCategory = FindClosestCategory("GBA", grassBasalArea);
            double landConditionCategory  = FindClosestCategory("LandCondition", landCondition);

            string sqlQuery = "SELECT " +
                              YearColumnName + ", " +
                              MonthColumnName + "," +
                              GrowthColumnName;

            if (ErosionColumnName != null && ErosionColumnName != "")
            {
                sqlQuery += "," + ErosionColumnName;
            }
            if (RunoffColumnName != null || RunoffColumnName != "")
            {
                sqlQuery += "," + RunoffColumnName;
            }
            if (RainfallColumnName != null || RainfallColumnName != "")
            {
                sqlQuery += "," + RainfallColumnName;
            }
            if (CoverColumnName != null || CoverColumnName != "")
            {
                sqlQuery += "," + CoverColumnName;
            }
            if (TBAColumnName != null || TBAColumnName != "")
            {
                sqlQuery += "," + TBAColumnName;
            }

            sqlQuery += " FROM " + TableName +
                        " WHERE " + RegionColumnName + " = " + region +
                        " AND " + LandIdColumnName + " = " + soil +
                        " AND " + GrassBAColumnName + " = " + grassBasalAreaCategory +
                        " AND " + LandConColumnName + " = " + landConditionCategory +
                        " AND " + StkRateColumnName + " = " + stkRateCategory;

            if (shuffler != null)
            {
                int shuffleStartYear = shuffler.ShuffledYears.Where(a => a.Year == startYear).FirstOrDefault().RandomYear;
                int shuffleEndYear   = shuffler.ShuffledYears.Where(a => a.Year == endYear).FirstOrDefault().RandomYear;

                // first year
                sqlQuery += " AND (( " + YearColumnName + " = " + shuffleStartYear + " AND " + MonthColumnName + " >= " + startMonth + ")";

                // any middle years
                for (int i = startYear + 1; i < endYear; i++)
                {
                    sqlQuery += " OR ( " + YearColumnName + " = " + shuffler.ShuffledYears[i] + ")";
                }

                //last year
                sqlQuery += " OR ( " + YearColumnName + " = " + shuffleEndYear + " AND " + MonthColumnName + " <= " + endMonth + "))";
            }
            else
            {
                if (startYear == endYear)
                {
                    sqlQuery += " AND (( " + YearColumnName + " = " + startYear + " AND " + MonthColumnName + " >= " + startMonth + " AND " + MonthColumnName + " < " + endMonth + ")"
                                + ")";
                }
                else
                {
                    sqlQuery += " AND (( " + YearColumnName + " = " + startYear + " AND " + MonthColumnName + " >= " + startMonth + ")"
                                + " OR  ( " + YearColumnName + " > " + startYear + " AND " + YearColumnName + " < " + endYear + ")"
                                + " OR  ( " + YearColumnName + " = " + endYear + " AND " + MonthColumnName + " < " + endMonth + ")"
                                + ")";
                }
            }

            DataTable results = SQLiteReader.ExecuteQuery(sqlQuery);

            if (results.Rows.Count == 0)
            {
                switch (MissingDataAction)
                {
                case OnMissingResourceActionTypes.ReportWarning:
                    // this is no longer an error to allow situations where there is no pasture production reported in a given period
                    string warn = $"No pasture production for was found for [{startMonth}/{startYear}] by [x={this.Name}]";
                    Warnings.CheckAndWrite(warn, Summary, this, MessageType.Warning);
                    break;

                default:
                    break;
                }
                return(null);
            }

            // re-label shuffled years
            if (shuffler != null)
            {
                foreach (DataRow row in results.Rows)
                {
                    row["Year"] = shuffler.ShuffledYears.Where(a => a.RandomYear == Convert.ToInt32(row["Year"], CultureInfo.InvariantCulture)).FirstOrDefault().Year;
                }
            }

            results.DefaultView.Sort = YearColumnName + ", " + MonthColumnName;

            foreach (DataRowView row in results.DefaultView)
            {
                pastureDetails.Add(DataRow2PastureDataType(row));
            }

            CheckAllMonthsWereRetrieved(pastureDetails, ecolCalculationDate, endDate,
                                        region, soil, grassBasalAreaCategory, landConditionCategory, stkRateCategory);

            return(pastureDetails);
        }
Пример #19
0
        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();
        }
Пример #20
0
        private void OnCLEMStartOfTimeStep(object sender, EventArgs e)
        {
            // if harvest tags provided for this crop then they will be used to define previous, next etc
            // while date month > harvest record look for previous and delete past events

            if (HarvestData.Count() > 0)
            {
                int clockYrMth = CalculateYearMonth(clock.Today);
                int position; // passed -1, current 0, future 1
                do
                {
                    int harvestYrMth = CalculateYearMonth(HarvestData.First().HarvestDate);
                    position = (clockYrMth > harvestYrMth) ? -1 : ((clockYrMth == harvestYrMth) ? 0 : 1);

                    // check for valid sequence
                    if (HarvestTagsUsed && HarvestData.FirstOrDefault().HarvestType != "")
                    {
                        if (previousTag == HarvestData.FirstOrDefault().HarvestType)
                        {
                            string warn = $"Invalid sequence of HarvetTags detected in [a={this.Name}]\r\nEnsure tags are ordered first, last in sequence.";
                            Warnings.CheckAndWrite(warn, Summary, this);
                        }
                        previousTag = HarvestData.FirstOrDefault().HarvestType;
                    }


                    switch (position)
                    {
                    case -1:
                        if (HarvestTagsUsed)
                        {
                            switch (HarvestData.FirstOrDefault().HarvestType)
                            {
                            case "first":
                                if (!performedHarvest)
                                {
                                    InsideMultiHarvestSequence  = true;
                                    StartCurrentSequenceHarvest = HarvestData.FirstOrDefault();
                                    EndCurrentSequenceHarvest   = HarvestData.Where(a => a.HarvestType == "last").FirstOrDefault();
                                }
                                break;

                            case "last":
                                // hit tagged last to delete as we've passed this date so out of multi harvest sequence
                                InsideMultiHarvestSequence = false;
                                PreviousHarvest            = HarvestData.FirstOrDefault();
                                break;

                            default:
                                break;
                            }
                        }
                        else
                        {
                            PreviousHarvest = HarvestData.FirstOrDefault();
                        }
                        HarvestData.RemoveAt(0);
                        break;

                    case 0:
                        performedHarvest = true;
                        if (HarvestTagsUsed)
                        {
                            switch (HarvestData.FirstOrDefault().HarvestType)
                            {
                            case "first":
                                // hit tagged first for current time step
                                InsideMultiHarvestSequence  = true;
                                StartCurrentSequenceHarvest = HarvestData.FirstOrDefault();
                                PreviousHarvest             = null;
                                EndCurrentSequenceHarvest   = HarvestData.Where(a => a.HarvestType == "last").FirstOrDefault();
                                break;

                            default:
                                NextHarvest     = HarvestData.FirstOrDefault();
                                PreviousHarvest = null;
                                break;
                            }
                        }
                        else
                        {
                            NextHarvest     = HarvestData.FirstOrDefault();
                            PreviousHarvest = null;
                        }
                        break;

                    case 1:
                        if (HarvestTagsUsed)
                        {
                            switch (HarvestData.FirstOrDefault().HarvestType)
                            {
                            case "first":
                                // hit tagged first for next harvest
                                NextHarvest = HarvestData.FirstOrDefault();
                                break;

                            default:
                                NextHarvest = HarvestData.FirstOrDefault();
                                break;
                            }
                        }
                        else
                        {
                            NextHarvest = HarvestData.FirstOrDefault();
                        }
                        break;

                    default:
                        break;
                    }
                } while (HarvestData.Count > 0 && position == -1);
            }
        }