source: addons/HeuristicLab.Problems.BioBoost/HeuristicLab.Problems.BioBoost/3.3/ProblemDescription/BioBoostProblemData.cs @ 16575

Last change on this file since 16575 was 16575, checked in by gkronber, 3 years ago

#2520: changed HeuristicLab.BioBoost addon to compile with new HL.Persistence

File size: 32.7 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.Data;
23using System.Diagnostics;
24using System.Text.RegularExpressions;
25using HeuristicLab.BioBoost.Data;
26using HeuristicLab.BioBoost.Evaluators;
27using HeuristicLab.BioBoost.Persistence;
28using HeuristicLab.BioBoost.Utils;
29using HeuristicLab.Common;
30using HeuristicLab.Core;
31using HeuristicLab.Data;
32using HeuristicLab.Parameters;
33using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
34using SharpMap.Data;
35using SharpMap.Data.Providers;
36using System;
37using System.Collections.Generic;
38using System.Globalization;
39using System.IO;
40using System.Linq;
41using GeoAPI.Geometries;
42using HeuristicLab.BioBoost.Representation;
43using HEAL.Attic;
44
45namespace HeuristicLab.BioBoost.ProblemDescription {
46
47  [StorableType("1A6A5B9A-0E82-4873-9F8F-87ABA1D8F125")]
48  public class BioBoostProblemData : ParameterizedNamedItem {
49
50    #region Events
51    public event EventHandler GeometryChanged;
52    protected void OnGeometryChanged() {
53      var h = GeometryChanged;
54      if (h != null) h(this, EventArgs.Empty);
55    }
56    #endregion
57
58    public new ParameterCollection Parameters {
59      get { return base.Parameters; }
60    }
61
62    // this prevents geometries from being checked by tree traversal
63    private readonly Closure<GeometryFeatureProvider> geometry = new Closure<GeometryFeatureProvider>(Closure<GeometryFeatureProvider>.Encapsulation.Referenced);
64
65    [Storable]
66    public GeometryFeatureProvider Geometry {
67      get { return geometry.Value; }
68      private set { geometry.Value = value; }
69    }
70
71    #region IStorableContent Members
72    public string Filename { get; set; }
73    #endregion
74
75    #region Parameters
76    public ValueParameter<StringArray> LocationNamesParameter {
77      get { return (ValueParameter<StringArray>)Parameters["LocationNames"]; }
78    }
79    public ValueParameter<NeighborhoodTable> NeighborsParameter {
80      get { return (ValueParameter<NeighborhoodTable>)Parameters["Neighbors"]; }
81    }
82    private ValueParameter<ItemCollection<DataItem>> DataItemsParameter {
83      get { return (ValueParameter<ItemCollection<DataItem>>)Parameters["DataItems"]; }
84    }
85    public ValueParameter<ParameterCollection> FeedstockPotentialsParameter {
86      get { return (ValueParameter<ParameterCollection>)Parameters["FeedstockPotentials"]; }
87    }
88    public ValueParameter<DoubleArray> InvestmentCostFactorsParameter {
89      get { return (ValueParameter<DoubleArray>)Parameters["InvestmentCostFactors"]; }
90    }
91    public ValueParameter<DoubleArray> MaintenanceCostFactorsParameter {
92      get { return (ValueParameter<DoubleArray>)Parameters["MaintenanceCostFactors"]; }
93    }
94    public ValueParameter<DoubleArray> FuelCostFactorsParameter {
95      get { return (ValueParameter<DoubleArray>)Parameters["FuelCostFactors"]; }
96    }
97    public ValueParameter<DoubleArray> LaborCostFactorsParameter {
98      get { return (ValueParameter<DoubleArray>)Parameters["LaborCostFactors"]; }
99    }
100    public ValueParameter<DoubleArray> OtherCostFactorsParameter {
101      get { return (ValueParameter<DoubleArray>)Parameters["OtherCostFactors"]; }
102    }
103    public ValueParameter<CheckedItemList<StringValue>> UtilizationsParameter {
104      get { return (ValueParameter<CheckedItemList<StringValue>>)Parameters["Utilizations"]; }
105    }
106    public ValueParameter<CheckedItemList<StringValue>> TransportTargetsParameter {
107      get { return (ValueParameter<CheckedItemList<StringValue>>)Parameters["TransportTargets"]; }
108    }
109    public ValueParameter<CheckedItemList<StringValue>> FinalProductsParameter {
110      get { return (ValueParameter<CheckedItemList<StringValue>>)Parameters["FinalProducts"]; }
111    }
112    public IValueParameter<DoubleMatrix> SplitRegionDistanceFactorsParameter {
113      get { return (IValueParameter<DoubleMatrix>) Parameters["SplitRegionDistanceFactors"]; }
114    }
115    public IValueParameter<StringMatrix> ProductLinksParameter { get { return (IValueParameter<StringMatrix>) Parameters["ProductLinks"]; } }
116    #endregion
117
118    #region Parameter Values
119    public StringArray LocationNames {
120      get { return LocationNamesParameter.Value; }
121      set { LocationNamesParameter.Value = value; }
122    }
123    public NeighborhoodTable Neighbors {
124      get { return NeighborsParameter.Value; }
125      set { NeighborsParameter.Value = value; }
126    }
127    public ItemCollection<DataItem> DataItems {
128      get { return DataItemsParameter.Value; }
129      private set {
130        DataItemsParameter.Value = value;
131        Products = value.OfType<Product>().ToList();
132        Conversions = value.OfType<Conversion>().ToLookup(c => c.Feedstock, c => c);
133        Logistics = value.OfType<Logistic>().ToLookup(l => l.Product, l => l);
134      }
135    }
136    public ParameterCollection FeedstockPotentials {
137      get { return FeedstockPotentialsParameter.Value; }
138      set { FeedstockPotentialsParameter.Value = value; }
139    }
140    public DoubleArray InvestmentCostFactors {
141      get { return InvestmentCostFactorsParameter.Value; }
142      set { InvestmentCostFactorsParameter.Value = value; }
143    }
144    public DoubleArray MaintenanceCostFactors {
145      get { return MaintenanceCostFactorsParameter.Value; }
146      set { MaintenanceCostFactorsParameter.Value = value; }
147    }
148    public DoubleArray FuelCostFactors {
149      get { return FuelCostFactorsParameter.Value; }
150      set { FuelCostFactorsParameter.Value = value; }
151    }
152    public DoubleArray LaborCostFactors {
153      get { return LaborCostFactorsParameter.Value; }
154      set { LaborCostFactorsParameter.Value = value; }
155    }
156    public DoubleArray OtherCostFactors {
157      get { return OtherCostFactorsParameter.Value; }
158      set { OtherCostFactorsParameter.Value = value; }
159    }
160    public CheckedItemList<StringValue> Utilizations {
161      get { return UtilizationsParameter.Value; }
162      set { UtilizationsParameter.Value = value; }
163    }
164    public CheckedItemList<StringValue> TransportTargets {
165      get { return TransportTargetsParameter.Value; }
166      set { TransportTargetsParameter.Value = value; }
167    }
168    public CheckedItemList<StringValue> FinalProducts {
169      get { return FinalProductsParameter.Value; }
170      set { FinalProductsParameter.Value = value; }
171    }
172    public DoubleMatrix SplitRegionDistanceFactors {
173      get { return SplitRegionDistanceFactorsParameter.Value; }
174      set { SplitRegionDistanceFactorsParameter.Value = value; }
175    }
176    public IEnumerable<Product> Products { get; private set; }
177    public ILookup<string, Conversion> Conversions { get; private set; }
178    public ILookup<string, Logistic> Logistics { get; private set; }
179    public StringMatrix ProductLinks {
180      get { return ProductLinksParameter.Value; }
181      set { ProductLinksParameter.Value = value; }
182    }
183    #endregion
184
185    #region Construction & Cloning
186
187    [StorableConstructor]
188    protected BioBoostProblemData(StorableConstructorFlag _) : base(_) { }
189    protected BioBoostProblemData(BioBoostProblemData orig, Cloner cloner)
190      : base(orig, cloner) {
191      Geometry = orig.Geometry;
192      DataItems = DataItems; // invoke setter to populate Products, Conversions and Logistics
193    }
194    public BioBoostProblemData() {
195      Parameters.Add(new ValueParameter<StringArray>("LocationNames", "The names of the individual locations."));
196      Parameters.Add(new ValueParameter<NeighborhoodTable>("Neighbors", "The table of neighborhood degrees."));
197      Parameters.Add(new ValueParameter<ItemCollection<DataItem>>("DataItems", "Data descriptors, e.g. for products or logistics and conversion processes."));
198      Parameters.Add(new ValueParameter<ParameterCollection>("FeedstockPotentials", "Imported feedstock potentials."));
199      Parameters.Add(new ValueParameter<DoubleArray>("InvestmentCostFactors", "The scaling factors for investment costs."));
200      Parameters.Add(new ValueParameter<DoubleArray>("MaintenanceCostFactors", "The scaling factors for maintenance costs."));
201      Parameters.Add(new ValueParameter<DoubleArray>("FuelCostFactors", "The scaling factors for fuel costs."));
202      Parameters.Add(new ValueParameter<DoubleArray>("LaborCostFactors", "The scaling factors for labor costs."));
203      Parameters.Add(new ValueParameter<DoubleArray>("OtherCostFactors", "The scaling factors for other costs."));
204      Parameters.Add(new ValueParameter<CheckedItemList<StringValue>>("Utilizations", "The possible utilization vectors."));
205      Parameters.Add(new ValueParameter<CheckedItemList<StringValue>>("TransportTargets", "The possible transport target vectors."));
206      Parameters.Add(new ValueParameter<CheckedItemList<StringValue>>("FinalProducts", "The list of possible final products used for quality evaluation."));
207      Parameters.Add(new ValueParameter<DoubleMatrix>("SplitRegionDistanceFactors", "Describes the reduction in distance if the region would be split into different numbers of smaller regions"));
208      Parameters.Add(new ValueParameter<StringMatrix>("ProductLinks", "List of tuples of product names that are linked in the logistic chain"));
209    }
210    public override IDeepCloneable Clone(Cloner cloner) {
211      return new BioBoostProblemData(this, cloner);
212    }
213    [StorableHook(HookType.AfterDeserialization)]
214    public void AfterDeserialization() {
215      DataItems = DataItems; // invoke setter to populate Products, Conversions and Logistics
216      TryUpgradeStringArrayToCheckedItemList("Utilizations");
217      TryUpgradeStringArrayToCheckedItemList("TransportTargets");
218      if (!Parameters.ContainsKey("SplitRegionDistanceFactors"))
219        Parameters.Add(new ValueParameter<DoubleMatrix>("SplitRegionDistanceFactors", "Describes the reduction in distance if the region would be split into different numbers of smaller regions"));
220      if (!Parameters.ContainsKey("ProductLinks"))
221        Parameters.Add(new ValueParameter<StringMatrix>("ProductLinks", "List of tuples of product names that are linked in the logistic chain"));
222      if (!Parameters.ContainsKey("FinalProducts"))
223        Parameters.Add(new ValueParameter<CheckedItemList<StringValue>>("FinalProducts", "The list of possible final products used for quality evaluation."));
224    }
225
226    private void TryUpgradeStringArrayToCheckedItemList(string parameterName) {
227      var oldParameter = Parameters[parameterName] as ValueParameter<StringArray>;
228      if (oldParameter != null) {
229        var newParameter = new ValueParameter<CheckedItemList<StringValue>>(oldParameter.Name, oldParameter.Description);
230        Parameters.Remove(parameterName);
231        foreach (var s in oldParameter.Value)
232          newParameter.Value.Add(new StringValue(s), true);
233        Parameters.Add(newParameter);
234      }
235    }
236    #endregion
237
238    public override void CollectParameterValues(IDictionary<string, IItem> values) {
239      base.CollectParameterValues(values);
240      foreach (var di in DataItems) {
241        di.CollectParameterValues(values);
242      }
243    }
244
245    #region Data Parsing & Import
246    private void AddParameter<TValue>(string name, TValue value) where TValue : class, IItem {
247      Parameters.Remove(name);
248      Parameters.Add(new ValueParameter<TValue>(name, value));
249    }
250
251    public void LoadFeedstockPotentials(string filename) {
252      var feedstockPotentials = new List<Tuple<string, List<double>>>();
253      var regionNames = new List<string>();
254      bool headerDone = false;
255      foreach (var line in File.ReadLines(filename)) {
256        var fields = line.Split(';');
257        if (!headerDone) {
258          feedstockPotentials.AddRange(
259            fields.Skip(1).Select(feedstockName => Tuple.Create(feedstockName, new List<double>())));
260          headerDone = true;
261        } else {
262          regionNames.Add(fields[0]);
263          for (int i = 1; i < fields.Length; i++) {
264            double value;
265            double.TryParse(fields[i], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out value);
266            feedstockPotentials[i - 1].Item2.Add(value);
267          }
268        }
269      }
270
271      if (LocationNames.Length > 0) {
272        var newRegionNames = new HashSet<string>(regionNames);
273        newRegionNames.ExceptWith(LocationNames);
274        var indexTable = LocationNames.Concat(newRegionNames)
275          .Select((n, i) => new { idx = i, name = n })
276          .ToDictionary(p => p.name, p => p.idx);
277        if (newRegionNames.Count > 0)
278          LocationNames = new StringArray(LocationNames.Concat(newRegionNames).ToArray());
279        foreach (var fp in feedstockPotentials) {
280          var potentials = new DoubleArray(indexTable.Count);
281          var potentialName = LayerDescriptor.PotentialsFromProblemData.NameWithPrefix(fp.Item1);
282          for (int i = 0; i < regionNames.Count; i++) {
283            potentials[indexTable[regionNames[i]]] = fp.Item2[i];
284          }
285          FeedstockPotentials.Remove(potentialName);
286          FeedstockPotentials.Add(new ValueParameter<DoubleArray>(potentialName, potentials));
287        }
288      } else {
289        LocationNames = new StringArray(regionNames.ToArray());
290        foreach (var fp in feedstockPotentials) {
291          FeedstockPotentials.Add(new ValueParameter<DoubleArray>(LayerDescriptor.PotentialsFromProblemData.NameWithPrefix(fp.Item1), new DoubleArray(fp.Item2.ToArray())));
292        }
293      }
294
295
296      Configure();
297    }
298
299    public void LoadShapeFile(string filename) {
300      Geometry = ShapeFileLoader.LoadShapeFile(filename);
301      OnGeometryChanged();
302    }
303
304    public void AddDoubleArrayParameters(IEnumerable<string> fields, IList<string> names, string prefix) {
305      int i = 0;
306      foreach (var f in fields) {
307        if (i >= names.Count) break;
308        var subfields = f.Split('/');
309        var values = new List<double>();
310        foreach (var sf in subfields) {
311          double d;
312          Double.TryParse(sf, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out d);
313          values.Add(d);
314        }
315        AddParameter(names[i] + prefix, new DoubleArray(values.ToArray()));
316        i++;
317      }
318    }
319
320    public void AddDoubleValueParameters(IEnumerable<string> fields, IList<string> names, string prefix) {
321      int i = 0;
322      foreach (var f in fields) {
323        if (i >= names.Count) break;
324        double d;
325        Double.TryParse(f, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out d);
326        AddParameter(names[i] + prefix, new DoubleValue(d));
327        i++;
328      }
329    }
330
331    public void AddIntValueParameters(IEnumerable<string> fields, IList<string> names, string prefix) {
332      int i = 0;
333      foreach (var f in fields) {
334        if (i >= names.Count) break;
335        int n;
336        Int32.TryParse(f, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out n);
337        AddParameter(names[i] + prefix, new IntValue(n));
338        i++;
339      }
340    }
341
342    public void LoadDistanceMatrix(string filename) {
343      var newLocationNames = new HashSet<string>();
344      var tuples = new List<Tuple<string, string, double>>();
345      var matrixName = "";
346      foreach (var line in File.ReadLines(filename)) {
347        var l = line.Trim();
348        if (l.StartsWith("#")) {
349          matrixName = l.TrimStart("# ".ToCharArray());
350        } else {
351          var fields = l.Split(';').ToList();
352          newLocationNames.Add(fields[0]);
353          newLocationNames.Add(fields[1]);
354          double d;
355          Double.TryParse(fields[2], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out d);
356          tuples.Add(Tuple.Create(fields[0], fields[1], d));
357        }
358      }
359      newLocationNames.ExceptWith(LocationNames);
360      if (newLocationNames.Count > 0) {
361        LocationNames = new StringArray(LocationNames.Concat(newLocationNames).ToArray());
362        foreach (ValueParameter<DoubleArray> p in FeedstockPotentials) {
363          p.Value = new DoubleArray(p.Value.Concat(new double[newLocationNames.Count]).ToArray());
364        }
365      }
366      var dm = new DistanceMatrix();
367      dm.Load(tuples, LocationNames, -1);
368      matrixName += "DistanceMatrix";
369      if (Parameters.ContainsKey(matrixName))
370        Parameters.Remove(matrixName);
371      Parameters.Add(new ValueParameter<DistanceMatrix>(matrixName, dm));
372      Configure();
373    }
374
375    public void LoadSplitRegionDistanceFactors(string filename) {
376      // format: regionName;factor2;factor3;factor4;...
377      var newLocationNames = new HashSet<string>();
378      var splits = new Dictionary<string, List<double>>();
379      int maxN = 0;
380      foreach (var line in File.ReadLines(filename)) {
381        var l = line.Trim();
382        if (l.ToUpper().StartsWith("NUTS")) continue;
383        var fields = line.Split(';').ToList();
384        newLocationNames.Add(fields[0]);
385        splits.Add(fields[0], fields.Skip(1).Select(s => ParseDouble(s, 1)).ToList());
386        maxN = Math.Max(fields.Count, maxN);
387      }
388      newLocationNames.ExceptWith(LocationNames);
389      if (newLocationNames.Count > 0) {
390        LocationNames = new StringArray(LocationNames.Concat(newLocationNames).ToArray());
391        SplitRegionDistanceFactors = new DoubleMatrix(LocationNames.Length, maxN - 1,
392          Enumerable.Range(2, maxN - 1).Select(d => d.ToString(NumberFormatInfo.InvariantInfo)),
393          LocationNames);
394        for (int row = 0; row < SplitRegionDistanceFactors.Rows; row++) {
395          List<double> values;
396          if (splits.TryGetValue(LocationNames[row], out values)) {
397            for (int col = 0; col < values.Count; col++) {
398              SplitRegionDistanceFactors[row, col] = values[col];
399            }
400          }
401        }
402      }
403    }
404
405    public static double ParseDouble(string s, double defaultValue = 0) {
406      double d;
407      if (Double.TryParse(s, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out d))
408        return d;
409      return defaultValue;
410    }
411
412    public void RemoveRegions(Regex regex) {
413      RemoveRegions(LocationNames.Where(n => regex.IsMatch(n)));
414    }
415
416    public void KeepRegions(Regex regex) {
417      RemoveRegions(LocationNames.Where(n => !regex.IsMatch(n)));
418    }
419
420    public void RemoveRegions(IEnumerable<string> regionNames) {
421      var regionNameSet = new HashSet<string>(regionNames);
422      var removedIndices =
423        LocationNames.Select((r, i) => new { r, i })
424                     .Where(ri => regionNameSet.Contains(ri.r))
425                     .Select(ri => ri.i)
426                     .ToList();
427      // remove region names
428      int nLocationNamesBefore = LocationNames.Length;
429      LocationNames = new StringArray(LocationNames.RemoveAtIndices(removedIndices).ToArray());
430      int nLocationNamesAfter = LocationNames.Length;
431      Debug.Assert(nLocationNamesBefore - removedIndices.Count == nLocationNamesAfter);
432      // remove region potentials
433      foreach (var potentialName in FeedstockPotentials.Select(p => p.Name).ToList()) {
434        var potentials = FeedstockPotentials[potentialName];
435        FeedstockPotentials.Remove(potentialName);
436        FeedstockPotentials.Add(
437          new ValueParameter<DoubleArray>(
438            potentialName,
439            new DoubleArray(((ValueParameter<DoubleArray>)potentials).Value.RemoveAtIndices(removedIndices).ToArray())));
440      }
441      // remove region distances
442      foreach (var distanceMatrixParam in Parameters.Where(p => p.Name.EndsWith("DistanceMatrix"))) {
443        var valueParam = distanceMatrixParam as IValueParameter;
444        if (valueParam != null && typeof(DistanceMatrix).IsAssignableFrom(valueParam.DataType)) {
445          var matrix = (DistanceMatrix)valueParam.Value;
446          valueParam.Value = matrix.CopyAndRemoveIndices(removedIndices);
447        }
448      }
449      // remove region neighbors
450      if (Neighbors != null)
451        Neighbors = Neighbors.CopyAndRemoveIndices(removedIndices);
452      // remove shapes & and create new geometry (copy on write)
453      var locationNameset = new HashSet<string>(LocationNames);
454      var keptRows = Geometry.Features.Cast<FeatureDataRow>()
455        .Where(row => locationNameset.Contains(row["NUTS_ID"]))
456        .ToList();
457      var newFeatures = new FeatureDataTable();
458      foreach (DataColumn column in Geometry.Features.Columns) {
459        newFeatures.Columns.Add(column.ColumnName, column.DataType);
460      }
461      foreach (FeatureDataRow row in keptRows) {
462        var newRow = (FeatureDataRow)newFeatures.LoadDataRow(row.ItemArray, LoadOption.OverwriteChanges);
463        newRow.Geometry = (IGeometry) row.Geometry.Clone();
464      }
465      newFeatures.AcceptChanges();
466      Geometry = new GeometryFeatureProvider(newFeatures);
467      OnGeometryChanged();
468      Configure();
469    }
470
471    public void LoadNeighbors(string filename) {
472      var locationNames = LocationNames.Select((n, i) => new { n, i }).ToDictionary(p => p.n, p => p.i);
473      var neighbors = new NeighborhoodTable();
474      foreach (var line in File.ReadLines(filename)) {
475        var fields = line.Split(';');
476        int id1, id2, degree;
477        if (locationNames.TryGetValue(fields[0], out id1) &&
478            locationNames.TryGetValue(fields[1], out id2) &&
479            Int32.TryParse(fields[2], out degree))
480          neighbors.AddEntry(id1, id2, degree);
481      }
482      neighbors.Check();
483      Neighbors = neighbors;
484    }
485
486    public void LoadDescriptors(string filename) {
487      var newItems = new ItemCollection<DataItem>(DataItem.ParseFile(filename));
488      if (DataItems != null) {
489        foreach (var oldItem in DataItems) {
490          if (!newItems.Any(i => i.IsEquivalentTo(oldItem)))
491            newItems.Add(oldItem);
492        }
493      }
494      DataItems = newItems;
495      Configure();
496    }
497
498    private void Configure() {
499      DistributeCostFactors();
500      DiscoverSolutionSpace();
501    }
502
503    private void DistributeCostFactors() {
504      int length = LocationNames.Length;
505      InvestmentCostFactors = new DoubleArray(length);
506      MaintenanceCostFactors = new DoubleArray(length);
507      FuelCostFactors = new DoubleArray(length);
508      LaborCostFactors = new DoubleArray(length);
509      OtherCostFactors = new DoubleArray(length);
510      var costFactorDescriptors = DataItems.OfType<CostFactors>().FirstOrDefault();
511      if (costFactorDescriptors == null) return;
512      for (int i = 0; i < length; i++) {
513        var countryCode = LocationNames[i].Substring(0, 2);
514        InvestmentCostFactors[i] = GetDoubleValueValue(costFactorDescriptors.Investment, countryCode, 1);
515        MaintenanceCostFactors[i] = GetDoubleValueValue(costFactorDescriptors.Maintenance, countryCode, 1);
516        FuelCostFactors[i] = GetDoubleValueValue(costFactorDescriptors.Fuel, countryCode, 1);
517        LaborCostFactors[i] = GetDoubleValueValue(costFactorDescriptors.Labor, countryCode, 1);
518        OtherCostFactors[i] = GetDoubleValueValue(costFactorDescriptors.Other, countryCode, 1);
519      }
520    }
521
522    private void DiscoverSolutionSpace() {
523      if (DataItems == null || DataItems.Count == 0) return;
524      var products = Products;
525      var logistics = Logistics;
526      var conversions = DataItems.OfType<Conversion>().ToList();
527
528      // perform conceptual logistic simulation
529      var usableProducts = products
530        .Where(product => FeedstockPotentials.ContainsKey(LayerDescriptor.PotentialsFromProblemData.NameWithPrefix(product.Label)))
531        .ToList();
532      var transportableProducts = usableProducts
533        .Where(p => logistics.Contains(p.Label))
534        .ToList();
535      var feasibleDecentralConversions = conversions
536        .Where(c => transportableProducts.Any(p => p.Label == c.Feedstock))
537        .ToList();
538      var possibleIntermediates = feasibleDecentralConversions
539        .Aggregate(Enumerable.Empty<string>(),
540                   (intermediates, conv) => intermediates.Concat(conv.Products.Select(p => p.Name)))
541        .ToList();
542      var transportableIntermediates = possibleIntermediates
543        .Where(logistics.Contains) // && intermediate exists as product?
544        .ToList();
545      var feasibleCentralConversions = conversions
546        .Where(c => transportableIntermediates.Any(i => i == c.Feedstock))
547        .ToList();
548      var finalProductLists = feasibleCentralConversions
549        .Select(c => c.Products.Where(p => { var v = (p.Value as DoubleValue); return v != null && v.Value > 0; }).Select(p => p.Name));
550      var finalProductList = finalProductLists.Aggregate(Enumerable.Empty<string>(), (x, y) => x.Concat(y));
551      var uniqueFinalProducts = finalProductList.GroupBy(p => p).Select(p => p.Key);
552      var valuableFinalProducts = uniqueFinalProducts.Where(productName => Products.Any(p => p.Label == productName && p.Price > 0));
553      var finalProducts = valuableFinalProducts.ToList();
554
555      // TODO: forward simulation sanity checks
556      // non-sold products
557      // non-(transported/converted/sold) intermediates
558      // non-transported/converted feedstocks
559
560      // reverse pruning
561      // select only central conversions for which valuable end-products exist
562      var necessaryCentralConversions = feasibleCentralConversions
563        .Where(c => c.Products.Any(p => products.Any(p1 => p1.Label == p.Name && p1.Price > 0)))
564        .ToList();
565      // transport only intermediates that need to be converted
566      var neccessaryIntermediates = transportableIntermediates
567        .Where(i => necessaryCentralConversions.Any(c => c.Feedstock == i))
568        .ToList();
569      // select only intermediates that either
570      // - need to transported and are further processed or
571      // - can be sold
572      var feasibleIntermediates = neccessaryIntermediates.Concat(
573        possibleIntermediates.Where(i => products.Any(p => p.Label == i && p.Price > 0)))
574        .ToList();
575      // produce only selected intermediates
576      var necessaryDecentralConversions = feasibleDecentralConversions
577        .Where(c => c.Products.Any(p => feasibleIntermediates.Contains(p.Name)))
578        .ToList();
579      // transport and buy only feedstocks that are converted and sold
580      var necessaryFeedstocks = necessaryDecentralConversions
581        .Select(c => c.Feedstock).ToList();
582
583      // enable feedstocks directly to central conversion? Yes, it automatically becomes a decentral conversion
584      // TODO: allow more than two echelons?
585
586      // configure search space:
587      UpdateSearchSpace(Utilizations, necessaryFeedstocks);
588      UpdateSearchSpace(TransportTargets, necessaryFeedstocks.Concat(neccessaryIntermediates));
589      UpdateSearchSpace(FinalProducts, finalProducts);
590
591      var q =
592        (from p1 in necessaryFeedstocks.Concat(neccessaryIntermediates)
593          from p2 in neccessaryIntermediates
594          where conversions.Any(c => c.Feedstock == p1 && c.Products.ContainsKey(p2))
595          select new {p1, p2}).Concat(
596        from p1 in neccessaryIntermediates
597        from p2 in finalProducts
598        where conversions.Any(c => c.Feedstock == p1 && c.Products.ContainsKey(p2))
599        select new {p1, p2})
600       .Distinct().ToList();
601      var productLinks = new StringMatrix(q.Count, 2);
602      for (int i = 0; i < q.Count; i++) {
603        productLinks[i, 0] = q[i].p1;
604        productLinks[i, 1] = q[i].p2;
605      }
606      ProductLinks = productLinks;
607
608    }
609
610    private void UpdateSearchSpace(CheckedItemList<StringValue> choices, IEnumerable<string> requirements) {
611      var req = new HashSet<string>(requirements);
612      for (int i = 0; i < choices.Count; i++) {
613        if (req.Contains(choices[i].Value)) {
614          req.Remove(choices[i].Value); // already there, not required anymore
615        } else {
616          choices.SetItemCheckedState(i, false);
617        }
618      }
619      foreach (var r in req) {
620        choices.Add(new StringValue(r), true);
621      }
622    }
623
624    private static double GetDoubleValueValue(ValueParameterCollection parameters, string name, double defaultValue) {
625      IValueParameter param;
626      if (!parameters.TryGetValue(name, out param)) return defaultValue;
627      var doubleValue = param.Value as DoubleValue;
628      if (doubleValue == null) return defaultValue;
629      return doubleValue.Value;
630    }
631
632    #endregion
633
634    #region Data Consistency Checks
635    public string CheckGeoData() {
636      var warnings = new List<string>();
637      CheckDistanceMatrices(warnings);
638      var shapeNames = CheckShapeNames(warnings);
639      var locationNames = CheckLocationNames(warnings);
640      FindMissing(warnings, locationNames, shapeNames, "locations without shapes");
641      FindMissing(warnings, shapeNames, locationNames, "shapes without data");
642      var totalPotentials = new double[LocationNames.Length];
643      foreach (var p in FeedstockPotentials.OfType<ValueParameter<DoubleArray>>()) {
644        totalPotentials = totalPotentials.Zip(p.Value, (a, b) => a + b).ToArray();
645        FindZeros(warnings, p.Value, p.Name);
646      }
647      FindZeros(warnings, totalPotentials, "any potential");
648      return string.Join("\n\n", warnings);
649    }
650
651    private void FindZeros(List<string> warnings, IEnumerable<double> values, string name) {
652      var zeros = values.Select((v, i) => new { v, i }).Where(p => p.v == 0).Select(p => LocationNames[p.i]).ToList();
653      if (zeros.Count > 0)
654        warnings.Add(string.Format("{0} regions with zero of {1}:\n  {2}",
655          zeros.Count, name, string.Join(", ", zeros)));
656    }
657
658    private static void FindMissing(List<string> warnings, IEnumerable<string> items, IEnumerable<string> other, string message) {
659      var missingItems = new HashSet<string>(items);
660      missingItems.ExceptWith(other);
661      if (missingItems.Count > 0)
662        warnings.Add(string.Format("{0} {1}:\n  {2}",
663          missingItems.Count, message, string.Join(", ", missingItems)));
664    }
665
666    private HashSet<string> CheckLocationNames(List<string> warnings) {
667      var duplicates = new List<String>();
668      var locationNames = new HashSet<string>();
669      foreach (var n in LocationNames) {
670        if (locationNames.Contains(n))
671          duplicates.Add(n);
672        locationNames.Add(n);
673      }
674      if (duplicates.Count > 0)
675        warnings.Add(string.Format("{0} duplicate location names:\n  {1}",
676          duplicates.Count, string.Join(", ", duplicates)));
677      return locationNames;
678    }
679
680    private HashSet<string> CheckShapeNames(List<string> warnings) {
681      var duplicates = new List<string>();
682      var shapeNames = new HashSet<string>();
683      foreach (FeatureDataRow row in Geometry.Features.Rows) {
684        var n = (string)row["NUTS_ID"];
685        if (shapeNames.Contains(n))
686          duplicates.Add(n);
687        shapeNames.Add(n);
688      }
689      if (duplicates.Count > 0)
690        warnings.Add(string.Format("{0} duplicate shape names:\n  {1}",
691          duplicates.Count, String.Join(", ", duplicates)));
692      return shapeNames;
693    }
694
695    private void CheckDistanceMatrices(List<string> warnings) {
696      foreach (var distanceMatrixParam in Parameters.Where(p => p.Name.EndsWith("DistanceMatrix"))) {
697        var valueParam = distanceMatrixParam as IValueParameter;
698        if (valueParam != null && typeof(DistanceMatrix).IsAssignableFrom(valueParam.DataType))
699          FindUnconnectedRegions((DistanceMatrix)valueParam.Value, warnings);
700      }
701    }
702
703    private void FindUnconnectedRegions(DistanceMatrix dm, List<string> warnings) {
704      var unconnected = new List<string>();
705      var hardlyConnected = new List<string>();
706      for (int i = 0; i < dm.Size; i++) {
707        var nConnected = 0;
708        for (int j = 0; j < i; j++) {
709          if (dm[i, j] > 0) nConnected++;
710        }
711        if (nConnected == 0)
712          unconnected.Add(LocationNames[i]);
713        else if (1.0 * nConnected < 1.0 * LocationNames.Length / 100)
714          hardlyConnected.Add(LocationNames[i]);
715      }
716      if (unconnected.Count > 0)
717        warnings.Add(string.Format("{0} unconnected regions:\n  {1}",
718          unconnected.Count, string.Join(", ", unconnected)));
719      if (hardlyConnected.Count > 0)
720        warnings.Add(string.Format("{0} connected to fewer than 1% of regions:\n  {1}",
721          hardlyConnected.Count, string.Join(", ", hardlyConnected)));
722    }
723
724    // TODO: check consistency of conversion process
725    #endregion
726  }
727}
728
Note: See TracBrowser for help on using the repository browser.