Exemplo n.º 1
0
        public double InventorySpaceUpperBound(double nextPeriodInventorySpaceLowerBound, double nextPeriodInventorySpaceUpperBound,
                                               double currentPeriodMinInventory, double currentPeriodMaxInventory, double inventoryPercentLoss)
        {
            InjectWithdrawRange currentPeriodInjectWithdrawRangeAtMaxInventory = GetInjectWithdrawRange(currentPeriodMaxInventory);

            double nextPeriodMaxInventoryFromThisPeriodMaxInventory = currentPeriodMaxInventory * (1 - inventoryPercentLoss)
                                                                      + currentPeriodInjectWithdrawRangeAtMaxInventory.MaxInjectWithdrawRate;
            double nextPeriodMinInventoryFromThisPeriodMaxInventory = currentPeriodMaxInventory * (1 - inventoryPercentLoss)
                                                                      + currentPeriodInjectWithdrawRangeAtMaxInventory.MinInjectWithdrawRate;

            if (nextPeriodMinInventoryFromThisPeriodMaxInventory <= nextPeriodInventorySpaceUpperBound &&
                nextPeriodInventorySpaceLowerBound <= nextPeriodMaxInventoryFromThisPeriodMaxInventory)
            {
                // No need to solve root as next period inventory space can be reached from the current period max inventory
                return(currentPeriodMaxInventory);
            }
            // TODO share code in method up to here with PiecewiseLinearInjectWithdrawConstraint

            double?inventorySpaceUpper = null;

            for (int i = 0; i < _injectWithdrawRanges.Length - 1; i++)
            {
                // TODO reuse values between loop iterations like in PiecewiseLinearInjectWithdrawConstraint, or not bother because will make code less clear?
                double maxWithdrawRate       = _injectWithdrawRanges[i].InjectWithdrawRange.MinInjectWithdrawRate;
                double bracketLowerInventory = _injectWithdrawRanges[i].Inventory;
                double bracketLowerInventoryAfterWithdraw = bracketLowerInventory * (1 - inventoryPercentLoss) + maxWithdrawRate;

                double bracketUpperInventory = _injectWithdrawRanges[i + 1].Inventory;
                double bracketUpperInventoryAfterWithdraw = bracketUpperInventory * (1 - inventoryPercentLoss) + maxWithdrawRate;

                if (bracketLowerInventoryAfterWithdraw <= nextPeriodInventorySpaceUpperBound &&
                    nextPeriodInventorySpaceUpperBound <= bracketUpperInventoryAfterWithdraw)
                {
                    // If there are multiple solutions we want to take the maximum one, so we keep overwriting the solution
                    inventorySpaceUpper = StorageHelper.InterpolateLinearAndSolve(bracketLowerInventory,
                                                                                  bracketLowerInventoryAfterWithdraw, bracketUpperInventory,
                                                                                  bracketUpperInventoryAfterWithdraw, nextPeriodInventorySpaceUpperBound);
                }
            }

            if (inventorySpaceUpper == null)
            {
                throw new ApplicationException("Storage inventory constraints cannot be satisfied.");
            }
            return(inventorySpaceUpper.Value);
        }
