source: branches/HeuristicLab.Problems.BioBoost/HeuristicLab.Problems.BioBoost/3.3/Representation/BioBoostCompoundSolution.cs @ 13075

Last change on this file since 13075 was 13075, checked in by gkronber, 7 years ago

#2499: added missing file DictionaryExtensions.cs

File size: 28.6 KB
Line 
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
22using System;
23using System.Collections.Generic;
24using System.Globalization;
25using System.IO;
26using System.Linq;
27using System.Runtime.InteropServices;
28using System.Text;
29using HeuristicLab.BioBoost.Evaluators;
30using HeuristicLab.BioBoost.Operators.Mutation;
31using HeuristicLab.BioBoost.ProblemDescription;
32using HeuristicLab.BioBoost.Utils;
33using HeuristicLab.Common;
34using HeuristicLab.Core;
35using HeuristicLab.Data;
36using HeuristicLab.Parameters;
37using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
38using NetTopologySuite.GeometriesGraph;
39using SharpMap;
40using SharpMap.Data.Providers;
41using SharpMap.Layers;
42using SharpMap.Utilities.Wfs;
43// using YamlDotNet.RepresentationModel.Serialization.NodeTypeResolvers;
44
45namespace 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}
Note: See TracBrowser for help on using the repository browser.