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.Data;
|
---|
23 | using System.Diagnostics;
|
---|
24 | using System.Text.RegularExpressions;
|
---|
25 | using HeuristicLab.BioBoost.Data;
|
---|
26 | using HeuristicLab.BioBoost.Evaluators;
|
---|
27 | using HeuristicLab.BioBoost.Persistence;
|
---|
28 | using HeuristicLab.BioBoost.Utils;
|
---|
29 | using HeuristicLab.Common;
|
---|
30 | using HeuristicLab.Core;
|
---|
31 | using HeuristicLab.Data;
|
---|
32 | using HeuristicLab.Parameters;
|
---|
33 | using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
|
---|
34 | using SharpMap.Data;
|
---|
35 | using SharpMap.Data.Providers;
|
---|
36 | using System;
|
---|
37 | using System.Collections.Generic;
|
---|
38 | using System.Globalization;
|
---|
39 | using System.IO;
|
---|
40 | using System.Linq;
|
---|
41 | using GeoAPI.Geometries;
|
---|
42 | using HeuristicLab.BioBoost.Representation;
|
---|
43 | using HEAL.Attic;
|
---|
44 |
|
---|
45 | namespace 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 |
|
---|