Пример #1
0
        /// <summary>
        /// return a list of components available given the specified types
        /// </summary>
        /// <param name="typesToFind">the list of types to locate</param>
        /// <returns>A list of names of components</returns>
        public IEnumerable <string> GetResourcesAvailableByName(object[] typesToFind)
        {
            List <string> results = new List <string>();
            Zone          zone    = this.FindAncestor <Zone>();

            if (!(zone is null))
            {
                ResourcesHolder resources = zone.FindChild <ResourcesHolder>();
                if (!(resources is null))
                {
                    foreach (object type in typesToFind)
                    {
                        if (type is string)
                        {
                            results.Add(type as string);
                        }
                        else if (type is Type)
                        {
                            var list = resources.FindResource(type as Type)?.FindAllChildren <IResourceType>().Select(a => (a as CLEMModel).NameWithParent);
                            if (list != null)
                            {
                                results.AddRange(resources.FindResource(type as Type).FindAllChildren <IResourceType>().Select(a => (a as CLEMModel).NameWithParent));
                            }
                        }
                    }
                }
            }
            return(results.AsEnumerable());
        }
Пример #2
0
        /// <summary>
        /// Validate this object
        /// </summary>
        /// <param name="validationContext"></param>
        /// <returns></returns>
        public IEnumerable <ValidationResult> Validate(ValidationContext validationContext)
        {
            var results = new List <ValidationResult>();

            if (TransmuteResourceTypeName != null && TransmuteResourceTypeName != "")
            {
                if (!TransmuteResourceTypeName.Contains("."))
                {
                    string[] memberNames = new string[] { "ResourceTypeName" };
                    results.Add(new ValidationResult("Invalid resource type entry. Please select resource type from the drop down list provided or ensure the value is formatted as ResourceGroup.ResourceType", memberNames));
                }
                else
                {
                    object result = resources.FindResource <ResourceBaseWithTransactions>(TransmuteResourceTypeName.Split('.').First());
                    if (result == null)
                    {
                        Summary.WriteWarning(this, $"Could not find resource group [r={TransmuteResourceTypeName.Split('.').First()}] in transmute [{this.Name}]{Environment.NewLine}The parent transmutation [{(this.Parent as CLEMModel).NameWithParent}] will not suceed without this resource and will not be performed");
                    }
                    else
                    {
                        object resultType = resources.FindResourceType <ResourceBaseWithTransactions, IResourceType>(this, TransmuteResourceTypeName, OnMissingResourceActionTypes.Ignore, OnMissingResourceActionTypes.Ignore);
                        if (resultType is null)
                        {
                            string[] memberNames = new string[] { "ResourceType" };
                            results.Add(new ValidationResult($"Could not find resource [r={TransmuteResourceTypeName.Split('.').First()}][r={TransmuteResourceTypeName.Split('.').Last()}] for [{this.Name}]{Environment.NewLine}The parent transmutation [{(this.Parent as CLEMModel).NameWithParent}] will not suceed without this resource and will not be performed", memberNames));
                        }
                    }
                }
            }

            // get pricing if available
            IResourceType parentResource = FindAncestor <CLEMResourceTypeBase>() as IResourceType;

            if (TransmuteStyle == TransmuteStyle.UsePricing)
            {
                if (shortfallPricing is null)
                {
                    string[] memberNames = new string[] { "Shortfall resource pricing" };
                    results.Add(new ValidationResult($"No resource pricing was found for [r={(parentResource as CLEMModel).NameWithParent}] required for a price based transmute [{this.Name}]{Environment.NewLine}Provide a pricing for the shortfall resource or use Direct transmute style", memberNames));
                }

                if (!(TransmuteResourceType is FinanceType))
                {
                    if (transmutePricing is null)
                    {
                        string[] memberNames = new string[] { "Transmute resource pricing" };
                        results.Add(new ValidationResult($"No resource pricing was found for [r={(TransmuteResourceType as CLEMModel).NameWithParent}] required for a price based transmute [{this.Name}]Provide a pricing for the transmute resource or use Direct transmute style", memberNames));
                    }
                }
            }

            return(results);
        }