Exemplo n.º 2
0
        public StepInjectWithdrawConstraint([NotNull] IEnumerable <InjectWithdrawRangeByInventory> injectWithdrawRanges)
        {
            if (injectWithdrawRanges == null)
            {
                throw new ArgumentNullException(nameof(injectWithdrawRanges));
            }

            _injectWithdrawRanges = injectWithdrawRanges.OrderBy(injectWithdrawRange => injectWithdrawRange.Inventory)
                                    .ToArray();
            if (_injectWithdrawRanges.Length < 2)
            {
                throw new ArgumentException("Inject/withdraw ranges collection must contain at least two elements.", nameof(injectWithdrawRanges));
            }


            InjectWithdrawRange secondHighestInventoryRange = _injectWithdrawRanges[_injectWithdrawRanges.Length - 2].InjectWithdrawRange;
            InjectWithdrawRange highestInventoryRange       = _injectWithdrawRanges[_injectWithdrawRanges.Length - 1].InjectWithdrawRange;

            if (!StorageHelper.EqualsWithinTol(secondHighestInventoryRange.MaxInjectWithdrawRate, highestInventoryRange.MaxInjectWithdrawRate, 1E-12))
            {
                throw new ArgumentException("Top two ratchets do not have he same max injection rate.", nameof(injectWithdrawRanges));
            }
            if (!StorageHelper.EqualsWithinTol(secondHighestInventoryRange.MinInjectWithdrawRate, highestInventoryRange.MinInjectWithdrawRate, 1E-12))
            {
                throw new ArgumentException("Top two ratchets do not have he same max withdrawal rate.", nameof(injectWithdrawRanges));
            }

            _inventories = _injectWithdrawRanges.Select(injectWithdrawRange => injectWithdrawRange.Inventory)
                           .ToArray();

            if (_inventories.Length > 2)
            {
                for (int i = 1; i < _inventories.Length - 1; i++) // Don't check the last pair as these should have same inject/withdraw rates, as checked for above
                {
                    if (_injectWithdrawRanges[i].InjectWithdrawRange.MaxInjectWithdrawRate > _injectWithdrawRanges[i - 1].InjectWithdrawRange.MaxInjectWithdrawRate)
                    {
                        throw new ArgumentException("Ratchet injection rates cannot increase with inventory.");
                    }
                    if (_injectWithdrawRanges[i].InjectWithdrawRange.MinInjectWithdrawRate > _injectWithdrawRanges[i - 1].InjectWithdrawRange.MinInjectWithdrawRate)
                    {
                        throw new ArgumentException("Ratchet withdrawal rates cannot decrease with inventory.");
                    }
                }
            }
        }
        public double InventorySpaceLowerBound(double nextPeriodInventorySpaceLowerBound, double nextPeriodInventorySpaceUpperBound,
                                               double currentPeriodMinInventory, double currentPeriodMaxInventory, double inventoryPercentLoss)
        {
            InjectWithdrawRange currentPeriodInjectWithdrawRangeAtMinInventory = GetInjectWithdrawRange(currentPeriodMinInventory);

            double nextPeriodMaxInventoryFromThisPeriodMinInventory = currentPeriodMinInventory * (1 - inventoryPercentLoss)
                                                                      + currentPeriodInjectWithdrawRangeAtMinInventory.MaxInjectWithdrawRate;
            double nextPeriodMinInventoryFromThisPeriodMinInventory = currentPeriodMinInventory * (1 - inventoryPercentLoss)
                                                                      + currentPeriodInjectWithdrawRangeAtMinInventory.MinInjectWithdrawRate;

            if (nextPeriodMinInventoryFromThisPeriodMinInventory <= nextPeriodInventorySpaceUpperBound &&
                nextPeriodInventorySpaceLowerBound <= nextPeriodMaxInventoryFromThisPeriodMinInventory)
            {
                // No need to solve root as next period inventory space can be reached from the current period min inventory
                return(currentPeriodMinInventory);
            }

            // Search for inventory bracket
            double bracketLowerInventory            = _injectWithdrawRanges[0].Inventory;
            double bracketLowerInventoryAfterInject = nextPeriodMaxInventoryFromThisPeriodMinInventory;

            for (int i = 1; i < _injectWithdrawRanges.Length; i++)
            {
                InjectWithdrawRangeByInventory bracketUpperDecisionRange = _injectWithdrawRanges[i];
                double bracketUpperInventory            = bracketUpperDecisionRange.Inventory;
                double bracketUpperInventoryAfterInject = bracketUpperInventory * (1 - inventoryPercentLoss) +
                                                          bracketUpperDecisionRange.InjectWithdrawRange.MaxInjectWithdrawRate;

                if (bracketLowerInventoryAfterInject <= nextPeriodInventorySpaceLowerBound &&
                    nextPeriodInventorySpaceLowerBound <= bracketUpperInventoryAfterInject)
                {
                    double inventorySpaceLower = StorageHelper.InterpolateLinearAndSolve(bracketLowerInventory,
                                                                                         bracketLowerInventoryAfterInject, bracketUpperInventory,
                                                                                         bracketUpperInventoryAfterInject, nextPeriodInventorySpaceLowerBound);
                    return(inventorySpaceLower);
                }

                bracketLowerInventoryAfterInject = bracketUpperInventoryAfterInject;
                bracketLowerInventory            = bracketUpperInventory;
            }

            throw new ApplicationException("Storage inventory constraints cannot be satisfied.");
        }
 public void Deconstruct(out double inventory, out InjectWithdrawRange injectWithdrawRange)
 {
     inventory           = Inventory;
     injectWithdrawRange = InjectWithdrawRange;
 }
 public InjectWithdrawRangeByInventory(double inventory, [NotNull] InjectWithdrawRange injectWithdrawRange)
 {
     Inventory           = inventory;
     InjectWithdrawRange = injectWithdrawRange ?? throw new ArgumentNullException(nameof(injectWithdrawRange));
 }
