[13069] | 1 | using HeuristicLab.BioBoost.Data;
|
---|
| 2 | using HeuristicLab.BioBoost.ProblemDescription;
|
---|
| 3 | using HeuristicLab.Common;
|
---|
| 4 | using HeuristicLab.Core;
|
---|
| 5 | using HeuristicLab.Data;
|
---|
| 6 | using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
|
---|
| 7 | using System;
|
---|
| 8 | using System.Collections.Generic;
|
---|
| 9 | using System.Linq;
|
---|
| 10 | using HeuristicLab.BioBoost.Representation;
|
---|
| 11 | using HeuristicLab.BioBoost.Utils;
|
---|
| 12 |
|
---|
| 13 | namespace HeuristicLab.BioBoost.Evaluators {
|
---|
| 14 | [StorableClass]
|
---|
| 15 | public class LogisticCostEvaluator : BioBoostEvaluator {
|
---|
| 16 |
|
---|
| 17 | private class Variant : IComparable<Variant> {
|
---|
| 18 | public double HandlingCost;
|
---|
| 19 | public double TransportCost;
|
---|
| 20 | public double Penalty;
|
---|
| 21 | public double Distance;
|
---|
| 22 | public Logistic Logistic;
|
---|
| 23 | public double Amount;
|
---|
| 24 | public int CompareTo(Variant other) {
|
---|
| 25 | return (int)((HandlingCost + TransportCost) - (other.HandlingCost + other.TransportCost));
|
---|
| 26 | }
|
---|
| 27 | }
|
---|
| 28 |
|
---|
| 29 | #region fields
|
---|
| 30 | private DoubleArray invest, maint, fuel, labor, other;
|
---|
| 31 | #endregion
|
---|
| 32 |
|
---|
| 33 | #region Construction & Cloning
|
---|
| 34 | [StorableConstructor]
|
---|
| 35 | protected LogisticCostEvaluator(bool isDeserializing) : base(isDeserializing) {}
|
---|
| 36 |
|
---|
| 37 | protected LogisticCostEvaluator(LogisticCostEvaluator original, Cloner cloner) : base(original, cloner) {}
|
---|
| 38 |
|
---|
| 39 | public LogisticCostEvaluator() { }
|
---|
| 40 |
|
---|
| 41 | public override IDeepCloneable Clone(Cloner cloner) {
|
---|
| 42 | return new LogisticCostEvaluator(this, cloner);
|
---|
| 43 | }
|
---|
| 44 | #endregion
|
---|
| 45 |
|
---|
| 46 | public override IOperation Apply() {
|
---|
| 47 | var logistics = ProblemData.Logistics;
|
---|
| 48 | var distances = new Dictionary<string, DistanceMatrix>();
|
---|
| 49 | var emissions = new Dictionary<string, DoubleArray>();
|
---|
| 50 | invest = ProblemData.InvestmentCostFactors;
|
---|
| 51 | maint = ProblemData.MaintenanceCostFactors;
|
---|
| 52 | fuel = ProblemData.FuelCostFactors;
|
---|
| 53 | labor = ProblemData.LaborCostFactors;
|
---|
| 54 | other = ProblemData.OtherCostFactors;
|
---|
| 55 | var prices = ProblemData.Products.ToDictionary(p => p.Label, p => p.Price); // TODO: regional scaling for supplies and side-products?
|
---|
| 56 | foreach (var product in logistics.Select(l => l.Key)) {
|
---|
| 57 | var amountsAtSource = GetFromScope<DoubleArray>(LayerDescriptor.AmountsAtSource.NameWithPrefix(product));
|
---|
| 58 | var costsAtSource = GetFromScope<DoubleArray>(LayerDescriptor.TotalCostsAtSource.NameWithPrefix(product));
|
---|
| 59 | var transportTargets = GetFromScope<IntArray>(LayerDescriptor.TransportTargets.NameWithPrefix(product));
|
---|
| 60 | if (amountsAtSource == null || transportTargets == null) continue;
|
---|
| 61 | var length = amountsAtSource.Length;
|
---|
| 62 | var sources = new ItemArray<ItemList<IntValue>>(length);
|
---|
| 63 | var amountsAtTarget = new DoubleArray(length);
|
---|
| 64 | var chosenVariants = new List<Variant>(new Variant[length]);
|
---|
| 65 | var costsAtTarget = new DoubleArray(length);
|
---|
| 66 | var totalProductHandlingCost = 0d;
|
---|
| 67 | var totalProductTransportCost = 0d;
|
---|
| 68 | var totalPenalty = 0d;
|
---|
| 69 | for (int i = 0; i < length; i++) {
|
---|
| 70 | var amount = amountsAtSource[i];
|
---|
| 71 | if (amount <= 0) continue;
|
---|
| 72 | var j = transportTargets[i];
|
---|
| 73 | if (j == -1) {
|
---|
| 74 | amountsAtSource[i] = 0; // no transport -> no feedstock collected
|
---|
| 75 | continue;
|
---|
| 76 | }
|
---|
| 77 | var chosenVariant = ChooseVariant(logistics[product], amount, i, j, distances);
|
---|
| 78 | if (chosenVariant != null) {
|
---|
| 79 | amountsAtTarget[j] += Math.Min(chosenVariant.Amount, amount); // cannot have more than before
|
---|
| 80 | costsAtTarget[j] += costsAtSource[i] + chosenVariant.HandlingCost + chosenVariant.TransportCost +
|
---|
| 81 | chosenVariant.Logistic.Transport.Emissions.TotalCost(prices, null)*chosenVariant.Amount*chosenVariant.Distance +
|
---|
| 82 | chosenVariant.Logistic.Handling.Emissions.TotalCost(prices, null)*chosenVariant.Amount;
|
---|
| 83 | totalProductHandlingCost += chosenVariant.HandlingCost;
|
---|
| 84 | totalProductTransportCost += chosenVariant.TransportCost;
|
---|
| 85 | totalPenalty += chosenVariant.Penalty;
|
---|
| 86 | AddEmissions(emissions, chosenVariant.Logistic.Handling.Emissions, amount, i, j, length);
|
---|
| 87 | AddEmissions(emissions, chosenVariant.Logistic.Transport.Emissions, amount*chosenVariant.Distance, i, j, length);
|
---|
| 88 | chosenVariants[i] = chosenVariant;
|
---|
| 89 | if (sources[j] != null) {
|
---|
| 90 | sources[j].Add(new IntValue(i));
|
---|
| 91 | } else {
|
---|
| 92 | sources[j] = new ItemList<IntValue>(new [] { new IntValue(i) });
|
---|
| 93 | }
|
---|
| 94 | } else {
|
---|
| 95 | amountsAtSource[i] = 0; // will be AmountsTransportedFromSource
|
---|
| 96 | }
|
---|
| 97 | }
|
---|
| 98 | RenameInScope(LayerDescriptor.AmountsAtSource.NameWithPrefix(product), LayerDescriptor.AmountsTransportedFromSource.NameWithPrefix(product));
|
---|
| 99 | PutInScope(LayerDescriptor.TransportModes.NameWithPrefix(product), chosenVariants.Select("", v => v.Logistic.Mode).ToStringArray(), false);
|
---|
| 100 | PutInScope(LayerDescriptor.TransportCosts.NameWithPrefix(product), chosenVariants.Select(0, v => v.TransportCost).ToDoubleArray(), false);
|
---|
| 101 | PutInScope(LayerDescriptor.TransportCostsPerT.NameWithPrefix(product), chosenVariants.Select(0, v => v.Amount == 0 ? 0 : v.TransportCost / v.Amount).ToDoubleArray(), false);
|
---|
| 102 | PutInScope(LayerDescriptor.TransportCostsPerTkm.NameWithPrefix(product), chosenVariants.Select(0, v => v.Amount == 0 || v.Distance == 0 ? 0 : v.TransportCost / v.Amount / v.Distance).ToDoubleArray(), false);
|
---|
| 103 | PutInScope(LayerDescriptor.HandlingCosts.NameWithPrefix(product), chosenVariants.Select(0, v => v.HandlingCost).ToDoubleArray(), false);
|
---|
| 104 | PutInScope(LayerDescriptor.RelativeHandlingCosts.NameWithPrefix(product), chosenVariants.Select(0, v => v.Amount == 0 ? 0 : v.HandlingCost / v.Amount).ToDoubleArray(), false);
|
---|
| 105 | AddInScope(LayerDescriptor.AmountsAtTarget.NameWithPrefix(product), amountsAtTarget, false);
|
---|
| 106 | AddInScope(LayerDescriptor.TotalCostsAtTarget.NameWithPrefix(product), costsAtTarget, false);
|
---|
| 107 | PutInScope(LayerDescriptor.RelativeCostAtTarget.NameWithPrefix(product), costsAtTarget.Zip(amountsAtTarget, (c,a) => a == 0 ? 0 : c/a).ToDoubleArray(), false);
|
---|
| 108 | PutInScope(LayerDescriptor.TransportDistance.NameWithPrefix(product), chosenVariants.Select(0, v => v.Distance).ToDoubleArray(), false);
|
---|
| 109 | PutInScope(LayerDescriptor.Tkm.NameWithPrefix(product), chosenVariants.Select(0, v => v.Distance*v.Amount).ToDoubleArray(), false);
|
---|
| 110 | var transportPenalties = chosenVariants.Select(0, v => v.Penalty).ToDoubleArray();
|
---|
| 111 | PutInScope(LayerDescriptor.ExceedingTransportDistancePenalty.NameWithPrefix(product), transportPenalties, false);
|
---|
| 112 | AddInScope(LayerDescriptor.TotalPenalty.Name, (DoubleArray)transportPenalties.Clone(), false);
|
---|
| 113 | PutInScope(LayerDescriptor.TotalLogisticCosts.NameWithPrefix(product), chosenVariants.Select(0, v => v.TransportCost + v.HandlingCost).ToDoubleArray(), false);
|
---|
| 114 | PutInScope(LayerDescriptor.LogisticCostsPerTon.NameWithPrefix(product), chosenVariants.Select(0, v => v.Amount == 0 ? 0 : (v.TransportCost + v.HandlingCost)/v.Amount).ToDoubleArray(), false);
|
---|
| 115 | //PutInScope(LayerDescriptor.LogisticCostsPerTonKm.NameWithPrefix(product), chosenVariants.Select(0, v => v.Amount * v.Distance == 0 ? 0 : (v.TransportCost + v.HandlingCost)/v.Amount/v.Distance).ToDoubleArray(), false);
|
---|
| 116 | AddInScope(LayerDescriptor.LocalAddedValue.Name, chosenVariants.Select(0, v => v.HandlingCost + v.TransportCost).ToDoubleArray(), false);
|
---|
| 117 | PutInScope(LayerDescriptor.Sources.NameWithPrefix(product), sources, false);
|
---|
| 118 | AddCost(LayerDescriptor.HandlingCosts.NameWithPrefix(product), totalProductHandlingCost);
|
---|
| 119 | AddCost(LayerDescriptor.TransportCosts.NameWithPrefix(product), totalProductTransportCost);
|
---|
| 120 | AddCost(LayerDescriptor.ExceedingTransportDistancePenalty.NameWithPrefix(product), totalPenalty);
|
---|
| 121 | }
|
---|
| 122 | foreach (var e in emissions) {
|
---|
| 123 | AddInScope(LayerDescriptor.AmountsAtSource.NameWithPrefix(e.Key), e.Value, false);
|
---|
| 124 | AddInScope(LayerDescriptor.DischargeCosts.NameWithPrefix(e.Key), e.Value.Select(x => !prices.ContainsKey(e.Key) ? 0 : (prices[e.Key] * x)).ToDoubleArray(), false);
|
---|
| 125 | }
|
---|
| 126 | return base.Apply();
|
---|
| 127 | }
|
---|
| 128 |
|
---|
| 129 | private Variant ChooseVariant(IEnumerable<Logistic> logistics, double amount, int i, int j, IDictionary<string, DistanceMatrix> distances) {
|
---|
| 130 | var variants = new List<Variant>();
|
---|
| 131 | foreach (var l in logistics) {
|
---|
| 132 | DistanceMatrix distanceMatrix = null;
|
---|
| 133 | if (!distances.TryGetValue(l.Distances, out distanceMatrix)) {
|
---|
| 134 | distanceMatrix = GetFromProblemData<DistanceMatrix>(l.Distances + "DistanceMatrix");
|
---|
| 135 | if (distanceMatrix != null)
|
---|
| 136 | distances[l.Distances] = distanceMatrix;
|
---|
| 137 | else
|
---|
| 138 | throw new InvalidOperationException("Distance Matrix \"" + l.Distances +
|
---|
| 139 | "\" for logistic action \"" + l.Mode +
|
---|
| 140 | "\" of product \"" + l.Product + "\" could not be loaded");
|
---|
| 141 | }
|
---|
| 142 | if (distanceMatrix == null) continue;
|
---|
| 143 | var distance = distanceMatrix[i, j];
|
---|
| 144 | var actualAmount = Math.Min(Math.Max(amount, l.MinAmount), l.MaxAmount);
|
---|
| 145 | var v = new Variant {
|
---|
| 146 | Amount = actualAmount,
|
---|
| 147 | Logistic = l,
|
---|
| 148 | HandlingCost = GetLogisticActionCost(l.Handling, actualAmount*(1-l.WaterContent), i, j),
|
---|
| 149 | TransportCost = GetLogisticActionCost(l.Transport, actualAmount*distance*(1-l.WaterContent), i, j),
|
---|
| 150 | Distance = distance
|
---|
| 151 | };
|
---|
| 152 | if (distance > l.MaxDistance)
|
---|
| 153 | v.Penalty = Penalty(distance, l.MaxDistance, distanceMatrix.Max);
|
---|
| 154 | variants.Add(v);
|
---|
| 155 | }
|
---|
| 156 | variants.Sort();
|
---|
| 157 | return variants.FirstOrDefault();
|
---|
| 158 | }
|
---|
| 159 |
|
---|
| 160 | public static double Penalty(double distance, double maxAllowedDistance, double maxPossibleDistance) {
|
---|
| 161 | return (distance - maxAllowedDistance)/maxPossibleDistance;
|
---|
| 162 | }
|
---|
| 163 |
|
---|
| 164 | private double GetLogisticActionCost(LogisticAction a, double amount, int i, int j) {
|
---|
| 165 | return
|
---|
| 166 | amount * (
|
---|
| 167 | a.Investment *(invest[i] + invest[j]) +
|
---|
| 168 | a.Maintenance *( maint[i] + maint[j]) +
|
---|
| 169 | a.Fuel *( fuel[i] + fuel[j]) +
|
---|
| 170 | a.Labor *( labor[i] + labor[j]) +
|
---|
| 171 | a.Other *( other[i] + other[j]))/2;
|
---|
| 172 | }
|
---|
| 173 |
|
---|
| 174 | private void AddEmissions(Dictionary<string, DoubleArray> totalEmissions, IEnumerable<IValueParameter> emissions, double amount, int i, int j, int length) {
|
---|
| 175 | foreach (var e in emissions) {
|
---|
| 176 | DoubleArray values = null;
|
---|
| 177 | if (!totalEmissions.TryGetValue(e.Name, out values)) {
|
---|
| 178 | totalEmissions.Add(e.Name, values = new DoubleArray(length));
|
---|
| 179 | }
|
---|
| 180 | values[i] += ((DoubleValue)e.Value).Value*amount/2;
|
---|
| 181 | values[j] += ((DoubleValue)e.Value).Value*amount/2;
|
---|
| 182 | }
|
---|
| 183 | }
|
---|
| 184 | }
|
---|
| 185 | }
|
---|