Пример #3
0
        /// <summary>
        /// Validate this object
        /// </summary>
        /// <param name="validationContext"></param>
        /// <returns></returns>
        public IEnumerable <ValidationResult> Validate(ValidationContext validationContext)
        {
            var results = new List <ValidationResult>();
            // ensure labour resource added
            Labour lab = Resources.FindResource <Labour>();

            if (lab == null)
            {
                Summary.WriteMessage(this, "[a=" + this.Parent.Name + "][f=" + this.Name + "] No labour resorces in simulation. Labour requirement will be ignored.", MessageType.Warning);
            }
            else
            if (lab.Children.Count <= 0)
            {
                Summary.WriteMessage(this, "[a=" + this.Parent.Name + "][f=" + this.Name + "] No labour resorce types are provided in the labour resource. Labour requirement will be ignored.", MessageType.Warning);
            }

            // check filter groups present
            if (this.Children.OfType <LabourFilterGroup>().Count() == 0)
            {
                Summary.WriteMessage(this, "No LabourFilterGroup is supplied with the LabourRequirement for [a=" + this.Parent.Name + "]. No labour will be used for this activity.", MessageType.Warning);
            }

            // check for individual nesting.
            foreach (LabourFilterGroup fg in this.FindAllChildren <LabourFilterGroup>())
            {
                LabourFilterGroup currentfg = fg;
                while (currentfg != null && currentfg.FindAllChildren <LabourFilterGroup>().Any())
                {
                    if (currentfg.FindAllChildren <LabourFilterGroup>().Count() > 1)
                    {
                        string[] memberNames = new string[] { "Labour requirement" };
                        results.Add(new ValidationResult(String.Format("Invalid nested labour filter groups in [f={0}] for [a={1}]. Only one nested filter group is permitted each branch. Additional filtering will be ignored.", currentfg.Name, this.Name), memberNames));
                    }
                    currentfg = currentfg.FindAllChildren <LabourFilterGroup>().FirstOrDefault();
                }
            }

            return(results);
        }
