1 | using System.Drawing;
|
---|
2 | using HeuristicLab.BioBoost.Data;
|
---|
3 | using HeuristicLab.BioBoost.Utils;
|
---|
4 | using HeuristicLab.Common;
|
---|
5 | using HeuristicLab.Core;
|
---|
6 | using HeuristicLab.Data;
|
---|
7 | using HeuristicLab.Operators;
|
---|
8 | using HeuristicLab.Optimization;
|
---|
9 | using HeuristicLab.Parameters;
|
---|
10 | using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
|
---|
11 | using System;
|
---|
12 | using System.Collections.Generic;
|
---|
13 | using System.Linq;
|
---|
14 | using HeuristicLab.BioBoost.Representation;
|
---|
15 |
|
---|
16 | namespace HeuristicLab.BioBoost.Evaluators {
|
---|
17 | [StorableClass]
|
---|
18 | [Item("ConversionCostEvaluator", "Simulates the conversion of all applicable conversion processes.")]
|
---|
19 | public class ConversionCostEvaluator : BioBoostEvaluator {
|
---|
20 |
|
---|
21 | #region fields
|
---|
22 | private DoubleArray invest, maint, fuel, labor, other;
|
---|
23 | #endregion
|
---|
24 |
|
---|
25 | #region Construction & Cloning
|
---|
26 | [StorableConstructor]
|
---|
27 | protected ConversionCostEvaluator(bool isDeserializing) : base(isDeserializing) {}
|
---|
28 | protected ConversionCostEvaluator(ConversionCostEvaluator original, Cloner cloner) : base(original, cloner) { }
|
---|
29 | public ConversionCostEvaluator() { }
|
---|
30 | public override IDeepCloneable Clone(Cloner cloner) {
|
---|
31 | return new ConversionCostEvaluator(this, cloner);
|
---|
32 | }
|
---|
33 | #endregion
|
---|
34 |
|
---|
35 | public override IOperation Apply() {
|
---|
36 | var conversions = ProblemData.Conversions;
|
---|
37 | var products = new Dictionary<string, DoubleArray>();
|
---|
38 | var prices = ProblemData.Products.ToDictionary(p => p.Label, p => p.Price);
|
---|
39 | DoubleArray productCosts = new DoubleArray(0);
|
---|
40 | invest = ProblemData.InvestmentCostFactors;
|
---|
41 | maint = ProblemData.MaintenanceCostFactors;
|
---|
42 | fuel = ProblemData.FuelCostFactors;
|
---|
43 | labor = ProblemData.LaborCostFactors;
|
---|
44 | other = ProblemData.OtherCostFactors;
|
---|
45 | foreach (var feedstock in conversions.Select(c => c.Key)) {
|
---|
46 | var feedstockAmounts = GetFromScope<DoubleArray>(LayerDescriptor.AmountsAtTarget.NameWithPrefix(feedstock));
|
---|
47 | var feedstockCosts = GetFromScope<DoubleArray>(LayerDescriptor.TotalCostsAtTarget.NameWithPrefix(feedstock));
|
---|
48 | if (feedstockAmounts == null) continue;
|
---|
49 | var length = feedstockAmounts.Length;
|
---|
50 | var totalConversionCost = 0d;
|
---|
51 | var totalConstructionCost = 0d;
|
---|
52 | var totalOperationCost = 0d;
|
---|
53 | var totalStorageCost = 0d;
|
---|
54 | var totalScalingPenalty = 0d;
|
---|
55 | var totalExceedingCapacityPenalty = 0d;
|
---|
56 | var converterCapacities = new DoubleArray(length);
|
---|
57 | var maxCapacities = new DoubleArray(length);
|
---|
58 | var storageCapacities = new DoubleArray(length);
|
---|
59 | var chosenConversions = new List<Variant>(new Variant[length]);
|
---|
60 | if (productCosts.Length < length)
|
---|
61 | productCosts = new DoubleArray(length);
|
---|
62 | for (int i = 0; i < length; i++) {
|
---|
63 | var amount = feedstockAmounts[i];
|
---|
64 | maxCapacities[i] = conversions[feedstock].Max(c => GetMaxCapacity(c, ProblemData.LocationNames[i]));
|
---|
65 | if (amount <= 0) continue;
|
---|
66 | var chosenConversion = ChooseConversion(conversions[feedstock], amount, i);
|
---|
67 | if (chosenConversion != null) {
|
---|
68 | //feedstockAmounts[i] = 0;
|
---|
69 | totalConversionCost += chosenConversion.Cost;
|
---|
70 | totalConstructionCost += chosenConversion.ConstructionCost;
|
---|
71 | totalOperationCost += chosenConversion.OperationCost;
|
---|
72 | totalStorageCost += chosenConversion.StorageCost;
|
---|
73 | totalScalingPenalty += chosenConversion.ScalingPenalty;
|
---|
74 | totalExceedingCapacityPenalty += chosenConversion.ExceedingCapacityPenalty;
|
---|
75 | converterCapacities[i] = chosenConversion.Capacity;
|
---|
76 | storageCapacities[i] = chosenConversion.StorageCapacity;
|
---|
77 | AddProducts(products, chosenConversion.Conversion.Products, Math.Min(chosenConversion.Amount, chosenConversion.Capacity), i, length);
|
---|
78 | AddProducts(products, chosenConversion.Conversion.ConstructionEmissions, chosenConversion.Capacity/chosenConversion.Conversion.DesignCapacity, i, length);
|
---|
79 | chosenConversions[i] = chosenConversion;
|
---|
80 | feedstockCosts[i] += chosenConversion.StorageCost;
|
---|
81 | productCosts[i] += feedstockCosts[i] + chosenConversion.Cost + chosenConversion.ConstructionCost + chosenConversion.OperationCost; // still without side products
|
---|
82 | } else {
|
---|
83 | feedstockAmounts[i] = 0;
|
---|
84 | }
|
---|
85 | }
|
---|
86 | // TODO: 2. Runde?!?!?
|
---|
87 | //RemoveFromScope(LayerDescriptor.AmountsAtTarget.NameWithPrefix(feedstock));
|
---|
88 | RenameInScope(LayerDescriptor.AmountsAtTarget.NameWithPrefix(feedstock), LayerDescriptor.AmountsAtTargetConverted.NameWithPrefix(feedstock));
|
---|
89 | var conversionCosts = chosenConversions.Select(0, v => v.Cost).ToDoubleArray();
|
---|
90 | var constructionCosts = chosenConversions.Select(0, v => v.ConstructionCost).ToDoubleArray();
|
---|
91 | var operationCosts = chosenConversions.Select(0, v => v.OperationCost).ToDoubleArray();
|
---|
92 | PutInScope(LayerDescriptor.ConversionCosts.NameWithPrefix(feedstock), conversionCosts, false);
|
---|
93 | PutInScope(LayerDescriptor.ConversionPlantConstructionCost.NameWithPrefix(feedstock), constructionCosts, false);
|
---|
94 | PutInScope(LayerDescriptor.ConversionPlantOperationCost.NameWithPrefix(feedstock), operationCosts, false);
|
---|
95 | PutInScope(LayerDescriptor.RelativeConversionCosts.NameWithPrefix(feedstock), conversionCosts.Zip(feedstockAmounts, (c, a) => a == 0 ? 0 : c/a).ToDoubleArray(), false);
|
---|
96 | PutInScope(LayerDescriptor.RelativeConversionPlantConstructionCosts.NameWithPrefix(feedstock), constructionCosts.Zip(feedstockAmounts, (c, a) => a == 0 ? 0 : c/a).ToDoubleArray(), false);
|
---|
97 | PutInScope(LayerDescriptor.RelativeConversionPlantOperationCosts.NameWithPrefix(feedstock), operationCosts.Zip(feedstockAmounts, (c, a) => a == 0 ? 0 : c/a).ToDoubleArray(), false);
|
---|
98 | var storageCosts = chosenConversions.Select(0, v => v.StorageCost).ToDoubleArray();
|
---|
99 | PutInScope(LayerDescriptor.StorageCost.NameWithPrefix(feedstock), storageCosts, false);
|
---|
100 | PutInScope(LayerDescriptor.RelativeStorageCost.NameWithPrefix(feedstock), storageCosts.Zip(feedstockAmounts, (c, a) => a == 0 ? 0 : c/a).ToDoubleArray(), false);
|
---|
101 | var totalLogisticCost = GetFromScope<DoubleArray>(LayerDescriptor.TotalLogisticCosts.NameWithPrefix(feedstock));
|
---|
102 | totalLogisticCost = totalLogisticCost.Zip(storageCosts, (l, s) => l + s).ToDoubleArray();
|
---|
103 | PutInScope(LayerDescriptor.TotalLogisticCosts.NameWithPrefix(feedstock), totalLogisticCost, false);
|
---|
104 | PutInScope(LayerDescriptor.LogisticCostsPerTon.NameWithPrefix(feedstock), totalLogisticCost.Zip(feedstockAmounts, (c, a) => a == 0 ? 0 : c/a).ToDoubleArray(), false);
|
---|
105 | PutInScope(LayerDescriptor.MaxConversionCapacity.NameWithPrefix(feedstock), maxCapacities, false);
|
---|
106 | var scalingPenalties = chosenConversions.Select(0, v => v.ScalingPenalty).ToDoubleArray();
|
---|
107 | var exceedingCapacityPenalties = chosenConversions.Select(0, v => v.ExceedingCapacityPenalty).ToDoubleArray();
|
---|
108 | PutInScope(LayerDescriptor.ScalingPenalty.NameWithPrefix(feedstock), scalingPenalties, false);
|
---|
109 | PutInScope(LayerDescriptor.ExceedingCapacityPenalties.NameWithPrefix(feedstock), exceedingCapacityPenalties, false);
|
---|
110 | AddInScope(LayerDescriptor.TotalPenalty.Name, scalingPenalties.Zip(exceedingCapacityPenalties, (s, c) => s+c).ToDoubleArray(), false);
|
---|
111 | PutInScope(LayerDescriptor.TotalCostsAtTarget.NameWithPrefix(feedstock), feedstockCosts, false); // now including storage costs
|
---|
112 | PutInScope(LayerDescriptor.RelativeCostAtTarget.NameWithPrefix(feedstock), feedstockCosts.Zip(feedstockAmounts, (c,a) => a == 0 ? 0 : c/a).ToDoubleArray(), false);
|
---|
113 | var totalDirectConversionCost = chosenConversions.Select(0, c => c.Cost + c.Conversion.Products.TotalCost(prices, c.Conversion.MainProduct)*c.Amount) .ToDoubleArray();
|
---|
114 | PutInScope(LayerDescriptor.TotalDirectConversionCost.NameWithPrefix(feedstock), totalDirectConversionCost, false);
|
---|
115 | //var mainProductAmounts = chosenConversions.Select((c, i) => c != null ? 0 : products[c.Conversion.MainProduct][i]).ToArray();
|
---|
116 | PutInScope(LayerDescriptor.RelativeTotalDirectConversionCost.NameWithPrefix(feedstock), totalDirectConversionCost.Zip(feedstockAmounts, (c, a) => a == 0 ? 0 : c/a).ToDoubleArray(), false);
|
---|
117 | PutInScope(LayerDescriptor.ScalingFactors.NameWithPrefix(feedstock), chosenConversions.Select(0, v => v.ScalingFactor).ToDoubleArray(), false);
|
---|
118 | var totalAmortizedConversionCost = totalDirectConversionCost
|
---|
119 | // .Zip(storageCosts, (t, s) => t + s) -> included in logistic costs
|
---|
120 | .Zip(constructionCosts, (t, c) => t + c)
|
---|
121 | .Zip(operationCosts, (t, o) => t + o)
|
---|
122 | .ToDoubleArray();
|
---|
123 | PutInScope(LayerDescriptor.TotalAmortizedConversionCosts.NameWithPrefix(feedstock), totalAmortizedConversionCost, false);
|
---|
124 | PutInScope(LayerDescriptor.RelativeTotalAmortizedConversionCosts.NameWithPrefix(feedstock), totalAmortizedConversionCost.Zip(feedstockAmounts, (c, a) => a == 0 ? 0 : c/a).ToDoubleArray(), false);
|
---|
125 | AddCost(LayerDescriptor.ConversionCosts.NameWithPrefix(feedstock), totalConversionCost);
|
---|
126 | AddCost(LayerDescriptor.ConversionPlantConstructionCost.NameWithPrefix(feedstock), totalConstructionCost);
|
---|
127 | AddCost(LayerDescriptor.ConversionPlantOperationCost.NameWithPrefix(feedstock), totalOperationCost);
|
---|
128 | AddCost(LayerDescriptor.StorageCost.NameWithPrefix(feedstock), totalStorageCost);
|
---|
129 | AddCost(LayerDescriptor.ScalingPenalty.NameWithPrefix(feedstock), totalScalingPenalty);
|
---|
130 | AddCost(LayerDescriptor.ExceedingCapacityPenalties.NameWithPrefix(feedstock), totalExceedingCapacityPenalty);
|
---|
131 | AddInScope(LayerDescriptor.ConverterCapacities.NameWithPrefix(feedstock), converterCapacities, false);
|
---|
132 | AddInScope(LayerDescriptor.StorageCapacities.NameWithPrefix(feedstock), storageCapacities, false);
|
---|
133 | AddInScope(LayerDescriptor.LocalAddedValue.Name, chosenConversions.Select(0, v => v.Cost + v.ConstructionCost + v.OperationCost).ToDoubleArray(), false);
|
---|
134 | foreach (var p in products) {
|
---|
135 | if (p.Value.Max() <= 0 || (prices.ContainsKey(p.Key) && prices[p.Key] <= 0)) continue; // don't report "production cost" for demands and waste products
|
---|
136 | var costsAtSource = new DoubleArray(productCosts.Select((c,i) => c + (chosenConversions[i] == null ? 0 : chosenConversions[i].Conversion.Products.TotalCost(prices, p.Key)*chosenConversions[i].Amount)).ToArray());
|
---|
137 | AddInScope(LayerDescriptor.TotalCostsAtSource.NameWithPrefix(p.Key), costsAtSource, false);
|
---|
138 | costsAtSource = GetFromScope<DoubleArray>(LayerDescriptor.TotalCostsAtSource.NameWithPrefix(p.Key));
|
---|
139 | PutInScope(LayerDescriptor.RelativeCostAtSource.NameWithPrefix(p.Key), costsAtSource.Zip(p.Value, (c,a) => a == 0 ? 0 : c/a).ToDoubleArray(), false);
|
---|
140 | }
|
---|
141 | }
|
---|
142 | foreach (var p in products) {
|
---|
143 | AddInScope(LayerDescriptor.AmountsAtSource.NameWithPrefix(p.Key), p.Value, false);
|
---|
144 | }
|
---|
145 | return base.Apply();
|
---|
146 | }
|
---|
147 |
|
---|
148 | private void AddProducts(Dictionary<string, DoubleArray> overallProducts, IEnumerable<IValueParameter> products, double amount, int i, int length) {
|
---|
149 | foreach (var e in products) {
|
---|
150 | DoubleArray values = null;
|
---|
151 | if (!overallProducts.TryGetValue(e.Name, out values)) {
|
---|
152 | overallProducts.Add(e.Name, values = new DoubleArray(length));
|
---|
153 | }
|
---|
154 | values[i] += ((DoubleValue)e.Value).Value*amount;
|
---|
155 | }
|
---|
156 | }
|
---|
157 |
|
---|
158 | private class Variant : IComparable<Variant> {
|
---|
159 | public Conversion Conversion;
|
---|
160 | public double Cost;
|
---|
161 | public double ConstructionCost;
|
---|
162 | public double OperationCost;
|
---|
163 | public double Capacity;
|
---|
164 | public double StorageCost;
|
---|
165 | public double StorageCapacity;
|
---|
166 | public double Amount;
|
---|
167 | public double ScalingPenalty;
|
---|
168 | public double ExceedingCapacityPenalty;
|
---|
169 | public double ScalingFactor;
|
---|
170 |
|
---|
171 | public int CompareTo(Variant other) {
|
---|
172 | return (int) (Cost - other.Cost);
|
---|
173 | }
|
---|
174 | }
|
---|
175 |
|
---|
176 | private Variant ChooseConversion(IEnumerable<Conversion> conversions, double amount, int i) {
|
---|
177 | var variants = new List<Variant>();
|
---|
178 | foreach (var c in conversions) { // TODO: select best scaling variant?
|
---|
179 | if (amount <= 0) continue;
|
---|
180 | var actualAmount = amount * (1-c.DryMatterLoss);
|
---|
181 | var v = new Variant {Capacity = actualAmount/c.UtilizationFactor, Conversion = c};
|
---|
182 | var regionName = ProblemData.LocationNames[i];
|
---|
183 | double maxCapacity = GetMaxCapacity(c, regionName);
|
---|
184 | v.ExceedingCapacityPenalty = CapacityPenalty(v.Capacity, maxCapacity);
|
---|
185 | //if (maxCapacity <= 0) continue;
|
---|
186 | var availableCapacity = GetValue(c.AvailableCapacities, regionName, 0);
|
---|
187 | var newCapacity = Math.Max(0, v.Capacity - availableCapacity);
|
---|
188 | v.ScalingFactor = v.Capacity/c.DesignCapacity;
|
---|
189 | var minScale = c.MinCapacity/c.DesignCapacity;
|
---|
190 | var maxScale = maxCapacity/c.DesignCapacity;
|
---|
191 | v.ScalingPenalty = ScalingPenalty(v.ScalingFactor, minScale, maxScale);
|
---|
192 | v.ConstructionCost = ScaledCost(newCapacity/c.DesignCapacity, maxScale, c.Construction, c.ConstructionScalingExponent);
|
---|
193 | v.OperationCost = ScaledCost(v.ScalingFactor, maxScale, c.Maintenance, c.MaintenanceScalingExponent);
|
---|
194 | v.OperationCost = (newCapacity/v.Capacity)*v.OperationCost + ((v.Capacity - newCapacity)/v.Capacity)*v.OperationCost*c.AvailableMaintenanceFactor;
|
---|
195 | v.Cost = c.Cost*actualAmount;
|
---|
196 | v.Amount = actualAmount;
|
---|
197 | v.StorageCapacity = v.Capacity/365*c.SafetyStock;
|
---|
198 | v.StorageCost = GetLogisticActionCost(c.Storage, v.StorageCapacity, i);
|
---|
199 | variants.Add(v);
|
---|
200 | }
|
---|
201 | variants.Sort();
|
---|
202 | return variants.FirstOrDefault();
|
---|
203 | }
|
---|
204 |
|
---|
205 | public static double GetMaxCapacity(Conversion c, string regionName) {
|
---|
206 | var maxCapacity = GetValue(c.MaxCapacities, regionName, -1);
|
---|
207 | if (maxCapacity < 0) maxCapacity = GetValue(c.MaxCapacities, "Default", -2);
|
---|
208 | return maxCapacity;
|
---|
209 | }
|
---|
210 |
|
---|
211 | private double GetLogisticActionCost(LogisticAction a, double amount, int i) {
|
---|
212 | return
|
---|
213 | amount*a.Investment*invest[i] +
|
---|
214 | amount*a.Maintenance*maint[i] +
|
---|
215 | amount*a.Fuel*fuel[i] +
|
---|
216 | amount*a.Labor*labor[i] +
|
---|
217 | amount*a.Other*other[i];
|
---|
218 | }
|
---|
219 |
|
---|
220 | private static double GetValue(ValueParameterCollection values, string name, double defaultValue) {
|
---|
221 | IValueParameter parameter;
|
---|
222 | if (values.TryGetValue(name, out parameter))
|
---|
223 | return ((DoubleValue) parameter.Value).Value;
|
---|
224 | return defaultValue;
|
---|
225 | }
|
---|
226 |
|
---|
227 | public static double ScalingPenalty(double scale, double minScale, double maxScale) {
|
---|
228 | if (scale > maxScale)
|
---|
229 | return Util.Sqr(scale-maxScale);
|
---|
230 | if (scale < minScale)
|
---|
231 | return 4*(minScale - scale)*scale/Util.Sqr(minScale) * 2;
|
---|
232 | return 0;
|
---|
233 | }
|
---|
234 |
|
---|
235 | public static double CapacityPenalty(double capacity, double maxCapacity) {
|
---|
236 | if (capacity > maxCapacity) {
|
---|
237 | if (maxCapacity == 0) {
|
---|
238 | return Math.Pow(capacity, 2);
|
---|
239 | } else {
|
---|
240 | return Math.Pow((capacity - maxCapacity)/maxCapacity, 2);
|
---|
241 | }
|
---|
242 | }
|
---|
243 | return 0;
|
---|
244 | }
|
---|
245 |
|
---|
246 | public static double ScaledCost(double scale, double maxScale, double baseCost, double exponent) {
|
---|
247 | if (scale > 10) return baseCost*Math.Pow(10, exponent - 1);
|
---|
248 | return baseCost*Math.Pow(scale, exponent);
|
---|
249 | }
|
---|
250 |
|
---|
251 | }
|
---|
252 | }
|
---|