Exemplo n.º 6
0
        public static double[] CalculateBangBangDecisionSet(InjectWithdrawRange injectWithdrawRange, double currentInventory, double inventoryLoss,
                                                            double nextStepMinInventory, double nextStepMaxInventory, double numericalTolerance, int numExtraDecisions = 0) // TODO remove default value of zero
        {
            if (nextStepMinInventory > nextStepMaxInventory)
            {
                throw new ArgumentException($"Parameter {nameof(nextStepMinInventory)} value cannot be higher than parameter {nameof(nextStepMaxInventory)} value.");
            }
            if (numExtraDecisions < 0)
            {
                throw new ArgumentException($"Parameter {nameof(numExtraDecisions)} must be non-negative.", nameof(numExtraDecisions));
            }

            double inventoryAfterLoss = currentInventory - inventoryLoss;

            double inventoryAfterMaxWithdrawal = injectWithdrawRange.MinInjectWithdrawRate + inventoryAfterLoss;
            double yieldedWithdrawalRate;

            if (inventoryAfterMaxWithdrawal > nextStepMaxInventory) // Max withdrawal still above next step max inventory
            {
                if (inventoryAfterMaxWithdrawal - nextStepMaxInventory < numericalTolerance)
                {
                    // Next period inventory is breached, but only by a small amount, probably due to root finding in PolynomialInjectWithdrawConstraint during inventory space reduction
                    yieldedWithdrawalRate = nextStepMaxInventory - inventoryAfterLoss; // TODO unit test code reaching here
                }
                else
                {
                    throw new ArgumentException("Inventory constraints cannot be fulfilled. This could potentially be fixed by increasing the numerical tolerance.");
                }
            }
            else if (inventoryAfterMaxWithdrawal > nextStepMinInventory)
            {
                yieldedWithdrawalRate = injectWithdrawRange.MinInjectWithdrawRate; // Unconstrained withdrawal
            }
            else
            {
                yieldedWithdrawalRate = nextStepMinInventory - inventoryAfterLoss; //constrained withdrawal (could be made positive to injection)
            }

            double inventoryAfterMaxInjection = injectWithdrawRange.MaxInjectWithdrawRate + inventoryAfterLoss;
            double yieldedInjectionRate;

            if (inventoryAfterMaxInjection < nextStepMinInventory) // Max injection still below next step min inventory constraint
            {
                if (nextStepMinInventory - inventoryAfterMaxInjection < numericalTolerance)
                {
                    // Next period inventory is breached, but only by a small amount, probably due to root finding in PolynomialInjectWithdrawConstraint during inventory space reduction
                    yieldedInjectionRate = nextStepMinInventory - inventoryAfterLoss; // TODO unit test code reaching here
                }
                else
                {
                    throw new ArgumentException("Inventory constraints cannot be fulfilled. This could potentially be fixed by increasing the numerical tolerance.");
                }
            }
            else if (inventoryAfterMaxInjection < nextStepMaxInventory)
            {
                yieldedInjectionRate = injectWithdrawRange.MaxInjectWithdrawRate; // Unconstrained injection
            }
            else
            {
                yieldedInjectionRate = nextStepMaxInventory - inventoryAfterLoss; // Constrained injection (could be made negative to withdrawal)
            }

            double[] decisionSet;
            if (yieldedWithdrawalRate >= 0.0 || yieldedInjectionRate <= 0.0) // No zero decision
            {
                if (numExtraDecisions > 0)
                {
                    decisionSet    = new double[numExtraDecisions + 2];
                    decisionSet[0] = yieldedWithdrawalRate;
                    decisionSet[decisionSet.Length - 1] = yieldedInjectionRate;
                    PopulateExtraDecisions(yieldedWithdrawalRate, yieldedInjectionRate, numExtraDecisions, new Span <double>(decisionSet, 1, numExtraDecisions));
                }
                else
                {
                    decisionSet = new double[] { yieldedWithdrawalRate, yieldedInjectionRate }
                };
            }
            else
            {
                if (numExtraDecisions > 0)
                {
                    decisionSet    = new double[numExtraDecisions * 2 + 3];
                    decisionSet[0] = yieldedWithdrawalRate;
                    decisionSet[decisionSet.Length - 1] = yieldedInjectionRate;
                    PopulateExtraDecisions(yieldedWithdrawalRate, 0, numExtraDecisions, new Span <double>(decisionSet, 1, numExtraDecisions));
                    PopulateExtraDecisions(0, yieldedInjectionRate, numExtraDecisions, new Span <double>(decisionSet, numExtraDecisions + 2, numExtraDecisions));
                }
                else
                {
                    decisionSet = new double[] { yieldedWithdrawalRate, 0.0, yieldedInjectionRate }
                };
            }

            return(decisionSet);

            // TODO case of yieldedWithdrawalRate equals to yieldedInjectionRate?
        }
 public ConstantInjectWithdrawConstraint([NotNull] InjectWithdrawRange injectWithdrawRange)
 {
     _injectWithdrawRange = injectWithdrawRange ?? throw new ArgumentNullException(nameof(injectWithdrawRange));
 }
 public ConstantInjectWithdrawConstraint(double minInjectWithdrawRate, double maxInjectWithdrawRate)
 {
     _injectWithdrawRange = new InjectWithdrawRange(minInjectWithdrawRate, maxInjectWithdrawRate);
 }