Пример #4
0
        private void OnCommencing(object sender, EventArgs e)
        {
            int lossModifier = 1;

            if (ReportLossesAsNegative)
            {
                lossModifier = -1;
            }

            // check if running from a CLEM.Market
            bool market = (FindAncestor <Zone>().GetType() == typeof(Market));

            List <string> variableNames = new List <string>
            {
                "[Clock].Today as Date"
            };

            if (IncludeFinancialYear)
            {
                Finance financeStore = resources.FindResourceGroup <Finance>();
                if (financeStore != null)
                {
                    variableNames.Add($"[Resources].{financeStore.Name}.FinancialYear as FY");
                }
            }

            List <string> eventNames = new List <string>();

            if (ResourceGroupsToReport != null && ResourceGroupsToReport.Trim() != "")
            {
                // check it is a ResourceGroup
                CLEMModel model = resources.FindResource <ResourceBaseWithTransactions>(ResourceGroupsToReport);
                if (model == null)
                {
                    summary.WriteMessage(this, String.Format("Invalid resource group [{0}] in ReportResourceBalances [{1}]\r\nEntry has been ignored", this.ResourceGroupsToReport, this.Name), MessageType.Warning);
                }
                else
                {
                    bool pricingIncluded = false;
                    if (model.GetType() == typeof(RuminantHerd))
                    {
                        pricingIncluded = model.FindAllDescendants <AnimalPricing>().Where(a => a.Enabled).Count() > 0;

                        variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.ID as uID");
                        variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.Breed as Breed");
                        variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.Sex as Sex");
                        variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.Age as Age");
                        variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.Weight as Weight");
                        variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.AdultEquivalent as AE");
                        variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.SaleFlag as Category");
                        variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.Class as Class");
                        variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.HerdName as RelatesTo");
                        variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.PopulationChangeDirection as Change");

                        // ToDo: add pricing for ruminants including buy and sell pricing
                        // Needs update in CLEMResourceTypeBase and link between ResourcePricing and AnimalPricing.
                    }
                    else
                    {
                        pricingIncluded = model.FindAllDescendants <ResourcePricing>().Where(a => a.Enabled).Count() > 0;

                        if (ReportStyle == ReportTransactionStyle.GainAndLossColumns)
                        {
                            variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastTransaction.Gain as Gain");
                            variableNames.Add("[Resources]." + this.ResourceGroupsToReport + $".LastTransaction.Loss * {lossModifier} as Loss");
                        }
                        else
                        {
                            variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastTransaction.TransactionType as Type");
                            variableNames.Add("[Resources]." + this.ResourceGroupsToReport + $".LastTransaction.AmountModifiedForLoss({ReportLossesAsNegative}) as Amount");
                        }

                        // get all converters for this type of resource
                        if (IncludeConversions)
                        {
                            var converterList = model.FindAllDescendants <ResourceUnitsConverter>().Select(a => a.Name).Distinct();
                            if (converterList != null)
                            {
                                foreach (var item in converterList)
                                {
                                    if (ReportStyle == ReportTransactionStyle.GainAndLossColumns)
                                    {
                                        variableNames.Add($"[Resources].{this.ResourceGroupsToReport}.LastTransaction.ConvertTo(\"{item}\",\"gain\",{ReportLossesAsNegative}) as {item}_Gain");
                                        variableNames.Add($"[Resources].{this.ResourceGroupsToReport}.LastTransaction.ConvertTo(\"{item}\",\"loss\",{ReportLossesAsNegative}) as {item}_Loss");
                                    }
                                    else
                                    {
                                        variableNames.Add($"[Resources].{this.ResourceGroupsToReport}.LastTransaction.ConvertTo(\"{item}\", {ReportLossesAsNegative}) as {item}_Amount");
                                    }
                                }
                            }
                        }

                        // add pricing
                        if (IncludePrice && pricingIncluded)
                        {
                            if (ReportStyle == ReportTransactionStyle.GainAndLossColumns)
                            {
                                variableNames.Add("[Resources]." + this.ResourceGroupsToReport + $".LastTransaction.ConvertTo(\"$gain\",\"gain\", {ReportLossesAsNegative}) as Price_Gain");
                                variableNames.Add("[Resources]." + this.ResourceGroupsToReport + $".LastTransaction.ConvertTo(\"$loss\",\"loss\", {ReportLossesAsNegative}) as Price_Loss");
                            }
                            else
                            {
                                variableNames.Add("[Resources]." + this.ResourceGroupsToReport + $".LastTransaction.ConvertTo(\"$gain\", {ReportLossesAsNegative}) as Price_Amount");
                            }
                        }

                        variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastTransaction.ResourceType.Name as Resource");
                        // if this is a multi CLEM model simulation then add a new column with the parent Zone name
                        if (FindAncestor <Simulation>().FindChild <Market>() != null)
                        {
                            variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastTransaction.Activity.CLEMParentName as Source");
                        }
                        variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastTransaction.Activity.Name as Activity");
                        variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastTransaction.RelatesToResource as RelatesTo");
                        variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastTransaction.Category as Category");
                    }
                }
                eventNames.Add("[Resources]." + this.ResourceGroupsToReport + ".TransactionOccurred");
            }

            VariableNames = variableNames.ToArray();
            EventNames    = eventNames.ToArray();
            SubscribeToEvents();
        }
