public static IAddMinInventory <T> WithConstantInjectWithdrawRange <T>([NotNull] this IAddInjectWithdrawConstraints <T> builder, double minInjectWithdrawRate, double maxInjectWithdrawRate) where T : ITimePeriod <T> { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } var constantInjectWithdrawConstraint = new ConstantInjectWithdrawConstraint(minInjectWithdrawRate, maxInjectWithdrawRate); return(builder.WithInjectWithdrawConstraint(constantInjectWithdrawConstraint)); }
private static IAddInjectionCost <T> AddInjectWithdrawRanges <T>( IAddInjectWithdrawConstraints <T> builder, IEnumerable <InjectWithdrawRangeByInventoryAndPeriod <T> > injectWithdrawRanges, Func <InjectWithdrawRangeByInventory[], IInjectWithdrawConstraint> constraintFactory) where T : ITimePeriod <T> { var injectWithdrawSortedList = new SortedList <T, IInjectWithdrawConstraint>(); var inventoryRangeList = new List <InventoryRange>(); foreach ((T period, IEnumerable <InjectWithdrawRangeByInventory> injectWithdrawRange) in injectWithdrawRanges) { if (period == null) { throw new ArgumentException("Null Period in collection.", nameof(injectWithdrawRanges)); } if (injectWithdrawRange == null) { throw new ArgumentException("Null InjectWithdrawRanges in collection.", nameof(injectWithdrawRange)); } InjectWithdrawRangeByInventory[] injectWithdrawRangeArray = injectWithdrawRange.ToArray(); if (injectWithdrawRangeArray.Length < 2) { throw new ArgumentException($"Period {period} contains less than 2 inject/withdraw/inventory constraints.", nameof(injectWithdrawRanges)); } IInjectWithdrawConstraint constraint; if (injectWithdrawRangeArray.Length == 2 && injectWithdrawRangeArray[0].InjectWithdrawRange.MinInjectWithdrawRate.AlmostEqual( injectWithdrawRangeArray[1].InjectWithdrawRange.MinInjectWithdrawRate, double.Epsilon) && injectWithdrawRangeArray[0].InjectWithdrawRange.MaxInjectWithdrawRate.AlmostEqual( injectWithdrawRangeArray[1].InjectWithdrawRange.MaxInjectWithdrawRate, double.Epsilon)) { // Two rows which represent constant inject/withdraw constraints over all inventories constraint = new ConstantInjectWithdrawConstraint(injectWithdrawRangeArray[0].InjectWithdrawRange); } else { constraint = constraintFactory(injectWithdrawRangeArray); } double minInventory = injectWithdrawRangeArray.Min(inventoryRange => inventoryRange.Inventory); double maxInventory = injectWithdrawRangeArray.Max(inventoryRange => inventoryRange.Inventory); try { injectWithdrawSortedList.Add(period, constraint); inventoryRangeList.Add(new InventoryRange(minInventory, maxInventory)); } catch (ArgumentException) // TODO unit test { throw new ArgumentException("Repeated periods found in inject/withdraw ranges.", nameof(injectWithdrawRanges)); } } if (injectWithdrawSortedList.Count == 0) { throw new ArgumentException("No inject/withdraw constrains provided.", nameof(injectWithdrawRanges)); } // TODO create helper method (in Cmdty.TimeSeries) to create TimeSeries from piecewise data? T firstPeriod = injectWithdrawSortedList.Keys[0]; T lastPeriod = injectWithdrawSortedList.Keys[injectWithdrawSortedList.Count - 1]; int numPeriods = lastPeriod.OffsetFrom(firstPeriod) + 1; var timeSeriesInjectWithdrawValues = new IInjectWithdrawConstraint[numPeriods]; var timeSeriesInventoryRangeValues = new InventoryRange[numPeriods]; T periodLoop = firstPeriod; IInjectWithdrawConstraint constraintLoop = injectWithdrawSortedList.Values[0]; InventoryRange inventoryRangeLoop = inventoryRangeList[0]; int arrayCounter = 0; int sortedListCounter = 0; do { if (periodLoop.Equals(injectWithdrawSortedList.Keys[sortedListCounter])) { constraintLoop = injectWithdrawSortedList.Values[sortedListCounter]; inventoryRangeLoop = inventoryRangeList[sortedListCounter]; sortedListCounter++; } timeSeriesInjectWithdrawValues[arrayCounter] = constraintLoop; timeSeriesInventoryRangeValues[arrayCounter] = inventoryRangeLoop; periodLoop = periodLoop.Offset(1); arrayCounter++; } while (periodLoop.CompareTo(lastPeriod) <= 0); var injectWithdrawTimeSeries = new TimeSeries <T, IInjectWithdrawConstraint>(firstPeriod, timeSeriesInjectWithdrawValues); var inventoryRangeTimeSeries = new TimeSeries <T, InventoryRange>(firstPeriod, timeSeriesInventoryRangeValues); IInjectWithdrawConstraint GetInjectWithdrawConstraint(T period) { if (period.CompareTo(injectWithdrawTimeSeries.End) > 0) { return(injectWithdrawTimeSeries[injectWithdrawTimeSeries.End]); } return(injectWithdrawTimeSeries[period]); } IAddMinInventory <T> addMinInventory = builder.WithInjectWithdrawConstraint(GetInjectWithdrawConstraint); double GetMinInventory(T period) { if (period.CompareTo(inventoryRangeTimeSeries.End) > 0) { return(inventoryRangeTimeSeries[inventoryRangeTimeSeries.End].MinInventory); } return(inventoryRangeTimeSeries[period].MinInventory); } IAddMaxInventory <T> addMaxInventory = addMinInventory.WithMinInventory(GetMinInventory); double GetMaxInventory(T period) { if (period.CompareTo(inventoryRangeTimeSeries.End) > 0) { return(inventoryRangeTimeSeries[inventoryRangeTimeSeries.End].MaxInventory); } return(inventoryRangeTimeSeries[period].MaxInventory); } IAddInjectionCost <T> addInjectionCost = addMaxInventory.WithMaxInventory(GetMaxInventory); return(addInjectionCost); }