diff --git a/tools/plotting/fastnnlo_approxtest_v2.py b/tools/plotting/fastnnlo_approxtest_v2.py
new file mode 100755
index 0000000000000000000000000000000000000000..f7e2ce6e3ab6d350d759c0d8e7d8d38aa0b9e0fe
--- /dev/null
+++ b/tools/plotting/fastnnlo_approxtest_v2.py
@@ -0,0 +1,670 @@
+#!/usr/bin/env python2
+#-*- coding:utf-8 -*-
+#
+# Make statistical evaluation plots of ensemble of one-to-one comparisons
+# between fastNLO interpolation tables and NNLOJET original results
+#
+# Version:
+#
+# created by K. Rabbertz: 13.07.2017
+# modified by B. Schillinger: 04.09.2018
+#
+#-----------------------------------------------------------------------
+#
+import argparse
+import glob
+import os
+import sys
+import matplotlib as mpl
+# mpl.use('Agg')
+import matplotlib.lines as mlines
+import matplotlib.patches as mpatches
+import matplotlib.pyplot as plt
+from matplotlib.ticker import (
+    MultipleLocator, FormatStrFormatter, AutoMinorLocator)  # for xticks
+import numpy as np
+# from copy import deepcopy
+from matplotlib import cm
+from fastnlo import fastNLOLHAPDF
+from fastnlo import SetGlobalVerbosity
+import re
+from StringIO import StringIO
+#import warnings
+# warnings.filterwarnings("error")
+
+
+def main():
+    parser = argparse.ArgumentParser()
+
+    # add arguments
+    parser.add_argument('-d', '--datfile', default='file.dat', required=True, nargs='?',
+                        help='.dat file for evaluation. All other matching .dat files that '
+                        'only differ in seed no. will be evaluated as well.')
+    parser.add_argument('-w', '--weightfile', default='weight.txt', required=True, nargs='?',
+                        help='.txt file containing weights.')
+    parser.add_argument('-v', '--variation', action='store_true',  # boolean value, True if -v chosen
+                        help='If option is chosen: Plotting for all scale variations.'
+                        'Otherwise (per default): Produce only central scale plots.'
+                        'If specific scale is chosen via -f <int>, this option (-v) is ignored.')
+
+    parser.add_argument('-f', '--fscl', nargs='?', type=int, required=False,
+                        help='Possible choices: int from fscl=1 to fscl=7.')
+
+    parser.add_argument('-o', '--outputfilename', required=False, nargs='?', type=str,
+                        help='Customise the first part of the output filename.'
+                        'Default: Same structure as datfile name.')
+
+    # parse arguments
+    args = vars(parser.parse_args())
+    namesp = parser.parse_args()
+    print "Plots for all scale variations chosen? -->", args['variation']
+
+    ################################################################################
+    # Style settings
+    # Location of matplotlibrc
+    # print mpl.matplotlib_fname()
+    # List active style settings
+    # print mpl.rcParams
+    # Needs matplotlib > 1.3
+    # plt.style.use('ggplot')
+    # plt.style.use('presentation')
+    params = {'legend.fontsize': 'x-large',
+              'figure.figsize': (16, 12),
+              'axes.labelsize':  'x-large',
+              'axes.titlesize':  'x-large',
+              #'axes.linewidth':  2, #increase default linewidth
+              'xtick.labelsize': 'x-large',
+              'ytick.labelsize': 'x-large'}
+    mpl.rcParams.update(params)
+
+    #
+    # Start
+    #
+    print "\n###########################################################################################"
+    print "# fastnnlo_approxtest.py: Plot statistical evaluation of fastNLO interpolation quality"
+    print "###########################################################################################\n"
+
+    #
+    # Default arguments             ##probably not anymore necessary
+    #
+    proc = '1jet'
+    jobn = 'LO-CMS7'
+    kinn = 'vBa'
+    obsv = 'fnl2332d_xptji_y1'
+    nmax = 99999
+    fscl = 1
+
+    #
+    # Parse arguments
+    #
+    datfile = args['datfile']
+    wgtfile = args['weightfile']
+    fscl = args['fscl']
+    print "fscl chosen by user: ", fscl
+    # arguments
+    print "Given datfile: ", datfile  # , '\n'
+    datbase = os.path.basename(datfile)
+    datargs = datbase.split(".")  # array containing filename-parts
+    print "Given weightfile: ", wgtfile  # , '\n'
+
+    # arguments from datfile
+    print 'arguments in datfile name: ', len(datargs)
+    print "datargs: ", datargs, '\n'
+    if len(datargs) == 4:
+        proc, jobn, obsv, ext = datargs
+    elif len(datargs) == 5:
+        proc, jobn, kinn, obsv, ext = datargs
+    elif len(datargs) == 6:
+        proc, jobn, kinn, obsv, seed, ext = datargs
+
+    print 'proc: ', proc
+    print 'jobn: ', jobn
+    print 'kinn: ', kinn
+    print 'obsv: ', obsv
+    print 'seed: ', seed
+    print '\n'
+
+    # Extract order/contribution from job type (substring before first '-')
+    order = jobn.split('-')[0]
+
+    # Prepare result arrays which are not used by Read_XS() function, but later
+    xm = []      # bin "center"
+    ntab = 0
+    xs_fnlt = []  # fastNLO results
+    nlin = 0
+    weights = []  # Weight factors
+
+    # Read binning and cross sections from NNLOJET dat file
+    datglob = datfile[:-(len(seed)+len(ext))]+'*.dat'
+    #print 'datglob ', datglob, '\n'
+
+    datfiles = glob.glob(datglob)
+    if not datfiles:
+        print >> sys.stderr, 'No NNLOJET dat files matching', datglob, 'found, aborted!'
+        sys.exit(1)
+    datfiles.sort()
+
+    xs_nnlo = None  # will be updated in case only one scale is being looked at
+    # otherwise xs_nnlo_list will be used (and all scales investigated)
+    xs_nnlo_list = None  # see comment above (just the other way round)
+    xs_fnll = None  # same logic as above
+    xs_fnll_list = None  # same logic as above
+
+    # New functionality! Check chosen options for scale setting.
+    if fscl in np.arange(1, 8, 1):  # if valid fscl is chosen via -f <int>
+        xl, xu, ndat, xs_nnlo, nobs, seeds = Read_XS(datfiles, fscl)
+    elif fscl is None:  # option -f not used
+        if (args['variation'] == True):  # evaluate all 7 scale variations (central + 6 others)
+            xs_nnlo_list = []  # will become a list of arrays
+            for fscl in np.arange(1, 8, 1):
+                # take bin bounds from first iteration (fscl=1), they stay the same
+                if fscl == 1:
+                    xl, xu, ndat, xs_nnlo_item, nobs, seeds = Read_XS(
+                        datfiles, fscl)
+                    xs_nnlo_list.append(xs_nnlo_item)
+                else:
+                    xs_nnlo_item = Read_XS(datfiles, fscl)[3]
+                    xs_nnlo_list.append(xs_nnlo_item)
+            # xs_nnlo_list contains 7 arrays now
+        elif (args['variation'] == False):  # evaluate central scale only
+            fscl = 1
+            xl, xu, ndat, xs_nnlo, nobs, seeds = Read_XS(datfiles, fscl)
+    else:  # in case -f is chosen with invalid value
+        sys.exit(
+            "No valid value for fscl chosen. Possible integers from fscl=1 up to fscl=7 .")
+
+    if xs_nnlo is not None:
+        print "shape of xs_nnlo: ", np.shape(xs_nnlo)
+    if xs_nnlo_list is not None:
+        print "shape of xs_nnlo_list", np.shape(xs_nnlo_list)
+
+    # Read weights per file per bin from Alex
+    wgtnams = np.genfromtxt(wgtfile, dtype=None, usecols=0)
+    wgttmps = np.loadtxt(wgtfile, usecols=(list(range(1, nobs+1))))
+    ntmp = len(wgtnams)
+    # Combine to key-value tuple ( name, weights[] ) and sort according to key=name
+    wgttup = zip(wgtnams, wgttmps)
+    wgttup.sort(key=lambda row: (row[0]))
+    # Unzip again
+    allnames, allweights = zip(*wgttup)
+    print 'datfile names in weightfile: \n', np.array(allnames), '\n'
+
+    #print 'datfiles array: \n', np.array(datfiles), '\n'
+    #print "weights..", weights
+    for dfile in datfiles:  # does not have to be changed, as choice of fscl makes no difference here
+        newna = './'+order+'/'+os.path.basename(dfile)
+        indexlin = allnames.index(newna)
+        print 'Weight file line no. ', indexlin, ' is for ', allnames[indexlin]
+        weights.append(allweights[indexlin])
+        nlin += 1
+    weights = np.array(weights)
+
+    print '\n', 'Using %s weight lines.' % nlin
+    #print '\n','weights-array: \n', weights,'\n'
+
+    # Evaluate cross sections from fastNLO tables
+    # INFO=0, WARNING=1
+    # SetGlobalVerbosity(1)
+    #fnlotabs = glob.glob(pord+'/'+scen+'.'+proc+'.'+pord+'-'+ecms+'.???.'+obsv+'.*.tab.gz')
+    # fnlotabs.sort()
+    # for fnlotab in fnlotabs:
+    #    print 'fastNLO table no. ', ntab, ' is ', fnlotab
+    #    fnlo = fastNLOLHAPDF(fnlotab)
+    #    fnlo.SetLHAPDFFilename('CT14nnlo')
+    #    fnlo.SetLHAPDFMember(0)
+    #    fnlo.SetMuFFunctionalForm(1); # kScale1=0
+    #    fnlo.SetMuRFunctionalForm(1); # kScale2=1
+    #    fnlo.CalcCrossSection()
+    #    xs_fnla.append(fnlo.GetCrossSection())
+    #    ntab += 1
+    #    if ntab==nmax: break
+    #print 'Using ', ntab, 'table files.'
+    #xs_fnla = np.array(xs_fnla)
+
+    # Evaluate cross sections from pre-evaluated fastNLO tables
+    # assume that corresponding logfiles are located in the same directory as the datfiles
+    log_list = []
+
+    for dfile in datfiles:
+        log_list.append(dfile[:-(len(ext)+1)]+'.log')
+    fnlologs = np.array(log_list)
+    print 'fnlologs: \n', fnlologs, '\n'
+
+    ############### adjusted to new logfiles (check if -v true or false) #########
+    fnlologs.sort()
+    if (xs_nnlo is not None):  # only one certain scale choice fscl is considered
+        xs_fnll, nlog = Read_logfile(fnlologs, fscl, seeds, nobs)
+    elif (xs_nnlo_list is not None):  # have list with values for all 7 fscl variations
+        xs_fnll_list = []
+        for fscl in range(1, 8, 1):
+            xs_fnll_item, nlog = Read_logfile(fnlologs, fscl, seeds, nobs)
+            xs_fnll_list.append(xs_fnll_item)
+
+    # print 'xs_fnll', xs_fnll ####just to have a look at it
+
+    # Check on identical file numbers, either for table or log files and weight lines
+    if ndat == nlog == nlin and ndat*nlog*nlin != 0:
+        print 'OK: Have equal number of dat files, fastNLO results, and weight lines: ndat = ', ndat, ', nlog = ', nlog, ', nlin = ', nlin
+    else:
+        #    sys.exit('ERROR: No matching file found or file number mismatch! ndat = '+str(ndat)+', ntab = '+str(ntab))
+        sys.exit('ERROR: No matching file found or file number mismatch! ndat = ' +
+                 str(ndat)+', nlog = '+str(nlog)+', nlin = '+str(nlin))
+
+    # prepare info_values tuple for plotting function
+    info_values = (weights, nobs, ndat, nlin, proc, jobn, order, obsv, args)
+
+    # Use plotting function:
+    print "\n"
+    if xs_fnll is not None:  # should be calculated already, in case only one fscl is investigated
+        Statistics_And_Plotting(fscl, xs_fnll, xs_nnlo, info_values)
+        # plot here and save the two plots
+        print "Plotting done. (fscl=%s)" % fscl
+    elif xs_fnll_list is not None:  # that list exists if all fscl from 1 to 7 are considered, otherwise it is None
+        r_f2nl_list = []
+        a_f2nl_list = []
+        for fscl in range(1, 8, 1):
+            xs_fnll = xs_fnll_list[fscl-1]
+            xs_nnlo = xs_nnlo_list[fscl-1]
+
+            # plot here and save plots
+            Statistics_And_Plotting(fscl, xs_fnll, xs_nnlo, info_values)
+            print "Plotting done for fscl= %s" % fscl
+
+    exit(0)
+    # what are these plots like?
+    for i_bin, (nnlo, fnlo, ratios, asyms, wgts) in enumerate(zip(xs_nnlo.T, xs_fnll.T, r_f2nl.T, a_f2nl.T, weights.T)):
+
+        if i_bin > 0:
+            exit(2222)
+        print 'NNLOJET cross sections'
+        bin_dists(nnlo, 'NNLOJET', r'$\sigma$ [pb]', 'blue', 'violet')
+        print 'fastNLO cross sections'
+        bin_dists(fnlo, 'fastNLO', r'$\sigma$ [pb]', 'blue', 'violet')
+        print 'Ratios'
+        bin_dists(ratios, 'Ratio', 'fastNLO/NNLOJET', 'blue', 'violet', True,
+                  rmed_f2nl[i_bin]-10.*riqd_f2nl[i_bin], rmed_f2nl[i_bin]+10.*riqd_f2nl[i_bin])
+        print 'Asymmetries'
+        bin_dists(asyms, 'Asymmetry', '(fastNLO-NNLOJET)/(fastNLO+NNLOJET)', 'blue', 'violet',
+                  True, amed_f2nl[i_bin]-10.*aiqd_f2nl[i_bin], amed_f2nl[i_bin]+10.*aiqd_f2nl[i_bin])
+
+################################################################################################
+##################################### END OF MAIN ##############################################
+################################################################################################
+
+
+### FUNCTIONS to be used in main() ###
+
+################################################################################
+# Define method for histogram statistics
+################################################################################
+def hist_stats(x, weights, axis=None):
+
+    # Fill proper np.array in case only 1. is given as weight
+    try:
+        iter(weights)
+    except TypeError:
+        weights = np.ones_like(x)*weights
+
+# Unweighted mean and sample variance (not population variance)
+    _ave = np.mean(x, axis=axis)
+    if x.shape[axis] == 1:
+        _var = np.zeros_like(_ave)
+    else:
+        _var = np.var(x, ddof=1, axis=axis)
+    _std = np.sqrt(_var)
+
+# Useful sums for weighted quantities
+    sumw = np.sum(weights, axis=axis)
+    sumw2 = np.sum(weights * weights, axis=axis)
+    sumwx = np.sum(weights * x, axis=axis)
+    sumwx2 = np.sum(weights * x * x, axis=axis)
+
+# Weighted mean and reliability weighted sample variance
+    _wave = []
+    _wvar = []
+    for i in range(len(sumw)):
+        if sumw[i] == 0:
+            _wave.append(np.nan)
+        else:
+            _wave.append(sumwx[i]/sumw[i])
+
+    if x.shape[axis] == 1:
+        _wvar = np.zeros_like(_ave)
+    else:
+        for i in range(len(sumw)):
+            if sumw[i] == 0:
+                _wvar.append(np.nan)
+            else:
+                _wvar.append(
+                    (sumwx2[i] - sumwx[i]*sumwx[i]/sumw[i]) / (sumw[i] - sumw2[i]/sumw[i]))
+
+    _wstd = np.sqrt(_wvar)
+
+# Median and half interquartile distance
+    _med = np.median(x, axis=axis)
+    _med_err = np.subtract(
+        *np.percentile(x, [75, 25], axis=axis, interpolation='linear'))/2.
+
+    return dict(mean=_ave,
+                stddev=_std,
+                weighted_mean=_wave,
+                weighted_stddev=_wstd,
+                median=_med,
+                iqd2=_med_err)
+################################################################################
+# End of method for histogram statistics
+################################################################################
+
+################################################################################
+# Function to read bin bounds and xs_nnlo from datfiles
+################################################################################
+
+
+def Read_XS(datfiles, fscl):  # takes list of datfiles (different seeds) and fscl
+    # Prepare result arrays
+    xl = []      # left bin border
+    xu = []      # right bin border
+    ndat = 0
+    xs_nnlo = []  # NNLOJET results
+    seeds = []   # Seed numbers for matching
+    # default maximum number of datfiles
+    nmax = 99999
+
+    print "Reading datfiles for fscl=%s ." % fscl
+    ixscol = 3 + 2 * (fscl-1)
+    for datfile in datfiles:
+        print 'Datfile no. ', ndat, ' is ', datfile
+        if ndat == 0:
+            xl.append(np.loadtxt(datfile, usecols=(0,)))
+            xu.append(np.loadtxt(datfile, usecols=(2,)))
+        xs_nnlo.append(np.loadtxt(datfile, usecols=(ixscol,)))
+        # print "xs_nnlo \n", xs_nnlo ## just to see what's happening
+        parts = datfile.split(".")
+        seed = parts[len(parts)-2]
+        seeds.append(seed)
+        ndat += 1
+        if ndat == nmax:
+            break
+    print 'Using ', ndat, 'dat files.'
+    xl = np.array(xl)
+    xu = np.array(xu)
+    xs_nnlo = np.array(xs_nnlo)/1000.  # Conversion of fb to pb
+
+    # Determine no. of observable bins
+    nobs = xl.size
+    print 'Number of observable bins: ', nobs, '\n'
+    return xl, xu, ndat, xs_nnlo, nobs, seeds
+################### End of function ###########################################
+###############################################################################
+
+###############################################################################
+# Function to read XS from logfile
+###############################################################################
+
+
+def Read_logfile(fnlologs, fscl, seeds, nobs):  # takes list of logfiles and fscl,
+                                                # as well as list of seeds and nobs (number of observable bins)
+    nlog = 0
+    # default maximum number of logfiles
+    nmax = 99999
+    xs_fnll = []
+    for fnlolog in fnlologs:
+        print 'fastNLO file no. ', nlog, ' is ', fnlolog
+        if not seeds[nlog] in fnlolog:
+            print 'Mismatch in result sort order between NNLOJET and fastNLO. Aborted!'
+            print 'seeds[', nlog, '] = ', seeds[nlog], ', fastNLO file = ', fnlolog
+        else:
+            print 'NNLOJET and fastNLO result correctly matched. seed is ', seeds[nlog]
+
+        xs_tmp = np.loadtxt(fnlolog, usecols=(
+            6,), comments=['#', ' #', 'C', 'L', 'N'])
+        #print "xs_tmp \n", xs_tmp
+
+        indi = (fscl-1)*nobs  # skip lines of lower fscl
+        indf = indi + nobs  # last line in logfile which belongs to certain fscl
+        xs_sub = xs_tmp[indi:indf]
+        xs_fnll.append(xs_sub)
+        nlog += 1
+        if nlog == nmax:
+            break
+    print 'Using ', nlog, 'pre-evaluated table files.'
+    xs_fnll = np.array(xs_fnll)
+    return xs_fnll, nlog
+############### End of function to read logfile ###############################
+###############################################################################
+
+###############################################################################
+# Function for Statistics and Plotting
+###############################################################################
+
+
+# takes pre-calculated values for specific fscl (process variables as tuple)
+def Statistics_And_Plotting(fscl, xs_fnll, xs_nnlo, info_values):
+    # get required variables from tuple
+    weights, nobs, ndat, nlin, proc, jobn, order, obsv, args = info_values
+
+    r_f2nl = xs_fnll/xs_nnlo
+    a_f2nl = (xs_fnll - xs_nnlo)/(xs_fnll + xs_nnlo)
+
+    # Ratio statistics
+    _ratio_stats = hist_stats(r_f2nl, weights=weights, axis=0)
+    rave_f2nl = _ratio_stats['mean']
+    rstd_f2nl = _ratio_stats['stddev']
+    rwav_f2nl = _ratio_stats['weighted_mean']
+    rwst_f2nl = _ratio_stats['weighted_stddev']
+    rmed_f2nl = _ratio_stats['median']
+    riqd_f2nl = _ratio_stats['iqd2']
+
+    # Asymmetry statistics
+    _asymm_stats = hist_stats(a_f2nl, weights=weights, axis=0)
+    aave_f2nl = _asymm_stats['mean']
+    astd_f2nl = _asymm_stats['stddev']
+    awav_f2nl = _asymm_stats['weighted_mean']
+    awst_f2nl = _asymm_stats['weighted_stddev']
+    amed_f2nl = _asymm_stats['median']
+    aiqd_f2nl = _asymm_stats['iqd2']
+
+    # Settings for x-axis of plots
+    # For setting small xticks for each bin with only certain labels
+    # Set minimum > unity in MultipleLocator
+    majorLocator = MultipleLocator(max((nobs//6), 1.1))
+    majorFormatter = FormatStrFormatter('%d')
+    minorLocator = MultipleLocator(1)  # set minor tick for each observable bin
+
+    # Ratio plot
+    limfs = 'x-large'
+    # renamed fig to fig1, so function can return it
+    fig1 = plt.figure(figsize=(16, 12))
+    ax1 = fig1.gca()
+    plt.title(r'Ratio: {} {} for {} using scale {}'.format(
+        proc, order, obsv, fscl), fontsize='x-large', fontweight='bold', loc='left')
+    plt.xlabel('Observable bin index', horizontalalignment='right',
+               x=1.0, verticalalignment='top', y=1.0)
+    plt.ylabel('fastNLO/NNLOJET', horizontalalignment='right',
+               x=1.0, verticalalignment='bottom', y=1.0)
+    plt.axhline(y=1.001, linestyle='--', linewidth=1.0, color='black')
+    plt.axhline(y=0.999, linestyle='--', linewidth=1.0, color='black')
+    plt.fill_between([0.0, 34.0], 0.999, 1.001, color='black', alpha=0.1)
+    # plt.text(33.6,1.00085,u'+1‰')
+    # plt.text(33.7,0.99885,u'–1‰')
+    plt.text(nobs+1.1, +1.00085, u'+1‰')
+    # location flexible adjusted to plotting range
+    plt.text(nobs+1.1, +0.99885, u'–1‰')
+
+    dx = 1./3.
+    xa = np.arange(1-dx, nobs-dx+1.e-6)
+    xb = np.arange(1, nobs + 1.e-6)
+    xc = np.arange(1+dx, nobs+dx+1.e-6)
+
+    rmean = plt.errorbar(xa, rave_f2nl, yerr=0.*rstd_f2nl, marker='v',
+                         linestyle='none', label=r'$\mu$ $\pm$ $\Delta\mu$', color='blue')
+    plt.errorbar(xa, rave_f2nl, yerr=rstd_f2nl/np.sqrt(ndat),
+                 marker='.', linestyle='none', color='blue')
+    if nlin > 0:
+        rmwgt = plt.errorbar(xb, rwav_f2nl, yerr=0.*rwst_f2nl, marker='^',
+                             linestyle='none', label=r'$\mu_w$ $\pm$ $\Delta\mu_w$', color='violet')
+        plt.errorbar(xb, rwav_f2nl, yerr=rwst_f2nl/np.sqrt(nlin),
+                     marker='.', linestyle='none', color='violet')
+    rmedian = plt.errorbar(xc, rmed_f2nl, yerr=riqd_f2nl, marker='s',
+                           linestyle='none', label=r'median $\pm$ IQD/2', color='red')
+
+    # plt.xlim(0.0,34.0)
+    plt.xlim(0, nobs+1)
+    #plt.xticks(range(0, nobs+2, nobs//6))
+    ax1.xaxis.set_major_locator(majorLocator)
+    ax1.xaxis.set_major_formatter(majorFormatter)
+    ax1.xaxis.set_minor_locator(minorLocator)
+    ax1.tick_params(which='both', width=1)
+    ax1.tick_params(which='major', length=7)
+    ax1.tick_params(which='minor', length=5)
+    plt.ylim(0.99, 1.01)
+    # plt.ylim(-50, 12000) #to display points for fscl=0 (log6file), was just a test
+    if nlin > 0:
+        handles = [rmean, rmwgt, rmedian]
+    else:
+        handles = [rmean, rmedian]
+    labels = [h.get_label() for h in handles]
+    legend = ax1.legend(handles, labels, title=r'No. of entries/bin = {}'.format(
+        ndat), loc='upper left', numpoints=1, handlelength=0)
+    legend.get_title().set_fontsize(limfs)
+
+    if args['outputfilename'] is None:
+        fignam = proc+'.'+jobn+'.'+obsv+'.'+'fscl' + \
+            str(fscl)+'.'+'approx_ratio_'+str(ndat)+'.png'
+    else:
+        fignam = args['outputfilename']+'.fscl' + \
+            str(fscl)+'.approx_ratio_'+str(ndat)+'.png'
+    plt.savefig(fignam)
+    #print "Ratio plot saved as: %s" %fignam
+    # plt.show()
+
+    # Asymmetry plot
+    limfs = 'x-large'
+    fig2 = plt.figure(figsize=(16, 12))  # renamed fig to fig2
+    ax2 = fig2.gca()
+    plt.title(r'Asymmetry: {} {} for {} using scale {}'.format(
+        proc, order, obsv, fscl), fontsize='x-large', fontweight='bold', loc='left')
+    plt.xlabel('Observable bin index', horizontalalignment='right',
+               x=1.0, verticalalignment='top', y=1.0)
+    plt.ylabel('(fastNLO-NNLOJET)/(fastNLO+NNLOJET)',
+               horizontalalignment='right', x=1.0, verticalalignment='bottom', y=1.0)
+    plt.axhline(y=-0.001, linestyle='--', linewidth=1.0, color='black')
+    plt.axhline(y=+0.001, linestyle='--', linewidth=1.0, color='black')
+    plt.fill_between([0.0, 34.0], -0.001, 0.001, color='black', alpha=0.1)
+    # plt.text(33.6,+0.00085,u'+1‰')
+    # plt.text(33.7,-0.00115,u'–1‰')
+    plt.text(nobs+1.1, +0.00085, u'+1‰')
+    # location flexible adjusted to plotting range
+    plt.text(nobs+1.1, -0.00115, u'–1‰')
+
+    dx = 1./3.
+    xa = np.arange(1-dx, nobs-dx+1.e-6)
+    xb = np.arange(1, nobs + 1.e-6)
+    xc = np.arange(1+dx, nobs+dx+1.e-6)
+
+    amean = plt.errorbar(xa, aave_f2nl, yerr=0.*astd_f2nl, marker='<',
+                         linestyle='none', label='$\mu$ $\pm$ $\Delta\mu$', color='orange')
+    plt.errorbar(xa, aave_f2nl, yerr=astd_f2nl/np.sqrt(ndat),
+                 marker='.', linestyle='none', color='orange')
+    if nlin > 0:
+        amwgt = plt.errorbar(xb, awav_f2nl, yerr=0.*awst_f2nl, marker='>',
+                             linestyle='none', label=r'$\mu_w$ $\pm$ $\Delta\mu_w$', color='brown')
+        plt.errorbar(xb, awav_f2nl, yerr=awst_f2nl/np.sqrt(ndat),
+                     marker='.', linestyle='none', color='brown')
+    amedian = plt.errorbar(xc, amed_f2nl, yerr=aiqd_f2nl, marker='8',
+                           linestyle='none', label=r'median $\pm$ IQD/2', color='green')
+
+    # plt.xlim(0.0,34.0)
+    plt.xlim(0, nobs+1)
+    ax2.xaxis.set_major_locator(majorLocator)
+    ax2.xaxis.set_major_formatter(majorFormatter)
+    ax2.xaxis.set_minor_locator(minorLocator)
+    ax2.tick_params(which='both', width=1)
+    ax2.tick_params(which='major', length=7)
+    ax2.tick_params(which='minor', length=5)
+    plt.ylim(-0.01, 0.01)
+    # plt.ylim(-1.02, 1.02) ## to display points for fscl=0 (log6file), just a test
+    if nlin > 0:
+        handles = [amean, amwgt, amedian]
+    else:
+        handles = [amean, amedian]
+    labels = [h.get_label() for h in handles]
+    legend = ax2.legend(handles, labels, title=r'No. of entries/bin = {}'.format(
+        ndat), loc='upper left', numpoints=1, handlelength=0)
+    legend.get_title().set_fontsize(limfs)
+
+    if args['outputfilename'] is None:
+        fignam = proc+'.'+jobn+'.'+obsv+'.'+'fscl' + \
+            str(fscl)+'.'+'approx_asymm_'+str(ndat)+'.png'
+    else:
+        # should fscl necessarily be added, too?
+        fignam = args['outputfilename']+'.fscl' + \
+            str(fscl)+'.approx_asymm_'+str(ndat)+'.png'
+    plt.savefig(fignam)
+    #print "Asymmetry plot saved as: %s" %fignam
+    # plt.show()
+
+    # produces 1 plot for asymmetry and one for ratio (so it is unnecessary to return all intern variables)
+    return fig1, fig2
+################### End of function for statistics & plotting ################
+##############################################################################
+
+
+# the following method was not used in default settings of original script
+
+################################################################################
+# Define method for binwise distribution plots
+################################################################################
+def bin_dists(q, label, xlabel, col1, col2, indicators=False, lval=0., rval=0.):
+    _min, _max = np.min(q), np.max(q)
+    _range = _max - _min
+    _nbins = min(int(ndat/5), 50)
+    _bins = np.linspace(_min, _max, _nbins + 1)
+
+    fig_dist = plt.figure(figsize=(12, 12))
+
+    _h1, _c1, _ = plt.hist(q, bins=_bins, color=col1)
+    _h2, _c2, _ = plt.hist(q, weights=wgts/np.sum(wgts)
+                           * ndat, bins=_bins, color=col2)
+
+    plt.gcf().clf()
+    plt.bar(_bins[:-1], _h1, color=col1, width=0.5*_range/_nbins)
+    plt.bar(_bins[:-1]+0.5*_range/_nbins, _h2,
+            color=col2, width=0.5*_range/_nbins)
+
+    plt.title(r'{}: {} {} for {}'.format(label, proc, order, obsv),
+              fontsize='x-large', fontweight='bold', loc='left')
+    plt.xlabel(xlabel, horizontalalignment='right',
+               x=1.0, verticalalignment='top', y=1.0)
+    plt.ylabel('Frequency', horizontalalignment='right',
+               x=1.0, verticalalignment='bottom', y=1.0)
+    raw = mpatches.Patch(color=col1, label='raw')
+    wgt = mpatches.Patch(color=col2, label='weighted')
+    if indicators:
+        vl1 = plt.axvline(x=lval, linestyle='--', linewidth=2.0,
+                          color='red', label=r'$\pm10$ IQD/2')
+        vl2 = plt.axvline(x=rval, linestyle='--', linewidth=2.0, color='red')
+        handles = [vl1, raw, wgt]
+    else:
+        handles = [raw, wgt]
+    labels = [h.get_label() for h in handles]
+#    plt.fill_between([-0.5,33.5],0.999,1.001, color='black', alpha=0.1)
+#    plt.text(33.6,1.00085,u'+1‰')
+#    plt.text(33.7,0.99885,u'–1‰')
+
+    plt.legend(handles, labels, title=r'Observable bin no. {}'.format(
+        i_bin), loc='upper left', numpoints=1)
+
+    plt.yscale('log')
+    plt.ylim((0.1, None))
+
+    fig_dist_name = "{}.{}.{}.approx_{:d}_{}_bin{:d}.png".format(
+        proc, jobn, obsv, ndat, label, i_bin)
+    fig_dist.savefig(fig_dist_name)
+    plt.close(fig_dist)
+
+###################################################################################
+
+
+if __name__ == "__main__":
+    main()
diff --git a/v2.5/toolkit/fastnlotoolkit/fastNLOReader.cc b/v2.5/toolkit/fastnlotoolkit/fastNLOReader.cc
index aa090f458637951e84fa7a366ca0c50849ddb9b4..b292eff6d6632f4448c8332307b9efd3f7c2c19b 100644
--- a/v2.5/toolkit/fastnlotoolkit/fastNLOReader.cc
+++ b/v2.5/toolkit/fastnlotoolkit/fastNLOReader.cc
@@ -1502,7 +1502,7 @@ void fastNLOReader::CalcCrossSectionv21(fastNLOCoeffAddFlex* c) {
       iCIBFlag2 = c->GetCoeffInfoBlockFlag2(iCIBIndex);
       dCIBCont  = c->GetCoeffInfoContent(iCIBIndex);
    } else {
-      logger.info["CalcCrossSectionv21"]<<"No CoeffInfoBlock found; uncertainties are initialised to zero."<<endl;
+      logger.debug["CalcCrossSectionv21"]<<"No CoeffInfoBlock found; uncertainties are initialised to zero."<<endl;
    }
 
    for (unsigned int i=0; i<NObsBin; i++) {
@@ -1618,7 +1618,7 @@ void fastNLOReader::CalcCrossSectionv20(fastNLOCoeffAddFix* c) {
       iCIBFlag2 = c->GetCoeffInfoBlockFlag2(iCIBIndex);
       dCIBCont  = c->GetCoeffInfoContent(iCIBIndex);
    } else {
-      logger.info["CalcCrossSectionv20"]<<"No CoeffInfoBlock found; uncertainties are initialised to zero."<<endl;
+      logger.debug["CalcCrossSectionv20"]<<"No CoeffInfoBlock found; uncertainties are initialised to zero."<<endl;
    }
 
    int scaleVar = c->GetNpow() == ILOord ? 0 : fScalevar;
diff --git a/v2.5/toolkit/fastnlotoolkit/fastNLOTools.cc b/v2.5/toolkit/fastnlotoolkit/fastNLOTools.cc
index 7947823dd9a3f86e25733b2dd02fe8cbecfc69d0..43dc8343e937b943346ade3071cd6fe397ad1b88 100644
--- a/v2.5/toolkit/fastnlotoolkit/fastNLOTools.cc
+++ b/v2.5/toolkit/fastnlotoolkit/fastNLOTools.cc
@@ -441,7 +441,10 @@ namespace fastNLOTools {
                   iss >> xs;
                   iss >> dxs;
                   if ( fabs(xs) > DBL_MIN ) {
+                     // Is negative, if NLO_only or NNLO_only x section at production was < 0; keep this as additional information.
                      Uncertainty.push_back(dxs/xs);
+                     // Only allow positive numbers with maximum value of 1, i.e. = 100% uncertainty maximum
+                     //                     Uncertainty.push_back(std::min(fabs(dxs/xs),1.0));
                   } else {
                      Uncertainty.push_back(0.);
                   }
diff --git a/v2.5/toolkit/fastnlotoolkit/include/fastnlotk/fastNLOTable.h b/v2.5/toolkit/fastnlotoolkit/include/fastnlotk/fastNLOTable.h
index bde76acde18a6c213c738605de8ca079d59488cc..25aec898414950010f91c38813a9cd2118b477f6 100644
--- a/v2.5/toolkit/fastnlotoolkit/include/fastnlotk/fastNLOTable.h
+++ b/v2.5/toolkit/fastnlotoolkit/include/fastnlotk/fastNLOTable.h
@@ -246,10 +246,6 @@ class fastNLOTable {
    /// Returns pointer to reference table if available, else returns NULL pointer
    fastNLOCoeffAddBase* GetReferenceTable(fastNLO::ESMOrder eOrder) const;
 
-   /// Handle InfoBlocks
-   int CreateInfoBlock(int no, fastNLOCoeffBase *newinfoblock);
-
-
 private:
    bool cmp(const double x1, const double x2) const;
    bool cmp(const std::vector<double>& x1, const std::vector<double >& x2) const;
diff --git a/v2.5/toolkit/pyext/Makefile.am b/v2.5/toolkit/pyext/Makefile.am
index c5de0bd90075e85a2500ababf6fc4977b49837e5..5499ed66b5171e60cf80a6f42f2807cd6ff77aa0 100644
--- a/v2.5/toolkit/pyext/Makefile.am
+++ b/v2.5/toolkit/pyext/Makefile.am
@@ -16,7 +16,7 @@ if ENABLE_PYEXT
 python_PYTHON = fastnlo.py
 pyexec_LTLIBRARIES = _fastnlo.la
 
-_fastnlo_la_SOURCES = fastnlo_wrap.cc
+_fastnlo_la_SOURCES = fastnlo_wrap.cc fastnlo_wrap.h
 _fastnlo_la_CPPFLAGS = $(PYTHON_CPPFLAGS) -I$(top_srcdir)/fastnlotoolkit/include/fastnlotk -I$(LHAPDFINCLUDEPATH)
 _fastnlo_la_LDFLAGS = -module -shared -avoid-version
 _fastnlo_la_LIBADD = $(top_srcdir)/fastnlotoolkit/libfastnlotoolkit.la
@@ -29,7 +29,7 @@ fastnlo_wrap.cc: fastnlo.i
 	$(SWIG) -c++ -python -I$(top_srcdir)/fastnlotoolkit/include/fastnlotk -o $@ -MD -MF $(DEPDIR)/fastnlo_wrap.Plo $<
 
 clean-local:
-	rm -f fastnlo_wrap.cc fastnlo.py
+	rm -f fastnlo_wrap.cc fastnlo_wrap.h fastnlo.py
 #$(DEPDIR)/fastnlo_wrap.Plo
 
 endif
diff --git a/v2.5/toolkit/pyext/fastnlo.i b/v2.5/toolkit/pyext/fastnlo.i
index 93536f3757362f3810ebe63cb9418aff40d35063..171cd26715c54093d5093c4e7997734f26fda2d7 100644
--- a/v2.5/toolkit/pyext/fastnlo.i
+++ b/v2.5/toolkit/pyext/fastnlo.i
@@ -1,4 +1,8 @@
-%module fastnlo
+// To be tested: Following suggestion from Dmitry Kalinkin, 12.06.2021
+%module(directors="1") fastnlo
+
+// generate directors for all classes that have virtual methods
+%feature("director");
 
 %{
 /**
@@ -47,7 +51,7 @@ namespace std {
 %}
 %include "../fastnlotoolkit/include/fastnlotk/speaker.h"
 %include "../fastnlotoolkit/include/fastnlotk/fastNLOPDFLinearCombinations.h"
-//%include "../fastnlotoolkit/include/fastnlotk/fastNLOConstants.h"
+%include "../fastnlotoolkit/include/fastnlotk/fastNLOConstants.h"
 %include "../fastnlotoolkit/include/fastnlotk/fastNLOTable.h"
 %include "../fastnlotoolkit/include/fastnlotk/fastNLOReader.h"
 %include "../fastnlotoolkit/include/fastnlotk/fastNLOLHAPDF.h"