Пример #5
0
        [EventSubscribe("FinalInitialise")] // "Commencing"
        private void OnCommencing(object sender, EventArgs e)
        {
            if (ResourceGroupsToReport is null || !ResourceGroupsToReport.Any())
            {
                return;
            }

            timers = FindAllChildren <IActivityTimer>();

            List <string> variableNames = new List <string>();

            if (ResourceGroupsToReport.Where(a => a.Contains("[Clock].Today")).Any() is false)
            {
                variableNames.Add("[Clock].Today as Date");
            }

            if (ResourceGroupsToReport != null)
            {
                for (int i = 0; i < this.ResourceGroupsToReport.Length; i++)
                {
                    // each variable name is now a ResourceGroup
                    bool isDuplicate = StringUtilities.IndexOfCaseInsensitive(variableNames, this.ResourceGroupsToReport[i].Trim()) != -1;
                    if (!isDuplicate && this.ResourceGroupsToReport[i] != string.Empty)
                    {
                        if (this.ResourceGroupsToReport[i].StartsWith("["))
                        {
                            variableNames.Add(this.ResourceGroupsToReport[i]);
                        }
                        else
                        {
                            // check it is a ResourceGroup
                            CLEMModel model = resources.FindResource <ResourceBaseWithTransactions>(this.ResourceGroupsToReport[i]);
                            if (model == null)
                            {
                                summary.WriteMessage(this, $"Invalid resource group [r={this.ResourceGroupsToReport[i]}] in ReportResourceBalances [{this.Name}]{Environment.NewLine}Entry has been ignored", MessageType.Warning);
                            }
                            else
                            {
                                if (model is Labour)
                                {
                                    string amountStr = "Amount";
                                    if (ReportLabourIndividuals)
                                    {
                                        amountStr = "Individuals";
                                    }

                                    for (int j = 0; j < (model as Labour).Items.Count; j++)
                                    {
                                        if (ReportAmount)
                                        {
                                            variableNames.Add("[Resources]." + this.ResourceGroupsToReport[i] + ".Items[" + (j + 1).ToString() + $"].{amountStr} as " + (model as Labour).Items[j].Name);
                                        }

                                        //TODO: what economic metric is needed for labour
                                        //TODO: add ability to report labour value if required
                                    }
                                }
                                else
                                {
                                    foreach (CLEMModel item in model.FindAllChildren <CLEMModel>())
                                    {
                                        string amountStr = "Amount";
                                        switch (item)
                                        {
                                        case FinanceType ftype:
                                            amountStr = "Balance";
                                            break;

                                        case LandType ltype:
                                            if (ReportLandEntire)
                                            {
                                                amountStr = "LandArea";
                                            }
                                            break;

                                        default:
                                            break;
                                        }
                                        if (item is RuminantType)
                                        {
                                            // add each variable needed
                                            foreach (var category in (model as RuminantHerd).GetReportingGroups(item as RuminantType))
                                            {
                                                if (ReportAmount)
                                                {
                                                    variableNames.Add($"[Resources].{this.ResourceGroupsToReport[i]}.GetRuminantReportGroup(\"{(item as IModel).Name}\",\"{category}\").Count as {item.Name.Replace(" ", "_")}{(((model as RuminantHerd).TransactionStyle != RuminantTransactionsGroupingStyle.Combined) ? $".{category.Replace(" ", "_")}" : "")}.Count");
                                                }
                                                if (ReportAnimalEquivalents)
                                                {
                                                    variableNames.Add($"[Resources].{this.ResourceGroupsToReport[i]}.GetRuminantReportGroup(\"{(item as IModel).Name}\",\"{category}\").TotalAdultEquivalent as {item.Name.Replace(" ", "_")}{(((model as RuminantHerd).TransactionStyle != RuminantTransactionsGroupingStyle.Combined) ? $".{category.Replace(" ", "_")}" : "")}.TotalAdultEquivalent");
                                                }
                                                if (ReportAnimalWeight)
                                                {
                                                    variableNames.Add($"[Resources].{this.ResourceGroupsToReport[i]}.GetRuminantReportGroup(\"{(item as IModel).Name}\",\"{category}\").TotalWeight as {item.Name.Replace(" ", "_")}{(((model as RuminantHerd).TransactionStyle != RuminantTransactionsGroupingStyle.Combined) ? $".{category.Replace(" ", "_")}" : "")}.TotalWeight");
                                                }
                                                if (ReportValue)
                                                {
                                                    variableNames.Add($"[Resources].{this.ResourceGroupsToReport[i]}.GetRuminantReportGroup(\"{(item as IModel).Name}\",\"{category}\").TotalPrice as {item.Name.Replace(" ", "_")}{(((model as RuminantHerd).TransactionStyle != RuminantTransactionsGroupingStyle.Combined) ? $".{category.Replace(" ", "_")}" : "")}.TotalPrice");
                                                }
                                            }
                                        }
                                        else
                                        {
                                            if (ReportAmount)
                                            {
                                                variableNames.Add($"[Resources].{this.ResourceGroupsToReport[i]}.{ item.Name}.{ amountStr } as { item.Name.Replace(" ", "_") }_Amount");
                                            }
                                            if (ReportValue & item.GetType() != typeof(FinanceType))
                                            {
                                                variableNames.Add($"[Resources].{this.ResourceGroupsToReport[i]}.{item.Name}.Value as { item.Name.Replace(" ", "_") }_DollarValue");
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            VariableNames = variableNames.ToArray();
            // Subscribe to events.
            if (EventNames == null || !EventNames.Where(a => a.Trim() != "").Any())
            {
                EventNames = new string[] { "[Clock].CLEMFinalizeTimeStep" }
            }
            ;

            SubscribeToEvents();
        }
Пример #6
0
        /// <summary>
        /// Determine resources available and perform transmutation if needed.
        /// </summary>
        /// <param name="resourceRequests">List of requests</param>
        /// <param name="uniqueActivityID">Unique id for the activity</param>
        public void CheckResources(IEnumerable<ResourceRequest> resourceRequests, Guid uniqueActivityID)
        {
            if (resourceRequests is null || !resourceRequests.Any())
            {
                this.Status = ActivityStatus.Success;
                return;
            }

            foreach (ResourceRequest request in resourceRequests)
            {
                request.ActivityID = uniqueActivityID;
                request.Available = 0;

                // If resource group does not exist then provide required.
                // This means when resource is not added to model it will not limit simulations
                if (request.ResourceType == null || Resources.FindResource(request.ResourceType) == null)
                {
                    request.Available = request.Required;
                    request.Provided = request.Required;
                }
                else
                {
                    if (request.ResourceType == typeof(Labour))
                        // get available labour based on rules.
                        request.Available = TakeLabour(request, false, this, Resources, this.OnPartialResourcesAvailableAction);
                    else
                        request.Available = TakeNonLabour(request, false);
                }
            }

            // are all resources available
            IEnumerable<ResourceRequest> shortfallRequests = resourceRequests.Where(a => a.Required > a.Available);
            if (shortfallRequests.Any())
                // check what transmutations can occur
                Resources.TransmutateShortfall(shortfallRequests);

            // check if need to do transmutations
            int countTransmutationsSuccessful = shortfallRequests.Where(a => a.TransmutationPossible == true && a.AllowTransmutation).Count();
            bool allTransmutationsSuccessful = (shortfallRequests.Where(a => a.TransmutationPossible == false && a.AllowTransmutation).Count() == 0);

            // OR at least one transmutation successful and PerformWithPartialResources
            if ((shortfallRequests.Any() && (shortfallRequests.Count() == countTransmutationsSuccessful)) || (countTransmutationsSuccessful > 0 && OnPartialResourcesAvailableAction == OnPartialResourcesAvailableActionTypes.UseResourcesAvailable))
            {
                // do transmutations.
                // this uses the current zone resources, but will find markets if needed in the process
                Resources.TransmutateShortfall(shortfallRequests, false);

                // recheck resource amounts now that resources have been topped up
                foreach (ResourceRequest request in resourceRequests)
                {
                    // get resource
                    request.Available = 0;
                    if (request.Resource != null)
                        // get amount available
                        request.Available = Math.Min(request.Resource.Amount, request.Required);
                }
            }

            bool deficitFound = false;
            // report any resource defecits here
            foreach (var item in resourceRequests.Where(a => (a.Required - a.Available) > 0.000001))
            {
                ResourceRequestEventArgs rrEventArgs = new ResourceRequestEventArgs() { Request = item };

                if (item.Resource != null && (item.Resource as Model).FindAncestor<Market>() != null)
                {
                    ActivitiesHolder marketActivities = Resources.FoundMarket.FindChild<ActivitiesHolder>();
                    if(marketActivities != null)
                        marketActivities.ActivitiesHolder_ResourceShortfallOccurred(this, rrEventArgs);
                }
                else
                    OnShortfallOccurred(rrEventArgs);
                Status = ActivityStatus.Partial;
                deficitFound = true;
            }
            if(!deficitFound)
                this.Status = ActivityStatus.Success;
        }
Пример #7
0
        /// <summary>
        /// Method to determine available labour based on filters and take it if requested.
        /// </summary>
        /// <param name="request">Resource request details</param>
        /// <param name="removeFromResource">Determines if only calculating available labour or labour removed</param>
        /// <param name="callingModel">Model calling this method</param>
        /// <param name="resourceHolder">Location of resource holder</param>
        /// <param name="partialAction">Action on partial resources available</param>
        /// <returns></returns>
        public static double TakeLabour(ResourceRequest request, bool removeFromResource, IModel callingModel, ResourcesHolder resourceHolder, OnPartialResourcesAvailableActionTypes partialAction )
        {
            double amountProvided = 0;
            double amountNeeded = request.Required;
            LabourFilterGroup current = request.FilterDetails.OfType<LabourFilterGroup>().FirstOrDefault();

            LabourRequirement lr;
            if (current!=null)
            {
                if (current.Parent is LabourRequirement)
                    lr = current.Parent as LabourRequirement;
                else
                    // coming from Transmutation request
                    lr = new LabourRequirement()
                    {
                        ApplyToAll = false,
                        MaximumPerPerson = 1000,
                        MinimumPerPerson = 0
                    };
            }
            else
                lr = callingModel.FindAllChildren<LabourRequirement>().FirstOrDefault();

            int currentIndex = 0;
            if (current==null)
                // no filtergroup provided so assume any labour
                current = new LabourFilterGroup();

            request.ResourceTypeName = "Labour";
            ResourceRequest removeRequest = new ResourceRequest()
            {
                ActivityID = request.ActivityID,
                ActivityModel = request.ActivityModel,
                AdditionalDetails = request.AdditionalDetails,
                AllowTransmutation = request.AllowTransmutation,
                Available = request.Available,
                FilterDetails = request.FilterDetails,
                Provided = request.Provided,
                Category = request.Category,
                RelatesToResource = request.RelatesToResource,
                Required = request.Required,
                Resource = request.Resource,
                ResourceType = request.ResourceType,
                ResourceTypeName = (request.Resource is null? "":(request.Resource as CLEMModel).NameWithParent)
            };

            // start with top most LabourFilterGroup
            while (current != null && amountProvided < amountNeeded)
            {
                IEnumerable<LabourType> items = resourceHolder.FindResource<Labour>().Items;
                items = items.Where(a => (a.LastActivityRequestID != request.ActivityID) || (a.LastActivityRequestID == request.ActivityID && a.LastActivityRequestAmount < lr.MaximumPerPerson));
                items = current.Filter(items);

                // search for people who can do whole task first
                while (amountProvided < amountNeeded && items.Where(a => a.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumPerPerson) >= request.Required).Count() > 0)
                {
                    // get labour least available but with the amount needed
                    LabourType lt = items.Where(a => a.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumPerPerson) >= request.Required).OrderBy(a => a.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumPerPerson)).FirstOrDefault();

                    double amount = Math.Min(amountNeeded - amountProvided, lt.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumPerPerson));

                    // limit to max allowed per person
                    amount = Math.Min(amount, lr.MaximumPerPerson);
                    // limit to min per person to do activity
                    if (amount < lr.MinimumPerPerson)
                    {
                        request.Category = "Min labour limit";
                        return amountProvided;
                    }

                    amountProvided += amount;
                    removeRequest.Required = amount;
                    if (removeFromResource)
                    {
                        lt.LastActivityRequestID = request.ActivityID;
                        lt.LastActivityRequestAmount = amount;
                        lt.Remove(removeRequest);
                        request.Provided += removeRequest.Provided;
                        request.Value += request.Provided * lt.PayRate();
                    }
                }

                // if still needed and allow partial resource use.
                if (partialAction == OnPartialResourcesAvailableActionTypes.UseResourcesAvailable)
                {
                    if (amountProvided < amountNeeded)
                    {
                        // then search for those that meet criteria and can do part of task
                        foreach (LabourType item in items.Where(a => a.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumPerPerson) >= 0).OrderByDescending(a => a.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumPerPerson)))
                        {
                            if (amountProvided >= amountNeeded)
                                break;

                            double amount = Math.Min(amountNeeded - amountProvided, item.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumPerPerson));

                            // limit to max allowed per person
                            amount = Math.Min(amount, lr.MaximumPerPerson);

                            // limit to min per person to do activity
                            if (amount >= lr.MinimumPerPerson)
                            {
                                amountProvided += amount;
                                removeRequest.Required = amount;
                                if (removeFromResource)
                                {
                                    if(item.LastActivityRequestID != request.ActivityID)
                                        item.LastActivityRequestAmount = 0;
                                    item.LastActivityRequestID = request.ActivityID;
                                    item.LastActivityRequestAmount += amount;
                                    item.Remove(removeRequest);
                                    request.Provided += removeRequest.Provided;
                                    request.Value += request.Provided * item.PayRate();
                                }
                            }
                            else
                                currentIndex = request.FilterDetails.Count;
                        }
                    }
                }
                currentIndex++;
                var currentFilterGroups = current.FindAllChildren<LabourFilterGroup>();
                if (currentFilterGroups.Any())
                    current = currentFilterGroups.FirstOrDefault();
                else
                    current = null;
            }
            // report amount gained.
            return amountProvided;
        }