[13071] | 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;
|
---|
[13069] | 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;
|
---|
[16575] | 43 | using HEAL.Attic;
|
---|
[13069] | 44 |
|
---|
| 45 | namespace HeuristicLab.BioBoost.ProblemDescription {
|
---|
| 46 |
|
---|
[16575] | 47 | [StorableType("1A6A5B9A-0E82-4873-9F8F-87ABA1D8F125")]
|
---|
[13069] | 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]
|
---|
[16575] | 188 | protected BioBoostProblemData(StorableConstructorFlag _) : base(_) { }
|
---|
[13069] | 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 |
|
---|