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