using System; using System.Collections.Generic; using System.Diagnostics; using System.Text.RegularExpressions; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Parameters; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; using System.Linq; using HeuristicLab.BioBoost.Representation; using HeuristicLab.BioBoost.Utils; namespace HeuristicLab.BioBoost.Evaluators { [StorableClass] public class ConcludingEvaluator : BioBoostEvaluator { #region Parameter public LookupParameter TotalCostParameter { get { return (LookupParameter) Parameters["TotalCost"]; } } public LookupParameter RemoveIntermediateResultsParameter { get { return (LookupParameter) Parameters["RemoveIntermediateResults"]; } } public ILookupParameter QualityCriteriaParameter { get { return (ILookupParameter) Parameters["QualityCriteria"]; } } #endregion #region Parameter Values public double TotalCost { get { return TotalCostParameter.ActualValue.Value; } set { TotalCostParameter.ActualValue = new DoubleValue(value); } } public bool RemoveIntermediateResults { get { return RemoveIntermediateResultsParameter.ActualValue.Value; } set { RemoveIntermediateResultsParameter.ActualValue = new BoolValue(value); } } public DoubleArray QualityCriteria { get { return QualityCriteriaParameter.ActualValue; } set { QualityCriteriaParameter.ActualValue = value; } } #endregion #region Construction & Cloning [StorableConstructor] protected ConcludingEvaluator(bool isDeserializing) : base(isDeserializing) {} public ConcludingEvaluator() { Parameters.Add(new LookupParameter("RemoveIntermediateResults", "Whether to remove intermediate results created during evaluation.")); Parameters.Add(new LookupParameter("TotalCost", "Overall cost of the whole scenario.")); Parameters.Add(new LookupParameter("QualityCriteria", "The list of qualities to consider.")); } public ConcludingEvaluator(ConcludingEvaluator original, Cloner cloner) : base(original, cloner) { } public override IDeepCloneable Clone(Cloner cloner) { return new ConcludingEvaluator(this, cloner); } [StorableHook(HookType.AfterDeserialization)] void AfterDeserialization() { if (!Parameters.ContainsKey("QualityCriteria")) Parameters.Add(new LookupParameter("QualityCriteria", "The list of qualities to consider.")); } #endregion private readonly Regex penaltyRegex = new Regex("[Pp]enalt(y|ies)$"); public override IOperation Apply() { CreateAggregationLayers(); var expenses = 0d; var revenues = 0d; var penalties = 0d; foreach (var cost in Costs) { var value = cost.Value as DoubleValue; if (value == null) continue; if (penaltyRegex.IsMatch(cost.Name)) { penalties += value.Value; } else if (value.Value > 0) { expenses += value.Value; } else { revenues -= value.Value; } } var roi = Math.Abs(expenses) < 1 ? revenues : (revenues - expenses)/expenses; TotalCost = roi - penalties; var qualities = new List {TotalCost}; if (RemoveIntermediateResults) { foreach (var r in IntermediateResults) { RemoveFromScope(r.Name); } RemoveFromScope(IntermediateResultsParameter.ActualName); RemoveFromScope(CostsParameter.ActualName); } foreach (var p in ProblemData.Products) { var amounts = GetFromScope(LayerDescriptor.AmountsAtSource.NameWithPrefix(p.Label)); if (amounts != null) { qualities.Add(amounts.Sum()); } } QualityCriteria = new DoubleArray(qualities.ToArray()); return base.Apply(); } private void CreateAggregationLayers() { // for transport targets (straw, biosyncrude) // aggregate sources (recursively) // straw -> straw targets // straw -> straw targets -> biosyncrude targets // biosyncrude -> biosyncrude targets var scope = ExecutionContext.Scope; // straw sources, biosyncrude sources var sources = scope.Variables .Where(v => LayerDescriptor.Sources.IsSuffixOf(v.Name)) .Where(v => v.Value is ItemArray>) .ToDictionary( v => LayerDescriptor.Sources.RemoveSuffixFrom(v.Name), v => (ItemArray>)v.Value); // determine straw 1, biosyncrude 2 ??? // -> find utilizations var feedstocks = scope.Variables .Where(v => LayerDescriptor.Utilizations.IsSuffixOf(v.Name)) .Select(v => LayerDescriptor.Utilizations.RemoveSuffixFrom(v.Name)) .ToHashSet(); if (feedstocks.Count > 1) throw new InvalidOperationException("Currently only one feedstock is allowed."); if (feedstocks.Count == 0) return; var feedstock = feedstocks.First(); var carriers = sources.Keys.Where(k => k != feedstock); if (carriers.Count() > 1) throw new InvalidOperationException("Currently only one carrier is allowed"); var carrier = carriers.Count() == 0 ? null : carriers.First(); var feedstockSources = sources[feedstock]; var carrierSources = carrier == null ? null : sources[carrier]; var finalProductName = GetFinalProductName(carrier ?? feedstock); if (finalProductName == null) finalProductName = "TransportFuel"; if (carrier == null) { AnalyzeSingleEchelon(feedstock, finalProductName, feedstockSources); } else { AnalyzeBinaryEchelon(feedstock, carrier, finalProductName, feedstockSources, carrierSources); } } private void AnalyzeBinaryEchelon(string feedstock, string carrier, string finalProductName, ItemArray> feedstockSources, ItemArray> carrierSources) { var length = feedstockSources.Length; var feedstockCosts = GetFromScope(LayerDescriptor.TotalCostsAtSource.NameWithPrefix(feedstock)); //var feedstockAmounts = GetFromScope(LayerDescriptor.AmountsTransportedFromSource.NameWithPrefix(feedstock)); var feedstockLogisticCosts = GetFromScope(LayerDescriptor.TotalLogisticCosts.NameWithPrefix(feedstock)); var carrierLogisticCosts = GetFromScope(LayerDescriptor.TotalLogisticCosts.NameWithPrefix(carrier)); var decentralConversionCosts = GetFromScope(LayerDescriptor.TotalAmortizedConversionCosts.NameWithPrefix(feedstock)); var finalProductAmounts = GetFromScope(LayerDescriptor.AmountsAtSource.NameWithPrefix(finalProductName)); var carrierProductAmounts = GetFromScope(LayerDescriptor.AmountsTransportedFromSource.NameWithPrefix(carrier)); var finalProductCosts = GetFromScope(LayerDescriptor.TotalCostsAtSource.NameWithPrefix(finalProductName)); var carrierProductCosts = GetFromScope(LayerDescriptor.TotalCostsAtSource.NameWithPrefix(carrier)); var centralConversionCosts = GetFromScope(LayerDescriptor.TotalAmortizedConversionCosts.NameWithPrefix(carrier)); var finalProductPrice = ProblemData.Products.FirstOrDefault(p => p.Label == finalProductName).PriceAtSource; var carrierProductPrice = ProblemData.Products.FirstOrDefault(p => p.Label == carrier).PriceAtSource; var centralPlantsFeedstockCosts = new DoubleArray(length); var centralPlantsFeedstockCostsRelative = new DoubleArray(length); var centralPlantsDecentralLogisticCosts = new DoubleArray(length); var centralPlantsDecentralLogisticCostsRelative = new DoubleArray(length); var centralPlantsDecentralConversionCosts = new DoubleArray(length); var centralPlantsDecentralConversionCostsRelative = new DoubleArray(length); var centralPlantsCentralLogisticCosts = new DoubleArray(length); var centralPlantsCentralLogisticCostsRelative = new DoubleArray(length); // var plantsCentralConversionCosts = new DoubleArray(length); already there var centralPlantsCentralConversionCostsRelative = new DoubleArray(length); var centralPlantsRoiFollowed = new DoubleArray(length); var centralPlantsRoiAggregated = new DoubleArray(length); var decentralPlantsFeedstockCosts = new DoubleArray(length); var decentralPlantsFeedstockCostsRelative = new DoubleArray(length); var decentralPlantsDecentralLogisticCosts = new DoubleArray(length); var decentralPlantsDecentralLogisticCostsRelative = new DoubleArray(length); //var decentralPlantsDecentralConversionCosts = new DoubleArray(length); var decentralPlantsDecentralConversionCostsRelative = new DoubleArray(length); var decentralPlantsRoiFollowed = new DoubleArray(length); var decentralPlantsRoiAggregated = new DoubleArray(length); for (int i = 0; i < carrierSources.Length; i++) { if (carrierSources[i] != null) { // central plant var plantsFeedstockSources = carrierSources[i] .Select(j => feedstockSources[j.Value].Select(s => s.Value)) .Aggregate((e1, e2) => e1.Concat(e2)) .ToHashSet(); var plantsCarrierSources = carrierSources[i].Select(j => j.Value).ToHashSet(); var finalProductAmount = finalProductAmounts[i]; centralPlantsFeedstockCosts[i] = feedstockCosts.FuncOverIndices(plantsFeedstockSources, Sum); centralPlantsFeedstockCostsRelative[i] = centralPlantsFeedstockCosts[i]/finalProductAmount; centralPlantsDecentralLogisticCosts[i] = feedstockLogisticCosts.FuncOverIndices(plantsFeedstockSources, Sum); centralPlantsDecentralLogisticCostsRelative[i] = centralPlantsDecentralLogisticCosts[i]/finalProductAmount; centralPlantsDecentralConversionCosts[i] = decentralConversionCosts.FuncOverIndices(plantsCarrierSources, Sum); centralPlantsDecentralConversionCostsRelative[i] = centralPlantsDecentralConversionCosts[i]/finalProductAmount; centralPlantsCentralLogisticCosts[i] = carrierLogisticCosts.FuncOverIndices(plantsCarrierSources, Sum); centralPlantsCentralLogisticCostsRelative[i] = centralPlantsCentralLogisticCosts[i]/finalProductAmount; centralPlantsCentralConversionCostsRelative[i] = centralConversionCosts[i]/finalProductAmount; var revenue = finalProductAmounts[i]*finalProductPrice; var profit = revenue - finalProductCosts[i]; centralPlantsRoiFollowed[i] = finalProductCosts[i] == 0 ? profit : profit/finalProductCosts[i]; var totalCost = centralPlantsFeedstockCosts[i] + centralPlantsDecentralLogisticCosts[i] + centralPlantsDecentralConversionCosts[i] + centralPlantsCentralLogisticCosts[i] + centralConversionCosts[i]; centralPlantsRoiAggregated[i] = (revenue - totalCost)/totalCost; } if (feedstockSources[i] != null) { // decentral plant var plantsFeedstockSources = feedstockSources[i].Select(j => j.Value).ToHashSet(); var carrierProductAmount = carrierProductAmounts[i]; decentralPlantsFeedstockCosts[i] = feedstockCosts.FuncOverIndices(plantsFeedstockSources, Sum); decentralPlantsFeedstockCostsRelative[i] = decentralPlantsFeedstockCosts[i]/carrierProductAmount; decentralPlantsDecentralLogisticCosts[i] = feedstockLogisticCosts.FuncOverIndices(plantsFeedstockSources, Sum); decentralPlantsDecentralLogisticCostsRelative[i] = decentralPlantsDecentralLogisticCosts[i]/carrierProductAmount; decentralPlantsDecentralConversionCostsRelative[i] = decentralConversionCosts[i]/carrierProductAmount; var revenue = carrierProductAmounts[i]*carrierProductPrice; var profit = revenue - carrierProductCosts[i]; decentralPlantsRoiFollowed[i] = carrierProductCosts[i] == 0 ? profit : profit/carrierProductCosts[i]; var totalCost = decentralPlantsFeedstockCosts[i] + decentralPlantsDecentralLogisticCosts[i] + decentralConversionCosts[i]; decentralPlantsRoiAggregated[i] = (revenue - totalCost)/totalCost; } } PutInScope(LayerDescriptor.FeedstockCosts.NameWithPrefix(finalProductName), centralPlantsFeedstockCosts, false); PutInScope(LayerDescriptor.FeedstockCostsRelative.NameWithPrefix(finalProductName), centralPlantsFeedstockCostsRelative, false); PutInScope(LayerDescriptor.DecentralLogisticCosts.NameWithPrefix(finalProductName), centralPlantsDecentralLogisticCosts, false); PutInScope(LayerDescriptor.DecentralLogisticCostsRelative.NameWithPrefix(finalProductName), centralPlantsDecentralLogisticCostsRelative, false); PutInScope(LayerDescriptor.DecentralConversionCosts.NameWithPrefix(finalProductName), centralPlantsDecentralConversionCosts, false); PutInScope(LayerDescriptor.DecentralConversionCostsRelative.NameWithPrefix(finalProductName), centralPlantsDecentralConversionCostsRelative, false); PutInScope(LayerDescriptor.CentralLogisticCosts.NameWithPrefix(finalProductName), centralPlantsCentralLogisticCosts, false); PutInScope(LayerDescriptor.CentralLogisticCostsRelative.NameWithPrefix(finalProductName), centralPlantsCentralLogisticCostsRelative, false); PutInScope(LayerDescriptor.CentralConversionCostsRelative.NameWithPrefix(finalProductName), centralPlantsCentralConversionCostsRelative, false); PutInScope(LayerDescriptor.RoiFollowed.NameWithPrefix(finalProductName), centralPlantsRoiFollowed, false); PutInScope(LayerDescriptor.RoiAggregated.NameWithPrefix(finalProductName), centralPlantsRoiAggregated, false); PutInScope(LayerDescriptor.FeedstockCosts.NameWithPrefix(carrier), decentralPlantsFeedstockCosts, false); PutInScope(LayerDescriptor.FeedstockCostsRelative.NameWithPrefix(carrier), decentralPlantsFeedstockCostsRelative, false); PutInScope(LayerDescriptor.DecentralLogisticCosts.NameWithPrefix(carrier), decentralPlantsDecentralLogisticCosts, false); PutInScope(LayerDescriptor.DecentralLogisticCostsRelative.NameWithPrefix(carrier), decentralPlantsDecentralLogisticCostsRelative, false); PutInScope(LayerDescriptor.DecentralConversionCostsRelative.NameWithPrefix(carrier), decentralPlantsDecentralConversionCostsRelative, false); PutInScope(LayerDescriptor.RoiFollowed.NameWithPrefix(carrier), decentralPlantsRoiFollowed, false); PutInScope(LayerDescriptor.RoiAggregated.NameWithPrefix(carrier), decentralPlantsRoiAggregated, false); } private void AnalyzeSingleEchelon(string feedstock, string finalProductName, ItemArray> feedstockSources) { var length = feedstockSources.Length; var feedstockCosts = GetFromScope(LayerDescriptor.TotalCostsAtSource.NameWithPrefix(feedstock)); var feedstockLogisticCosts = GetFromScope(LayerDescriptor.TotalLogisticCosts.NameWithPrefix(feedstock)); var centralConversionCosts = GetFromScope(LayerDescriptor.TotalAmortizedConversionCosts.NameWithPrefix(feedstock)); var finalProductAmounts = GetFromScope(LayerDescriptor.AmountsAtSource.NameWithPrefix(finalProductName)); var finalProductCosts = GetFromScope(LayerDescriptor.TotalCostsAtSource.NameWithPrefix(finalProductName)); var finalProductPrice = ProblemData.Products.FirstOrDefault(p => p.Label == finalProductName).PriceAtSource; var centralPlantsFeedstockCosts = new DoubleArray(length); var centralPlantsFeedstockCostsRelative = new DoubleArray(length); var centralPlantsCentralLogisticCosts = new DoubleArray(length); var centralPlantsCentralLogisticCostsRelative = new DoubleArray(length); var centralPlantsCentralConversionCostsRelative = new DoubleArray(length); var centralPlantsRoiFollowed = new DoubleArray(length); var centralPlantsRoiAggregated = new DoubleArray(length); for (int i = 0; i < feedstockSources.Length; i++) { if (feedstockSources[i] != null) { var plantsFeedstockSources = feedstockSources[i].Select(j => j.Value).ToHashSet(); var finalProductAmount = finalProductAmounts[i]; centralPlantsFeedstockCosts[i] = feedstockCosts.FuncOverIndices(plantsFeedstockSources, Sum); centralPlantsFeedstockCostsRelative[i] = centralPlantsFeedstockCosts[i]/finalProductAmount; centralPlantsCentralLogisticCosts[i] = feedstockLogisticCosts.FuncOverIndices(plantsFeedstockSources, Sum); centralPlantsCentralLogisticCostsRelative[i] = centralPlantsCentralLogisticCosts[i]/finalProductAmount; centralPlantsCentralConversionCostsRelative[i] = centralConversionCosts[i]/finalProductAmount; var revenue = finalProductAmounts[i]*finalProductPrice; var profit = revenue - finalProductCosts[i]; centralPlantsRoiFollowed[i] = finalProductCosts[i] == 0 ? profit : profit/finalProductCosts[i]; var totalCost = centralPlantsFeedstockCosts[i] + centralPlantsCentralLogisticCosts[i] + centralConversionCosts[i]; centralPlantsRoiAggregated[i] = (revenue - totalCost)/totalCost; } } PutInScope(LayerDescriptor.FeedstockCosts.NameWithPrefix(finalProductName), centralPlantsFeedstockCosts, false); PutInScope(LayerDescriptor.FeedstockCostsRelative.NameWithPrefix(finalProductName), centralPlantsFeedstockCostsRelative, false); PutInScope(LayerDescriptor.CentralLogisticCosts.NameWithPrefix(finalProductName), centralPlantsCentralLogisticCosts, false); PutInScope(LayerDescriptor.CentralLogisticCostsRelative.NameWithPrefix(finalProductName), centralPlantsCentralLogisticCostsRelative, false); PutInScope(LayerDescriptor.CentralConversionCostsRelative.NameWithPrefix(finalProductName), centralPlantsCentralConversionCostsRelative, false); PutInScope(LayerDescriptor.RoiFollowed.NameWithPrefix(finalProductName), centralPlantsRoiFollowed, false); PutInScope(LayerDescriptor.RoiAggregated.NameWithPrefix(finalProductName), centralPlantsRoiAggregated, false); } private static double Sum(IEnumerable values) { return values.Sum(); } private string GetFinalProductName(string feedstock) { var productLinks = ProblemData.ProductLinks; for (int i = 0; i < productLinks.Rows; i++) { if (productLinks[i, 0] == feedstock) return productLinks[i, 1]; } return null; } } }