Exemplo n.º 1
0
        /// <summary>
        /// Plots the frequencies of a given item configuration file.
        /// </summary>
        /// <param name="filepath">The item configuration.</param>
        public static void PlotSimpleInventoryFrequencies(string filepath)
        {
            // Set group count
            int groupCount = 10;

            // Parse the given file
            Console.WriteLine("Parsing generator config ...");
            SimpleItemGeneratorConfiguration config = InstanceIO.ReadSimpleItemGeneratorConfig(filepath);
            string outputBasename    = config.Name;
            string frequencyFilename = config.Name;
            string directory         = Path.GetDirectoryName(filepath);

            // Build frequency groups of the items
            Console.WriteLine("Building frequency groups ...");
            int[]         frequencyGroups = new int[groupCount]; double maxItemWeight = config.ItemWeights.Max(w => w.Value);
            bool          plotWeights                 = config.ItemDescriptionWeights != null && config.ItemDescriptionWeights.Count > 0;
            List <double> weights                     = plotWeights ? config.ItemDescriptionWeights.Select(w => w.Value).OrderBy(w => w).ToList() : null;
            bool          plotBundleSize              = config.ItemDescriptionBundleSizes != null && config.ItemDescriptionBundleSizes.Count > 0;
            List <int>    bundleSizes                 = plotBundleSize ? config.ItemDescriptionBundleSizes.Select(w => w.Value).OrderBy(w => w).ToList() : null;
            double        overallWeight               = config.ItemWeights.Sum(w => w.Value);
            List <double> probabilities               = config.ItemWeights.Select(w => w.Value / overallWeight).OrderBy(p => p).ToList();
            List <double> frequencies                 = config.ItemWeights.Select(w => w.Value / maxItemWeight).OrderBy(f => f).ToList();
            List <double> frequenciesForGroupBuilding = frequencies.ToList();

            for (int i = 0; i < frequencyGroups.Length; i++)
            {
                double groupFreqCap = (i + 1.0) / frequencyGroups.Length;
                int    countInRange = frequenciesForGroupBuilding.TakeWhile(f => f <= groupFreqCap).Count();
                frequencyGroups[i]          = countInRange;
                frequenciesForGroupBuilding = frequenciesForGroupBuilding.Skip(countInRange).ToList();
            }
            // Write data files
            Console.WriteLine("Generating plot data files ...");
            // Write the data file for the group based plot
            string frequencyGroupsPlotDataFile = outputBasename + "groups.dat";

            using (StreamWriter sw = new StreamWriter(Path.Combine(directory, frequencyGroupsPlotDataFile), false))
            {
                sw.WriteLine(IOConstants.GNU_PLOT_COMMENT_LINE + " frequencyvalue groupcount");
                for (int i = 0; i < frequencyGroups.Length; i++)
                {
                    // Calculate x-value for group (use mid between lower and upper group boundaries)
                    double groupXValue =
                        (((double)i / frequencyGroups.Length)) +
                        ((((double)(i + 1) / frequencyGroups.Length) - ((double)i / frequencyGroups.Length)) / 2.0);
                    sw.WriteLine(groupXValue.ToString(IOConstants.FORMATTER) + IOConstants.GNU_PLOT_VALUE_SPLIT + frequencyGroups[i].ToString());
                }
            }
            // Write the data file for a simple SKU/frequency plot
            string frequencySimplePlotDataFile = outputBasename + "simple.dat";

            using (StreamWriter sw = new StreamWriter(Path.Combine(directory, frequencySimplePlotDataFile), false))
            {
                sw.WriteLine(IOConstants.GNU_PLOT_COMMENT_LINE + " sku frequency");
                // Write frequencies in reverse order
                int sku = 0;
                foreach (var freq in frequencies.OrderByDescending(f => f))
                {
                    // Update
                    sku++;
                    // Flush
                    sw.WriteLine(sku.ToString() + IOConstants.GNU_PLOT_VALUE_SPLIT + freq.ToString(IOConstants.FORMATTER));
                }
            }
            // Write the data file for a cumulative SKU/frequency plot
            string frequencyCumSimplePlotDataFile = outputBasename + "cumulative.dat";

            using (StreamWriter sw = new StreamWriter(Path.Combine(directory, frequencyCumSimplePlotDataFile), false))
            {
                sw.WriteLine(IOConstants.GNU_PLOT_COMMENT_LINE + " sku cum.frequency");
                // Prepare frequency info
                double cumFrequency = 0; double overallFrequency = frequencies.Sum(); int sku = 0;
                // Aggregate and write cumulative frequencies starting with the largest
                foreach (var freq in frequencies.OrderByDescending(f => f))
                {
                    // Update
                    sku++; cumFrequency += freq;
                    // Flush
                    sw.WriteLine(sku.ToString() + IOConstants.GNU_PLOT_VALUE_SPLIT + (cumFrequency / overallFrequency).ToString(IOConstants.FORMATTER));
                }
            }
            // Write data file for the real probability plot
            string probabilityPlotDataFile = outputBasename + "probability.dat";

            using (StreamWriter sw = new StreamWriter(Path.Combine(directory, probabilityPlotDataFile), false))
            {
                sw.WriteLine(IOConstants.GNU_PLOT_COMMENT_LINE + " sku probability");
                for (int i = 0; i < probabilities.Count; i++)
                {
                    // Reverse order (start with the highest probability)
                    sw.WriteLine((probabilities.Count - i).ToString() + IOConstants.GNU_PLOT_VALUE_SPLIT + probabilities[i].ToString(IOConstants.FORMATTER));
                }
            }
            // Write data file for the item description weights plot
            string itemDescriptionWeightsPlotDataFile = outputBasename + "weights.dat";

            if (plotWeights)
            {
                using (StreamWriter sw = new StreamWriter(Path.Combine(directory, itemDescriptionWeightsPlotDataFile), false))
                {
                    sw.WriteLine(IOConstants.GNU_PLOT_COMMENT_LINE + " sku weight");
                    for (int i = 0; i < weights.Count; i++)
                    {
                        // Write
                        sw.WriteLine((i + 1).ToString() + IOConstants.GNU_PLOT_VALUE_SPLIT + weights[i].ToString(IOConstants.FORMATTER));
                    }
                }
            }
            // Write data file for the item description weights plot
            string itemDescriptionBundleSizesPlotDataFile = outputBasename + "bundlesizes.dat";

            if (plotBundleSize)
            {
                using (StreamWriter sw = new StreamWriter(Path.Combine(directory, itemDescriptionBundleSizesPlotDataFile), false))
                {
                    sw.WriteLine(IOConstants.GNU_PLOT_COMMENT_LINE + " sku bundlesize");
                    for (int i = 0; i < bundleSizes.Count; i++)
                    {
                        // Write
                        sw.WriteLine((i + 1).ToString() + IOConstants.GNU_PLOT_VALUE_SPLIT + bundleSizes[i].ToString(IOConstants.FORMATTER));
                    }
                }
            }
            // Generate plot script
            Console.WriteLine("Generating plot scipt ...");
            string plotScriptName = outputBasename + ".gp";

            using (StreamWriter sw = new StreamWriter(Path.Combine(directory, plotScriptName)))
            {
                sw.WriteLine("reset");
                sw.WriteLine("# Output definition");
                sw.WriteLine("set terminal pdfcairo enhanced size 7, 3 font \"Consolas, 12\"");
                sw.WriteLine("set lmargin 13");
                sw.WriteLine("set rmargin 13");
                sw.WriteLine("# Parameters");
                sw.WriteLine("set key right top Right");
                sw.WriteLine("set grid");
                sw.WriteLine("set style fill solid 0.75");
                sw.WriteLine("# Line-Styles");
                sw.WriteLine("set style line 1 linetype 1 linecolor rgb \"" + PlotColoring.GetHexCode(PlotColors.MediumBlue) + "\" linewidth 1");
                sw.WriteLine("set output \"" + frequencyFilename + ".pdf\"");
                sw.WriteLine("set title \"" + frequencyFilename + "\"");
                sw.WriteLine("set xlabel \"Frequency\"");
                sw.WriteLine("set ylabel \"SKU count\"");
                sw.WriteLine("plot \\");
                sw.WriteLine("\"" + frequencyGroupsPlotDataFile + "\" u 1:2 w boxes linestyle 1 t \"SKU frequencies\"");
                sw.WriteLine("set title \"" + frequencyFilename + "\"");
                sw.WriteLine("set xlabel \"SKU\"");
                sw.WriteLine("set ylabel \"frequency\"");
                sw.WriteLine("plot \\");
                sw.WriteLine("\"" + frequencySimplePlotDataFile + "\" u 1:2 w steps linestyle 1 t \"SKU frequencies\"");
                sw.WriteLine("set title \"" + frequencyFilename + "\"");
                sw.WriteLine("set xlabel \"SKU\"");
                sw.WriteLine("set ylabel \"rel. cum. frequency\"");
                sw.WriteLine("plot \\");
                sw.WriteLine("\"" + frequencyCumSimplePlotDataFile + "\" u 1:2 w steps linestyle 1 t \"cum. SKU frequencies\"");
                sw.WriteLine("set title \"" + frequencyFilename + "\"");
                sw.WriteLine("set xlabel \"SKU\"");
                sw.WriteLine("set ylabel \"probability\"");
                sw.WriteLine("plot \\");
                sw.WriteLine("\"" + probabilityPlotDataFile + "\" u 1:2 w steps linestyle 1 t \"SKU probabilities\"");
                if (plotWeights)
                {
                    sw.WriteLine("set title \"" + frequencyFilename + "\"");
                    sw.WriteLine("set xlabel \"SKU\"");
                    sw.WriteLine("set ylabel \"size\"");
                    sw.WriteLine("plot \\");
                    sw.WriteLine("\"" + itemDescriptionWeightsPlotDataFile + "\" u 1:2 w steps linestyle 1 t \"SKU size\"");
                }
                if (plotBundleSize)
                {
                    sw.WriteLine("set title \"" + frequencyFilename + "\"");
                    sw.WriteLine("set xlabel \"SKU\"");
                    sw.WriteLine("set ylabel \"units\"");
                    sw.WriteLine("plot \\");
                    sw.WriteLine("\"" + itemDescriptionBundleSizesPlotDataFile + "\" u 1:2 w steps linestyle 1 t \"SKU replenishment order size\"");
                }
                sw.WriteLine("reset");
                sw.WriteLine("exit");
            }
            string commandScriptName = outputBasename + ".cmd";

            using (StreamWriter sw = new StreamWriter(Path.Combine(directory, commandScriptName)))
            {
                sw.WriteLine("gnuplot " + plotScriptName);
            }
            // Log
            Console.WriteLine("Calling plot script ...");
            // Execute plot script
            DataProcessor.ExecuteScript(Path.Combine(directory, commandScriptName), (string msg) => { Console.WriteLine(msg); });
        }
