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 System;
|
---|
23 | using System.Collections.Generic;
|
---|
24 | using System.Globalization;
|
---|
25 | using System.IO;
|
---|
26 | using System.Linq;
|
---|
27 | using System.Runtime.InteropServices;
|
---|
28 | using System.Text;
|
---|
29 | using HeuristicLab.BioBoost.Evaluators;
|
---|
30 | using HeuristicLab.BioBoost.Operators.Mutation;
|
---|
31 | using HeuristicLab.BioBoost.ProblemDescription;
|
---|
32 | using HeuristicLab.BioBoost.Utils;
|
---|
33 | using HeuristicLab.Common;
|
---|
34 | using HeuristicLab.Core;
|
---|
35 | using HeuristicLab.Data;
|
---|
36 | using HeuristicLab.Parameters;
|
---|
37 | using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
|
---|
38 | using NetTopologySuite.GeometriesGraph;
|
---|
39 | using SharpMap;
|
---|
40 | using SharpMap.Data.Providers;
|
---|
41 | using SharpMap.Layers;
|
---|
42 | using SharpMap.Utilities.Wfs;
|
---|
43 | // using YamlDotNet.RepresentationModel.Serialization.NodeTypeResolvers;
|
---|
44 |
|
---|
45 | namespace HeuristicLab.BioBoost.Representation {
|
---|
46 | [StorableClass]
|
---|
47 | public sealed class BioBoostCompoundSolution : ParameterizedNamedItem, IStorableContent {
|
---|
48 | public class TransportInfo {
|
---|
49 | public string Product;
|
---|
50 | public int SrcRegionIdx;
|
---|
51 | public int DestRegionIdx;
|
---|
52 | public string SrcRegion;
|
---|
53 | public string DestRegion;
|
---|
54 | public string Mode;
|
---|
55 | public double Distance;
|
---|
56 | public double Amount;
|
---|
57 | }
|
---|
58 |
|
---|
59 | [Storable]
|
---|
60 | public Dictionary<string, IntArray> IntValues;
|
---|
61 |
|
---|
62 | [Storable]
|
---|
63 | public Dictionary<string, DoubleArray> DoubleValues;
|
---|
64 |
|
---|
65 | [Storable]
|
---|
66 | public Dictionary<string, StringArray> StringValues;
|
---|
67 |
|
---|
68 | [Storable]
|
---|
69 | public Dictionary<string, ItemArray<ItemList<IntValue>>> TransportTargets;
|
---|
70 |
|
---|
71 | [Storable]
|
---|
72 | private BioBoostProblemData problemDataRef;
|
---|
73 |
|
---|
74 | public BioBoostProblemData ProblemDataReference {
|
---|
75 | get { return problemDataRef; }
|
---|
76 | set {
|
---|
77 | if (problemDataRef != value) {
|
---|
78 | problemDataRef = value;
|
---|
79 | EvaluateAndUpdateAllResults(); // also raises OnSolutionChanged (re-evaluation is only done when the problem is set by an external entity)
|
---|
80 | }
|
---|
81 | }
|
---|
82 | }
|
---|
83 |
|
---|
84 |
|
---|
85 | #region Parameters
|
---|
86 | public ValueParameter<StringArray> LocationNamesParameter {
|
---|
87 | get { return (ValueParameter<StringArray>)Parameters["LocationNames"]; }
|
---|
88 | }
|
---|
89 |
|
---|
90 | public FixedValueParameter<DoubleValue> QualityParameter {
|
---|
91 | get { return (FixedValueParameter<DoubleValue>)Parameters["Quality"]; }
|
---|
92 | }
|
---|
93 |
|
---|
94 | public ValueParameter<DoubleArray> QualitiesParameter {
|
---|
95 | get { return (ValueParameter<DoubleArray>)Parameters["Qualities"]; }
|
---|
96 | }
|
---|
97 | #endregion
|
---|
98 |
|
---|
99 | #region Parameter Values
|
---|
100 | public StringArray LocationNames {
|
---|
101 | get { return LocationNamesParameter.Value; }
|
---|
102 | private set { LocationNamesParameter.Value = value; }
|
---|
103 | }
|
---|
104 |
|
---|
105 | public double Quality {
|
---|
106 | get { return QualityParameter.Value.Value; }
|
---|
107 | private set { QualityParameter.Value.Value = value; }
|
---|
108 | }
|
---|
109 | public DoubleArray Qualities {
|
---|
110 | get { return QualitiesParameter.Value; }
|
---|
111 | private set { QualitiesParameter.Value = value; }
|
---|
112 | }
|
---|
113 | #endregion
|
---|
114 |
|
---|
115 | private readonly Closure<GeometryFeatureProvider> geometry = new Closure<GeometryFeatureProvider>(Closure<GeometryFeatureProvider>.Encapsulation.Referenced);
|
---|
116 |
|
---|
117 | [Storable]
|
---|
118 | public GeometryFeatureProvider Geometry { get { return geometry.Value; } private set { geometry.Value = value; } }
|
---|
119 |
|
---|
120 | public event EventHandler SolutionChanged;
|
---|
121 |
|
---|
122 | private void OnSolutionChanged() {
|
---|
123 | var handler = SolutionChanged;
|
---|
124 | if (handler != null)
|
---|
125 | handler(this, EventArgs.Empty);
|
---|
126 | }
|
---|
127 |
|
---|
128 | private static bool IsSlimKey(string key) {
|
---|
129 | if (LayerDescriptor.Utilizations.IsSuffixOf(key)) return true;
|
---|
130 | if (LayerDescriptor.ConverterCapacities.IsSuffixOf(key)) return true;
|
---|
131 | if (LayerDescriptor.RelativeCostAtSource.IsSuffixOf(key)) return true;
|
---|
132 | return false;
|
---|
133 | }
|
---|
134 |
|
---|
135 | #region Construction & Cloning
|
---|
136 | [StorableConstructor]
|
---|
137 | private BioBoostCompoundSolution(bool isDeserializing) : base(isDeserializing) { }
|
---|
138 | private BioBoostCompoundSolution(BioBoostCompoundSolution orig, Cloner cloner, bool full = true)
|
---|
139 | : base(orig, cloner) {
|
---|
140 | problemDataRef = orig.problemDataRef; // doesn't re-evaluate the solution
|
---|
141 | Geometry = orig.Geometry;
|
---|
142 | Quality = orig.Quality;
|
---|
143 | Qualities = (DoubleArray)orig.Qualities.Clone();
|
---|
144 | IntValues = orig.IntValues.ToDictionary(kvp => kvp.Key, kvp => (IntArray)kvp.Value.Clone());
|
---|
145 | if (full) {
|
---|
146 | DoubleValues = orig.DoubleValues.ToDictionary(kvp => kvp.Key, kvp => (DoubleArray)kvp.Value.Clone());
|
---|
147 | StringValues = orig.StringValues.ToDictionary(kvp => kvp.Key, kvp => (StringArray)kvp.Value.Clone());
|
---|
148 | if (orig.TransportTargets != null)
|
---|
149 | TransportTargets = orig.TransportTargets.ToDictionary(kvp => kvp.Key, kvp => (ItemArray<ItemList<IntValue>>)kvp.Value.Clone());
|
---|
150 | } else {
|
---|
151 | DoubleValues = orig.DoubleValues.Where(kvp => IsSlimKey(kvp.Key)).ToDictionary(kvp => kvp.Key, kvp => (DoubleArray)kvp.Value.Clone());
|
---|
152 | StringValues = new Dictionary<string, StringArray>();
|
---|
153 | if (orig.TransportTargets != null)
|
---|
154 | TransportTargets = new Dictionary<string, ItemArray<ItemList<IntValue>>>();
|
---|
155 | }
|
---|
156 | }
|
---|
157 | private BioBoostCompoundSolution() {
|
---|
158 | Parameters.Add(new ValueParameter<StringArray>("LocationNames", "The names of the different locations."));
|
---|
159 | Parameters.Add(new FixedValueParameter<DoubleValue>("Quality", "The quality of the solution as produced by the evaluator.", new DoubleValue()));
|
---|
160 | Parameters.Add(new ValueParameter<DoubleArray>("Qualities", "The quality criteria of the solution as produced by the evaluator.", new DoubleArray()));
|
---|
161 | IntValues = new Dictionary<string, IntArray>();
|
---|
162 | DoubleValues = new Dictionary<string, DoubleArray>();
|
---|
163 | StringValues = new Dictionary<string, StringArray>();
|
---|
164 | TransportTargets = new Dictionary<string, ItemArray<ItemList<IntValue>>>();
|
---|
165 | }
|
---|
166 |
|
---|
167 |
|
---|
168 | private bool TryAddValue(IScope scope, string valueName) {
|
---|
169 | IVariable var;
|
---|
170 | if (!scope.Variables.TryGetValue(valueName, out var))
|
---|
171 | return false;
|
---|
172 | var doubleArray = var.Value as DoubleArray;
|
---|
173 | if (doubleArray != null && !DoubleValues.ContainsKey(valueName)) {
|
---|
174 | DoubleValues.Add(valueName, (DoubleArray)doubleArray.Clone());
|
---|
175 | return true;
|
---|
176 | }
|
---|
177 | var intArray = var.Value as IntArray;
|
---|
178 | if (intArray != null && !IntValues.ContainsKey(valueName)) {
|
---|
179 | IntValues.Add(valueName, (IntArray)intArray.Clone());
|
---|
180 | return true;
|
---|
181 | }
|
---|
182 | var stringArray = var.Value as StringArray;
|
---|
183 | if (stringArray != null && !StringValues.ContainsKey(valueName)) {
|
---|
184 | StringValues.Add(valueName, (StringArray)stringArray.Clone());
|
---|
185 | return true;
|
---|
186 | }
|
---|
187 | var itemArray = var.Value as ItemArray<ItemList<IntValue>>;
|
---|
188 | if (itemArray != null && !TransportTargets.ContainsKey(valueName)) {
|
---|
189 | TransportTargets.Add(valueName, (ItemArray<ItemList<IntValue>>)itemArray.Clone());
|
---|
190 | }
|
---|
191 | return false;
|
---|
192 | }
|
---|
193 |
|
---|
194 | private bool TryAddValue(BioBoostProblemData problemData, string valueName) {
|
---|
195 | IParameter param;
|
---|
196 | if (problemData.FeedstockPotentials.TryGetValue(valueName, out param)) {
|
---|
197 | var valueParam = param as IValueParameter;
|
---|
198 | if (valueParam != null) {
|
---|
199 | var doubleArray = valueParam.Value as DoubleArray;
|
---|
200 | if (doubleArray != null && !DoubleValues.ContainsKey(valueName)) {
|
---|
201 | DoubleValues.Add(valueName, (DoubleArray)doubleArray.Clone());
|
---|
202 | return true;
|
---|
203 | }
|
---|
204 | var intArray = valueParam.Value as IntArray;
|
---|
205 | if (intArray != null && !IntValues.ContainsKey(valueName)) {
|
---|
206 | IntValues.Add(valueName, (IntArray)intArray.Clone());
|
---|
207 | return true;
|
---|
208 | }
|
---|
209 | var stringArray = valueParam.Value as StringArray;
|
---|
210 | if (stringArray != null && !StringValues.ContainsKey(valueName)) {
|
---|
211 | StringValues.Add(valueName, (StringArray)stringArray.Clone());
|
---|
212 | return true;
|
---|
213 | }
|
---|
214 | var itemArray = valueParam.Value as ItemArray<ItemList<IntValue>>;
|
---|
215 | if (itemArray != null && !TransportTargets.ContainsKey(valueName)) {
|
---|
216 | TransportTargets.Add(valueName, (ItemArray<ItemList<IntValue>>)itemArray.Clone());
|
---|
217 | return true;
|
---|
218 | }
|
---|
219 | }
|
---|
220 | }
|
---|
221 | return false;
|
---|
222 | }
|
---|
223 |
|
---|
224 | public BioBoostCompoundSolution(IScope scope, BioBoostProblemData problemData)
|
---|
225 | : this() {
|
---|
226 | this.problemDataRef = problemData; // not cloned on purpose
|
---|
227 | Geometry = problemData.Geometry; // TODO: maybe clone geometry once?
|
---|
228 | LocationNames = (StringArray)problemData.LocationNames.Clone();
|
---|
229 | IVariable qualityVariable;
|
---|
230 | // TotalCost is the quality produced by the aggregate evaluator
|
---|
231 | if (scope.Variables.TryGetValue("TotalCost", out qualityVariable)) {
|
---|
232 | Quality = ((DoubleValue)qualityVariable.Value).Value; // TODO: should be read only
|
---|
233 | } else if (scope.Variables.TryGetValue("Quality", out qualityVariable)) {
|
---|
234 | Quality = ((DoubleValue)qualityVariable.Value).Value; // TODO: should be read only
|
---|
235 | }
|
---|
236 | if (scope.Variables.TryGetValue("QualityCriteria", out qualityVariable)) {
|
---|
237 | Qualities = (DoubleArray)qualityVariable.Value.Clone(); // TODO: should be read only
|
---|
238 | }
|
---|
239 | var isUpgradedSolution = scope.Variables.Any(v => v.Name.Contains(LayerDescriptor.AmountsAtSource.FullName));
|
---|
240 | foreach (var p in problemData.Products) {
|
---|
241 | if (!isUpgradedSolution) {
|
---|
242 | if (TryAddValue(scope, LayerDescriptor.TransportTargets.NameWithPrefix(p.Label)))
|
---|
243 | TryAddValue(scope, LayerDescriptor.Utilizations.NameWithPrefix(p.Label));
|
---|
244 | } else {
|
---|
245 | TryAddValue(problemData, LayerDescriptor.PotentialsFromProblemData.NameWithPrefix(p.Label));
|
---|
246 | TryAddValue(scope, p.Label); // emissions
|
---|
247 | foreach (var layer in LayerDescriptor.Layers) { TryAddValue(scope, layer.NameWithPrefix(p.Label)); }
|
---|
248 | }
|
---|
249 | }
|
---|
250 | if (isUpgradedSolution)
|
---|
251 | foreach (var layer in LayerDescriptor.Layers) { TryAddValue(scope, layer.Name); }
|
---|
252 | }
|
---|
253 |
|
---|
254 | public override IDeepCloneable Clone(Cloner cloner) {
|
---|
255 | return new BioBoostCompoundSolution(this, cloner);
|
---|
256 | }
|
---|
257 | public BioBoostCompoundSolution SlimClone() {
|
---|
258 | return new BioBoostCompoundSolution(this, new Cloner(), false);
|
---|
259 | }
|
---|
260 | [StorableHook(HookType.AfterDeserialization)]
|
---|
261 | private void AfterDeserialization() {
|
---|
262 | if (StringValues == null)
|
---|
263 | StringValues = new Dictionary<string, StringArray>();
|
---|
264 | // backwards compatibility
|
---|
265 | if (!Parameters.ContainsKey("Quality"))
|
---|
266 | Parameters.Add(new FixedValueParameter<DoubleValue>("Quality", new DoubleValue(double.NaN))); // TODO: use different quality value to indicate that the value is not known?
|
---|
267 | if (!Parameters.ContainsKey("Qualities"))
|
---|
268 | Parameters.Add(new ValueParameter<DoubleArray>("Qualities", "The quality criteria of the solution as produced by the evaluator.", new DoubleArray()));
|
---|
269 | }
|
---|
270 | #endregion
|
---|
271 |
|
---|
272 | public void UpdateTo(BioBoostCompoundSolution bestSolution) {
|
---|
273 | if (LocationNames.SequenceEqual(bestSolution.LocationNames)) { // TODO: replace with comparison of problem reference
|
---|
274 | Quality = bestSolution.Quality;
|
---|
275 | Qualities = (DoubleArray)bestSolution.Qualities.Clone();
|
---|
276 | IntValues = bestSolution.IntValues.ToDictionary(kvp => kvp.Key, kvp => (IntArray)kvp.Value.Clone());
|
---|
277 | DoubleValues = bestSolution.DoubleValues.ToDictionary(kvp => kvp.Key, kvp => (DoubleArray)kvp.Value.Clone());
|
---|
278 | StringValues = bestSolution.StringValues.ToDictionary(kvp => kvp.Key, kvp => (StringArray)kvp.Value.Clone());
|
---|
279 | TransportTargets = bestSolution.TransportTargets.ToDictionary(kvp => kvp.Key, kvp => (ItemArray<ItemList<IntValue>>)kvp.Value.Clone());
|
---|
280 | OnSolutionChanged();
|
---|
281 | } else {
|
---|
282 | throw new ArgumentException("Invalid solution update, proposed solution has different problem instance.");
|
---|
283 | }
|
---|
284 | }
|
---|
285 |
|
---|
286 | public static DoubleArray FindAmountsTransportedFromSource(IEnumerable<KeyValuePair<string, DoubleArray>> dict, string productName) {
|
---|
287 | if (LayerDescriptor.TransportTargets.IsSuffixOf(productName))
|
---|
288 | productName = LayerDescriptor.TransportTargets.RemoveSuffixFrom(productName);
|
---|
289 | foreach (var kvp in dict) {
|
---|
290 | if (LayerDescriptor.AmountsTransportedFromSource.IsSuffixOf(kvp.Key) &&
|
---|
291 | LayerDescriptor.AmountsTransportedFromSource.RemoveSuffixFrom(kvp.Key) == productName)
|
---|
292 | return kvp.Value;
|
---|
293 | }
|
---|
294 | return null;
|
---|
295 | }
|
---|
296 |
|
---|
297 | #region convenience methods for retrieving values
|
---|
298 |
|
---|
299 | public double TotalFeedstockCosts {
|
---|
300 | get {
|
---|
301 | // first get the list of feedstock (all products which are utilized)
|
---|
302 | var feedStockLabels =
|
---|
303 | DoubleValues.Keys
|
---|
304 | .Where(k => LayerDescriptor.Utilizations.IsSuffixOf(k))
|
---|
305 | .Select(k => LayerDescriptor.Utilizations.RemoveSuffixFrom(k));
|
---|
306 |
|
---|
307 | return feedStockLabels
|
---|
308 | .Select(feedstock => DoubleValues[LayerDescriptor.TotalCostsAtSource.NameWithPrefix(feedstock)].Sum()) // sum over regions
|
---|
309 | .Sum(); // sum over feedstock
|
---|
310 | }
|
---|
311 | }
|
---|
312 |
|
---|
313 | public double TotalTransportCosts {
|
---|
314 | get {
|
---|
315 | // here we add transport and handling costs
|
---|
316 | // keys for all transport costs end with BioBoostProblem.TransportCostsName
|
---|
317 | // keys for all handling costs end with BioBoostProblem.HandlingCostsName
|
---|
318 | return DoubleValues
|
---|
319 | .Where(kvp => LayerDescriptor.TransportCosts.IsSuffixOf(kvp.Key) || LayerDescriptor.HandlingCosts.IsSuffixOf(kvp.Key))
|
---|
320 | .Select(kvp => kvp.Value.Sum()) // sum over all regions
|
---|
321 | .Sum(); // sum over all products
|
---|
322 | }
|
---|
323 | }
|
---|
324 |
|
---|
325 | public double TotalCosts {
|
---|
326 | get {
|
---|
327 | // we add all costs (including all revenues from selling products)
|
---|
328 | // assumes that revenues are also labeled as costs!
|
---|
329 | return DoubleValues
|
---|
330 | .Where(kvp => kvp.Key.EndsWith("Cost") || kvp.Key.EndsWith("Costs"))
|
---|
331 | .Sum(kvp => kvp.Value.Where(v => v > 0).Sum()); // sum over products and regions
|
---|
332 | }
|
---|
333 | }
|
---|
334 |
|
---|
335 | public double TotalTransportFuelAmount {
|
---|
336 | get {
|
---|
337 | // assumes that the values are stored as "TransportFuel" + BioBoostProblem.AmountsAtSourceName
|
---|
338 | return DoubleValues
|
---|
339 | .Where(kvp => kvp.Key == LayerDescriptor.AmountsAtSource.NameWithPrefix("TransportFuel"))
|
---|
340 | .Sum(kvp => kvp.Value.Sum());
|
---|
341 | }
|
---|
342 | }
|
---|
343 |
|
---|
344 | public IEnumerable<TransportInfo> Transports {
|
---|
345 | get {
|
---|
346 | foreach (var pair in TransportVectors) {
|
---|
347 | var prod = pair.Item1;
|
---|
348 | var modes = StringValues[LayerDescriptor.TransportModes.NameWithPrefix(prod)];
|
---|
349 | var distances = DoubleValues[LayerDescriptor.TransportDistance.NameWithPrefix(prod)];
|
---|
350 | var amounts = DoubleValues[LayerDescriptor.AmountsTransportedFromSource.NameWithPrefix(prod)];
|
---|
351 | for (int srcIdx = 0; srcIdx < pair.Item2.Length; srcIdx++) {
|
---|
352 | var destIdx = pair.Item2[srcIdx];
|
---|
353 | if (destIdx == -1) continue;
|
---|
354 | var info = new TransportInfo {
|
---|
355 | Product = prod,
|
---|
356 | SrcRegionIdx = srcIdx,
|
---|
357 | DestRegionIdx = destIdx,
|
---|
358 | SrcRegion = LocationNames[srcIdx],
|
---|
359 | DestRegion = LocationNames[destIdx],
|
---|
360 | Mode = modes[srcIdx],
|
---|
361 | Distance = distances[srcIdx],
|
---|
362 | Amount = amounts[srcIdx]
|
---|
363 | };
|
---|
364 | yield return info;
|
---|
365 | }
|
---|
366 | }
|
---|
367 | }
|
---|
368 | }
|
---|
369 |
|
---|
370 | public IEnumerable<Tuple<string, int[]>> TransportVectors {
|
---|
371 | get {
|
---|
372 | // here we yield one tuple for each product containing the product name and transport target map
|
---|
373 | // we know transport targets are int-arrays and the key ends with 'BioBoostProblem.TransportTargetsName'
|
---|
374 | foreach (var p in IntValues.Where(p => LayerDescriptor.TransportTargets.IsSuffixOf(p.Key))) {
|
---|
375 | var productLabel = LayerDescriptor.TransportTargets.RemoveSuffixFrom(p.Key);
|
---|
376 | yield return Tuple.Create(productLabel, p.Value.ToArray());
|
---|
377 | }
|
---|
378 | }
|
---|
379 | }
|
---|
380 |
|
---|
381 | public IEnumerable<Tuple<string, double[]>> UtilizationVectors {
|
---|
382 | get {
|
---|
383 | // here we yield one tuple for each feedstock containing the feedstock name and the utilization factor
|
---|
384 | // we know feedstock utilizations are double-arrays and the key ends with 'BioBoostProblem.UtilizationsName'
|
---|
385 | foreach (var p in DoubleValues.Where(p => LayerDescriptor.Utilizations.IsSuffixOf(p.Key))) {
|
---|
386 | var productLabel = LayerDescriptor.Utilizations.RemoveSuffixFrom(p.Key);
|
---|
387 | yield return Tuple.Create(productLabel, p.Value.ToArray());
|
---|
388 | }
|
---|
389 | }
|
---|
390 | }
|
---|
391 |
|
---|
392 | public double GetUtilization(string feedstock, string currentRegion) {
|
---|
393 | return DoubleValues[LayerDescriptor.Utilizations.NameWithPrefix(feedstock)][IndexOfLoc(currentRegion)];
|
---|
394 | }
|
---|
395 |
|
---|
396 | public void SetUtilization(string feedstock, string currentRegion, double newUtil) {
|
---|
397 | SetUtilization(feedstock, IndexOfLoc(currentRegion), newUtil);
|
---|
398 | }
|
---|
399 | public void SetUtilization(string feedstock, int regionIdx, double newUtil) {
|
---|
400 | DoubleValues[LayerDescriptor.Utilizations.NameWithPrefix(feedstock)][regionIdx] = newUtil;
|
---|
401 | EvaluateAndUpdateAllResults();
|
---|
402 | }
|
---|
403 |
|
---|
404 | public void SetTransport(string product, int srcIdx, int destIdx) {
|
---|
405 | IntValues[LayerDescriptor.TransportTargets.NameWithPrefix(product)][srcIdx] = destIdx;
|
---|
406 | EvaluateAndUpdateAllResults();
|
---|
407 | }
|
---|
408 | public void SetTransport(string product, string srcNuts, string destNuts) {
|
---|
409 | SetTransport(product, IndexOfLoc(srcNuts), IndexOfLoc(destNuts));
|
---|
410 | }
|
---|
411 |
|
---|
412 | public void EvaluateAndUpdateAllResults() {
|
---|
413 | if (problemDataRef == null) {
|
---|
414 | // for old persisted problems
|
---|
415 | throw new NotSupportedException("The problem data has not been set for this solution. Editing and re-evaluation is not possible");
|
---|
416 | }
|
---|
417 | var evalScope = AggregateEvaluator.Evaluate(
|
---|
418 | problemDataRef,
|
---|
419 | DoubleValues.Where(kvp => LayerDescriptor.Utilizations.IsSuffixOf(kvp.Key)),
|
---|
420 | IntValues.Where(kvp => LayerDescriptor.TransportTargets.IsSuffixOf(kvp.Key))
|
---|
421 | );
|
---|
422 |
|
---|
423 | var newSolution = new BioBoostCompoundSolution(evalScope, problemDataRef);
|
---|
424 | this.UpdateTo(newSolution);
|
---|
425 | }
|
---|
426 |
|
---|
427 | private int IndexOfLoc(string locationName) {
|
---|
428 | for (int i = 0; i < LocationNames.Length; i++) {
|
---|
429 | if (LocationNames[i] == locationName) return i;
|
---|
430 | }
|
---|
431 | return -1;
|
---|
432 | }
|
---|
433 | #endregion
|
---|
434 |
|
---|
435 | public override void CollectParameterValues(IDictionary<string, IItem> values) {
|
---|
436 | base.CollectParameterValues(values);
|
---|
437 |
|
---|
438 | // also produce additional values (virtual parameters) for most important results
|
---|
439 | foreach (var kvp in DoubleValues) {
|
---|
440 | var vFiltered = kvp.Value.Where(x => x > 0);
|
---|
441 | if (!vFiltered.Any()) continue;
|
---|
442 |
|
---|
443 | if (kvp.Key.Contains("Relative") || kvp.Key.Contains("Capacities")) {
|
---|
444 | values.Add(kvp.Key + " (min)", new DoubleValue(vFiltered.Min()));
|
---|
445 | values.Add(kvp.Key + " (max)", new DoubleValue(vFiltered.Max()));
|
---|
446 | values.Add(kvp.Key + " (avg)", new DoubleValue(vFiltered.Average()));
|
---|
447 | } else if (kvp.Key.Contains("Utilization")) {
|
---|
448 | values.Add(kvp.Key + " (count)", new DoubleValue(vFiltered.Count())); // |{u | u > 0}|
|
---|
449 | values.Add(kvp.Key + " (avg)", new DoubleValue(vFiltered.Average()));
|
---|
450 | } else if (!kvp.Key.Contains("Potentials")) {
|
---|
451 | values.Add(kvp.Key + " (sum)", new DoubleValue(kvp.Value.Sum()));
|
---|
452 | values.Add(kvp.Key + " (count)", new DoubleValue(vFiltered.Count()));
|
---|
453 | values.Add(kvp.Key + " (min)", new DoubleValue(vFiltered.Min()));
|
---|
454 | values.Add(kvp.Key + " (max)", new DoubleValue(vFiltered.Max()));
|
---|
455 | values.Add(kvp.Key + " (avg)", new DoubleValue(vFiltered.Average()));
|
---|
456 | }
|
---|
457 | }
|
---|
458 |
|
---|
459 | var finalProductNames = DoubleValues.Keys.Where(k => LayerDescriptor.AmountsAtSource.IsSuffixOf(k))
|
---|
460 | .Select(k => LayerDescriptor.AmountsAtSource.RemoveSuffixFrom(k));
|
---|
461 | finalProductNames = finalProductNames
|
---|
462 | .Where(k => DoubleValues.ContainsKey(LayerDescriptor.DischargeCosts.NameWithPrefix(k)) &&
|
---|
463 | DoubleValues[LayerDescriptor.DischargeCosts.NameWithPrefix(k)].Sum() < 0);
|
---|
464 |
|
---|
465 | foreach (var finalProductName in finalProductNames) {
|
---|
466 | var costs = DoubleValues
|
---|
467 | .Where(kvp => !kvp.Key.StartsWith(finalProductName))
|
---|
468 | .Where(kvp => kvp.Key.EndsWith("Costs") || kvp.Key.EndsWith("Cost"))
|
---|
469 | .Select(kvp => kvp.Value.Sum())
|
---|
470 | .Sum();
|
---|
471 |
|
---|
472 | var amount = DoubleValues[LayerDescriptor.AmountsAtSource.NameWithPrefix(finalProductName)].Sum();
|
---|
473 |
|
---|
474 | values.Add(finalProductName + " net cost [€/a]", new DoubleValue(costs));
|
---|
475 | values.Add(finalProductName + " net cost [€/t]", new DoubleValue(costs / amount));
|
---|
476 | }
|
---|
477 | }
|
---|
478 |
|
---|
479 |
|
---|
480 | public double PerformPostProcessing(TextWriter logger = null) {
|
---|
481 | // DeletePenalizedFeedstockTransportsUtilizations();
|
---|
482 | // DeletePlantsWithScalingFactorBelow(0.8, feedstockName);
|
---|
483 | // DeletePlantsWithScalingFactorBelow(0.8, intermediateName);
|
---|
484 | // DeleteEmptyPlants(intermediateName); // maybe this is covered by previous functions?
|
---|
485 | string feedstockName = null, intermediateName = null;
|
---|
486 | IDictionary<int, int[]> decentralPlantSources = null, centralPlantSources = null;
|
---|
487 | foreach (var transportTargets in IntValues.Where(kvp => LayerDescriptor.TransportTargets.IsSuffixOf(kvp.Key))) {
|
---|
488 | var name = LayerDescriptor.TransportTargets.RemoveSuffixFrom(transportTargets.Key);
|
---|
489 | var utilizationLayerName = LayerDescriptor.Utilizations.NameWithPrefix(name);
|
---|
490 | var utilizationsKvp = DoubleValues.FirstOrDefault(kvp => kvp.Key == utilizationLayerName);
|
---|
491 | if (utilizationsKvp.Key != null) {
|
---|
492 | feedstockName = name;
|
---|
493 | decentralPlantSources = transportTargets.Value.SourceIndices();
|
---|
494 | } else {
|
---|
495 | intermediateName = name;
|
---|
496 | centralPlantSources = transportTargets.Value.SourceIndices();
|
---|
497 | }
|
---|
498 | }
|
---|
499 | if (feedstockName == null || intermediateName == null || decentralPlantSources == null ||
|
---|
500 | centralPlantSources == null)
|
---|
501 | throw new Exception("Post Processing Failed");
|
---|
502 |
|
---|
503 | var utilizations =
|
---|
504 | DoubleValues.First(kvp => kvp.Key == LayerDescriptor.Utilizations.NameWithPrefix(feedstockName)).Value;
|
---|
505 | var feedstockUsageBefore =
|
---|
506 | utilizations.Zip(
|
---|
507 | (DoubleArray)((IValueParameter)problemDataRef.FeedstockPotentials[feedstockName + "Potentials"]).Value,
|
---|
508 | (u, p) => u * p).Sum();
|
---|
509 |
|
---|
510 | // remove penalized transports
|
---|
511 | var feedstockTransportPenaltyLayer =
|
---|
512 | DoubleValues.First(
|
---|
513 | kvp => kvp.Key == LayerDescriptor.ExceedingTransportDistancePenalty.NameWithPrefix(feedstockName)).Value;
|
---|
514 | var removableSuppliers = feedstockTransportPenaltyLayer
|
---|
515 | .Select((penalty, idx) => new { penalty, idx })
|
---|
516 | .Where(pi => pi.penalty > 0)
|
---|
517 | .Select(pi => new List<int> { pi.idx }).ToList();
|
---|
518 |
|
---|
519 | // remove undersize central plants
|
---|
520 | var centralConversion = ProblemDataReference.Conversions[feedstockName].First();
|
---|
521 | var minCentralScalingFactor = centralConversion.MinCapacity / centralConversion.DesignCapacity;
|
---|
522 | var centralScalingFactors =
|
---|
523 | DoubleValues.First(kvp => kvp.Key == LayerDescriptor.ScalingFactors.NameWithPrefix(intermediateName)).Value;
|
---|
524 | removableSuppliers.AddRange(centralScalingFactors
|
---|
525 | .Select((factor, idx) => new { factor, idx })
|
---|
526 | .Where(fi => fi.factor < minCentralScalingFactor)
|
---|
527 | .Select(fi => GetSuppliers(new[] { centralPlantSources, decentralPlantSources }, fi.idx).ToList())
|
---|
528 | .Where(l => l.Count > 0));
|
---|
529 |
|
---|
530 | // remove undersize decentral plants
|
---|
531 | var decentralConversion = ProblemDataReference.Conversions[intermediateName].First();
|
---|
532 | var minDeCentralScalingFactor = decentralConversion.MinCapacity / decentralConversion.DesignCapacity;
|
---|
533 | var decentralScalingFactors =
|
---|
534 | DoubleValues.First(kvp => kvp.Key == LayerDescriptor.ScalingFactors.NameWithPrefix(feedstockName)).Value;
|
---|
535 | removableSuppliers.AddRange(decentralScalingFactors
|
---|
536 | .Select((factor, idx) => new { factor, idx })
|
---|
537 | .Where(fi => fi.factor < minDeCentralScalingFactor)
|
---|
538 | .Select(fi => GetSuppliers(new[] { decentralPlantSources }, fi.idx).ToList())
|
---|
539 | .Where(l => l.Count > 0));
|
---|
540 |
|
---|
541 | EvaluateAndUpdateAllResults();
|
---|
542 | if (logger != null) logger.Write("Pruning plants (q={0})...", Quality);
|
---|
543 | int successes = 0;
|
---|
544 | int k = 0, lastK = 0;
|
---|
545 | foreach (var variant in removableSuppliers) {
|
---|
546 | var oldUtilizations = (DoubleArray)utilizations.Clone();
|
---|
547 | var oldQuality = Quality;
|
---|
548 | foreach (int j in variant) utilizations[j] = 0;
|
---|
549 | DoubleValues.Remove(LayerDescriptor.Utilizations.NameWithPrefix(feedstockName));
|
---|
550 | DoubleValues.Add(LayerDescriptor.Utilizations.NameWithPrefix(feedstockName), utilizations);
|
---|
551 | EvaluateAndUpdateAllResults();
|
---|
552 | if (Quality < oldQuality) {
|
---|
553 | for (int i = 0; i < utilizations.Length; i++) utilizations[i] = oldUtilizations[i];
|
---|
554 | DoubleValues.Remove(LayerDescriptor.Utilizations.NameWithPrefix(feedstockName));
|
---|
555 | DoubleValues.Add(LayerDescriptor.Utilizations.NameWithPrefix(feedstockName), utilizations);
|
---|
556 | } else {
|
---|
557 | successes++;
|
---|
558 | }
|
---|
559 | if (k++ > lastK + removableSuppliers.Count / 20 && logger != null) {
|
---|
560 | logger.Write("{0}%...", k * 100 / removableSuppliers.Count);
|
---|
561 | lastK = k;
|
---|
562 | }
|
---|
563 | }
|
---|
564 | EvaluateAndUpdateAllResults();
|
---|
565 | if (logger != null) logger.WriteLine("... pruned {0} of {1} variants (q={2})", successes, removableSuppliers.Count, Quality);
|
---|
566 |
|
---|
567 | var feedstockUsageAfter =
|
---|
568 | utilizations.Zip((DoubleArray)((IValueParameter)problemDataRef.FeedstockPotentials[feedstockName + "Potentials"]).Value,
|
---|
569 | (u, p) => u * p).Sum();
|
---|
570 | var utilizationFactor = feedstockUsageAfter / feedstockUsageBefore;
|
---|
571 | return utilizationFactor;
|
---|
572 | }
|
---|
573 |
|
---|
574 | public static IEnumerable<int> GetSuppliers(IEnumerable<IDictionary<int, int[]>> suppliers, int i) {
|
---|
575 | var lists = suppliers.ToList();
|
---|
576 | switch (lists.Count) {
|
---|
577 | case 0: {
|
---|
578 | yield break;
|
---|
579 | }
|
---|
580 | case 1: {
|
---|
581 | foreach (var j in lists[0].ElementAtOrDefault(i, new int[0]))
|
---|
582 | yield return j;
|
---|
583 | break;
|
---|
584 | }
|
---|
585 | default: {
|
---|
586 | foreach (
|
---|
587 | var x in lists[0].ApplyAt(i,
|
---|
588 | v => v.Select(j => GetSuppliers(lists.Skip(1), j))
|
---|
589 | .Aggregate(Enumerable.Empty<int>(), (l1, l2) => l1.Concat(l2)),
|
---|
590 | () => new int[0]))
|
---|
591 | yield return x;
|
---|
592 | break;
|
---|
593 | }
|
---|
594 | }
|
---|
595 | }
|
---|
596 |
|
---|
597 | #region unused
|
---|
598 | public void CreateJsonSolution() {
|
---|
599 | var builder = new StringBuilder();
|
---|
600 | builder.Append("{ \"type\": \"FeatureCollection\", \n\"features\": [");
|
---|
601 | for (int i = 0; i < LocationNames.Length; i++) {
|
---|
602 | if (i > 0) builder.Append(",");
|
---|
603 | builder.Append("\n{ \"type\": \"Feature\", \"properties\": { \"NUTS_ID\": \"");
|
---|
604 | builder.Append(LocationNames[i]);
|
---|
605 | builder.Append("\"");
|
---|
606 | foreach (var kvp in IntValues) {
|
---|
607 | var target = LocationNames[i];
|
---|
608 | if (kvp.Value[i] >= 0) {
|
---|
609 | target = LocationNames[kvp.Value[i]];
|
---|
610 | }
|
---|
611 | builder.Append(",\n \"");
|
---|
612 | builder.Append(kvp.Key);
|
---|
613 | builder.Append("\": \"");
|
---|
614 | builder.Append(target);
|
---|
615 | builder.Append("\"");
|
---|
616 | }
|
---|
617 | foreach (var kvp in DoubleValues) {
|
---|
618 | builder.Append(",\n \"");
|
---|
619 | builder.Append(kvp.Key);
|
---|
620 | builder.Append("\": ");
|
---|
621 | builder.Append(kvp.Value[i].ToString("R", CultureInfo.InvariantCulture));
|
---|
622 | }
|
---|
623 | builder.Append("} }");
|
---|
624 | }
|
---|
625 | builder.Append("\n]\n}");
|
---|
626 |
|
---|
627 | File.WriteAllText(@"c:\temp\test.json", builder.ToString());
|
---|
628 | }
|
---|
629 | #endregion
|
---|
630 |
|
---|
631 | public string Filename { get; set; }
|
---|
632 | }
|
---|
633 | }
|
---|