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