Exemplo n.º 2
0
        /// <summary>
        /// Plot information about the SKUs used during the execution of a simulation.
        /// </summary>
        /// <param name="resultDir">The result directory containing all necessary files.</param>
        /// <param name="logger">A logger used to log some progress.</param>
        public static void PlotExecutionItemDescriptionInfo(string resultDir, Action <string> logger)
        {
            // Set base name
            string filesBasename = "itemdescriptionplot";

            logger?.Invoke("Parsing results and preparing intermediate files ...");
            // Get instance name
            string instanceName;

            using (StreamReader sr = new StreamReader(Path.Combine(resultDir, IOConstants.StatFileNames[IOConstants.StatFile.InstanceName])))
            {
                string instanceNameLine = "";
                while (string.IsNullOrWhiteSpace((instanceNameLine = sr.ReadLine()))) /* Nothing to do - the head of the loop does the work */ } {
                instanceName = instanceNameLine.Trim();
        }
        // Fetch config name
        string configName;

        using (StreamReader sr = new StreamReader(Path.Combine(resultDir, IOConstants.StatFileNames[IOConstants.StatFile.ControllerName])))
        {
            string configNameLine = "";
            while (string.IsNullOrWhiteSpace((configNameLine = sr.ReadLine())))       /* Nothing to do - the head of the loop does the work */
            {
            }
            configName = configNameLine.Trim();
        }
        // Parse measured information
        List <ItemDescriptionFrequencyDatapoint> datapoints = new List <ItemDescriptionFrequencyDatapoint>();

        using (StreamReader sr = new StreamReader(Path.Combine(resultDir, IOConstants.StatFileNames[IOConstants.StatFile.ItemDescriptionStatistics])))
        {
            // Parse item description information
            string line;
            while ((line = sr.ReadLine()) != null)
            {
                // Trim
                line = line.Trim();
                // Skip empty or comment lines
                if (string.IsNullOrWhiteSpace(line) || line.StartsWith(IOConstants.COMMENT_LINE))
                {
                    continue;
                }
                // Actually parse the line
                datapoints.Add(new ItemDescriptionFrequencyDatapoint(line));
            }
        }
        logger?.Invoke("Found " + datapoints.Count + " datapoints!");
        // Output intermediate file
        string datFilename = filesBasename + ".dat";

        using (StreamWriter sw = new StreamWriter(Path.Combine(resultDir, datFilename), false))
        {
            sw.WriteLine(IOConstants.GNU_PLOT_COMMENT_LINE + "OrderedIndex" + IOConstants.GNU_PLOT_VALUE_SPLIT + "StaticFrequency" + IOConstants.GNU_PLOT_VALUE_SPLIT + "MeasuredFrequency" + IOConstants.GNU_PLOT_VALUE_SPLIT + "OrderCount");
            int newIndex = 0;
            foreach (var datapoint in datapoints.OrderByDescending(dp => dp.StaticFrequency))
            {
                sw.WriteLine(
                    (++newIndex).ToString() + IOConstants.GNU_PLOT_VALUE_SPLIT +
                    datapoint.StaticFrequency.ToString(IOConstants.FORMATTER) + IOConstants.GNU_PLOT_VALUE_SPLIT +
                    datapoint.MeasuredFrequency.ToString(IOConstants.FORMATTER) + IOConstants.GNU_PLOT_VALUE_SPLIT +
                    datapoint.OrderCount.ToString(IOConstants.FORMATTER));
            }
        }
        // Generate plot script
        logger?.Invoke("Generating plot script ...");
        string plotScriptname = filesBasename + ".gp";

        using (StreamWriter sw = new StreamWriter(Path.Combine(resultDir, plotScriptname), false))
        {
            sw.WriteLine("reset");
            sw.WriteLine("# Output definition");
            sw.WriteLine("set terminal pdfcairo enhanced size 7, 3 font \"Consolas, 12\"");
            sw.WriteLine("set lmargin 13");
            sw.WriteLine("set rmargin 13");
            sw.WriteLine("# Parameters");
            sw.WriteLine("set key right top Right");
            sw.WriteLine("set grid");
            sw.WriteLine("# Line-Styles");
            sw.WriteLine("set style line 1 linetype 1 linecolor rgb \"" + PlotColoring.GetHexCode(PlotColors.MediumGrey) + "\" linewidth 3");
            sw.WriteLine("set style line 2 linetype 1 linecolor rgb \"" + PlotColoring.GetHexCode(PlotColors.MediumGreen) + "\" linewidth 3");
            sw.WriteLine("set style line 3 linetype 1 linecolor rgb \"" + PlotColoring.GetHexCode(PlotColors.MediumRed) + "\" linewidth 3");
            sw.WriteLine("set output \"" + filesBasename + ".pdf\"");
            sw.WriteLine("set title \"" + instanceName + " / " + configName + "\"");
            sw.WriteLine("set xlabel \"SKUs\"");
            sw.WriteLine("set ylabel \"Frequency\"");
            sw.WriteLine("set y2label \"# items ordered\"");
            sw.WriteLine("set y2tics");
            sw.WriteLine("set yrange [" + 0.ToString(IOConstants.FORMATTER) + ":" + 1.ToString(IOConstants.FORMATTER) + "]");
            sw.WriteLine("set y2range [" + 0.ToString(IOConstants.FORMATTER) + ":" + (datapoints.Max(d => d.OrderCount)).ToString(IOConstants.FORMATTER) + "]");
            sw.WriteLine("plot \\");
            sw.WriteLine("\"" + datFilename + "\" u 1:2 w lines linestyle 1 t \"Static frequencies\", \\");
            sw.WriteLine("\"" + datFilename + "\" u 1:3 w lines linestyle 2 t \"Measured frequencies\", \\");
            sw.WriteLine("\"" + datFilename + "\" u 1:4 w lines axes x1y2 linestyle 3 t \"Order count\"");
            sw.WriteLine("reset");
            sw.WriteLine("exit");
        }
        // Execute plot script
        string commandScriptname = filesBasename + ".cmd";

        using (StreamWriter sw = new StreamWriter(Path.Combine(resultDir, commandScriptname), false))
            sw.WriteLine("gnuplot " + Path.GetFileName(plotScriptname));
        // Execute plot script
        DataProcessor.ExecuteScript(Path.Combine(resultDir, commandScriptname), logger);
    }
