1 | #region License Information
|
---|
2 | /* HeuristicLab
|
---|
3 | * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
|
---|
4 | *
|
---|
5 | * This file is part of HeuristicLab.
|
---|
6 | *
|
---|
7 | * HeuristicLab is free software: you can redistribute it and/or modify
|
---|
8 | * it under the terms of the GNU General Public License as published by
|
---|
9 | * the Free Software Foundation, either version 3 of the License, or
|
---|
10 | * (at your option) any later version.
|
---|
11 | *
|
---|
12 | * HeuristicLab is distributed in the hope that it will be useful,
|
---|
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
15 | * GNU General Public License for more details.
|
---|
16 | *
|
---|
17 | * You should have received a copy of the GNU General Public License
|
---|
18 | * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
|
---|
19 | */
|
---|
20 | #endregion
|
---|
21 |
|
---|
22 | using HeuristicLab.BioBoost.Data;
|
---|
23 | using HeuristicLab.BioBoost.ProblemDescription;
|
---|
24 | using HeuristicLab.Common;
|
---|
25 | using HeuristicLab.Core;
|
---|
26 | using HeuristicLab.Data;
|
---|
27 | using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
|
---|
28 | using System;
|
---|
29 | using System.Collections.Generic;
|
---|
30 | using System.Linq;
|
---|
31 | using HeuristicLab.BioBoost.Representation;
|
---|
32 | using HeuristicLab.BioBoost.Utils;
|
---|
33 | using HEAL.Attic;
|
---|
34 |
|
---|
35 | namespace HeuristicLab.BioBoost.Evaluators {
|
---|
36 | [StorableType("C633F527-7C12-478D-AECA-00D3E54ED2D2")]
|
---|
37 | public class LogisticCostEvaluator : BioBoostEvaluator {
|
---|
38 |
|
---|
39 | private class Variant : IComparable<Variant> {
|
---|
40 | public double HandlingCost;
|
---|
41 | public double TransportCost;
|
---|
42 | public double Penalty;
|
---|
43 | public double Distance;
|
---|
44 | public Logistic Logistic;
|
---|
45 | public double Amount;
|
---|
46 | public int CompareTo(Variant other) {
|
---|
47 | return (int)((HandlingCost + TransportCost) - (other.HandlingCost + other.TransportCost));
|
---|
48 | }
|
---|
49 | }
|
---|
50 |
|
---|
51 | #region fields
|
---|
52 | private DoubleArray invest, maint, fuel, labor, other;
|
---|
53 | #endregion
|
---|
54 |
|
---|
55 | #region Construction & Cloning
|
---|
56 | [StorableConstructor]
|
---|
57 | protected LogisticCostEvaluator(StorableConstructorFlag _) : base(_) { }
|
---|
58 |
|
---|
59 | protected LogisticCostEvaluator(LogisticCostEvaluator original, Cloner cloner) : base(original, cloner) {}
|
---|
60 |
|
---|
61 | public LogisticCostEvaluator() { }
|
---|
62 |
|
---|
63 | public override IDeepCloneable Clone(Cloner cloner) {
|
---|
64 | return new LogisticCostEvaluator(this, cloner);
|
---|
65 | }
|
---|
66 | #endregion
|
---|
67 |
|
---|
68 | public override IOperation Apply() {
|
---|
69 | var logistics = ProblemData.Logistics;
|
---|
70 | var distances = new Dictionary<string, DistanceMatrix>();
|
---|
71 | var emissions = new Dictionary<string, DoubleArray>();
|
---|
72 | invest = ProblemData.InvestmentCostFactors;
|
---|
73 | maint = ProblemData.MaintenanceCostFactors;
|
---|
74 | fuel = ProblemData.FuelCostFactors;
|
---|
75 | labor = ProblemData.LaborCostFactors;
|
---|
76 | other = ProblemData.OtherCostFactors;
|
---|
77 | var prices = ProblemData.Products.ToDictionary(p => p.Label, p => p.Price); // TODO: regional scaling for supplies and side-products?
|
---|
78 | foreach (var product in logistics.Select(l => l.Key)) {
|
---|
79 | var amountsAtSource = GetFromScope<DoubleArray>(LayerDescriptor.AmountsAtSource.NameWithPrefix(product));
|
---|
80 | var costsAtSource = GetFromScope<DoubleArray>(LayerDescriptor.TotalCostsAtSource.NameWithPrefix(product));
|
---|
81 | var transportTargets = GetFromScope<IntArray>(LayerDescriptor.TransportTargets.NameWithPrefix(product));
|
---|
82 | if (amountsAtSource == null || transportTargets == null) continue;
|
---|
83 | var length = amountsAtSource.Length;
|
---|
84 | var sources = new ItemArray<ItemList<IntValue>>(length);
|
---|
85 | var amountsAtTarget = new DoubleArray(length);
|
---|
86 | var chosenVariants = new List<Variant>(new Variant[length]);
|
---|
87 | var costsAtTarget = new DoubleArray(length);
|
---|
88 | var totalProductHandlingCost = 0d;
|
---|
89 | var totalProductTransportCost = 0d;
|
---|
90 | var totalPenalty = 0d;
|
---|
91 | for (int i = 0; i < length; i++) {
|
---|
92 | var amount = amountsAtSource[i];
|
---|
93 | if (amount <= 0) continue;
|
---|
94 | var j = transportTargets[i];
|
---|
95 | if (j == -1) {
|
---|
96 | amountsAtSource[i] = 0; // no transport -> no feedstock collected
|
---|
97 | continue;
|
---|
98 | }
|
---|
99 | var chosenVariant = ChooseVariant(logistics[product], amount, i, j, distances);
|
---|
100 | if (chosenVariant != null) {
|
---|
101 | amountsAtTarget[j] += Math.Min(chosenVariant.Amount, amount); // cannot have more than before
|
---|
102 | costsAtTarget[j] += costsAtSource[i] + chosenVariant.HandlingCost + chosenVariant.TransportCost +
|
---|
103 | chosenVariant.Logistic.Transport.Emissions.TotalCost(prices, null)*chosenVariant.Amount*chosenVariant.Distance +
|
---|
104 | chosenVariant.Logistic.Handling.Emissions.TotalCost(prices, null)*chosenVariant.Amount;
|
---|
105 | totalProductHandlingCost += chosenVariant.HandlingCost;
|
---|
106 | totalProductTransportCost += chosenVariant.TransportCost;
|
---|
107 | totalPenalty += chosenVariant.Penalty;
|
---|
108 | AddEmissions(emissions, chosenVariant.Logistic.Handling.Emissions, amount, i, j, length);
|
---|
109 | AddEmissions(emissions, chosenVariant.Logistic.Transport.Emissions, amount*chosenVariant.Distance, i, j, length);
|
---|
110 | chosenVariants[i] = chosenVariant;
|
---|
111 | if (sources[j] != null) {
|
---|
112 | sources[j].Add(new IntValue(i));
|
---|
113 | } else {
|
---|
114 | sources[j] = new ItemList<IntValue>(new [] { new IntValue(i) });
|
---|
115 | }
|
---|
116 | } else {
|
---|
117 | amountsAtSource[i] = 0; // will be AmountsTransportedFromSource
|
---|
118 | }
|
---|
119 | }
|
---|
120 | RenameInScope(LayerDescriptor.AmountsAtSource.NameWithPrefix(product), LayerDescriptor.AmountsTransportedFromSource.NameWithPrefix(product));
|
---|
121 | PutInScope(LayerDescriptor.TransportModes.NameWithPrefix(product), chosenVariants.Select("", v => v.Logistic.Mode).ToStringArray(), false);
|
---|
122 | PutInScope(LayerDescriptor.TransportCosts.NameWithPrefix(product), chosenVariants.Select(0, v => v.TransportCost).ToDoubleArray(), false);
|
---|
123 | PutInScope(LayerDescriptor.TransportCostsPerT.NameWithPrefix(product), chosenVariants.Select(0, v => v.Amount == 0 ? 0 : v.TransportCost / v.Amount).ToDoubleArray(), false);
|
---|
124 | PutInScope(LayerDescriptor.TransportCostsPerTkm.NameWithPrefix(product), chosenVariants.Select(0, v => v.Amount == 0 || v.Distance == 0 ? 0 : v.TransportCost / v.Amount / v.Distance).ToDoubleArray(), false);
|
---|
125 | PutInScope(LayerDescriptor.HandlingCosts.NameWithPrefix(product), chosenVariants.Select(0, v => v.HandlingCost).ToDoubleArray(), false);
|
---|
126 | PutInScope(LayerDescriptor.RelativeHandlingCosts.NameWithPrefix(product), chosenVariants.Select(0, v => v.Amount == 0 ? 0 : v.HandlingCost / v.Amount).ToDoubleArray(), false);
|
---|
127 | AddInScope(LayerDescriptor.AmountsAtTarget.NameWithPrefix(product), amountsAtTarget, false);
|
---|
128 | AddInScope(LayerDescriptor.TotalCostsAtTarget.NameWithPrefix(product), costsAtTarget, false);
|
---|
129 | PutInScope(LayerDescriptor.RelativeCostAtTarget.NameWithPrefix(product), costsAtTarget.Zip(amountsAtTarget, (c,a) => a == 0 ? 0 : c/a).ToDoubleArray(), false);
|
---|
130 | PutInScope(LayerDescriptor.TransportDistance.NameWithPrefix(product), chosenVariants.Select(0, v => v.Distance).ToDoubleArray(), false);
|
---|
131 | PutInScope(LayerDescriptor.Tkm.NameWithPrefix(product), chosenVariants.Select(0, v => v.Distance*v.Amount).ToDoubleArray(), false);
|
---|
132 | var transportPenalties = chosenVariants.Select(0, v => v.Penalty).ToDoubleArray();
|
---|
133 | PutInScope(LayerDescriptor.ExceedingTransportDistancePenalty.NameWithPrefix(product), transportPenalties, false);
|
---|
134 | AddInScope(LayerDescriptor.TotalPenalty.Name, (DoubleArray)transportPenalties.Clone(), false);
|
---|
135 | PutInScope(LayerDescriptor.TotalLogisticCosts.NameWithPrefix(product), chosenVariants.Select(0, v => v.TransportCost + v.HandlingCost).ToDoubleArray(), false);
|
---|
136 | PutInScope(LayerDescriptor.LogisticCostsPerTon.NameWithPrefix(product), chosenVariants.Select(0, v => v.Amount == 0 ? 0 : (v.TransportCost + v.HandlingCost)/v.Amount).ToDoubleArray(), false);
|
---|
137 | //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);
|
---|
138 | AddInScope(LayerDescriptor.LocalAddedValue.Name, chosenVariants.Select(0, v => v.HandlingCost + v.TransportCost).ToDoubleArray(), false);
|
---|
139 | PutInScope(LayerDescriptor.Sources.NameWithPrefix(product), sources, false);
|
---|
140 | AddCost(LayerDescriptor.HandlingCosts.NameWithPrefix(product), totalProductHandlingCost);
|
---|
141 | AddCost(LayerDescriptor.TransportCosts.NameWithPrefix(product), totalProductTransportCost);
|
---|
142 | AddCost(LayerDescriptor.ExceedingTransportDistancePenalty.NameWithPrefix(product), totalPenalty);
|
---|
143 | }
|
---|
144 | foreach (var e in emissions) {
|
---|
145 | AddInScope(LayerDescriptor.AmountsAtSource.NameWithPrefix(e.Key), e.Value, false);
|
---|
146 | AddInScope(LayerDescriptor.DischargeCosts.NameWithPrefix(e.Key), e.Value.Select(x => !prices.ContainsKey(e.Key) ? 0 : (prices[e.Key] * x)).ToDoubleArray(), false);
|
---|
147 | }
|
---|
148 | return base.Apply();
|
---|
149 | }
|
---|
150 |
|
---|
151 | private Variant ChooseVariant(IEnumerable<Logistic> logistics, double amount, int i, int j, IDictionary<string, DistanceMatrix> distances) {
|
---|
152 | var variants = new List<Variant>();
|
---|
153 | foreach (var l in logistics) {
|
---|
154 | DistanceMatrix distanceMatrix = null;
|
---|
155 | if (!distances.TryGetValue(l.Distances, out distanceMatrix)) {
|
---|
156 | distanceMatrix = GetFromProblemData<DistanceMatrix>(l.Distances + "DistanceMatrix");
|
---|
157 | if (distanceMatrix != null)
|
---|
158 | distances[l.Distances] = distanceMatrix;
|
---|
159 | else
|
---|
160 | throw new InvalidOperationException("Distance Matrix \"" + l.Distances +
|
---|
161 | "\" for logistic action \"" + l.Mode +
|
---|
162 | "\" of product \"" + l.Product + "\" could not be loaded");
|
---|
163 | }
|
---|
164 | if (distanceMatrix == null) continue;
|
---|
165 | var distance = distanceMatrix[i, j];
|
---|
166 | var actualAmount = Math.Min(Math.Max(amount, l.MinAmount), l.MaxAmount);
|
---|
167 | var v = new Variant {
|
---|
168 | Amount = actualAmount,
|
---|
169 | Logistic = l,
|
---|
170 | HandlingCost = GetLogisticActionCost(l.Handling, actualAmount*(1-l.WaterContent), i, j),
|
---|
171 | TransportCost = GetLogisticActionCost(l.Transport, actualAmount*distance*(1-l.WaterContent), i, j),
|
---|
172 | Distance = distance
|
---|
173 | };
|
---|
174 | if (distance > l.MaxDistance)
|
---|
175 | v.Penalty = Penalty(distance, l.MaxDistance, distanceMatrix.Max);
|
---|
176 | variants.Add(v);
|
---|
177 | }
|
---|
178 | variants.Sort();
|
---|
179 | return variants.FirstOrDefault();
|
---|
180 | }
|
---|
181 |
|
---|
182 | public static double Penalty(double distance, double maxAllowedDistance, double maxPossibleDistance) {
|
---|
183 | return (distance - maxAllowedDistance)/maxPossibleDistance;
|
---|
184 | }
|
---|
185 |
|
---|
186 | private double GetLogisticActionCost(LogisticAction a, double amount, int i, int j) {
|
---|
187 | return
|
---|
188 | amount * (
|
---|
189 | a.Investment *(invest[i] + invest[j]) +
|
---|
190 | a.Maintenance *( maint[i] + maint[j]) +
|
---|
191 | a.Fuel *( fuel[i] + fuel[j]) +
|
---|
192 | a.Labor *( labor[i] + labor[j]) +
|
---|
193 | a.Other *( other[i] + other[j]))/2;
|
---|
194 | }
|
---|
195 |
|
---|
196 | private void AddEmissions(Dictionary<string, DoubleArray> totalEmissions, IEnumerable<IValueParameter> emissions, double amount, int i, int j, int length) {
|
---|
197 | foreach (var e in emissions) {
|
---|
198 | DoubleArray values = null;
|
---|
199 | if (!totalEmissions.TryGetValue(e.Name, out values)) {
|
---|
200 | totalEmissions.Add(e.Name, values = new DoubleArray(length));
|
---|
201 | }
|
---|
202 | values[i] += ((DoubleValue)e.Value).Value*amount/2;
|
---|
203 | values[j] += ((DoubleValue)e.Value).Value*amount/2;
|
---|
204 | }
|
---|
205 | }
|
---|
206 | }
|
---|
207 | }
|
---|