Exemplo n.º 3
0
        /// <summary>
        /// Plots all well sortedness graphs the processor can find in a direct sub-directory of the given one.
        /// </summary>
        /// <param name="parentDirectory"></param>
        /// <param name="dataType">The type of data to plot.</param>
        /// <param name="overlayType">Indicates which overlay will be plotted.</param>
        /// <param name="pathtimeAggregationLength">The length in path time to group different rows of pods.</param>
        /// <param name="timeAggregationLength">The legnth in time to aggregate results by.</param>
        public static void PlotAll(string parentDirectory, WellsortednessBaseDataType dataType, WellsortednessPlotOverlay overlayType, double pathtimeAggregationLength, double timeAggregationLength)
        {
            // Get directories
            string[] dirs = Directory.EnumerateDirectories(parentDirectory).ToArray();
            // Iterate all dirs
            Parallel.ForEach(dirs, new ParallelOptions()
            {
                MaxDegreeOfParallelism = 1                                            /* No parallelization for now, this may be a reason for weird behavior that was observed */
            }, (string dir) =>
            {
                // Log
                Console.WriteLine("Preparing dir: " + dir + " ...");
                // Check for the file
                string dataFile = Path.Combine(dir, IOConstants.StatFileNames[IOConstants.StatFile.WellSortednessPollingRaw]);
                if (File.Exists(dataFile))
                {
                    // --> Parse the instance and config names
                    string instanceName;
                    using (StreamReader sr = new StreamReader(Path.Combine(dir, IOConstants.StatFileNames[IOConstants.StatFile.InstanceName])))
                    {
                        string instanceNameLine = "";
                        while (string.IsNullOrWhiteSpace((instanceNameLine = sr.ReadLine()))) /* Nothing to do - the head of the loop does the work */ } {
                        instanceName = instanceNameLine.Trim();
                }
                // Fetch config name
                string configName;
                using (StreamReader sr = new StreamReader(Path.Combine(dir, IOConstants.StatFileNames[IOConstants.StatFile.ControllerName])))
                {
                    string configNameLine = "";
                    while (string.IsNullOrWhiteSpace((configNameLine = sr.ReadLine())))       /* Nothing to do - the head of the loop does the work */
                    {
                    }
                    configName = configNameLine.Trim();
                }
                // --> Parse the data
                List <WellSortednessDatapoint> datapoints = new List <WellSortednessDatapoint>();
                using (StreamReader sr = new StreamReader(dataFile))
                {
                    string line;
                    while ((line = sr.ReadLine()) != null)
                    {
                        // Trim
                        line = line.Trim();
                        // Skip empty or comment lines
                        if (string.IsNullOrWhiteSpace(line) || line.StartsWith(IOConstants.COMMENT_LINE))
                        {
                            continue;
                        }
                        // Actually parse the line
                        datapoints.Add(new WellSortednessDatapoint(line));
                    }
                }
                // Sort all datapoints
                datapoints = datapoints.OrderBy(d => d.TimeStamp).ToList();
                // --> Aggregate datapoints per time window (if desired)
                bool aggregateTime = datapoints.Select(d => d.TimeStamp).Distinct().AllOrderedTuple((double first, double second) => second - first < timeAggregationLength);
                if (aggregateTime)
                {
                    double currentWindowEnd = timeAggregationLength;
                    List <WellSortednessDatapoint> aggregatedDatapoints = new List <WellSortednessDatapoint>();
                    while (datapoints.Any())
                    {
                        // Get datapoints of current window
                        List <WellSortednessDatapoint> windowDatapoints = datapoints.TakeWhile(d => d.TimeStamp < currentWindowEnd).ToList();
                        // Check whether there are any datapoints in the timewindow
                        if (windowDatapoints.Any())
                        {
                            // Remove them from the original list
                            datapoints.RemoveAll(d => d.TimeStamp < currentWindowEnd);
                            // Convert path time info
                            var groups = windowDatapoints.SelectMany(d => d.PathTimeFrequencies).GroupBy(d => d.PathTime);
                            // Create aggregated datapoints
                            aggregatedDatapoints.Add(new WellSortednessDatapoint(
                                                         // New timestamp is right in the middle of the current window
                                                         currentWindowEnd - (timeAggregationLength / 2.0),
                                                         groups.Select(g => new WellSortednessPathTimeTuple()
                                {
                                    PathTime = g.Key,
                                    PodCount = g.Average(e => e.PodCount),
                                    // Make sure there is only one storage location count for all datapoints in the time window for this row
                                    StorageLocationCount  = g.Select(e => e.StorageLocationCount).Distinct().Single(),
                                    ContentFrequencySum   = g.Average(e => e.ContentFrequencySum),
                                    OutputStationTripsSum = g.Average(e => e.OutputStationTripsSum),
                                    SKUFrequencySum       = g.Average(e => e.SKUFrequencySum),
                                    PodSpeedSum           = g.Average(e => e.PodSpeedSum),
                                    PodUtilitySum         = g.Average(e => e.PodUtilitySum),
                                    PodCombinedScoreSum   = g.Average(e => e.PodCombinedScoreSum),
                                }).ToList()));
                        }
                        // Move time window
                        currentWindowEnd += timeAggregationLength;
                    }
                    // Replace original datapoints with aggregated ones
                    datapoints = aggregatedDatapoints;
                }
                // --> Aggregate datapoints per path time length (if desired)
                if (pathtimeAggregationLength > 0)
                {
                    foreach (var datapoint in datapoints)
                    {
                        double currentWindowEnd       = pathtimeAggregationLength;
                        datapoint.PathTimeFrequencies = datapoint.PathTimeFrequencies.OrderBy(d => d.PathTime).ToList();
                        List <WellSortednessPathTimeTuple> aggregatedDatapoints = new List <WellSortednessPathTimeTuple>();
                        while (datapoint.PathTimeFrequencies.Any())
                        {
                            // Get datapoints of current window
                            List <WellSortednessPathTimeTuple> windowDatapoints = datapoint.PathTimeFrequencies.TakeWhile(d => d.PathTime < currentWindowEnd).ToList();
                            // Check whether there are any datapoints in the timewindow
                            if (windowDatapoints.Any())
                            {
                                // Remove them from the original list
                                datapoint.PathTimeFrequencies.RemoveAll(d => d.PathTime < currentWindowEnd);
                                // Create aggregated datapoints
                                aggregatedDatapoints.Add(new WellSortednessPathTimeTuple()
                                    {
                                        // New pathtime is right in the middle of the current window
                                        PathTime = currentWindowEnd - (pathtimeAggregationLength / 2.0),
                                        PodCount = windowDatapoints.Sum(d => d.PodCount),
                                        // Make sure there is only one storage location count for all datapoints in the time window for this row
                                        StorageLocationCount  = windowDatapoints.Sum(e => e.StorageLocationCount),
                                        ContentFrequencySum   = windowDatapoints.Sum(d => d.ContentFrequencySum),
                                        OutputStationTripsSum = windowDatapoints.Sum(d => d.OutputStationTripsSum),
                                        SKUFrequencySum       = windowDatapoints.Sum(d => d.SKUFrequencySum),
                                        PodSpeedSum           = windowDatapoints.Sum(d => d.PodSpeedSum),
                                        PodUtilitySum         = windowDatapoints.Sum(d => d.PodUtilitySum),
                                        PodCombinedScoreSum   = windowDatapoints.Sum(d => d.PodCombinedScoreSum),
                                    });
                            }
                            // Move time window
                            currentWindowEnd += pathtimeAggregationLength;
                        }
                        // Replace original datapoints with aggregated ones
                        datapoint.PathTimeFrequencies = aggregatedDatapoints;
                    }
                }
                // --> Parse information about throughput times
                string orderDataFile    = Path.Combine(dir, IOConstants.StatFileNames[IOConstants.StatFile.OrderProgressionRaw]);
                string bundleDataFile   = Path.Combine(dir, IOConstants.StatFileNames[IOConstants.StatFile.BundleProgressionRaw]);
                string distanceDataFile = Path.Combine(dir, IOConstants.StatFileNames[IOConstants.StatFile.TraveledDistanceProgressionRaw]);
                Dictionary <double, double> orderThroughputTimes  = new Dictionary <double, double>();
                Dictionary <double, double> bundleThroughputTimes = new Dictionary <double, double>();
                Dictionary <double, double> orderTurnoverTimes    = new Dictionary <double, double>();
                Dictionary <double, double> bundleTurnoverTimes   = new Dictionary <double, double>();
                Dictionary <double, int> orderCounts         = new Dictionary <double, int>();
                Dictionary <double, int> bundleCounts        = new Dictionary <double, int>();
                Dictionary <double, double> distanceTraveled = new Dictionary <double, double>();
                // Get information about order handling over time
                using (StreamReader sr = new StreamReader(orderDataFile))
                {
                    // Parse order information
                    List <OrderHandledDatapoint> orderHandledDatapoints = new List <OrderHandledDatapoint>(); string line;
                    while ((line = sr.ReadLine()) != null)
                    {
                        // Trim
                        line = line.Trim();
                        // Skip empty or comment lines
                        if (string.IsNullOrWhiteSpace(line) || line.StartsWith(IOConstants.COMMENT_LINE))
                        {
                            continue;
                        }
                        // Actually parse the line
                        orderHandledDatapoints.Add(new OrderHandledDatapoint(line));
                    }
                    // Obtain values for the different time-stamps
                    orderHandledDatapoints = orderHandledDatapoints.OrderBy(d => d.TimeStamp).ToList();
                    // Get data per timestamp
                    double previousThroughputValue = 0; double previousTurnoverValue = 0;
                    foreach (var timestamp in datapoints.Select(d => d.TimeStamp).Distinct().OrderBy(d => d))
                    {
                        IEnumerable <OrderHandledDatapoint> datapointsOfSection = orderHandledDatapoints.TakeWhile(d => d.TimeStamp <= timestamp);
                        // Measure count
                        orderCounts[timestamp] = datapointsOfSection.Count();
                        // Measure timings
                        if (datapointsOfSection.Any())
                        {
                            // Measure avg. time
                            orderThroughputTimes[timestamp] = datapointsOfSection.Average(d => d.ThroughputTime);
                            previousThroughputValue         = orderThroughputTimes[timestamp];
                            orderTurnoverTimes[timestamp]   = datapointsOfSection.Average(d => d.TurnoverTime);
                            previousTurnoverValue           = orderTurnoverTimes[timestamp];
                        }
                        else
                        {
                            // No new measurement - use the one from the point before
                            orderThroughputTimes[timestamp] = previousThroughputValue;
                            orderTurnoverTimes[timestamp]   = previousTurnoverValue;
                        }
                        orderHandledDatapoints = orderHandledDatapoints.Skip(datapointsOfSection.Count()).ToList();
                    }
                }
                // Get information about bundle handling over time
                using (StreamReader sr = new StreamReader(bundleDataFile))
                {
                    // Parse bundle information
                    List <BundleHandledDatapoint> bundleHandledDatapoints = new List <BundleHandledDatapoint>(); string line;
                    while ((line = sr.ReadLine()) != null)
                    {
                        // Trim
                        line = line.Trim();
                        // Skip empty or comment lines
                        if (string.IsNullOrWhiteSpace(line) || line.StartsWith(IOConstants.COMMENT_LINE))
                        {
                            continue;
                        }
                        // Actually parse the line
                        bundleHandledDatapoints.Add(new BundleHandledDatapoint(line));
                    }
                    // Obtain values for the different time-stamps
                    bundleHandledDatapoints = bundleHandledDatapoints.OrderBy(d => d.TimeStamp).ToList();
                    // Get data per timestamp
                    double previousThroughputValue = 0; double previousTurnoverValue = 0;
                    foreach (var timestamp in datapoints.Select(d => d.TimeStamp).Distinct().OrderBy(d => d))
                    {
                        IEnumerable <BundleHandledDatapoint> datapointsOfSection = bundleHandledDatapoints.TakeWhile(d => d.TimeStamp <= timestamp);
                        // Measure count
                        bundleCounts[timestamp] = datapointsOfSection.Count();
                        // Measure timings
                        if (datapointsOfSection.Any())
                        {
                            // Measure avg. time
                            bundleThroughputTimes[timestamp] = datapointsOfSection.Average(d => d.ThroughputTime);
                            previousThroughputValue          = bundleThroughputTimes[timestamp];
                            bundleTurnoverTimes[timestamp]   = datapointsOfSection.Average(d => d.ThroughputTime);
                            previousTurnoverValue            = bundleTurnoverTimes[timestamp];
                        }
                        else
                        {
                            // No new measurement - use the one from the point before
                            bundleThroughputTimes[timestamp] = previousThroughputValue;
                            bundleTurnoverTimes[timestamp]   = previousTurnoverValue;
                        }
                        bundleHandledDatapoints = bundleHandledDatapoints.Skip(datapointsOfSection.Count()).ToList();
                    }
                }
                // Get information about distance traveled over time
                using (StreamReader sr = new StreamReader(distanceDataFile))
                {
                    // Parse datapoints
                    List <DistanceDatapoint> distanceTraveledDatapoints = new List <DistanceDatapoint>(); string line;
                    while ((line = sr.ReadLine()) != null)
                    {
                        // Trim
                        line = line.Trim();
                        // Skip empty or comment lines
                        if (string.IsNullOrWhiteSpace(line) || line.StartsWith(IOConstants.COMMENT_LINE))
                        {
                            continue;
                        }
                        // Actually parse the line
                        distanceTraveledDatapoints.Add(new DistanceDatapoint(line));
                    }
                    // Obtain values for the different time-stamps
                    distanceTraveledDatapoints = distanceTraveledDatapoints.OrderBy(d => d.TimeStamp).ToList();
                    // Get data per timestamp
                    foreach (var timestamp in datapoints.Select(d => d.TimeStamp).Distinct().OrderBy(d => d))
                    {
                        // Get datapoints of time window
                        IEnumerable <DistanceDatapoint> datapointsOfSection = distanceTraveledDatapoints.TakeWhile(d => d.TimeStamp <= timestamp);
                        // Measure distance traveled within time window
                        distanceTraveled[timestamp] = datapointsOfSection.Sum(d => d.DistanceTraveled);
                        // Remove measured datapoints
                        distanceTraveledDatapoints = distanceTraveledDatapoints.Skip(datapointsOfSection.Count()).ToList();
                    }
                }
                // --> Get information about the storage locations per pathtime
                Dictionary <double, int> storageLocationsPerPathTime = datapoints.First().PathTimeFrequencies.ToDictionary(k => k.PathTime, v => (int)v.StorageLocationCount);
                // Log
                Console.WriteLine("Parsed " + datapoints.Count + " timestamps - plotting ...");
                // Initiate dat-file generator
                double maxPathTime = double.NegativeInfinity;
                double maxValue    = double.NegativeInfinity;
                Action <IDictionary <double, string> > fileGenerator =
                    (IDictionary <double, string> files) =>
                {
                    // Write a dat file for all available timestamps
                    foreach (var timeEntry in datapoints)
                    {
                        // Write the dat file for the plot at this timestamp
                        string fileName            = GetRelDatFileName(dir, dataType, timeEntry.TimeStamp);
                        files[timeEntry.TimeStamp] = fileName;
                        using (StreamWriter sw = new StreamWriter(fileName))
                        {
                            sw.WriteLine(IOConstants.GNU_PLOT_COMMENT_LINE + " pathtime " + dataType.ToString());
                            foreach (var pathTimeEntry in timeEntry.PathTimeFrequencies)
                            {
                                // Write the single line for the respective pathtime
                                sw.WriteLine(
                                    pathTimeEntry.PathTime.ToString(IOConstants.FORMATTER) + IOConstants.GNU_PLOT_VALUE_SPLIT +
                                    GetWellsortednessValue(pathTimeEntry, dataType).ToString(IOConstants.FORMATTER));
                                // Keep track of max values for setting ranges later on
                                if (pathTimeEntry.PathTime > maxPathTime)
                                {
                                    maxPathTime = pathTimeEntry.PathTime;
                                }
                                if (GetWellsortednessValue(pathTimeEntry, dataType) > maxValue)
                                {
                                    maxValue = GetWellsortednessValue(pathTimeEntry, dataType);
                                }
                            }
                        }
                    }
                };
                // --> Prepare intermediate files
                // Prepare frequency files
                Dictionary <double, string> frequencyDataFiles = new Dictionary <double, string>();
                fileGenerator(frequencyDataFiles);
                // Prepare throughput intermediate file
                string throughputDatafile = "wellsortednessMetaData.dat";
                using (StreamWriter sw = new StreamWriter(Path.Combine(dir, throughputDatafile), false))
                {
                    sw.WriteLine(IOConstants.GNU_PLOT_COMMENT_LINE + " timestamp bundlethroughput orderthroughput bundlecount ordercount distancetraveled");
                    foreach (var timestamp in datapoints.Select(d => d.TimeStamp).OrderBy(d => d))
                    {
                        // Write the single line for the respective timestamp
                        sw.WriteLine(
                            timestamp.ToString(IOConstants.FORMATTER) + IOConstants.GNU_PLOT_VALUE_SPLIT +
                            bundleThroughputTimes[timestamp].ToString(IOConstants.FORMATTER) + IOConstants.GNU_PLOT_VALUE_SPLIT +
                            orderThroughputTimes[timestamp].ToString(IOConstants.FORMATTER) + IOConstants.GNU_PLOT_VALUE_SPLIT +
                            bundleTurnoverTimes[timestamp].ToString(IOConstants.FORMATTER) + IOConstants.GNU_PLOT_VALUE_SPLIT +
                            orderTurnoverTimes[timestamp].ToString(IOConstants.FORMATTER) + IOConstants.GNU_PLOT_VALUE_SPLIT +
                            bundleCounts[timestamp].ToString(IOConstants.FORMATTER) + IOConstants.GNU_PLOT_VALUE_SPLIT +
                            orderCounts[timestamp].ToString(IOConstants.FORMATTER) + IOConstants.GNU_PLOT_VALUE_SPLIT +
                            distanceTraveled[timestamp].ToString(IOConstants.FORMATTER));
                    }
                }
                // Prepare storage locations intermediate file
                string storageLocationsDataFile = "wellsortednessStorageLocations.dat";
                using (StreamWriter sw = new StreamWriter(Path.Combine(dir, storageLocationsDataFile), false))
                {
                    sw.WriteLine(IOConstants.GNU_PLOT_COMMENT_LINE + " pathtime storagelocationcount");
                    foreach (var pathTimeTuple in storageLocationsPerPathTime.OrderBy(kvp => kvp.Key))
                    {
                        // Write the single line for the respective pathtime
                        sw.WriteLine(
                            pathTimeTuple.Key.ToString(IOConstants.FORMATTER) + IOConstants.GNU_PLOT_VALUE_SPLIT +
                            pathTimeTuple.Value.ToString(IOConstants.FORMATTER));
                    }
                }
                // Generate plot script
                string plotScriptName = Path.Combine(dir, "wellsortedness-" + dataType.ToString() + "-" + overlayType.ToString() + ".gp");
                using (StreamWriter sw = new StreamWriter(plotScriptName))
                {
                    // Init
                    sw.WriteLine("reset");
                    sw.WriteLine("# Output definition");
                    sw.WriteLine("set terminal pdfcairo enhanced size 7, 3 font \"Consolas, 12\"");
                    sw.WriteLine("set lmargin 13");
                    sw.WriteLine("set rmargin 13");
                    // Define first parameters
                    sw.WriteLine("# Parameters");
                    sw.WriteLine("set key right top Right");
                    sw.WriteLine("set xlabel \"PathTime\"");
                    sw.WriteLine("set xrange [" + 0.ToString(IOConstants.FORMATTER) + ":" + (maxPathTime * 1.1).ToString(IOConstants.FORMATTER) + "]");
                    sw.WriteLine("set grid");
                    sw.WriteLine("set style fill solid 0.75");
                    // Define line styles
                    sw.WriteLine("# Line-Styles");
                    PlotColors overlayColor = PlotColors.MediumGreen;
                    switch (overlayType)
                    {
                    case WellsortednessPlotOverlay.BundleThroughputTime: overlayColor = PlotColors.MediumTurquoise; break;

                    case WellsortednessPlotOverlay.OrderThroughputTime: overlayColor = PlotColors.MediumGreen; break;

                    case WellsortednessPlotOverlay.BundleTurnoverTime: overlayColor = PlotColors.MediumBlue; break;

                    case WellsortednessPlotOverlay.OrderTurnoverTime: overlayColor = PlotColors.MediumOrange; break;

                    case WellsortednessPlotOverlay.BundleCount: overlayColor = PlotColors.MediumViolet; break;

                    case WellsortednessPlotOverlay.OrderCount: overlayColor = PlotColors.MediumRed; break;

                    case WellsortednessPlotOverlay.DistanceTraveled: overlayColor = PlotColors.MediumYellow; break;

                    case WellsortednessPlotOverlay.None:
                    default: /* Color will not be used anyway */ break;
                    }
                    sw.WriteLine("set style line 1 linetype 1 linecolor rgb \"" + PlotColoring.GetHexCode(PlotColors.MediumBlue) + "\" linewidth 1");
                    sw.WriteLine("set style line 2 linetype 1 linecolor rgb \"" + PlotColoring.GetHexCode(overlayColor) + "\" linewidth 3");
                    sw.WriteLine("set style line 3 linetype 1 linecolor rgb \"" + PlotColoring.GetHexCode(PlotColors.MediumGrey) + "\" linewidth 2 pt 2");
                    // Add pathtime plot
                    sw.WriteLine("set output \"wellsortednessGraphStorageLocations.pdf\"");
                    sw.WriteLine("set yrange [" + 0.ToString(IOConstants.FORMATTER) + ":" + (storageLocationsPerPathTime.Values.Max() * 1.1).ToString(IOConstants.FORMATTER) + "]");
                    sw.WriteLine("set ylabel \"Count\"");
                    sw.WriteLine("plot \\");
                    sw.WriteLine("\"" + storageLocationsDataFile + "\" u 1:2 w boxes linestyle 1 t \"storage locations\"");
                    // Define parameters for the frequency plots
                    switch (overlayType)
                    {
                    case WellsortednessPlotOverlay.None: /* Nothing to do */ break;

                    case WellsortednessPlotOverlay.BundleThroughputTime:
                        {
                            sw.WriteLine("set x2label \"Simulation time\"");
                            sw.WriteLine("set y2label \"Throughput time\"");
                            sw.WriteLine("set x2tics");
                            sw.WriteLine("set y2tics");
                            sw.WriteLine("set x2range [" + 0.ToString(IOConstants.FORMATTER) + ":" + bundleThroughputTimes.Max(t => t.Key).ToString(IOConstants.FORMATTER) + "]");
                            sw.WriteLine("set y2range [" + (0.9 * bundleThroughputTimes.Min(t => t.Value)).ToString(IOConstants.FORMATTER) + ":" +
                                         (1.1 * bundleThroughputTimes.Max(t => t.Value)).ToString(IOConstants.FORMATTER) + "]");
                        }
                        break;

                    case WellsortednessPlotOverlay.OrderThroughputTime:
                        {
                            sw.WriteLine("set x2label \"Simulation time\"");
                            sw.WriteLine("set y2label \"Throughput time\"");
                            sw.WriteLine("set x2tics");
                            sw.WriteLine("set y2tics");
                            sw.WriteLine("set x2range [" + 0.ToString(IOConstants.FORMATTER) + ":" + orderThroughputTimes.Max(t => t.Key).ToString(IOConstants.FORMATTER) + "]");
                            sw.WriteLine("set y2range [" + (0.9 * orderThroughputTimes.Min(t => t.Value)).ToString(IOConstants.FORMATTER) + ":" +
                                         (1.1 * orderThroughputTimes.Max(t => t.Value)).ToString(IOConstants.FORMATTER) + "]");
                        }
                        break;

                    case WellsortednessPlotOverlay.BundleTurnoverTime:
                        {
                            sw.WriteLine("set x2label \"Simulation time\"");
                            sw.WriteLine("set y2label \"Turnover time\"");
                            sw.WriteLine("set x2tics");
                            sw.WriteLine("set y2tics");
                            sw.WriteLine("set x2range [" + 0.ToString(IOConstants.FORMATTER) + ":" + bundleTurnoverTimes.Max(t => t.Key).ToString(IOConstants.FORMATTER) + "]");
                            sw.WriteLine("set y2range [" + (0.9 * bundleTurnoverTimes.Min(t => t.Value)).ToString(IOConstants.FORMATTER) + ":" +
                                         (1.1 * bundleTurnoverTimes.Max(t => t.Value)).ToString(IOConstants.FORMATTER) + "]");
                        }
                        break;

                    case WellsortednessPlotOverlay.OrderTurnoverTime:
                        {
                            sw.WriteLine("set x2label \"Simulation time\"");
                            sw.WriteLine("set y2label \"Turnover time\"");
                            sw.WriteLine("set x2tics");
                            sw.WriteLine("set y2tics");
                            sw.WriteLine("set x2range [" + 0.ToString(IOConstants.FORMATTER) + ":" + orderTurnoverTimes.Max(t => t.Key).ToString(IOConstants.FORMATTER) + "]");
                            sw.WriteLine("set y2range [" + (0.9 * orderTurnoverTimes.Min(t => t.Value)).ToString(IOConstants.FORMATTER) + ":" +
                                         (1.1 * orderTurnoverTimes.Max(t => t.Value)).ToString(IOConstants.FORMATTER) + "]");
                        }
                        break;

                    case WellsortednessPlotOverlay.BundleCount:
                        {
                            sw.WriteLine("set x2label \"Simulation time\"");
                            sw.WriteLine("set y2label \"Bundle count\"");
                            sw.WriteLine("set x2tics");
                            sw.WriteLine("set y2tics");
                            sw.WriteLine("set x2range [" + 0.ToString(IOConstants.FORMATTER) + ":" + bundleCounts.Max(t => t.Key).ToString(IOConstants.FORMATTER) + "]");
                            sw.WriteLine("set y2range [" + (0.9 * bundleCounts.Min(t => t.Value)).ToString(IOConstants.FORMATTER) + ":" +
                                         (1.1 * bundleCounts.Max(t => t.Value)).ToString(IOConstants.FORMATTER) + "]");
                        }
                        break;

                    case WellsortednessPlotOverlay.OrderCount:
                        {
                            sw.WriteLine("set x2label \"Simulation time\"");
                            sw.WriteLine("set y2label \"Order count\"");
                            sw.WriteLine("set x2tics");
                            sw.WriteLine("set y2tics");
                            sw.WriteLine("set x2range [" + 0.ToString(IOConstants.FORMATTER) + ":" + orderCounts.Max(t => t.Key).ToString(IOConstants.FORMATTER) + "]");
                            sw.WriteLine("set y2range [" + (0.9 * orderCounts.Min(t => t.Value)).ToString(IOConstants.FORMATTER) + ":" +
                                         (1.1 * orderCounts.Max(t => t.Value)).ToString(IOConstants.FORMATTER) + "]");
                        }
                        break;

                    case WellsortednessPlotOverlay.DistanceTraveled:
                        {
                            sw.WriteLine("set x2label \"Simulation time\"");
                            sw.WriteLine("set y2label \"Distance traveled (in m)\"");
                            sw.WriteLine("set x2tics");
                            sw.WriteLine("set y2tics");
                            sw.WriteLine("set x2range [" + 0.ToString(IOConstants.FORMATTER) + ":" + distanceTraveled.Max(t => t.Key).ToString(IOConstants.FORMATTER) + "]");
                            sw.WriteLine("set y2range [" + (0.9 * distanceTraveled.Min(t => t.Value)).ToString(IOConstants.FORMATTER) + ":" +
                                         (1.1 * distanceTraveled.Max(t => t.Value)).ToString(IOConstants.FORMATTER) + "]");
                        }
                        break;

                    default: throw new ArgumentException("Unknown overlay type: " + overlayType);
                    }
                    // Quick define script generator function
                    Action <KeyValuePair <double, string>, int> plotScriptGenAction = (KeyValuePair <double, string> plotdatafile, int datIndex) =>
                    {
                        string datFile = Path.GetFileName(Path.GetDirectoryName(plotdatafile.Value)) + "/" + Path.GetFileName(plotdatafile.Value);
                        sw.WriteLine("set title \"" + instanceName + " / " + configName + " / " + TimeSpan.FromSeconds(plotdatafile.Key).ToString(IOConstants.TIMESPAN_FORMAT_HUMAN_READABLE_DAYS) + "\"");
                        switch (overlayType)
                        {
                        case WellsortednessPlotOverlay.None:
                            {
                                sw.WriteLine("plot \\");
                                sw.WriteLine("\"" + datFile + "\" u 1:" + datIndex + " w boxes linestyle 1 t \"well-sortedness\"");
                            }
                            break;

                        case WellsortednessPlotOverlay.BundleThroughputTime:
                            {
                                sw.WriteLine("overlayx=" + plotdatafile.Key.ToString(IOConstants.FORMATTER));
                                sw.WriteLine("overlayy=" + bundleThroughputTimes[plotdatafile.Key].ToString(IOConstants.FORMATTER));
                                sw.WriteLine("plot \\");
                                sw.WriteLine("\"" + datFile + "\" u 1:" + datIndex + " w boxes linestyle 1 t \"well-sortedness\", \\");
                                sw.WriteLine("\"" + throughputDatafile + "\" u 1:2 w steps axes x2y2 linestyle 2 t \"bundle throughput time\", \\");
                                sw.WriteLine("\"+\" u (overlayx):(overlayy) w points axes x2y2 linestyle 3 t \"current time\"");
                            }
                            break;

                        case WellsortednessPlotOverlay.OrderThroughputTime:
                            {
                                sw.WriteLine("overlayx=" + plotdatafile.Key.ToString(IOConstants.FORMATTER));
                                sw.WriteLine("overlayy=" + orderThroughputTimes[plotdatafile.Key].ToString(IOConstants.FORMATTER));
                                sw.WriteLine("plot \\");
                                sw.WriteLine("\"" + datFile + "\" u 1:" + datIndex + " w boxes linestyle 1 t \"well-sortedness\", \\");
                                sw.WriteLine("\"" + throughputDatafile + "\" u 1:3 w steps axes x2y2 linestyle 2 t \"order throughput time\", \\");
                                sw.WriteLine("\"+\" u (overlayx):(overlayy) w points axes x2y2 linestyle 3 t \"current time\"");
                            }
                            break;

                        case WellsortednessPlotOverlay.BundleTurnoverTime:
                            {
                                sw.WriteLine("overlayx=" + plotdatafile.Key.ToString(IOConstants.FORMATTER));
                                sw.WriteLine("overlayy=" + bundleTurnoverTimes[plotdatafile.Key].ToString(IOConstants.FORMATTER));
                                sw.WriteLine("plot \\");
                                sw.WriteLine("\"" + datFile + "\" u 1:" + datIndex + " w boxes linestyle 1 t \"well-sortedness\", \\");
                                sw.WriteLine("\"" + throughputDatafile + "\" u 1:4 w steps axes x2y2 linestyle 2 t \"bundle turnover time\", \\");
                                sw.WriteLine("\"+\" u (overlayx):(overlayy) w points axes x2y2 linestyle 3 t \"current time\"");
                            }
                            break;

                        case WellsortednessPlotOverlay.OrderTurnoverTime:
                            {
                                sw.WriteLine("overlayx=" + plotdatafile.Key.ToString(IOConstants.FORMATTER));
                                sw.WriteLine("overlayy=" + orderTurnoverTimes[plotdatafile.Key].ToString(IOConstants.FORMATTER));
                                sw.WriteLine("plot \\");
                                sw.WriteLine("\"" + datFile + "\" u 1:" + datIndex + " w boxes linestyle 1 t \"well-sortedness\", \\");
                                sw.WriteLine("\"" + throughputDatafile + "\" u 1:5 w steps axes x2y2 linestyle 2 t \"order turnover time\", \\");
                                sw.WriteLine("\"+\" u (overlayx):(overlayy) w points axes x2y2 linestyle 3 t \"current time\"");
                            }
                            break;

                        case WellsortednessPlotOverlay.BundleCount:
                            {
                                sw.WriteLine("overlayx=" + plotdatafile.Key.ToString(IOConstants.FORMATTER));
                                sw.WriteLine("overlayy=" + bundleCounts[plotdatafile.Key].ToString(IOConstants.FORMATTER));
                                sw.WriteLine("plot \\");
                                sw.WriteLine("\"" + datFile + "\" u 1:" + datIndex + " w boxes linestyle 1 t \"well-sortedness\", \\");
                                sw.WriteLine("\"" + throughputDatafile + "\" u 1:6 w steps axes x2y2 linestyle 2 t \"bundles stored\", \\");
                                sw.WriteLine("\"+\" u (overlayx):(overlayy) w points axes x2y2 linestyle 3 t \"current time\"");
                            }
                            break;

                        case WellsortednessPlotOverlay.OrderCount:
                            {
                                sw.WriteLine("overlayx=" + plotdatafile.Key.ToString(IOConstants.FORMATTER));
                                sw.WriteLine("overlayy=" + orderCounts[plotdatafile.Key].ToString(IOConstants.FORMATTER));
                                sw.WriteLine("plot \\");
                                sw.WriteLine("\"" + datFile + "\" u 1:" + datIndex + " w boxes linestyle 1 t \"well-sortedness\", \\");
                                sw.WriteLine("\"" + throughputDatafile + "\" u 1:7 w steps axes x2y2 linestyle 2 t \"orders fulfilled\", \\");
                                sw.WriteLine("\"+\" u (overlayx):(overlayy) w points axes x2y2 linestyle 3 t \"current time\"");
                            }
                            break;

                        case WellsortednessPlotOverlay.DistanceTraveled:
                            {
                                sw.WriteLine("overlayx=" + plotdatafile.Key.ToString(IOConstants.FORMATTER));
                                sw.WriteLine("overlayy=" + distanceTraveled[plotdatafile.Key].ToString(IOConstants.FORMATTER));
                                sw.WriteLine("plot \\");
                                sw.WriteLine("\"" + datFile + "\" u 1:" + datIndex + " w boxes linestyle 1 t \"well-sortedness\", \\");
                                sw.WriteLine("\"" + throughputDatafile + "\" u 1:8 w steps axes x2y2 linestyle 2 t \"distance traveled\", \\");
                                sw.WriteLine("\"+\" u (overlayx):(overlayy) w points axes x2y2 linestyle 3 t \"current time\"");
                            }
                            break;

                        default:
                            break;
                        }
                    };
                    // Add frequency plots
                    sw.WriteLine("set output \"ws-" + dataType.ToString() + "-" + overlayType.ToString() + ".pdf\"");
                    sw.WriteLine("set yrange [" + 0.ToString(IOConstants.FORMATTER) + ":" + (maxValue * 1.1).ToString(IOConstants.FORMATTER) + "]");
                    sw.WriteLine("set ylabel \"Value\"");
                    foreach (var plotdatafile in frequencyDataFiles.OrderBy(p => p.Key))
                    {
                        plotScriptGenAction(plotdatafile, 2);
                    }
                    sw.WriteLine("reset");
                    sw.WriteLine("exit");
                }
                string commandScriptName = Path.Combine(dir, Path.GetFileNameWithoutExtension(plotScriptName) + ".cmd");
                using (StreamWriter sw = new StreamWriter(commandScriptName))
                {
                    sw.WriteLine("gnuplot " + Path.GetFileName(plotScriptName));
                }
                // Log
                Console.WriteLine("Calling plot script ...");
                // Execute plot script
                DataProcessor.ExecuteScript(commandScriptName, (string msg) => { Console.WriteLine(msg); });
            }
});
        }