diff --git a/DCNumPro_1.0.py b/DCNumPro_1.0.py
deleted file mode 100644
index 9c9917ea33d44dab7e46df67c46ccb0cea659596..0000000000000000000000000000000000000000
--- a/DCNumPro_1.0.py
+++ /dev/null
@@ -1,4132 +0,0 @@
-""" Configuration start """
-# Perform on cluster? False = no, True = yes
-cluster = False
-# Graphically display particles in 3D? False = no, True = yes
-display_particles = False
-# Final test for overlaps? False = no, True = yes
-final_test = True
-# Display initial distribution vs. distribution of placed particles? False = no, True = yes
-display_distribution = True
-# Display detailed progress: status of particle at each step? False = no, True = yes
-# If False, only overall percentage progress will be displayed
-display_detailed_progress = False
-# Display solver warnings? False = no, True = yes
-display_solver_warnings = False
-# Output Excel file
-name_excel_file = 'Test_300'
-# Number of particles 
-n_particles_sim = 500
-# Number of classes
-n_classes = 10
-# Median
-mu = 500
-# Standard deviation
-sigma = 130
-# Estimated hold-up
-holdup_est = 0.6
-# Random seed
-seed = 5
-# Number of cores for multiprocessing. 0 = automated determination of available cores
-n_cores = 6
-# Display particle within its cell? False = no, True = yes
-# Is used for debugging or illustriation
-display_particle_cell = False
-# Specify holdup steps
-holdup_delta = 0.025
-# Stepsize for random loose packing simulation
-stepsize = 1
-""" Configuration end """ 
-""" Import packages """
-import os
-import time
-import sys
-import math
-import random
-import xlwt
-import warnings
-import multiprocessing
-import numpy as np
-import pandas as pd
-import sympy as sy
-import scipy.stats as stats
-import matplotlib.pyplot as plt
-from scipy.integrate import quad
-from scipy.stats import norm
-from scipy.stats import lognorm
-from cycler import cycler
-from functools import reduce
-from scipy.optimize import fsolve
-from scipy.optimize import minimize
-from tess import Container
-from functools import partial
-from scipy.spatial import Delaunay
-from scipy.spatial import ConvexHull
-from mpl_toolkits.mplot3d.art3d import Poly3DCollection
-from tqdm import tqdm
-from collections import defaultdict
-from collections import Counter
-""" Simulation starts from here """ 
-# Solver warning messages
-if display_solver_warnings == False:
-    warnings.filterwarnings("ignore")   
-# If calculation on cluster overwrite parameters above
-# Specify parameter in the .job file  
-if cluster == True:
-    import argparse
-    parser = argparse.ArgumentParser(description='Train Mask R-CNN to detect particles.')
-    parser.add_argument('--name_excel_file', required=True)
-    parser.add_argument('--n_particles_sim', required=True, type=int)
-    parser.add_argument('--n_classes', required=True, type=int)    
-    parser.add_argument('--holdup_est', required=True, type=float)
-    parser.add_argument('--seed', required=True, type=int)
-    parser.add_argument('--n_cores', required=True, type=int)
-    parser.add_argument('--holdup_delta', required=True, type=float)
-    parser.add_argument('--stepsize', required=True, type=float)
-    parser.add_argument('--mu', required=True, type=float)
-    parser.add_argument('--sigma', required=True, type=float)
-    args = parser.parse_args()
-    NAME_EXCEL_FILE = (args.name_excel_file + '.xls')
-    N_PARTICLES_SIM = args.n_particles_sim
-    N_CLASSES = args.n_classes
-    HOLDUP_EST = args.holdup_est
-    SEED = args.seed
-    N_CORES = args.n_cores
-    HOLDUP_DELTA = args.holdup_delta
-    STEPSIZE = args.stepsize
-    mu = args.mu
-    sigma = args.sigma
-    ROOT_DIR = os.path.join("/rwthfs/rz/cluster", os.path.abspath("../output"))
-else:
-    NAME_EXCEL_FILE = (name_excel_file + '.xls')
-    N_PARTICLES_SIM = int(n_particles_sim)
-    N_CLASSES = int(n_classes)
-    HOLDUP_EST = float(holdup_est)
-    SEED = int(seed)
-    N_CORES = int(n_cores)
-    HOLDUP_DELTA = float(holdup_delta)
-    STEPSIZE = stepsize
-    ROOT_DIR = os.path.abspath("../output")
-
-""" Functions """
-def calc_distribution(N_PARTICLES_SIM, N_CLASSES):
-    """
-    Generate nuber distribution of particles based on the defined distribution parameters mu and sigma calculated from D_32.
-    Args:
-    D_32 (list of floats): Sauter mean diameter.
-    N_PARTICLES_SIM (float): Number of particles.
-    N_CLASSES (float): Number of classes.
-    Returns:
-    d_particles (list of floats): Particle diameters.
-    """
-    # CDF parameter for log-normal DSD
-    # Mean of log values
-    mu_log = (np.log((mu**2)/(((sigma**2)+(mu**2))**0.5)))
-    # Standard deviation of log values
-    sigma_log = ((np.log(((sigma**2)/(mu**2))+1))**0.5)
-    # DSD boundaries which are d_05 and d_95 corresponding to the 5th and 95th percentile of the DSD 
-    d_05, d_95 = calc_DSD_boundaries(mu_log, sigma_log)
-    # Class interval
-    delta_class = (d_95-d_05)/N_CLASSES
-    # Class diameters
-    d_classes = np.linspace((d_05+delta_class/2), (d_95-delta_class/2), N_CLASSES)
-    # Determine minimum and maximum class diameter
-    d_min = np.min(d_classes)
-    d_max = np.max(d_classes)
-    # Class boundaries
-    boundaries = [d_05 + i * delta_class for i in range(N_CLASSES+1)]
-    # Class sizes
-    class_sizes, q0_initial, q3_initial, volume_total = calc_class_sizes(mu_log, sigma_log, boundaries, N_PARTICLES_SIM, d_classes)
-    # Sauter mean diameter
-    D_32 = np.sum(q0_initial * d_classes**3) / np.sum(q0_initial * d_classes**2)
-    # Actual number of particles
-    n_particles_init = int(np.sum(class_sizes))
-    # Create array q0 [class_sizes, d_classes]
-    q0 = np.concatenate((class_sizes.reshape(-1,1), d_classes.reshape(-1,1)), axis=1)
-
-    return q0, n_particles_init, q0_initial, q3_initial, d_classes, d_min, d_max, volume_total, d_05, d_95, delta_class, D_32
-
-def calc_DSD_boundaries(mu, sigma): 
-    """
-    Calculate DSD boundaries d_05 and d_95 corresponding to the 5th and 95th percentile of the DSD.
-    Args:
-    mu (float): Mean of the logarithm of the values.
-    sigma (float): Standard deviation of the logarithm of the values.
-    Returns:
-    d_min (float): Minimum diameter.
-    d_max (float): Maximum diameter.
-    """
-    # Find the z-value corresponding to the 5th and 95th percentile of the standard normal distribution
-    d_05_percentile = norm.ppf(0.05)
-    d_95_percentile = norm.ppf(0.95)
-    # Calculate the 5th and 95th percentile of the log-normal distribution
-    d_05 = np.exp(mu + sigma * d_05_percentile)
-    d_95 = np.exp(mu + sigma * d_95_percentile)
-
-    return d_05, d_95
-
-def calc_class_sizes(mu, sigma, boundaries, N_PARTICLES_SIM, d_classes):
-    """
-    Calculate class sizes for a log-normal distribution with parameters mu and sigma.
-    Args:
-    mu (float): Mean of the logarithm of the values.
-    sigma (float): Standard deviation of the logarithm of the values.
-    boundaries (list of floats): Boundaries of the classes.
-    Returns:
-    class_sizes (list of floats): Quantity for each class defined by the boundaries.
-    """
-    # Ensure boundaries are sorted
-    boundaries = sorted(boundaries)
-    # Create a log-normal distribution object
-    # Scale parameter for lognorm is sigma
-    # Scale parameter exp(mu) for shifting the distribution
-    dist = lognorm(s=sigma, scale=np.exp(mu))
-    # Calculate probabilities for each interval
-    q3_initial = []
-    for i in range(len(boundaries) - 1):
-        lower_bound = boundaries[i]
-        upper_bound = boundaries[i + 1]
-        lb = dist.cdf(lower_bound)
-        ub = dist.cdf(upper_bound)
-        # Probability that a value falls within the interval [lower_bound, upper_bound]
-        prob = dist.cdf(upper_bound) - dist.cdf(lower_bound)
-        # Calculate class sizes
-        q3_initial.append(prob)
-    q3_initial = np.array(q3_initial)
-    # Convert class volume probabilities into number probabilities
-    q0_initial = q3_initial / d_classes**3
-    # Normalize number probabilities. 
-    # It ignores that the class volume probabilities include only 90vol% of the distribution.
-    # However, since we use number probabilities to distribute a certain number of particles 
-    # within these considered 90vol%, this calculation is valid
-    q0_initial /= np.sum(q0_initial)
-    # Distribute the nubmer of particles to the classes considering number probabilities and round
-    class_sizes = N_PARTICLES_SIM * np.array(q0_initial)
-    np.round(class_sizes, decimals=0, out=class_sizes)
-    # Total particle volume
-    volume_total = np.sum(class_sizes * (math.pi * d_classes**3 / 6))
-
-    return class_sizes, q0_initial, q3_initial, volume_total
-
-def calc_particles(q0, n_particles_init):
-    """
-    Fill an array with particles from number distribution q0 and determine their drop coordinates randomly.
-    Args:
-    q0 (array): Number distribution.
-    n_particles_init (float): Actualnumber of particles.
-    Returns:
-    M_particles_initial (array): Array with particles and their drop coordinates
-    """
-    # Array columns: 
-    # 0: x-coordinate, 1: y-coordinate, 2: z-coordinate, 3: index, 4: diameter, 5: class number (0 to n)
-    M_particles_initial = np.zeros(shape=(int(n_particles_init), 6)) 
-    # Assign particle diameters and class numbers
-    j = 0
-    for k in range(len(q0)):
-        n = int(q0[k, 0])
-        for i in range(n):
-            M_particles_initial[j, 4] = q0[k, 1]
-            M_particles_initial[j, 5] = k
-            j = j + 1
-    # Assign indices and randomly generated coordinates
-    for i in range(len(M_particles_initial)):
-        M_particles_initial[i, 0] = random.uniform(-A_0 + (M_particles_initial[i, 4]) / 2, A_0 - (M_particles_initial[i, 4]) / 2)
-        M_particles_initial[i, 1] = random.uniform(-A_0 + (M_particles_initial[i, 4]) / 2, A_0 - (M_particles_initial[i, 4]) / 2)
-        M_particles_initial[i, 2] = None
-        M_particles_initial[i, 3] = i
-    np.random.shuffle(M_particles_initial)
-
-    return M_particles_initial
-
-def particle_info(M_particles, index):
-    # x-coordinate
-    x = M_particles[index, 0]
-    # y-coordinate
-    y = M_particles[index, 1]
-    # z-coordinate
-    z = M_particles[index, 2]
-    # Index
-    p_index = M_particles[index, 3]
-    # Diameter
-    p_diameter = M_particles[index, 4]
-    # Class
-    p_class = int(M_particles[index, 5])
-    
-    return x, y, z, p_index, p_diameter, p_class
-
-def find_z_drop(y, x, M_particle_steady, test_range, d_max_steady):
-    """ Determine drop position of particles """
-    # Define testing area
-    y_min = y - test_range
-    y_max = y + test_range
-    x_min = x - test_range
-    x_max = x + test_range
-    # Check particles in steady state for interraction with testing area:
-    # in y direction
-    particle_to_y = np.argwhere(np.logical_and(M_particle_steady[:, 2] > y_min, M_particle_steady[:, 2] < y_max))
-    # in x direction
-    particle_to_x = np.argwhere(np.logical_and(M_particle_steady[:, 3] > x_min, M_particle_steady[:, 3] < x_max))
-    # Combine the results of both tests (arrays with indices of particles which interract with testing area)
-    particle_to_check = reduce(np.intersect1d, (particle_to_y, particle_to_x))
-    # Convert array to list
-    particle_to_check = np.array(particle_to_check).tolist()
-    # Create list with z-coordinates of interracting particles
-    z_list = []
-    for i in particle_to_check:
-        value = M_particle_steady[i, 1]
-        z_list.append(value)
-    # Check list for entries and pick the max value
-    if not z_list:
-        max_z_value = 0
-    else:
-        max_z_value = max(z_list)
-    # Calculate dropping height
-    z = (max_z_value + d_max_steady)*1.2
-
-    return z
-
-def calc_overlap(d_i, z_i, y_i, x_i, M_particle_steady, test_range):
-    """ 
-    Function that checks overlaps of an unstable particle.
-    It also double check the collision criteria of the calc_sedimentation function.
-    If calc_sedimentation function works properly, only one overlapping should be identified.
-    """
-    # Variable to identifiycollisions with walls
-    walls = 0 
-    # Counting variable for number of collisions with steady state particles
-    n_overlaps = 0
-    # Array with indices of particles in steady state which overlap with the considered particle
-    overlaps = []
-    """ Define region of interrest """
-    z_min = z_i - test_range
-    if z_min < 0:
-        z_min = 0
-    z_max = z_i + test_range
-    y_min = y_i - test_range
-    y_max = y_i + test_range
-    x_min = x_i - test_range
-    x_max = x_i + test_range
-    """ Checking particles within the testing range for possible overlappings """
-    # Arrays in x, y and z direction with indices of particles, which could overlap with the considered particle
-    x_array = np.argwhere(np.logical_and(M_particle_steady[:, 3] > x_min, M_particle_steady[:, 3] < x_max))
-    y_array = np.argwhere(np.logical_and(M_particle_steady[:, 2] > y_min, M_particle_steady[:, 2] < y_max))
-    z_array = np.argwhere(np.logical_and(M_particle_steady[:, 1] > z_min, M_particle_steady[:, 1] < z_max))
-    # Combine the arrays
-    particle_to_check = reduce(np.intersect1d, (x_array, y_array, z_array))
-    # Convert array to list
-    particle_to_check = np.array(particle_to_check).tolist()
-    """ Check each particle that may overlap for an actual overlapping """
-    for i in particle_to_check:
-        # Retrieve information about particles that may overlap with the particle under consideration from the steady state array
-        # Coordinates
-        z_j = float(M_particle_steady[i, 1])
-        y_j = float(M_particle_steady[i, 2])
-        x_j = float(M_particle_steady[i, 3])
-        # Diameter
-        d_j = float(M_particle_steady[i, 5])
-        # distance between the particle's centers when they collide
-        dist_required = (d_i + d_j) / 2
-        # Actual distance between the particle's centers 
-        distance_particles = math.sqrt((z_i-z_j)**2 + (y_i-y_j)**2 + (x_i-x_j)**2)
-        # Check whether particles overlap and store the overlapping particle in array
-        if dist_required - distance_particles > 1e-9:
-            overlaps.append(i)
-    # Remove duplicates from a list
-    overlaps = list(set(overlaps))
-    # Determine number of collisions
-    n_overlaps = len(overlaps)
-    """ Now checking for overlappings with walls """
-    # Wall in positive x direction
-    if (x_i + d_i / 2) >= A_0:
-        walls = 1
-    # Wall in negative x direction
-    elif (x_i - d_i / 2) <= -A_0:
-        walls = -1
-    # Wall in positive y direction
-    elif (y_i + d_i / 2) >= A_0:
-        walls = 2
-    # Wall in negative y direction
-    elif (y_i - d_i / 2) <= -A_0:
-        walls = -2
-    # Wall in positive x direction and positive y direction    
-    elif (x_i + d_i / 2) >= A_0 and (y_i + d_i / 2) >= A_0:
-        walls = 3
-    # Wall in negative x direction and positive y direction 
-    elif (x_i - d_i / 2) <= -A_0 and (y_i + d_i / 2) >= A_0:
-        walls = -3
-    # Wall in positive x direction and negative y direction 
-    elif (x_i + d_i / 2) >= A_0 and (y_i - d_i / 2) <= -A_0:
-        walls = 4
-    # Wall in negative x direction and negaive y direction 
-    elif (x_i - d_i / 2) <= -A_0 and (y_i - d_i / 2) <= -A_0:
-        walls = -4
-
-    return n_overlaps, overlaps, walls
-
-def position_update1(y_i, x_i, d_i, M_particle_steady, overlaps):
-    """ Function that updates the position of the considered particle overlapping with one steady state particle. """
-    # Coordinates of the steady state particle 
-    y1 = float(M_particle_steady[overlaps[0], 2])
-    x1 = float(M_particle_steady[overlaps[0], 3])
-    # Diameter of the steady state particle 
-    d1 = float(M_particle_steady[overlaps[0], 5])
-    # Required distance between the centers of the considered particle and the steady state particle
-    dist_1i = (d1 + d_i) / 2  
-    # Actual distance between the centers of the considered particle and the steady state particle in x and y coordinate
-    delta_x = x_i - x1
-    delta_y = y_i - y1
-    dist_1i_current = np.sqrt(delta_x**2 + delta_y**2)
-    # Calculate the scaling factor
-    scaling_factor = dist_1i / dist_1i_current
-    # Calculate the new position
-    x = x1 + delta_x * scaling_factor
-    y = y1 + delta_y * scaling_factor
-
-    return y, x
-
-def position_update2(z_i, y_i, x_i, d_i, M_particle_steady, overlaps):
-    """
-    Function to update the position of the considered particle if it overlaps with two particles.
-    There are two mathematical solutions of the new position, but only one logical. 
-    A solver is used to determine these solutions.
-    First, an intersection circle plane is calculated corresponding to the intersection area of two auxiliary spheres.
-    The intersection plane is used to identify the guesses of the both solutions of the considered particle's new position.
-    Moreover, the plane is used to check wheter the solver solutions are correct.
-    The centers of the auxiliary spheres corresponds to the center coordinates of the both steady state particles.
-    The radii of the auxiliary spheres corresponds to the sums of the steady state particles' radii and the considered particle's radius.
-    To identify the logical solution of the both solutions, the distances between the original position and the new positions are calculated.
-    The new position with the lowest distance to the original position is the logical solution.
-    """
-    # Used in case an error occurs during position update.
-    error_position_update2 = 0
-    # Coordinates of the first particle in steady state
-    z1 = float(M_particle_steady[overlaps[0], 1])
-    y1 = float(M_particle_steady[overlaps[0], 2])
-    x1 = float(M_particle_steady[overlaps[0], 3])
-    # Diameter of the first particle in steady state
-    d1 = float(M_particle_steady[overlaps[0], 5])
-    # Center coordinates of the first auxiliary sphere
-    c1 = np.array([z1, y1, x1])
-    # Radius of the first auxiliary sphere 
-    r1 = (d_i + d1) / 2
-    # Coordinates of the second particle in steady state
-    z2 = float(M_particle_steady[overlaps[1], 1])
-    y2 = float(M_particle_steady[overlaps[1], 2])
-    x2 = float(M_particle_steady[overlaps[1], 3])
-    # Diameter of the second particle in steady state
-    d2 = float(M_particle_steady[overlaps[1], 5])
-    # Center coordinates of the second auxiliary sphere
-    c2 = np.array([z2, y2, x2])
-    # Radius of the second auxiliary sphere 
-    r2 = (d_i + d2) / 2
-    """ Calculation of the intersection circle plane """  
-    # Vector between the centers
-    V_centers_12 = c2 - c1
-    # Distance between the centers
-    dist_centers_12 = np.linalg.norm(V_centers_12)
-    # Normal vector of the intersection circle plane
-    V_unit_centers_12 = V_centers_12 / dist_centers_12
-    # Distance from the first sphere's center to the intersection circle plane
-    dist_c1_plane = (r1**2 - r2**2 + dist_centers_12**2) / (2 * dist_centers_12)
-    # Center of the intersection circle (point on the intersection plane closest to sphere1)
-    c_ic = c1 + dist_c1_plane * V_unit_centers_12
-    """ 
-    Calculation of the reflected position of the considered particle.
-    First, the vector between the centers of the considered particle and the intersection circle is determined.
-    Then, this vector is reflected such a way that the projection of this vector on the yx plane is rotated by 180 degrees on the yx plane.
-    The coordiantes of the reflected position are used as second guess for the solver to determine the second solver solution. 
-    """
-    # Vector between the centers of the considered particle and the intersection circle
-    V_centers_ici = np.array([z_i, y_i, x_i]) - np.array(c_ic)
-    # Reflected vector
-    V_reflected = np.array([V_centers_ici[0], -V_centers_ici[1], -V_centers_ici[2]])
-    # Reflected position
-    P_reflected = np.array(c_ic) + V_reflected
-    # Coordinates for the initial guess
-    x_01 = x_i
-    y_01 = y_i
-    x_02 = P_reflected[2]
-    y_02 = P_reflected[1]
-    # Initial guess values
-    initial_guess1 = [x_01, y_01]
-    initial_guess2 = [x_02, y_02]
-    """ 
-    Solver to find possible new positions of the considered particle
-    The first guess is the logical one. The second is just to determine the second solver solution.
-    The second solver solution is just to check wheter the first one is the right one 
-    """
-    x_s1, y_s1 = fsolve(solver_position2, initial_guess1, args=(c1, c2, r1, r2, z_i))
-    x_s2, y_s2 = fsolve(solver_position2, initial_guess2, args=(c1, c2, r1, r2, z_i))
-    """ Check wheter the solver found two different solutions or the same twice """
-    # Error value is 1 if the solver found the same position twice
-    if x_s1 == x_s2 and y_s1 == y_s2:
-        error_position_update2 = 1
-    # Exclude values behind the simulation walls
-    if x_s1 < -A_0 or x_s1 > A_0:
-        x_s1 = None
-    if x_s2 < -A_0 or x_s2 > A_0:
-        x_s2 = None
-    if y_s1 < -A_0 or y_s1 > A_0:
-        y_s1 = None
-    if y_s2 < -A_0 or y_s2 > A_0:
-        y_s2 = None    
-    """ 
-    Determine the actual new position of the considered particle from the both solver solutions.
-    Must be the closest position to the original position of the considered particle
-    """
-    # Choose the new position based on the validity of values
-    if y_s1 == None or x_s1 == None:
-        if y_s2 != None and x_s2 != None:
-            x = x_s2
-            y = y_s2
-        else:
-            error_position_update2 = 3
-            return y_i, x_i, error_position_update2   
-    elif y_s2 == None or x_s2 == None:
-        if y_s1 != None or x_s1 != None:
-            x = x_s1
-            y = y_s1
-        else: 
-            error_position_update2 = 3
-            return y_i, x_i, error_position_update2
-    else:
-        # Vectors between the centers of the considered particle's original position and the new positions
-        V_centers_new1 = np.array([z_i, y_i, x_i]) - np.array([z_i, y_s1, x_s1])
-        V_centers_new2 = np.array([z_i, y_i, x_i]) - np.array([z_i, y_s2, x_s2])
-        # Distances between these centers
-        dist_centers_new1 = np.linalg.norm(V_centers_new1)
-        dist_centers_new2 = np.linalg.norm(V_centers_new2)
-        # Choose new position based on the shortest distance
-        if dist_centers_new1 <= dist_centers_new2:
-            x = x_s1
-            y = y_s1
-        else:
-            x = x_s2
-            y = y_s2
-    """ 
-    Check wheter the new positions' coordinates are located on the intersection plane 
-    Just double checking wheter the solver found correct solutions
-    The equation of the intersection plane is Az+By+Cx+D=0, where A, B, C and D are the equation coefficients.
-    """
-    # Determine the plane equation coefficients
-    # The normal vector to the plane is the same as the V_centers_12
-    coeff_A, coeff_B, coeff_C = V_unit_centers_12
-    # Calculate D using the point on the plane
-    coeff_D = -(coeff_A * c_ic[0] + coeff_B * c_ic[1] + coeff_C * c_ic[2])
-    # Check whether the new positions of considered particle is located on the intersection plane
-    on_plane = check_point_on_plane(z_i, y, x, coeff_A, coeff_B, coeff_C, coeff_D)
-    # Error value is 2 if the position of the first solver solution is not located on the intersection plane
-    if on_plane == False:
-        error_position_update2 = 2
-
-    return y, x, error_position_update2
-
-def solver_position2(params, c1, c2, r1, r2, z_i):
-    """ Solver to calculate the new position of the considered particle if it overlaps with two particles """
-    # Solver variables
-    x, y = params
-    # Equation of a circle. The circles coresponds here to the auxiliary spheres
-    eq1 = (x - c1[2])**2 + (y - c1[1])**2 + (z_i - c1[0])**2 - r1**2
-    eq2 = (x - c2[2])**2 + (y - c2[1])**2 + (z_i - c2[0])**2 - r2**2
-    return [eq1, eq2]
-
-def check_point_on_plane(z_i, y, x, coeff_A, coeff_B, coeff_C, coeff_D): 
-    """ Function that checks whether a point lies on a plane or not (plane equation in coordinate form) """
-    # Calculate the left-hand side of the plane equation
-    check_zero = z_i*coeff_A + y*coeff_B + x*coeff_C + coeff_D
-    # Check if the left-hand side is approximately zero (account for floating-point precision)
-    on_plane = abs(check_zero) < 1e-9
-    return on_plane
-
-def position_update3(z_i, y_i, x_i, d_i, M_particle_steady, overlaps):
-    """
-    Function to update the position of the considered particle if it overlaps with three particles.
-    There are two mathematical solutions of the new position, but only one logical. 
-    A solver is used to determine these solutions.
-    First, a triangle plane is calculated which crosses the centers of the three steady state particles.
-    Moreover, the centroid of the triangle is determined.
-    The triangle plane is used to identify the guesses of the both solutions of the considered particle's new position.
-    It is tested whether the both solutions differ and if they are correct.
-    To identify the logical solution of the both solutions, the z-coordinates of the both soultions and the centroid are compared.
-    The new position with the z-coordinate above the centroid is the logical solution.
-    """
-    # Used in case an error occurs during position update.
-    error_position_update3 = 0
-    # Coordinates of the first particle in steady state
-    z1 = float(M_particle_steady[overlaps[0], 1])
-    y1 = float(M_particle_steady[overlaps[0], 2])
-    x1 = float(M_particle_steady[overlaps[0], 3])
-    # Diameter of the first particle in steady state
-    d1 = float(M_particle_steady[overlaps[0], 5])
-    # Center coordinates of the first auxiliary sphere
-    c1 = np.array([z1, y1, x1])
-    # Radius of the first auxiliary sphere 
-    r1 = (d_i + d1) / 2
-    # Coordinates of the second particle in steady state
-    z2 = float(M_particle_steady[overlaps[1], 1])
-    y2 = float(M_particle_steady[overlaps[1], 2])
-    x2 = float(M_particle_steady[overlaps[1], 3])
-    # Diameter of the second particle in steady state
-    d2 = float(M_particle_steady[overlaps[1], 5])
-    # Center coordinates of the second auxiliary sphere
-    c2 = np.array([z2, y2, x2])
-    # Radius of the second auxiliary sphere 
-    r2 = (d_i + d2) / 2
-    # Coordinates of the third particle in steady state
-    z3 = float(M_particle_steady[overlaps[2], 1])
-    y3 = float(M_particle_steady[overlaps[2], 2])
-    x3 = float(M_particle_steady[overlaps[2], 3])
-    # Diameter of the third particle in steady state
-    d3 = float(M_particle_steady[overlaps[2], 5])
-    # Center coordinates of the third auxiliary sphere
-    c3 = np.array([z3, y3, x3])
-    # Radius of the third auxiliary sphere 
-    r3 = (d_i + d3) / 2
-    """ Calculation of the triangle's plane """
-    # Calculate vectors AB and AC
-    AB = np.array([z2 - z1, y2 - y1, x2 - x1])
-    AC = np.array([z3 - z1, y3 - y1, x3 - x1])
-    # Compute the normal vector to the plane (cross product of AB and AC)
-    V_triangle_norm = np.cross(AB, AC)
-    A, B, C = V_triangle_norm
-    # Calculate D using the plane equation Ax + By + Cz + D = 0
-    # Using point A (x1, y1, z1) to find D
-    D = -(A * z1 + B * y1 + C * x1)
-    """ 
-    Calculation of the reflected position of the considered particle.
-    First, the distance between the center of the considered particle and the triangle's plane is determined.
-    Then, the position of the considered particle is reflected about the triangle's plane
-    The coordiantes of the reflected position are used as second guess for the solver to determine the second solver solution. 
-    """
-    # Calculate the distance from the considered particle's center to the plane
-    distance_pi = (A * z_i + B * y_i + C * x_i + D) / np.linalg.norm(V_triangle_norm)
-    # Calculate the reflection point on the plane
-    P_reflected = np.array([z_i, y_i, x_i]) - 2 * distance_pi * V_triangle_norm / np.linalg.norm(V_triangle_norm)
-    """ Solver to find possible new position of the considered particle """
-    # Coordinates for the initial guess
-    x_01 = x_i
-    y_01 = y_i
-    z_01 = z_i
-    x_02 = P_reflected[2]
-    y_02 = P_reflected[1]
-    z_02 = P_reflected[0]
-    # Initial guess values
-    initial_guess1 = [x_01, y_01, z_01]
-    initial_guess2 = [x_02, y_02, z_02]
-    # Solver
-    x_s1, y_s1, z_s1 = fsolve(solver_position3, initial_guess1, args=(c1, c2, c3, r1, r2, r3))
-    x_s2, y_s2, z_s2 = fsolve(solver_position3, initial_guess2, args=(c1, c2, c3, r1, r2, r3))
-    """ Check wheter the solver found two different solutions or the same twice """
-    # Error value is 1 if the solver found the same position twice
-    if x_s1 == x_s2 and y_s1 == y_s2 and z_s1 == z_s2:
-        error_position_update3 = 1
-    # Exclude values behind the simulation walls
-    if x_s1 < -A_0 or x_s1 > A_0:
-        x_s1 = None
-    if x_s2 < -A_0 or x_s2 > A_0:
-        x_s2 = None
-    if y_s1 < -A_0 or y_s1 > A_0:
-        y_s1 = None
-    if y_s2 < -A_0 or y_s2 > A_0:
-        y_s2 = None 
-    """ 
-    Determine the actual new position of the considered particle from the both solver solutions.
-    Must be the closest position to the original position of the considered particle
-    """
-    # Z-coordinate of polygon's centroid    
-    z_cen = (z1 + z2 + z3) / 3
-    # If the center of the considered particle is located above the centroid, 
-    # the coordinates of the first solver solution are assigned to the center of the considered particle
-    if x_s1 != None and y_s1 != None and z_s1 > z_cen:
-        z = z_s1
-        y = y_s1
-        x = x_s1
-    # If the center of the considered particle is located above the centroid, 
-    # the coordinates of the second solver solution are assigned to the center of the considered particle
-    elif x_s2 != None and y_s2 != None and z_s2 > z_cen: 
-        z = z_s2
-        y = y_s2
-        x = x_s2
-    else: 
-        error_position_update3 = 2
-        return z_i, y_i, x_i, error_position_update3
-    """ 
-    Check wheter the distances between the centers of the three steady state particles 
-    and the new center position of the considered particle are in the samerange as the sums of their radii.
-    """
-    same_range = check_distances3(r1, r2, r3, c1, c2, c3, z, y, x)
-    # Error value is 2 if the distance between the steady state particles and the position of the solver solution 
-    # is not in the same range as the sums of their radii
-    if same_range == False:
-        error_position_update3 = 3
-        return z_i, y_i, x_i, error_position_update3
-
-    return z, y, x, error_position_update3
-
-def solver_position3(params, c1, c2, c3, r1, r2, r3):
-    """ Solver to calculate the new position of the considered particle if it overlaps with three particles """
-    # Solver variables
-    x, y, z = params
-    # Equation of a circle. The circles coresponds here to the auxiliary spheres
-    eq1 = (x - c1[2])**2 + (y - c1[1])**2 + (z - c1[0])**2 - r1**2
-    eq2 = (x - c2[2])**2 + (y - c2[1])**2 + (z - c2[0])**2 - r2**2
-    eq3 = (x - c3[2])**2 + (y - c3[1])**2 + (z - c3[0])**2 - r3**2
-    return [eq1, eq2, eq3]
-
-def check_distances3(r1, r2, r3, c1, c2, c3, z_s, y_s, x_s): 
-    """ Function that checks wheter the distances between the centers of the three steady state particles 
-    and the new center position of the considered particle are in the same range as the sums of their radii. """
-    # True if distances are in the same range as the sums of radii
-    same_range = True
-    # Vectors between the centers of the steady state particles and the new center positions of the considered particle    
-    V_centers_1i = np.array([z_s, y_s, x_s]) - c1
-    V_centers_2i = np.array([z_s, y_s, x_s]) - c2
-    V_centers_3i = np.array([z_s, y_s, x_s]) - c3
-    # Distance between the centers of the steady state particles and the new center position of the considered particle
-    dist_centers_1i = np.linalg.norm(V_centers_1i)
-    dist_centers_2i = np.linalg.norm(V_centers_2i)
-    dist_centers_3i = np.linalg.norm(V_centers_3i)
-    # Check if the left-hand side is approximately zero (account for floating-point precision)
-    if abs(dist_centers_1i-r1) > 1e-9 or abs(dist_centers_2i-r2) > 1e-9 or abs(dist_centers_3i-r3) > 1e-9:
-        same_range = False
-
-    return same_range
-
-def check_steadystate3(z_i, y_i, x_i, M_particle_steady, overlaps):
-    """ 
-    Function to check the steady state.
-    As soon as considered particle collides with three steady state particles, a steady state check is performed.
-    Considered particle is in steady state if its center lies within the y-x-plane of a triangle located between the 
-    steady state particle centers. 
-    Barycentric coordinate method is used to determine if the center of the considered particle lies within the polygon.
-    Moreover, barycentric coordinates are used to determine the subdomain of the triangle where the center lies.
-    Based on the subdomain, the particle rolling direction is identified.
-    """
-    # Used in case an error occurs during steadystate check.
-    error_check_steadystate3 = 0
-    # Variable for steady state check condition
-    steady_check = 0
-    # Coordinates of the first particle in steady state
-    z1 = float(M_particle_steady[overlaps[0], 1])
-    y1 = float(M_particle_steady[overlaps[0], 2])
-    x1 = float(M_particle_steady[overlaps[0], 3])
-    # Coordinates of the second particle in steady state
-    z2 = float(M_particle_steady[overlaps[1], 1])
-    y2 = float(M_particle_steady[overlaps[1], 2])
-    x2 = float(M_particle_steady[overlaps[1], 3])
-    # Coordinates of the third particle in steady state
-    z3 = float(M_particle_steady[overlaps[2], 1])
-    y3 = float(M_particle_steady[overlaps[2], 2])
-    x3 = float(M_particle_steady[overlaps[2], 3])
-    # Z-coordinate of triangle's centroid
-    z_cen = (z1 + z2 + z3) / 3
-    # Check whether the center of the considered particle is located above the centroid
-    # Double checking at this point, since already handled in the position_update3 function.
-    # Error value is 1 when the center is located below the centroid
-    if z_i < z_cen:
-        error_check_steadystate3 = 1
-        return steady_check, overlaps, error_check_steadystate3
-    """ Barycentric coordinate technique """
-    # Denominator
-    denominator = ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3))
-    # Check whether the triangle points are collinear
-    # Error value is 2 when the triangle points are collinear
-    if denominator == 0:
-        error_check_steadystate3 = 2  
-        return steady_check, overlaps, error_check_steadystate3
-    # Barycentric coordinates
-    a = ((y2 - y3) * (x_i - x3) + (x3 - x2) * (y_i - y3)) / denominator
-    b = ((y3 - y1) * (x_i - x3) + (x1 - x3) * (y_i - y3)) / denominator
-    c = 1 - a - b
-    # Determine the subdomain of the triangle and thus wheter it is in steady state or in which direction it is rolling
-    if (0 <= a <= 1) and (0 <= b <= 1) and (0 <= c <= 1):
-        steady_check = 3
-    elif a < 0 and b >= 0 and c >= 0:
-        # Rolling along steady state particle Nr.2 and Nr.3
-        overlaps = np.delete(overlaps, 0)
-        steady_check = 2
-    elif b < 0 and a >= 0 and c >= 0:
-        # Rolling along steady state particle Nr.1 and Nr.3
-        overlaps = np.delete(overlaps, 1)
-        steady_check = 2
-    elif c < 0 and a >= 0 and b >= 0:
-        # Rolling along steady state particle Nr.1 and Nr.2
-        overlaps = np.delete(overlaps, 2)
-        steady_check = 2
-    elif a < 0 and b < 0 and c > 1:
-        # Rolling along steady state particle Nr.3
-        overlaps = np.delete(overlaps, [0, 1])
-        steady_check = 1
-    elif a < 0 and c < 0 and b > 1:
-        # Rolling along steady state particle Nr.2
-        overlaps = np.delete(overlaps, [0, 2])
-        steady_check = 1
-    elif b < 0 and c < 0 and a > 1:
-        # Rolling along steady state particle Nr.1 
-        overlaps = np.delete(overlaps, [1, 2])
-        steady_check = 1
-    else:
-        # Error value is 3 when the subdomain of the triangle could not be determined correctly
-        error_check_steadystate3 = 3
-
-    return steady_check, overlaps, error_check_steadystate3
-
-def position_update1w(z_i, y_i, x_i, d_i, M_particle_steady, overlaps, walls):
-    """
-    Function to update the position of the considered particle if it overlaps with one particle and one wall.
-    There are two mathematical solutions of the new position, but only one logical. 
-    A solver is used to determine these solutions.
-    First, an intersection circle plane is calculated corresponding to the intersection area of one auxiliary sphere and the wall.
-    The intersection plane is used to identify the guesses of the both solutions of the considered particle's new position.
-    Moreover, the plane is used to check wheter the solver solutions are correct.
-    The center of the auxiliary sphere corresponds to the center coordinates of the steady state particle.
-    The radius of the auxiliary sphere corresponds to the sums of the steady state particle's radius and the considered particle's radius.
-    To identify the logical solution of the both solutions, the distances between the original position and the new positions are calculated.
-    The new position with the lowest distance to the original position is the logical solution.
-    """
-    # Used in case an error occurs during position update.
-    error_position_update1w = 0
-    # Coordinates of the particle in steady state
-    z1 = float(M_particle_steady[overlaps[0], 1])
-    y1 = float(M_particle_steady[overlaps[0], 2])
-    x1 = float(M_particle_steady[overlaps[0], 3])
-    # Diameter of the particle in steady state
-    d1 = float(M_particle_steady[overlaps[0], 5])
-    # Center coordinates of the auxiliary sphere
-    c1 = np.array([z1, y1, x1])
-    # Radius of the auxiliary sphere 
-    r1 = (d_i + d1) / 2
-    # Coordinates of the wall
-    z_w1 = z1
-    if walls == 1:
-        x_w1 = A_0
-        y_w1 = y1
-    elif walls == -1:
-        x_w1 = -A_0
-        y_w1 = y1
-    elif walls == 2:
-        x_w1 = x1
-        y_w1 = A_0
-    elif walls == -2:
-        x_w1 = x1
-        y_w1 = -A_0
-    # Center coordinates of the wall
-    c_w1 = np.array([z_w1, y_w1, x_w1])
-    # Distance between wall and center of considered particle if they are in contact
-    dist_wi = d_i / 2
-    """ Calculation of the intersection circle plane """  
-    # Vector between the centers
-    V_centers_1w = c_w1 - c1
-    # Distance between the centers
-    dist_centers_1w = np.linalg.norm(V_centers_1w)
-    # Normal vector of the intersection circle plane
-    V_unit_centers_1w = V_centers_1w / dist_centers_1w
-    # Distance from the first sphere's center to the intersection circle plane
-    dist_c1_plane = dist_centers_1w - dist_wi
-    # Center of the intersection circle (point on the intersection plane closest to sphere1)
-    c_ic = c1 + dist_c1_plane * V_unit_centers_1w
-    """ 
-    Calculation of the reflected position of the considered particle.
-    First, the vector between the centers of the considered particle and the intersection circle is determined.
-    Then, this vector is reflected such a way that the projection of this vector on the yx plane is rotated by 180 degrees on the yx plane.
-    The coordiantes of the reflected position are used as second guess for the solver to determine the second solver solution. 
-    """
-    # Vector between the centers of the considered particle and the intersection circle
-    V_centers_ici = np.array([z_i, y_i, x_i]) - np.array(c_ic)
-    # Reflected vector
-    V_reflected = np.array([V_centers_ici[0], -V_centers_ici[1], -V_centers_ici[2]])
-    # Reflected position
-    P_reflected = np.array(c_ic) + V_reflected
-    # Coordinates for the initial guess
-    x_01 = x_i
-    y_01 = y_i
-    x_02 = P_reflected[2]
-    y_02 = P_reflected[1]
-    # If contact is with X-wall
-    if walls == 1 or walls == -1:
-        # Initial guess values
-        initial_guess1 = y_01
-        initial_guess2 = y_02
-        # X-coordinate of the new position
-        if walls == 1:
-            x_s = A_0 - d_i / 2
-        elif walls == -1:
-            x_s = -A_0 + d_i / 2   
-        """ 
-        Solver to find possible new positions of the considered particle
-        The first guess is the logical one. The second is just to determine the second solver solution.
-        The second solver solution is just to check wheter the first one is the right one 
-        """
-        y_s1 = fsolve(solver_position1w, initial_guess1, args=(c1, r1, z_i, x_s, walls))
-        y_s2 = fsolve(solver_position1w, initial_guess2, args=(c1, r1, z_i, x_s, walls))
-        y_s1 = y_s1[0]
-        y_s2 = y_s2[0]
-        """ Check wheter the solver found two different solutions or the same twice """
-        # Error value is 1 if the solver found the same position twice
-        if y_s1 == y_s2:
-            error_position_update1w = 1
-        # Exclude values behind the simulation walls
-        if y_s1 < -A_0 or y_s1 > A_0:
-            y_s1 = None
-        if y_s2 < -A_0 or y_s2 > A_0:
-            y_s2 = None
-        """ 
-        Determine the actual new position of the considered particle from the both solver solutions.
-        Must be the closest position to the original position of the considered particle
-        """
-        # Coose the new position based on the validity of values
-        if y_s1 == None and y_s2 != None:
-            y = y_s2
-        elif y_s2 == None and y_s1 != None:
-            y = y_s1
-        elif y_s1 == None and y_s2 == None:
-            error_position_update1w = 3
-            return y_i, x_i, error_position_update1w
-        else:
-            # Vectors between the centers of the considered particle's original position and the new positions
-            V_centers_new1 = np.array([z_i, y_i, x_i]) - np.array([z_i, y_s1, x_s])
-            V_centers_new2 = np.array([z_i, y_i, x_i]) - np.array([z_i, y_s2, x_s])
-            # Distances between these centers
-            dist_centers_new1 = np.linalg.norm(V_centers_new1)
-            dist_centers_new2 = np.linalg.norm(V_centers_new2)
-            # Choose new position based on the shortest distance
-            if dist_centers_new1 <= dist_centers_new2:
-                y = y_s1
-            else:
-                y = y_s2
-        x = x_s
-    # If contact is with Y-wall
-    elif walls == 2 or walls == -2:
-        # Initial guess values
-        initial_guess1 = x_01
-        initial_guess2 = x_02
-        # Y-coordinate of the new position
-        if walls == 2:
-            y_s = A_0 - d_i / 2
-        elif walls == -2:
-            y_s = -A_0 + d_i / 2   
-        """ 
-        Solver to find possible new positions of the considered particle
-        The first guess is the logical one. The second is just to determine the second solver solution.
-        The second solver solution is just to check wheter the first one is the right one 
-        """
-        x_s1 = fsolve(solver_position1w, initial_guess1, args=(c1, r1, z_i, y_s, walls))
-        x_s2 = fsolve(solver_position1w, initial_guess2, args=(c1, r1, z_i, y_s, walls))
-        x_s1 = x_s1[0]
-        x_s2 = x_s2[0]
-        """ Check wheter the solver found two different solutions or the same twice """
-        # Error value is 1 if the solver found the same position twice
-        if x_s1 == x_s2:
-            error_position_update1w = 1
-        # Exclude values behind the simulation walls
-        if x_s1 < -A_0 or x_s1 > A_0:
-            x_s1 = None
-        if x_s2 < -A_0 or x_s2 > A_0:
-            x_s2 = None
-        """ 
-        Determine the actual new position of the considered particle from the both solver solutions.
-        Must be the closest position to the original position of the considered particle
-        """
-        # Coose the new position based on the validity of values
-        if x_s1 == None and x_s2 != None:
-            x = x_s2
-        elif x_s2 == None and x_s1 != None:
-            x = x_s1
-        elif x_s1 == None and x_s2 == None:
-            error_position_update1w = 3
-            return y_i, x_i, error_position_update1w
-        else:   
-            # Vectors between the centers of the considered particle's original position and the new positions
-            V_centers_new1 = np.array([z_i, y_i, x_i]) - np.array([z_i, y_s, x_s1])
-            V_centers_new2 = np.array([z_i, y_i, x_i]) - np.array([z_i, y_s, x_s2])
-            # Distances between these centers
-            dist_centers_new1 = np.linalg.norm(V_centers_new1)
-            dist_centers_new2 = np.linalg.norm(V_centers_new2)
-            # Choose new position based on the shortest distance
-            if dist_centers_new1 <= dist_centers_new2:
-                x = x_s1
-            else:
-                x = x_s2
-        # Assign x-coordinate
-        y = y_s
-    """ 
-    Check wheter the new positions' coordinates are located on the intersection plane 
-    Just double checking wheter the solver found correct solutions
-    The equation of the intersection plane is Az+By+Cx+D=0, where A, B, C and D are the equation coefficients.
-    """
-    # Determine the plane equation coefficients
-    # The normal vector to the plane is the same as the V_centers_12
-    coeff_A, coeff_B, coeff_C = V_unit_centers_1w
-    # Calculate D using the point on the plane
-    coeff_D = -(coeff_A * c_ic[0] + coeff_B * c_ic[1] + coeff_C * c_ic[2])
-    # Check whether the new positions of considered particle is located on the intersection plane
-    on_plane = check_point_on_plane(z_i, y, x, coeff_A, coeff_B, coeff_C, coeff_D)
-    # Error value is 2 if the position of the first solver solution is not located on the intersection plane
-    if on_plane == False:
-        error_position_update1w = 2
-        return y_i, x_i, error_position_update1w
-
-    return y, x, error_position_update1w
-
-def solver_position1w(params, c1, r1, z_i, par, walls):
-    """ Solver to calculate the new position of the considered particle if it overlaps with one particle and one wall """
-    if walls == 1 or walls == -1:
-        # Solver variables
-        y = params
-        # Equation of a circle. The circles coresponds here to the auxiliary spheres
-        eq1 = (par - c1[2])**2 + (y - c1[1])**2 + (z_i - c1[0])**2 - r1**2
-        return eq1
-    elif walls == 2 or walls == -2:
-        # Solver variables
-        x = params
-        # Equation of a circle. The circles coresponds here to the auxiliary spheres
-        eq1 = (x - c1[2])**2 + (par - c1[1])**2 + (z_i - c1[0])**2 - r1**2
-        return eq1  
-
-def position_update2w(z_i, y_i, x_i, d_i, M_particle_steady, overlaps, walls):
-    """
-    Function to update the position of the considered particle if it overlaps with two particles and one wall.
-    There are two mathematical solutions of the new position, but only one logical. 
-    A solver is used to determine these solutions.
-    First, a triangle plane is calculated which crosses the centers of the two steady state particles and the wall.
-    Moreover, the centroid of the triangle is determined.
-    The triangle plane is used to identify the guesses of the both solutions of the considered particle's new position.
-    It is tested whether the both solutions differ and if they are correct.
-    To identify the logical solution of the both solutions, the z-coordinates of the both soultions and the centroid are compared.
-    The new position with the z-coordinate above the centroid is the logical solution.
-    """
-    # Used in case an error occurs during position update.
-    error_position_update2w = 0
-    # Coordinates of the first particle in steady state
-    z1 = float(M_particle_steady[overlaps[0], 1])
-    y1 = float(M_particle_steady[overlaps[0], 2])
-    x1 = float(M_particle_steady[overlaps[0], 3])
-    # Diameter of the first particle in steady state
-    d1 = float(M_particle_steady[overlaps[0], 5])
-    # Center coordinates of the first auxiliary sphere
-    c1 = np.array([z1, y1, x1])
-    # Radius of the first auxiliary sphere 
-    r1 = (d_i + d1) / 2
-    # Coordinates of the second particle in steady state
-    z2 = float(M_particle_steady[overlaps[1], 1])
-    y2 = float(M_particle_steady[overlaps[1], 2])
-    x2 = float(M_particle_steady[overlaps[1], 3])
-    # Diameter of the second particle in steady state
-    d2 = float(M_particle_steady[overlaps[1], 5])
-    # Center coordinates of the second auxiliary sphere
-    c2 = np.array([z2, y2, x2])
-    # Radius of the second auxiliary sphere 
-    r2 = (d_i + d2) / 2
-    # Coordinates of the wall
-    z_w1 = (z1+z2)/2
-    if walls == 1:
-        x_w1 = A_0
-        y_w1 = (y1+y2)/2
-    elif walls == -1:
-        x_w1 = -A_0
-        y_w1 = (y1+y2)/2
-    elif walls == 2:
-        x_w1 = (x1+x2)/2
-        y_w1 = A_0
-    elif walls == -2:
-        x_w1 = (x1+x2)/2
-        y_w1 = -A_0
-    """ Calculation of the triangle's plane """
-    # Calculate vectors AB and AC
-    AB = np.array([z2 - z1, y2 - y1, x2 - x1])
-    AC = np.array([z_w1 - z1, y_w1 - y1, x_w1 - x1])
-    # Compute the normal vector to the plane (cross product of AB and AC)
-    V_triangle_norm = np.cross(AB, AC)
-    A, B, C = V_triangle_norm
-    # Calculate D using the plane equation Ax + By + Cz + D = 0
-    # Using point A (x1, y1, z1) to find D
-    D = -(A * z1 + B * y1 + C * x1)
-    """ 
-    Calculation of the reflected position of the considered particle.
-    First, the distance between the center of the considered particle and the triangle's plane is determined.
-    Then, the position of the considered particle is reflected about the triangle's plane
-    The coordiantes of the reflected position are used as second guess for the solver to determine the second solver solution. 
-    """
-    # Calculate the distance from the considered particle's center to the plane
-    distance_pi = (A * z_i + B * y_i + C * x_i + D) / np.linalg.norm(V_triangle_norm)
-    # Calculate the reflection point on the plane
-    P_reflected = np.array([z_i, y_i, x_i]) - 2 * distance_pi * V_triangle_norm / np.linalg.norm(V_triangle_norm)
-    """ Solver to find possible new position of the considered particle """
-    # Coordinates for the initial guess
-    x_01 = x_i
-    y_01 = y_i
-    z_01 = z_i
-    x_02 = P_reflected[2]
-    y_02 = P_reflected[1]
-    z_02 = P_reflected[0]
-    # If contact is with X-wall
-    if walls == 1 or walls == -1:
-        # Initial guess values
-        initial_guess1 = [y_01, z_01]
-        initial_guess2 = [y_02, z_02]
-        # X-coordinate of the new position
-        if walls == 1:
-            x_s1 = A_0 - d_i / 2
-            x_s2 = A_0 - d_i / 2
-        elif walls == -1:
-            x_s1 = -A_0 + d_i / 2 
-            x_s2 = -A_0 + d_i / 2
-        # Solver
-        y_s1, z_s1 = fsolve(solver_position2w, initial_guess1, args=(c1, c2, r1, r2, x_s1, walls))
-        y_s2, z_s2 = fsolve(solver_position2w, initial_guess2, args=(c1, c2, r1, r2, x_s2, walls))
-        """ Check wheter the solver found two different solutions or the same twice """
-        # Error value is 1 if the solver found the same position twice
-        if y_s1 == y_s2 and z_s1 == z_s2:
-            error_position_update2w = 1
-        # Exclude values behind the simulation walls
-        if y_s1 < -A_0 or y_s1 > A_0:
-            y_s1 = None
-        if y_s2 < -A_0 or y_s2 > A_0:
-            y_s2 = None    
-    # If contact is with Y-wall
-    if walls == 2 or walls == -2:    
-        # Initial guess values
-        initial_guess1 = [x_01, z_01]
-        initial_guess2 = [x_02, z_02]
-        # X-coordinate of the new position
-        if walls == 2:
-            y_s1 = A_0 - d_i / 2
-            y_s2 = A_0 - d_i / 2
-        elif walls == -2:
-            y_s1 = -A_0 + d_i / 2 
-            y_s2 = -A_0 + d_i / 2
-        # Solver
-        x_s1, z_s1 = fsolve(solver_position2w, initial_guess1, args=(c1, c2, r1, r2, y_s1, walls))
-        x_s2, z_s2 = fsolve(solver_position2w, initial_guess2, args=(c1, c2, r1, r2, y_s2, walls))
-        """ Check wheter the solver found two different solutions or the same twice """
-        # Error value is 1 if the solver found the same position twice
-        if x_s1 == x_s2 and z_s1 == z_s2:
-            error_position_update2w = 1       
-        # Exclude values behind the simulation walls
-        if x_s1 < -A_0 or x_s1 > A_0:
-            y_s1 = None
-        if x_s2 < -A_0 or x_s2 > A_0:
-            x_s2 = None
-    """ 
-    Determine the actual new position of the considered particle from the both solver solutions.
-    Must be the closest position to the original position of the considered particle
-    """
-    # Z-coordinate of triangles's centroid    
-    z_cen = (z1 + z2 + z_w1) / 3
-    # If the center of the considered particle is located above the centroid, 
-    # the coordinates of the first solver solution are assigned to the center of the considered particle
-    if x_s1 != None and y_s1 != None and z_s1 > z_cen:
-        z = z_s1
-        y = y_s1
-        x = x_s1
-    # If the center of the considered particle is located above the centroid, 
-    # the coordinates of the second solver solution are assigned to the center of the considered particle
-    elif x_s2 != None and y_s2 != None and z_s2 > z_cen: 
-        z = z_s2
-        y = y_s2
-        x = x_s2
-    else: 
-        error_position_update2w = 2
-        return z_i, y_i, x_i, error_position_update2w
-    """ 
-    Check wheter the distances between the centers of the three steady state particles 
-    and the new center position of the considered particle are in the samerange as the sums of their radii.
-    """
-    same_range = check_distances2w(r1, r2, c1, c2, d_i, z, y, x, walls)
-    # Error value is 2 if the distance between the steady state particles and the position of the solver solution 
-    # is not in the same range as the sums of their radii
-    if same_range == False:
-        error_position_update2w = 3
-        return z_i, y_i, x_i, error_position_update2w
-
-    return z, y, x, error_position_update2w
-
-def solver_position2w(params, c1, c2, r1, r2, par, walls):
-    """ Solver to calculate the new position of the considered particle if it overlaps with two particles and one wall """
-    if walls == 1 or walls == -1:
-        # Solver variables
-        y, z = params
-        # Equation of a circle. The circles coresponds here to the auxiliary spheres
-        eq1 = (par - c1[2])**2 + (y - c1[1])**2 + (z - c1[0])**2 - r1**2
-        eq2 = (par - c2[2])**2 + (y - c2[1])**2 + (z - c2[0])**2 - r2**2
-        return [eq1, eq2]
-    elif walls == 2 or walls == -2:
-        # Solver variables
-        x, z = params
-        # Equation of a circle. The circles coresponds here to the auxiliary spheres
-        eq1 = (x - c1[2])**2 + (par - c1[1])**2 + (z - c1[0])**2 - r1**2
-        eq2 = (x - c2[2])**2 + (par - c2[1])**2 + (z - c2[0])**2 - r2**2
-        return [eq1, eq2]
-
-def check_distances2w(r1, r2, c1, c2, d_i, z_s, y_s, x_s, walls): 
-    """ Function that checks wheter the distances between the centers of the two steady state particles, the wall, 
-    and the new center position of the considered particle are in the same range as the sums of their radii. """
-    # True if distances are in the same range as the sums of radii
-    same_range = True
-    # Vectors between the centers of the steady state particles and the new center positions of the considered particle    
-    V_centers_1i = np.array([z_s, y_s, x_s]) - c1
-    V_centers_2i = np.array([z_s, y_s, x_s]) - c2
-    # Distance between the centers of the steady state particles and the new center position of the considered particle
-    dist_centers_1i = np.linalg.norm(V_centers_1i)
-    dist_centers_2i = np.linalg.norm(V_centers_2i)
-    # Distance between the center of the considered particle and the wall
-    if walls == 1 or walls == -1: 
-        dist_wi = A_0 - abs(x_s) 
-    elif walls == 2 or walls == -2:
-        dist_wi = A_0 - abs(y_s)
-    # Check if the left-hand side is approximately zero (account for floating-point precision)
-    if abs(dist_centers_1i-r1) > 1e-9 or abs(dist_centers_2i-r2) > 1e-9 or abs(dist_wi-d_i/2) > 1e-9:
-        same_range = False
-
-    return same_range
-
-def check_steadystate2w(z_i, y_i, x_i, particle_i, M_particle_steady, overlaps, walls):
-    """ 
-    Function to check the steady state.
-    As soon as considered particle collides with two steady state particles and one wall, a steady state check is performed.
-    Considered particle is in steady state if its center lies within the y-x-plane of a triangle located between the 
-    steady state particle centers and the wall. 
-    Barycentric coordinate method is used to determine if the center of the considered particle lies within the polygon.
-    Moreover, barycentric coordinates are used to determine the subdomain of the triangle where the center lies.
-    Based on the subdomain, the particle rolling direction is identified.
-    """
-    # Used in case an error occurs during steadystate check.
-    error_check_steadystate3 = 0
-    # Variable for steady state check condition
-    steady_check = 0
-    # Coordinates of the first particle in steady state
-    z1 = float(M_particle_steady[overlaps[0], 1])
-    y1 = float(M_particle_steady[overlaps[0], 2])
-    x1 = float(M_particle_steady[overlaps[0], 3])
-    # Coordinates of the second particle in steady state
-    z2 = float(M_particle_steady[overlaps[1], 1])
-    y2 = float(M_particle_steady[overlaps[1], 2])
-    x2 = float(M_particle_steady[overlaps[1], 3])
-    # Coordinates of the wall
-    z_w1 = z_i
-    if walls == 1:
-        x_w1 = A_0
-        y_w1 = y_i
-    elif walls == -1:
-        x_w1 = -A_0
-        y_w1 = y_i
-    elif walls == 2:
-        x_w1 = x_i
-        y_w1 = A_0
-    elif walls == -2:
-        x_w1 = x_i
-        y_w1 = -A_0
-    # Z-coordinate of triangle's centroid
-    z_cen = (z1 + z2 + z_w1) / 3
-    # Check whether the center of the considered particle is located above the centroid
-    # Double checking at this point, since already handled in the position_update3 function.
-    # Error value is 1 when the center is located below the centroid
-    if z_i < z_cen:
-        error_check_steadystate3 = 1
-        return steady_check, overlaps, error_check_steadystate3
-    """ Barycentric coordinate technique """
-    # Denominator
-    denominator = ((y2 - y_w1) * (x1 - x_w1) + (x_w1 - x2) * (y1 - y_w1))
-    # Check whether the triangle points are collinear
-    # Error value is 2 when the triangle points are collinear
-    if denominator == 0:
-        error_check_steadystate3 = 2  
-        return steady_check, overlaps, error_check_steadystate3
-    # Barycentric coordinates
-    a = ((y2 - y_w1) * (x_i - x_w1) + (x_w1 - x2) * (y_i - y_w1)) / denominator
-    b = ((y_w1 - y1) * (x_i - x_w1) + (x1 - x_w1) * (y_i - y_w1)) / denominator
-    c = 1 - a - b
-    # Determine the subdomain of the triangle and thus wheter it is in steady state or in which direction it is rolling
-    if (0 <= a <= 1) and (0 <= b <= 1) and (0 <= c <= 1):
-        steady_check = 3
-    elif a < 0 and b >= 0 and c >= 0:
-        # Rolling along steady state particle Nr.2 and wall
-        overlaps = np.delete(overlaps, 0)
-        steady_check = 4
-    elif b < 0 and a >= 0 and c >= 0:
-        # Rolling along steady state particle Nr.1 and wall
-        overlaps = np.delete(overlaps, 1)
-        steady_check = 4
-    elif c < 0 and a >= 0 and b >= 0:
-        # Rolling along steady state particle Nr.1 and Nr.2
-        steady_check = 2
-    elif a < 0 and c < 0 and b > 1:
-        # Rolling along steady state particle Nr.2 
-        overlaps = np.delete(overlaps, 0)
-        steady_check = 1
-    elif b < 0 and c < 0 and a > 1:
-        # Rolling along steady state particle Nr.1 
-        overlaps = np.delete(overlaps, 1)
-        steady_check = 1
-    else:
-        # Error value is 3 when the subdomain of the triangle could not be determined correctly
-        error_check_steadystate3 = 3
-
-    return steady_check, overlaps, error_check_steadystate3
-
-def position_update1w2(z_i, y_i, x_i, d_i, M_particle_steady, overlaps, walls):
-    """
-    Function to update the position of the considered particle if it overlaps with one particle and two walls.
-    There are two mathematical solutions of the new position, but only one logical. 
-    A solver is used to determine these solutions.
-    First, a triangle plane is calculated which crosses the center of the steady state particle and the walls.
-    Moreover, the centroid of the triangle is determined.
-    The triangle plane is used to identify the guesses of the both solutions of the considered particle's new position.
-    It is tested whether the both solutions differ and if they are correct.
-    To identify the logical solution of the both solutions, the z-coordinates of the both soultions and the centroid are compared.
-    The new position with the z-coordinate above the centroid is the logical solution.
-    """
-    # Used in case an error occurs during position update.
-    error_position_update1w2 = 0
-    # Coordinates of the first particle in steady state
-    z1 = float(M_particle_steady[overlaps[0], 1])
-    y1 = float(M_particle_steady[overlaps[0], 2])
-    x1 = float(M_particle_steady[overlaps[0], 3])
-    # Diameter of the first particle in steady state
-    d1 = float(M_particle_steady[overlaps[0], 5])
-    # Center coordinates of the first auxiliary sphere
-    c1 = np.array([z1, y1, x1])
-    # Radius of the first auxiliary sphere 
-    r1 = (d_i + d1) / 2
-    # Coordinates of the wall
-    z_w1 = z1
-    z_w2 = z1
-    if walls == 3:
-        x_w1 = A_0
-        y_w1 = y_i
-        x_w2 = x_i
-        y_w2 = A_0
-    elif walls == -3:
-        x_w1 = -A_0
-        y_w1 = y_i
-        x_w2 = x_i
-        y_w2 = A_0
-    elif walls == 4:
-        x_w1 = A_0
-        y_w1 = y_i
-        x_w2 = x_i
-        y_w2 = -A_0
-    elif walls == -4:
-        x_w1 = -A_0
-        y_w1 = y_i
-        x_w2 = x_i
-        y_w2 = -A_0
-    """ Calculation of the triangle's plane """
-    # Calculate vectors AB and AC
-    AB = np.array([z_w2 - z1, y_w2 - y1, x_w2 - x1])
-    AC = np.array([z_w1 - z1, y_w1 - y1, x_w1 - x1])
-    # Compute the normal vector to the plane (cross product of AB and AC)
-    V_triangle_norm = np.cross(AB, AC)
-    A, B, C = V_triangle_norm
-    # Calculate D using the plane equation Ax + By + Cz + D = 0
-    # Using point A (x1, y1, z1) to find D
-    D = -(A * z1 + B * y1 + C * x1)
-    """ 
-    Calculation of the reflected position of the considered particle.
-    First, the distance between the center of the considered particle and the triangle's plane is determined.
-    Then, the position of the considered particle is reflected about the triangle's plane
-    The coordiantes of the reflected position are used as second guess for the solver to determine the second solver solution. 
-    """
-    # Calculate the distance from the considered particle's center to the plane
-    distance_pi = (A * z_i + B * y_i + C * x_i + D) / np.linalg.norm(V_triangle_norm)
-    # Calculate the reflection point on the plane
-    P_reflected = np.array([z_i, y_i, x_i]) - 2 * distance_pi * V_triangle_norm / np.linalg.norm(V_triangle_norm)
-    """ Solver to find possible new position of the considered particle """
-    # Coordinates for the initial guess
-    z_01 = z_i
-    z_02 = P_reflected[0]
-    # Initial guess values
-    initial_guess1 = z_01
-    initial_guess2 = z_02
-    # X and y-coordinates of the new position
-    if walls == 3:
-        x = A_0 - d_i / 2
-        y = A_0 - d_i / 2
-    elif walls == -3:
-        x = -A_0 + d_i / 2 
-        y = A_0 - d_i / 2
-    elif walls == 4:
-        x = A_0 - d_i / 2
-        y = -A_0 + d_i / 2
-    elif walls == -4:
-        x = -A_0 + d_i / 2
-        y = -A_0 + d_i / 2
-    # Solver
-    z_s1 = fsolve(solver_position1w2, initial_guess1, args=(c1, r1, x, y))
-    z_s2 = fsolve(solver_position1w2, initial_guess2, args=(c1, r1, x, y))
-    z_s1 = z_s1[0]
-    z_s2 = z_s2[0]  
-    """ Check wheter the solver found two different solutions or the same twice """
-    # Error value is 1 if the solver found the same position twice
-    if z_s1 == z_s2:
-        error_position_update1w2 = 1
-    """ 
-    Determine the actual new position of the considered particle from the both solver solutions.
-    Must be the closest position to the original position of the considered particle
-    """
-    # Z-coordinate of polygon's centroid    
-    z_cen = z1
-    # If the center of the considered particle is located above the centroid, 
-    # the coordinates of the first solver solution are assigned to the center of the considered particle
-    if z_s1 > z_cen:
-        z = z_s1
-    # If the center of the considered particle is located above the centroid, 
-    # the coordinates of the second solver solution are assigned to the center of the considered particle
-    elif z_s2 > z_cen: 
-        z = z_s2
-    else: 
-        error_position_update1w2 = 2
-        return z_i, y_i, x_i, error_position_update1w2
-    """ 
-    Check wheter the distances between the centers of the three steady state particles 
-    and the new center position of the considered particle are in the samerange as the sums of their radii.
-    """
-    same_range = check_distances1w2(r1, c1, d_i, z, y, x)
-    # Error value is 2 if the distance between the steady state particles and the position of the solver solution 
-    # is not in the same range as the sums of their radii
-    if same_range == False:
-        error_position_update1w2 = 3
-        return z_i, y_i, x_i, error_position_update1w2
-
-    return z, y, x, error_position_update1w2
-
-def solver_position1w2(params, c1, r1, x_s, y_s):
-    """ Solver to calculate the new position of the considered particle if it overlaps one particle and two walls """
-    # Solver variables
-    z = params
-    # Equation of a circle. The circles coresponds here to the auxiliary spheres
-    eq1 = (x_s - c1[2])**2 + (y_s - c1[1])**2 + (z - c1[0])**2 - r1**2
-    return eq1
-
-def check_distances1w2(r1, c1, d_i, z_s, y_s, x_s): 
-    """ Function that checks wheter the distances between the center of one steady state particles, the walls, 
-    and the new center position of the considered particle are in the same range as the sums of their radii. """
-    # True if distances are in the same range as the sums of radii
-    same_range = True
-    # Vectors between the centers of the steady state particles and the new center positions of the considered particle    
-    V_centers_1i = np.array([z_s, y_s, x_s]) - c1
-    # Distance between the centers of the steady state particles and the new center position of the considered particle
-    dist_centers_1i = np.linalg.norm(V_centers_1i)
-    # Distances between the center of the considered particle and the walls
-    dist_w1i = A_0 - abs(x_s) 
-    dist_w2i = A_0 - abs(y_s)
-    # Check if the left-hand side is approximately zero (account for floating-point precision)
-    if abs(dist_centers_1i-r1) > 1e-9 or abs(dist_w2i-d_i/2) > 1e-9 or abs(dist_w1i-d_i/2) > 1e-9:
-        same_range = False
-
-    return same_range
-
-def check_steadystate1w2(z_i, y_i, x_i, M_particle_steady, overlaps, walls):
-    """ 
-    Function to check the steady state.
-    As soon as considered particle collides with one steady state particle and two walls, a steady state check is performed.
-    Considered particle is in steady state if its center lies within the y-x-plane of a triangle located between the 
-    steady state particle center and the walls. 
-    Barycentric coordinate method is used to determine if the center of the considered particle lies within the polygon.
-    Moreover, barycentric coordinates are used to determine the subdomain of the triangle where the center lies.
-    Based on the subdomain, the particle rolling direction is identified.
-    """
-    # Used in case an error occurs during steadystate check.
-    error_check_steadystate1w2 = 0
-    # Variable for steady state check condition
-    steady_check = 0
-    # Coordinates of the first particle in steady state
-    z1 = float(M_particle_steady[overlaps[0], 1])
-    y1 = float(M_particle_steady[overlaps[0], 2])
-    x1 = float(M_particle_steady[overlaps[0], 3])
-    # Coordinates of the wall
-    if walls == 3:
-        x_w1 = A_0
-        y_w1 = y_i
-        x_w2 = x_i
-        y_w2 = A_0
-    elif walls == -3:
-        x_w1 = -A_0
-        y_w1 = y_i
-        x_w2 = x_i
-        y_w2 = A_0
-    elif walls == 4:
-        x_w1 = A_0
-        y_w1 = y_i
-        x_w2 = x_i
-        y_w2 = -A_0
-    elif walls == -4:
-        x_w1 = -A_0
-        y_w1 = y_i
-        x_w2 = x_i
-        y_w2 = -A_0
-    # Z-coordinate of triangle's centroid
-    z_cen = z1
-    # Check whether the center of the considered particle is located above the centroid
-    # Double checking at this point, since already handled in the position_update3 function.
-    # Error value is 1 when the center is located below the centroid
-    if z_i < z_cen:
-        error_check_steadystate1w2 = 1
-        return steady_check, overlaps, error_check_steadystate1w2
-    """ Barycentric coordinate technique """
-    # Denominator
-    denominator = ((y_w2 - y_w1) * (x1 - x_w1) + (x_w1 - x_w2) * (y1 - y_w1))
-    # Check whether the triangle points are collinear
-    # Error value is 2 when the triangle points are collinear
-    if denominator == 0:
-        error_check_steadystate1w2 = 2  
-        return steady_check, overlaps, error_check_steadystate1w2
-    # Barycentric coordinates
-    a = ((y_w2 - y_w1) * (x_i - x_w1) + (x_w1 - x_w2) * (y_i - y_w1)) / denominator
-    b = ((y_w1 - y1) * (x_i - x_w1) + (x1 - x_w1) * (y_i - y_w1)) / denominator
-    c = 1 - a - b
-    # Determine the subdomain of the triangle and thus wheter it is in steady state or in which direction it is rolling
-    if (0 <= a <= 1) and (0 <= b <= 1) and (0 <= c <= 1):
-        steady_check = 3
-    elif b < 0 and a >= 0 and c >= 0:
-        # Rolling along steady state particle Nr.1 and wall Nr.2
-        if walls == 3 or walls == -3:
-            walls = 2
-        elif walls == 4 or walls == -4:
-            walls = -2
-        steady_check = 4
-    elif c < 0 and a >= 0 and b >= 0:
-        # Rolling along steady state particle Nr.1 and wall Nr.1
-        if walls == 3 or walls == 4:
-            walls = 1
-        elif walls == -3 or walls == -4:
-            walls = -1
-        steady_check = 4
-    elif b < 0 and c < 0 and a > 1:
-        # Rolling along steady state particle Nr.1 
-        steady_check = 1
-    else:
-        # Error value is 3 when the subdomain of the triangle could not be determined correctly
-        error_check_steadystate1w2 = 3
-
-    return steady_check, overlaps, walls, error_check_steadystate1w2
-
-def position_update2w2(z_i, y_i, x_i, d_i, M_particle_steady, overlaps, walls):
-    """
-    Function to update the position of the considered particle if it overlaps with two particles and two walls.
-    There are two mathematical solutions of the new position, but only one logical. 
-    A solver is used to determine these solutions.
-    First, a polygon plane is calculated which crosses the centers of the two steady state particles and the walls.
-    Moreover, the centroid of the polygon is determined.
-    The polygon plane is used to identify the guesses of the both solutions of the considered particle's new position.
-    It is tested whether the both solutions differ and if they are correct.
-    To identify the logical solution of the both solutions, the z-coordinates of the both soultions and the centroid are compared.
-    The new position with the z-coordinate above the centroid is the logical solution.
-    """
-    # Used in case an error occurs during position update.
-    error_position_update2w2 = 0
-    # Coordinates of the first particle in steady state
-    z1 = float(M_particle_steady[overlaps[0], 1])
-    y1 = float(M_particle_steady[overlaps[0], 2])
-    x1 = float(M_particle_steady[overlaps[0], 3])
-    # Diameter of the first particle in steady state
-    d1 = float(M_particle_steady[overlaps[0], 5])
-    # Radius of the first auxiliary sphere 
-    r1 = (d_i + d1) / 2
-    # Center coordinates of the first auxiliary sphere
-    c1 = np.array([z1, y1, x1])
-    # Coordinates of the second particle in steady state
-    z2 = float(M_particle_steady[overlaps[1], 1])
-    y2 = float(M_particle_steady[overlaps[1], 2])
-    x2 = float(M_particle_steady[overlaps[1], 3])
-    # Diameter of the second particle in steady state
-    d2 = float(M_particle_steady[overlaps[1], 5])
-    # Radius of the first auxiliary sphere 
-    r2 = (d_i + d2) / 2
-    # Center coordinates of the second auxiliary sphere
-    c2 = np.array([z2, y2, x2])
-    # Coordinates of the wall
-    z_w1 = (z1+z2)/2
-    z_w2 = (z1+z2)/2
-    """ Solver to find possible new position of the considered particle """    
-    # Array with the center coordinates and radii of the steady state particles
-    particles = [
-    {'center': c1, 'diameter': d1},
-    {'center': c2, 'diameter': d2}]
-    # Coordinates for the initial guess
-    x_01 = x_i
-    y_01 = y_i
-    z_01 = z_i
-    # Initial guess values
-    initial_guess = [x_01, y_01, z_01]
-    # Bounds
-    bnds = [(None, None), (-A_0, A_0), (-A_0, A_0)]
-    # Perform the optimization
-    result = minimize(solver_position2w2, initial_guess, args=(particles, d_i), method='L-BFGS-B', bounds=bnds, tol=1e-10)
-    # Extract the coordinates
-    z_s, y_s, x_s = result.x[:3]
-    """ 
-    Check wheter the distances between the centers of the four steady state particles 
-    and the new center position of the considered particle are in the samerange as the sums of their radii.
-    """
-    # Check if the left-hand side is approximately zero (account for floating-point precision)
-    same_range = check_distances2w2(r1, r2, c1, c2, d_i, z_s, y_s, x_s)
-    # Error value is 1 if the distance between the steay state particles and the position of the first solver solution 
-    # is not in the same range as the sums of their radii
-    if same_range == False:
-        error_position_update2w2 = 1
-        return z_i, y_i, x_i, error_position_update2w2
-    """ 
-    Check whether the new position of the considered particles is located above the centroid of the steady state particles
-    """
-    # Z-coordinate of polygon's centroid
-    z_cen = (z1 + z2 + z_w1 + z_w2) / 4
-    # If the center of the considered particle is located above the centroid, 
-    # the new coordinates are assigned to the center of the considered particle
-    if z_s > z_cen:
-        z = z_s
-        y = y_s
-        x = x_s   
-    # If the center of the considered particle is located below the centroid, 
-    # the new coordinates are not correct 
-    else:
-        error_position_update2w2 = 2
-        return z_i, y_i, x_i, error_position_update2w2
-
-    return z, y, x, error_position_update2w2
-
-def solver_position2w2(params, particles, d_i):
-    """ Solver to calculate the new position of the considered particle if it overlaps with two particles and two walls """
-    # Center coordinates of the considered particle are the optimizing parameters
-    c_i = np.array(params[:3])
-    # Mini
-    total_error = 0
-    # Calculate the total error between the actual and required distances of the particle centers
-    for particle in particles:
-        # Center of the steady state particle
-        c_j = particle['center']
-        # Diameter of the steady state particle
-        d_j = particle['diameter']
-        # Actual distance between the centers of the considered particle and the steady state particle
-        distance = np.linalg.norm(c_i - c_j)
-        # Required distance between the centers of the considered particle and the steady state particle
-        required_distance = (d_i + d_j)/2
-        # Minimization function
-        total_error += 100*(distance - required_distance)/required_distance
-    # Actual distance between the center of the considered particle and the wall
-    distance_w1 = A_0 - abs(c_i[2])
-    distance_w2 = A_0 - abs(c_i[1])
-    # Required distance between the center of the considered particle and the steady state particle
-    required_distance_w = d_i/2
-    # Update minimization function
-    total_error += 100*abs(distance_w1 - required_distance_w)/required_distance_w
-    total_error += 100*abs(distance_w2 - required_distance_w)/required_distance_w
-
-    return total_error
-
-def check_distances2w2(r1, r2, c1, c2, d_i, z_s, y_s, x_s): 
-    """ Function that checks wheter the distances between the centers of the two steady state particles, the walls, 
-    and the new center position of the considered particle are in the same range as the sums of their radii. """
-    # True if distances are in the same range as the sums of radii
-    same_range = True
-    # Vectors between the centers of the steady state particles and the new center positions of the considered particle    
-    V_centers_1i = np.array([z_s, y_s, x_s]) - c1
-    V_centers_2i = np.array([z_s, y_s, x_s]) - c2
-    # Distance between the centers of the steady state particles and the new center position of the considered particle
-    dist_centers_1i = np.linalg.norm(V_centers_1i)
-    dist_centers_2i = np.linalg.norm(V_centers_2i)
-    # Distance between the center of the considered particle and the wall 
-    dist_w1i = A_0 - abs(x_s)
-    dist_w2i = A_0 - abs(y_s)
-    # Check if the left-hand side is approximately zero (account for floating-point precision)
-    if abs(dist_centers_1i-r1) > 1e-9 or abs(dist_centers_2i-r2) > 1e-9 or abs(dist_w1i-d_i/2) > 1e-9 or abs(dist_w2i-d_i/2) > 1e-9:
-        same_range = False
-
-    return same_range
-
-def check_steadystate2w2(z_i, y_i, x_i, M_particle_steady, overlaps, walls):
-    """ 
-    Function to check the steady state.
-    As soon as considered particle collides with two steady state particles and two walls, a steady state check is performed.
-    Considered particle is in steady state if its center lies within the y-x-plane of a polygon located between the 
-    steady state particle centers and the walls. 
-    Barycentric coordinate method is used to determine if the center of the condsidered particle lies within the polygon.
-    For this purpose, the polygon is devided into two triangles.
-    Moreover, barycentric coordinates are used to determine the subdomain of the polygon where the point lies.
-    Based on the subdomain, the particle rolling direction is identified.
-    """
-    # Used in case an error occurs during steadystate check.
-    error_check_steadystate2w2 = 0
-    # Variable for steady state check condition
-    steady_check = 0
-    # Coordinates of the first particle in steady state
-    z1 = float(M_particle_steady[overlaps[0], 1])
-    y1 = float(M_particle_steady[overlaps[0], 2])
-    x1 = float(M_particle_steady[overlaps[0], 3])
-    # Coordinates of the second particle in steady state
-    z2 = float(M_particle_steady[overlaps[1], 1])
-    y2 = float(M_particle_steady[overlaps[1], 2])
-    x2 = float(M_particle_steady[overlaps[1], 3])
-    # Coordinates of the wall
-    z_w1 = (z1+z2)/2
-    z_w2 = (z1+z2)/2
-    if walls == 3:
-        x_w1 = A_0
-        y_w1 = y_i
-        x_w2 = x_i
-        y_w2 = A_0
-    elif walls == -3:
-        x_w1 = -A_0
-        y_w1 = y_i
-        x_w2 = x_i
-        y_w2 = A_0
-    elif walls == 4:
-        x_w1 = A_0
-        y_w1 = y_i
-        x_w2 = x_i
-        y_w2 = -A_0
-    elif walls == -4:
-        x_w1 = -A_0
-        y_w1 = y_i
-        x_w2 = x_i
-        y_w2 = -A_0
-    # Z-coordinate of polygon's centroid
-    z_cen = (z1 + z2 + z_w1 + z_w2) / 4
-    # Check whether the center of the considered particle is located above the centroid
-    # Double checking at this point, since already handled in the position_update4 function.
-    # Error value is 1 when the center is located below the centroid
-    if z_i < z_cen:
-        error_check_steadystate2w2 = 1
-        return steady_check, overlaps, error_check_steadystate2w2
-    """ 
-    The polygon is devided into two triangles. 
-    The first triangle is defined by the centers of the steady state particles 1, 2 and 4. 1=A, 2=B, 4=D
-    The second triangle is defined by the centers of the steady state particles 2, 3 and 4. 2=B, 3=C, 4=D
-    Barycentric coordinate technique is applied on each triangle
-    """
-    # Denominator of the first triangle
-    denominator1 = ((y2 - y_w1) * (x1 - x_w1) + (x_w1 - x2) * (y1 - y_w1))
-    # Denominator of the second triangle
-    denominator2 = ((y_w2 - y_w1) * (x2 - x_w1) + (x_w1 - x_w2) * (y2 - y_w1))
-    # Check whether the triangle points are collinear
-    # Error value is 2 when the triangle points are collinear
-    if denominator1 == 0 or denominator2 == 0:
-        error_check_steadystate2w2 = 2 
-        return steady_check, overlaps, error_check_steadystate2w2 
-    # Barycentric coordinates of the first triangle
-    a = ((y2 - y_w1) * (x_i - x_w1) + (x_w1 - x2) * (y_i - y_w1)) / denominator1
-    b1 = ((y_w1 - y1) * (x_i - x_w1) + (x1 - x_w1) * (y_i - y_w1)) / denominator1
-    d1 = 1 - a - b1
-    # Barycentric coordinates of the second triangle
-    b2 = ((y_w2 - y_w1) * (x_i - x_w1) + (x_w1 - x_w2) * (y_i - y_w1)) / denominator2
-    c = ((y_w1 - y2) * (x_i - x_w1) + (x2 - x_w1) * (y_i - y_w1)) / denominator2
-    d2 = 1 - b2 - c
-    # Determine the subdomain of the polygon and thus wheter it is in steady state or in which direction it is rolling
-    if (0 <= a <= 1) and (0 <= b1 <= 1) and (0 <= d1 <= 1):
-        steady_check = 3
-    elif (0 <= b2 <= 1) and (0 <= c <= 1) and (0 <= d2 <= 1):
-        steady_check = 3
-    elif b1 < 0 and d1 < 0:
-        # Rolling along steady state particle Nr.1
-        overlaps = np.delete(overlaps, 1)
-        steady_check = 1
-    elif d1 < 0 and d2 < 0:
-        # Rolling along steady state particle Nr.2
-        overlaps = np.delete(overlaps, 0)
-        steady_check = 1
-    elif d1 < 0 and b1 >= 0 and d2 >= 0:
-        # Rolling along steady state particle Nr.1 and Nr.2
-        steady_check = 2
-    elif d2 < 0 and b2 >= 0 and d1 >= 0:
-        # Rolling along steady state particle Nr.2 and wall Nr.1
-        if walls == 3 or walls == 4:
-            walls = 1
-        elif walls == -3 or walls == -4:
-            walls = -1
-        overlaps = np.delete(overlaps, 0)
-        steady_check = 4
-    elif b1 < 0 and d1 >= 0 and b2 >= 0:
-        # Rolling along steady state particle Nr.1 and wall Nr.2
-        if walls == 3 or walls == -3:
-            walls = 2
-        elif walls == 4 or walls == -4:
-            walls = -2
-        overlaps = np.delete(overlaps, 1)
-        steady_check = 4
-    else:
-        # Error value is 3 when the subdomain of the polygon could not be determined correctly
-        error_check_steadystate2w2 = 3
-
-    return steady_check, overlaps, walls, error_check_steadystate2w2
-
-def plot_particles(M_particle_steady):
-    """ Function to graphically display spheres in 3D """
-    fig = plt.figure()
-    ax = fig.add_subplot(111, projection='3d')
-    # Center coordinates of the steady state particles
-    centers = M_particle_steady[:,[1,2,3]]
-    # Radii
-    radii = M_particle_steady[:,5]/2
-    # Iterate through all spheres
-    for center, radius in zip(centers, radii):
-        # Create a grid of points
-        u = np.linspace(0, 2 * np.pi, 100)
-        v = np.linspace(0, np.pi, 100)
-        x = radius * np.outer(np.cos(u), np.sin(v)) + center[2]  # x-axis in the (z, y, x) order
-        y = radius * np.outer(np.sin(u), np.sin(v)) + center[1]  # y-axis in the (z, y, x) order
-        z = radius * np.outer(np.ones(np.size(u)), np.cos(v)) + center[0]  # z-axis in the (z, y, x) order
-        # Plot the surface
-        ax.plot_surface(x, y, z, color='b', alpha=0.3)
-    # Set the same limits for x, y, and z axes
-    ax.set_xlim(-1000, 1000)
-    ax.set_ylim(-1000, 1000)
-    ax.set_zlim(0, 500)
-    # Set the aspect ratio to be equal
-    ax.set_aspect('equal', adjustable='box')
-    # Set labels
-    ax.set_xlabel('X')
-    ax.set_ylabel('Y')
-    ax.set_zlabel('Z')
-    # Show the plot
-    plt.show()
-
-def voro_tess(M_particle_tess, tess_start, tess_end, diameters_0): 
-    """ Function to perform Tesselation """
-    cntr = Container(M_particle_tess, limits=(tess_start, tess_end), radii=(diameters_0/2), periodic=False)
-    # Cell volumes
-    volumes_cells = [round(v.volume(), 3) for v in cntr]
-    # Cell neighbors
-    cell_neighbors = [v.neighbors() for v in cntr]
-    # Nested tuple structure with x,y,z coordinates of the cell face normals in form:
-    # [[(x,y,z), (x,y,z), ...], [(x,y,z), (x,y,z), ...], ...]
-    cell_face_normals = [v.normals() for v in cntr]
-    # Nested tuple structure with x,y,z coordinates of the cell verticies in form:
-    # [[(x,y,z), (x,y,z), ...], [(x,y,z), (x,y,z), ...], ...]
-    cell_vertices = [v.vertices() for v in cntr]
-    # A list of the indices of the vertices of each face.
-    vertices_indices = [v.face_vertices() for v in cntr]
-
-    return volumes_cells, cell_neighbors, cell_face_normals, cell_vertices, vertices_indices
-
-def calc_grow(particle_i, volumes_particles, diameters, cell_face_fixed, distances_faces_max, cell_face_normals, vertices_indices, cell_vertices, display_particle_cell, particle_status, cell_status):
-    # Particle volume
-    volume_particle_i = volumes_particles[particle_i]
-    # Particle radius
-    radius_particle_i = diameters[particle_i] / 2
-    # List of fixed faces of the considered particle
-    cell_face_fixed_i = cell_face_fixed[particle_i]
-    # Verticies of the cell corresponding to the particle
-    cell_vertices_i = cell_vertices[particle_i]
-    # List of maximal distances between particle center and cell faces for the considered particle
-    distances_faces_max_i = distances_faces_max[particle_i]
-    # Normal vectors of cell faces corresponding to the particle
-    cell_face_normals_i = cell_face_normals[particle_i]
-    # Indices of the vertices of each cell face corresponding to the particle.
-    vertices_indices_i = vertices_indices[particle_i]
-    # Cell status
-    cell_status_i = cell_status[particle_i]
-    # Initial guess for solver
-    initial_guess = radius_particle_i 
-    # Bounds
-    bnds = [(0, np.max(distances_faces_max_i))]
-    # Solver method
-    solver_method = 'SLSQP'
-    if cell_status_i == False:
-        # Perform the optimization
-        result = minimize(solver_distances, initial_guess, args=(cell_face_fixed_i, distances_faces_max_i, volume_particle_i, cell_face_normals_i, vertices_indices_i, cell_vertices_i, display_particle_cell, radius_particle_i), method=solver_method, bounds=bnds, tol=1e-3)
-        # Obtain the value of the minimized function
-        minimized_value = result.fun
-        # If value to high, mark particle as invalid
-        if minimized_value > 1.0:
-            particle_status[particle_i] = False
-        # Extract the distance
-        distance_face_var = result.x[0]
-        # Iterate through each face of the cell to update the list of fixed cell faces
-        for face in range(len(cell_face_normals_i)):
-            if cell_face_fixed[particle_i][face] == True:
-                continue
-            else:
-                # If the distance is less than or equal to the radius of the sphere, the face intersects with the sphere. 
-                # Thus it is set to fixed
-                if (np.abs(distance_face_var) - distances_faces_max[particle_i][face]) > 1e-9:
-                    cell_face_fixed[particle_i][face] = True  
-    else:
-        # Iterate through each face of the cell to update the list of fixed cell faces
-        for face in range(len(cell_face_normals_i)):
-            if cell_face_fixed[particle_i][face] == True:
-                continue
-            else:
-                cell_face_fixed[particle_i][face] = True
-
-    return cell_face_fixed, particle_status
-
-def solver_distances(params, cell_face_fixed_i, distances_faces_max_i, volume_particle_i, cell_face_normals_i, vertices_indices_i, cell_vertices_i, display_particle_cell, radius_particle_i):
-    """ Solver to calculate the distance between the particle center and the variable cell faces """
-    # Variable distance from particle center to the cell face
-    distance_face_var = params
-    # Minimization value
-    total_error = 0
-    # Calculate new verticies
-    cell_vertices_i_new = calc_verticies(cell_face_normals_i, vertices_indices_i, distances_faces_max_i, distance_face_var, cell_face_fixed_i, cell_vertices_i, display_particle_cell, radius_particle_i)
-    # Check arrayfor NaN values
-    if np.isnan(cell_vertices_i_new).any():
-        total_error = 10000
-    else:
-        # Calculate polyhedron volume 
-        volume_polyhedron = calc_volume_polyhedron(cell_vertices_i_new)
-        # Minimization function
-        total_error = 100*abs(volume_particle_i - volume_polyhedron) / volume_particle_i
-
-    return total_error
-
-def calc_verticies(cell_face_normals_i, vertices_indices_i, distances_faces_max_i, distance_face_var, cell_face_fixed_i, cell_vertices_i, display_particle_cell, radius_particle_i):
-    """ Function to calculate new verticies coordinates """
-    # Predefine list with aim distances between particle center and cell faces
-    distances_faces = []
-    # Chech each face of polyhedron
-    for face_index in range(len(cell_face_normals_i)):
-        # If face is from beginning fixed set distance to particle radius
-        if cell_face_fixed_i[face_index] == True:
-            distance_face = distances_faces_max_i[face_index]
-        # If the distance of variable face is bigger than the distance between particle center and the cell face (max. distance)
-        # set the distance equal to the distance between particle center and the cell face
-        elif distance_face_var >= distances_faces_max_i[face_index]:
-            distance_face = distances_faces_max_i[face_index]
-        # Else set distance to variable distance value
-        else:
-            distance_face = distance_face_var
-        # Update list
-        distances_faces.append(distance_face)
-    # Calculate the difference between the aim distances and maximal distances
-    delta_distances = [abs(a - b) for a, b in zip(distances_faces_max_i, distances_faces)]
-    # Number of vertices
-    n_verticies = len(cell_vertices_i)
-    # Predefine list for new vertices
-    cell_vertices_i_new = [(None, None, None) for _ in range(n_verticies)]
-    # Consider each vertex of polyhedron
-    for vertex_index in range(len(cell_vertices_i)):
-        # Find every face that is connected with the considered vertex
-        face_index = [i for i, subarray in enumerate(vertices_indices_i) if vertex_index in subarray]
-        # Define move distances of vetex 
-        distances_move = [delta_distances[i] for i in face_index]
-        # Define move vectors of vetex
-        vectors_move = np.array([cell_face_normals_i[i] for i in face_index])
-        # Compute the displacement vector
-        displacement = sum(distance * normal for distance, normal in zip(distances_move, vectors_move))
-        # Calculate the new position coordinates of vertex
-        verticies_coordinates = cell_vertices_i[vertex_index] - displacement
-        # Update list
-        cell_vertices_i_new[vertex_index] = verticies_coordinates
-    # Convert the list of arrays to a 2D NumPy array
-    cell_vertices_i_new = np.vstack(cell_vertices_i_new)
-
-    """ Graphically display particles in 3D """
-    if display_particle_cell == True:
-        plot_particle_cell(cell_vertices_i_new, vertices_indices_i, cell_face_fixed_i, radius_particle_i)
-    
-    return cell_vertices_i_new
-
-def plot_particle_cell(cell_vertices_i_new, vertices_indices_i, cell_face_fixed_i, radius_particle_i):
-    """ Function to graphically display particle and cell """
-    # Boolean list indicating which faces to mark in red
-    mark_faces = cell_face_fixed_i
-    # Calculate the convex hull
-    #hull = ConvexHull(cell_vertices_i_new)
-    # Extract faces
-    #faces = hull.simplices
-    # Create a 3D plot
-    fig = plt.figure()
-    ax = fig.add_subplot(111, projection='3d')
-    # Function to get coordinates of vertices for each face
-    def get_face_vertices(face_indices):
-        return [cell_vertices_i_new[idx] for idx in face_indices]
-    # Create Poly3DCollection from faces
-    poly3d = Poly3DCollection([get_face_vertices(face) for face in vertices_indices_i], alpha=0.25, edgecolors='r')
-    # Set face colors based on mark_faces list
-    face_colors = ['red' if mark else 'blue' for mark in mark_faces]
-    poly3d.set_facecolor(face_colors)
-    # Add collection to plot
-    ax.add_collection3d(poly3d)
-    # Plot vertices
-    ax.scatter(cell_vertices_i_new[:, 0], cell_vertices_i_new[:, 1], cell_vertices_i_new[:, 2])
-    # Create a grid of points of the particle
-    u = np.linspace(0, 2 * np.pi, 100)
-    v = np.linspace(0, np.pi, 100)
-    x = radius_particle_i * np.outer(np.cos(u), np.sin(v)) + center_particle_i[0]  # x-axis in the (z, y, x) order
-    y = radius_particle_i * np.outer(np.sin(u), np.sin(v)) + center_particle_i[1]  # y-axis in the (z, y, x) order
-    z = radius_particle_i * np.outer(np.ones(np.size(u)), np.cos(v)) + center_particle_i[2]  # z-axis in the (z, y, x) order
-    # Plot the surface
-    ax.plot_surface(x, y, z, color='b', alpha=0.3)
-    # Label vertices
-    for i, v in enumerate(cell_vertices_i_new):
-        ax.text(v[0], v[1], v[2], str(i), color='black', fontsize=10)
-    # Set the aspect ratio to be equal
-    ax.set_aspect('equal', adjustable='box')
-    # Set labels
-    ax.set_xlabel('X')
-    ax.set_ylabel('Y')
-    ax.set_zlabel('Z')
-    # Show plot
-    plt.show()
-
-def calc_volume_polyhedron(cell_vertices_i_new):
-    """ Function to calculates the polyhedron volume based on the Tetrahedral Decomposition method"""
-    # Polyhedron volume
-    volume_polyhedron = 0
-    # Perform Delaunay triangulation to get tetrahedra
-    delaunay = Delaunay(cell_vertices_i_new)
-    # Consider each tetrahedron
-    for simplex in delaunay.simplices:
-        # Get tetrahedron verticies
-        verticies_tetrahedron = cell_vertices_i_new[simplex]
-        # Calculate the tetrahedron volumes
-        volume_tetrahedron = calc_tetrahedron_volume(verticies_tetrahedron)
-        # Calculate the volume of the polyhedron by summing tetrahedron volumes
-        volume_polyhedron += volume_tetrahedron
-
-    return volume_polyhedron
-
-def calc_tetrahedron_volume(tetra):
-    """ Function to calculates the volume of a tetrahedron based on its vertices. """
-    # Tetrahedron vertices
-    a, b, c, d = tetra
-    # Calculate tetrahedron volume
-    volume_tetrahedron = abs(np.dot(a-d, np.cross(b-d, c-d))) / 6
-
-    return volume_tetrahedron
-
-def solver_grow_factor(params, holdup_end, volume_cube, diameters_0, volumes_cells):
-    """ Solver to calculate the grow factor """
-    # Variable grow factor
-    grow_factor = params
-    # Minimization value
-    total_error = 0
-    # New diameters
-    diameters_new = diameters_0 * grow_factor
-    # New volumes
-    volumes_particles_new = (np.pi / 6) * diameters_new **(3)
-    # Correct volumes
-    for i in range(len(volumes_particles_new)):
-        if volumes_particles_new[i] - volumes_cells[i] > 1e-9:
-            volumes_particles_new[i] = volumes_cells[i]
-    # New holdup                
-    holdup_new = np.sum(volumes_particles_new) / volume_cube
-    # 
-    total_error = 100*abs(holdup_end - holdup_new) / holdup_end
-
-    return total_error
-
-def calc_contact_probabilities(average_contacts, contact_probabilities, cell_face_fixed, diameters_neighbors, diameters_0, grow_step, particle_status):
-    """ 
-    This function calculates the average contact number of each class and the contact probability between classes.
-    Particles which are in contact with walls at their initial state are excluded.
-    If solver issues occur at growing simulation of particles those particles are excluded.
-    Wall contacts are excluded.
-    Only contacts between fixed faces are included in the calculation.    
-    """
-    """ Count contacts """
-    # Initialize a dictionary to hold the contact counts
-    contact_counts = defaultdict(lambda: defaultdict(int))
-    # Iterate over each particle and its neighbors to count the contacts
-    for particle_i in range(len(diameters_0)):
-        particle = diameters_0[particle_i]
-        for neighbor_j in range(len(diameters_neighbors[particle_i])):
-            neighbor = diameters_neighbors[particle_i][neighbor_j]
-            # Exclude particle with status "False", wall contacts (None values) and faces which are not fixed
-            if particle_status[particle_i] != False and neighbor != None and cell_face_fixed[particle_i][neighbor_j] == True:  
-                # Increase number of contacts
-                contact_counts[particle][neighbor] += 1
-    """ Calculate total contacts """
-    # Initialize a dictionary to hold the total contacts for each particle class
-    total_contacts = defaultdict(int)
-    # Sum the total contacts for each particle class
-    for class_particle in contact_counts:
-        total_contacts[class_particle] += sum(contact_counts[class_particle].values())
-    # Sort value_counts by keys (values) in ascending order and convert to a list of tuples
-    total_contacts_sorted = sorted(total_contacts.items())
-    # Extract only the counts (second column)
-    total_contacts_values = [count for value, count in total_contacts_sorted]
-    """ Calculate class sizes """
-    # Count occurrences of each diameter 
-    class_size = Counter()
-    # Iterate through data_list and include values based on include_list
-    for class_diameter, status in zip(diameters_0, particle_status):
-        if status == True:
-            class_size[class_diameter] += 1
-    # Sort value_counts by keys (values) in ascending order and extract only the counts
-    class_size_values = [class_size[value] for value in sorted(class_size)]
-    """ Calculate average contact nubmers based on class sizes and total contacts """
-    # Perform element-wise division
-    for i in range(len(class_size_values)):
-        average_contacts[grow_step][i] = total_contacts_values[i] / class_size_values[i]
-    """ Calculate contact probability based on the contact counts and total contacts """
-    # Initialize a dictionary to hold the probabilities
-    contact_probabilities_dict = defaultdict(lambda: defaultdict(float))
-    # Iterate over each particle and its neighbors to calculate the probabilities
-    for class_particle, classes_neighbors in contact_counts.items():
-        for class_neighbor, count_contacts in classes_neighbors.items():
-            # Avoid division by zero
-            if total_contacts[class_particle] > 0:  
-                # Calculate the contact probabilities
-                contact_probabilities_dict[class_particle][class_neighbor] = 100 * count_contacts / total_contacts[class_particle]
-    # Convert the contact_probabilities dictionary into a sorted list (sorted in ascending order)
-    contact_counts_sorted = sorted(set(contact_counts))
-    # Iterate over each particle and its neighbors to update the list in ascending order
-    for class_particle_i in range(len(contact_counts_sorted)):
-        class_particle = contact_counts_sorted[class_particle_i]
-        row = []
-        for class_neighbor in contact_counts_sorted:
-            row.append(contact_probabilities_dict[class_particle].get(class_neighbor, 0))
-        contact_probabilities[grow_step][class_particle_i] = row
-
-    return average_contacts, contact_probabilities
-
-def calc_q(diameters, n_particle, d_classes):
-    ' Function to calculate the number and volume distributions'
-    # Predefine array for class sizes after random loose packing simulation
-    class_sizes = np.zeros(len(d_classes), dtype=int)
-    # Categorize cell diameters into classes
-    for particle in diameters:
-        for i in range(len(d_classes)):
-            lb_class = d_classes[i] - delta_class/2
-            ub_class = d_classes[i] + delta_class/2
-            if lb_class <= particle < ub_class:
-                class_sizes[i] += 1
-                break
-    # Final number distribution after growing simulation
-    q0 = class_sizes / float(n_particle)
-    # Final volume distribution after growing simulation
-    q3 = 0.9*(q0 * (np.pi/6) * d_classes**3) / np.sum(q0 * (np.pi/6) * d_classes**3)
-
-    return q0, q3
-
-""" MAIN """
-if __name__ == "__main__":
-    # Time stamp
-    start_time_rlp = time.time()
-    # Set certain variables to integer
-    SEED = int(SEED)
-    N_PARTICLES_SIM = int(N_PARTICLES_SIM)
-    print('Starting simulation with seed', SEED)
-    # Set random seed
-    random.seed(SEED)
-    np.random.seed(SEED)
-    sys.stdout.write("Generating particle size distribution...")
-    # Number distribution of particles
-    q0, n_particles_init, q0_initial, q3_initial, d_classes, d_min, d_max, volume_total, d_05, d_95, delta_class, D_32 = calc_distribution(N_PARTICLES_SIM, N_CLASSES)
-    # Volume of the simulation environment
-    volume_sim = volume_total / HOLDUP_EST
-    # Cuboid's edge lenght (µm)
-    L_0 = volume_sim ** (1/3)
-    # Half of cuboid's edge lenght (µm)
-    A_0 = L_0 / 2
-    # Mix particles in array and determine their coordinates randomly --> initial array for particles
-    M_particles_initial = calc_particles(q0, n_particles_init)
-    # Array for particles in steady state. 
-    # Each row corresponds to one particle.
-    # Columns: 0: index of this array, 1: z-coordinate, 2: y-coordinate, 3: x-coordinate, 4: steady state status,
-    # 5: particle diameter, 6: index of initial array, 7: particle class 
-    # further columns (len(q0)) corresponds to number of classes of the distribution.
-    # Thus, contact particle classes can be assigned to each particle in array.
-    M_particle_steady = np.zeros((1, (8 + len(q0))))
-    sys.stdout.write("completed\n")
-    # Use tqdm to track progress
-    with tqdm(total=n_particles_init, desc='Random loose packing simulation progress', unit='particle') as pbar:
-        # Drop and roll loop:
-        for particle_i in range(len(M_particles_initial)):
-            if display_detailed_progress == True:
-                print('Particle Nr.:', particle_i)
-            """ Take information of considered particles from initial particle array to define its initial state """
-            x, y, z, index_i, d, class_i = particle_info(M_particles_initial, particle_i)
-            """ Define test range: the distance between the centers of the considered particle and the bigest particlein steady state"""
-            # List of all particles in steady state
-            d_steady = M_particle_steady[:, 5] 
-            # Diameter of biggest particle in steady state
-            d_max_steady = np.max(d_steady) 
-            # Test range
-            test_range = 1.2*((d + d_max_steady) / 2)
-            # The first particle is simply set because there is nothing there before.
-            if particle_i == 0:
-                # Calculate new z-coordinate
-                z = 0 + d / 2
-                """Fill steady state array"""
-                # Particle index
-                M_particle_steady[0, 0] = particle_i
-                # z-coordinate
-                M_particle_steady[0, 1] = z
-                # y-coordinate
-                M_particle_steady[0, 2] = y
-                # x-coordinate
-                M_particle_steady[0, 3] = x
-                # Contact with ground, 1=yes
-                M_particle_steady[0, 4] = 1
-                # Particle diameter
-                M_particle_steady[0, 5] = d
-                # Initial index
-                M_particle_steady[0, 6] = index_i
-                # Particle class
-                M_particle_steady[0, 7] = class_i
-                continue
-            # Calculate dropping height based on the location of the heighest particle
-            z = find_z_drop(y, x, M_particle_steady, test_range, d_max_steady)
-            # Initial dropping position
-            z_drop = z
-            y_drop = y
-            x_drop = x
-            """ 
-            While loop for particle sedimentation.
-            In this loop it is checked whether a particle sediments or collides with other particles.
-            If the considered particle does not overlap with other particles, it is moved downwards.
-            If the considered particle overlaps with other particles, 
-            depending on the number of overlappings, its position is updated by the corresponding function.
-            The position is updated so that the particles touch each other but do not overlap.
-            After the position update the considered particleis tested for overlapping again because it could 
-            overlap with other particles due to the update. 
-            Depending on the number of overlaps, the position is updated over and over until no overlapping after 
-            the position update occurs. Only when no overlappings are present it is moved downwards.
-            When three or four overlappings are present, the particle is checked for steady state.
-            If it is not in steady state, it is calculated at which particle it would roll along in the next loop step 
-            and the corresponding position update function is applied. This is neccessary to prevent the particle to get stucked.
-            This loop is repeated agian and again until the considered particle reches its steady state position at the ground, 
-            at three or at four particles.
-            """
-            # Counter variables and lists
-            # Number of overplaps
-            n_overlaps = 0
-            # List of overlaps
-            overlaps = []
-            # Variable for overlapping walls
-            walls = 0
-            # Error Variables
-            error_position_update2 = 0
-            error_position_update3 = 0
-            error_position_update4 = 0
-            error_position_update1w = 0
-            error_position_update2w = 0
-            error_position_update3w = 0
-            error_position_update1w2 = 0
-            error_position_update2w2 = 0
-            error_check_steadystate3 = 0
-            error_check_steadystate4 = 0
-            error_check_steadystate2w = 0
-            error_check_steadystate3w = 0
-            error_check_steadystate1w2 = 0
-            error_check_steadystate2w2 = 0
-            # Argument for the while loop
-            steady = 0
-            # Aborting variable for the while loop 
-            counter_sedi = 0
-            # Borders of the aborting variable for the while loop
-            counter_sedi_max1 = 10000
-            counter_sedi_max2 = 2 * counter_sedi_max1
-            counter_sedi_max3 = 3 * counter_sedi_max1
-            # Error counter
-            n_error = 0
-            # If fatal error is True, the while loop is leaved
-            fatal_error = False
-            # While loop fpr particle sedimentation
-            while steady == 0:
-                # Increase aborting variable for the while loop
-                counter_sedi = counter_sedi + 1
-                # Try again if it takes too long or an error occurs
-                if counter_sedi == counter_sedi_max1 or n_error == 1:
-                    z = z_drop
-                    y = y_drop
-                    x = y_drop
-                    if n_error == 1:
-                        n_error = n_error + 1
-                    continue
-                # Assign new coordinates if it takes again too long or an error occurs again
-                if counter_sedi == counter_sedi_max2 or n_error == 3:
-                    x = random.uniform(-A_0 + d / 2, A_0 - d / 2)
-                    y = random.uniform(-A_0 + d / 2, A_0 - d / 2)
-                    z = find_z_drop(y, x, M_particle_steady, test_range, d_max_steady)
-                    if n_error == 3:
-                        n_error = n_error + 1
-                    continue
-                # Abort while loop by abort variable if trying again and assigning new coordinates does not work 
-                # or an error occurs three times
-                if counter_sedi > counter_sedi_max3 or n_error == 5:
-                    n_error = 0
-                    if display_detailed_progress == True:
-                        print('Timeout in the sedimentation loop or too many errors. Check sedimentation loop.')
-                    fatal_error = True
-                    break
-                # Calculate overlapping/collision
-                n_overlaps, overlaps, walls = calc_overlap(d, z, y, x, M_particle_steady, test_range)
-                # If the considered particle does not overlap with other particles, it is moved downwards.
-                if n_overlaps == 0:
-                    # If z == d/2, the considered particle reached the ground
-                    if z == d/2:
-                        # If steady = 1, steady state position is reched on the ground
-                        steady = 1
-                    else:
-                        z = z - STEPSIZE   
-                # Overlap with one steady state particle in total
-                elif n_overlaps == 1:
-                    # index of the first steady state particle, the considered particle collides with
-                    index1 = overlaps[0]
-                    # No overlappings with walls
-                    if walls == 0:
-                        if display_detailed_progress == True:
-                            print('Particle Nr.', particle_i ,'collides with one steady state particle.')
-                        # If the considered particle overlaps with one steady state particle, 
-                        # its position is updated by position_update1 function. 
-                        y, x = position_update1(y, x, d, M_particle_steady, overlaps)
-                    # Overlap with one wall   
-                    elif walls == 1 or walls == -1 or walls == 2 or walls == -2:
-                        if display_detailed_progress == True:
-                            print('Particle Nr.', particle_i ,'collides with one steady state particle and wall.')
-                        # If the considered particle overlaps with one steady state particle and one wall, 
-                        # its position is updated by position_update1w function.
-                        y_s, x_s, error_position_update1w = position_update1w(z, y, x, d, M_particle_steady, overlaps, walls)
-                        # If no range error occurs, the new coordinates are assignet
-                        if error_position_update1w == 0:
-                            y = y_s
-                            x = x_s
-                        # Error value is 1 if the solver found the same new position of the considered particle twice.
-                        # The error is only displayed but the calculation is continued, as the position could be correct
-                        elif error_position_update1w == 1:
-                            if display_detailed_progress == True:
-                                print('Error in the error_position_update1w function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                            y = y_s
-                            x = x_s                    
-                        # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                        # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                        elif error_position_update1w == 2:
-                            if display_detailed_progress == True:
-                                print('Error in the error_position_update1w function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                            z = z + STEPSIZE / 2
-                            continue
-                        # Error value is 3 if the both solver solutions lies behind the simulation borders
-                        elif error_position_update1w == 3:
-                            if display_detailed_progress == True:
-                                print('Error in the error_position_update1w function. The both solver solutions lies behind the simulation borders. ')
-                            n_error = n_error + 1
-                            continue
-                    # Overlap with two walls
-                    elif walls == 3 or walls == -3 or walls == 4 or walls == -4:
-                        if display_detailed_progress == True:
-                            print('Particle Nr.', particle_i ,'collides with one steady state particle and two walls.')
-                        # If the considered particle overlaps with one steady state particle and two walls, 
-                        # its position is updated by position_update1w function.
-                        z_s, y_s, x_s, error_position_update1w2 = position_update1w2(z, y, x, d, M_particle_steady, overlaps, walls)
-                        # Error value is 1 if the solver found the same new position of the considered particle twice.
-                        # The error is only displayed but the calculation is continued, as the position could be correct
-                        if error_position_update1w2 == 1:
-                            if display_detailed_progress == True:
-                                print('Error in the error_position_update1w2 function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                        # Error value is 2 if the position of the considered particle is located below the centroid of the steady state particles
-                        elif error_position_update1w2 == 2:
-                            if display_detailed_progress == True:
-                                print('Error in the error_position_update1w2 function. Position of the considered particle is located below the centroid of the steady state particles.')
-                            z = z + STEPSIZE / 2
-                            continue
-                        # Error value is 3 if the position of the first solver solution (the logical one) is not located on the intersection plane
-                        elif error_position_update1w2 == 3:
-                            if display_detailed_progress == True:
-                                print('Error in the error_position_update1w2 function. The logical new position determnined by the solver is not located on the intersection plane. ')
-                            z = z + STEPSIZE / 2
-                            continue
-                        # If particleis in contact with one steady state particle and two walls, it could be in steady state
-                        # Checking for overlaps after position update
-                        n_overlaps, overlaps1w2, walls = calc_overlap(d, z_s, y_s, x_s, M_particle_steady, test_range)
-                        # If no overlaps occure, the particle is tested for steady state
-                        if n_overlaps == 0:
-                            # With three contact partners, the considered particle can be in a steady state, 
-                            # which is tested by the check_steadystate1w2 function
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i ,'is tested for steady state at one particle and two walls.')
-                            steady_check, overlaps, walls, error_check_steadystate1w2 = check_steadystate1w2(z_s, y_s, x_s, M_particle_steady, overlaps, walls)
-                            # Error value is 1 if particles center is located below the triangle's centroid of the steady state particles
-                            if error_check_steadystate1w2 == 1:
-                                if display_detailed_progress == True:
-                                    print('Error in the check_steadystate3 function. The center of the considered particle is located below the triangles centroid of the steady state particles.')
-                                n_error = n_error + 1
-                                continue
-                            # Error value is 2 if the triangle points of the steady state particles are collinear
-                            elif error_check_steadystate1w2 == 2:
-                                if display_detailed_progress == True:
-                                    print('Error in the check_steadystate3 function. The triangle points of the steady state particles are collinear.')
-                                n_error = n_error + 1
-                                continue
-                            # Error value is 3 if the subdomain of the triangle could not be determined correctly
-                            elif error_check_steadystate1w2 == 3:
-                                if display_detailed_progress == True:
-                                    print('Error in the check_steadystate3 function. The subdomain of the triangle could not be determined correctly.')
-                                n_error = n_error + 1
-                                continue
-                            # If steady_check == 0, the particle is in steady state
-                            if steady_check == 3:
-                                # Assigning the coordinates of the considered partcile to its steady state coordinates at three particles
-                                z = z_s
-                                y = y_s
-                                x = x_s
-                                # If steady = 2, steady state position is reched on one particle and two walls
-                                steady = 6
-                            # If steady_check == 1, the particle is not in steady state and would roll along one steady state particle
-                            # Thus its position is updated by position_update1 function
-                            elif steady_check == 1:
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along one particle')
-                                # Function for position update at one contact.
-                                y, x = position_update1(y, x, d, M_particle_steady, overlaps)
-                            # If steady_check == 2, the particle is not in steady state and would roll along one steady state particle and wall
-                            # Thus its position is updated by position_update2 function    
-                            elif steady_check == 4:
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along one particle and wall')
-                                # Function for position update at two contacts.
-                                y_s, x_s, error_position_update1w = position_update1w(z, y, x, d, M_particle_steady, overlaps, walls)
-                                # If no range error occurs, the new coordinates are assignet
-                                if error_position_update1w == 0:
-                                    y = y_s
-                                    x = x_s
-                                # Error value is 1 if the solver found the same new position of the considered particle twice.
-                                # The error is only displayed but the calculation is continued, as the position could be correct
-                                elif error_position_update1w == 1:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update1w function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                                    y = y_s
-                                    x = x_s                    
-                                # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                                # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                                elif error_position_update1w == 2:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update1w function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                                    z = z + STEPSIZE / 2
-                                    continue
-                                # Error value is 3 if the both solver solutions lies behind the simulation borders
-                                elif error_position_update1w == 3:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update1w function. The both solver solutions lies behind the simulation borders. ')
-                                    n_error = n_error + 1
-                                    continue
-                            continue
-                        # If overlaps occur, the considered particle is moved 1/10 of the step upwards an handled again in the next loop.
-                        else:        
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i ,'collides with three steady state particles and two walls.')
-                            z = z + STEPSIZE / 2
-                            continue
-                    # Checking for overlaps after position update
-                    n_overlaps, overlaps, walls = calc_overlap(d, z, y, x, M_particle_steady, test_range)
-                    # Overlap with two steady state particles in total
-                    if n_overlaps == 1:
-                        # index of the second steady state particle, the considered particle collides with
-                        index2 = overlaps[0]
-                        # Update overlaps array
-                        overlaps = np.array([index1, index2])
-                        # No overlappings with walls
-                        if walls == 0:
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i ,'collides with two steady state particles.')
-                            # If the considered particle overlaps with one steady state particle after position update at one particle,
-                            # it is in contact with two particles. Thus, its position is updated by position_update2 function.
-                            y_s, x_s, error_position_update2 = position_update2(z, y, x, d, M_particle_steady, overlaps)
-                            # If no range error occurs, the new coordinates are assignet
-                            if error_position_update2 == 0:
-                                y = y_s
-                                x = x_s
-                            # Error value is 1 if the solver found the same new position of the considered particle twice.
-                            # The error is only displayed but the calculation is continued, as the position could be correct
-                            elif error_position_update2 == 1:
-                                if display_detailed_progress == True:
-                                    print('Error in the error_position_update2 function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                                y = y_s
-                                x = x_s                    
-                            # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                            # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                            elif error_position_update2 == 2:
-                                if display_detailed_progress == True:
-                                    print('Error in the error_position_update2 function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                                z = z + STEPSIZE / 2
-                                continue
-                                # Error value is 3 if the both solver solutions lies behind the simulation borders
-                            elif error_position_update2 == 3:
-                                if display_detailed_progress == True:
-                                    print('Error in the error_position_update1w function. The both solver solutions lies behind the simulation borders. ')
-                                n_error = n_error + 1
-                                continue
-                        # Overlap with one wall
-                        elif walls == 1 or walls == -1 or walls == 2 or walls == -2:
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i ,'collides with two steady state particles and one wall.')
-                            # If the considered particle overlaps with one steady state particle and one wall after position update at one particle,
-                            # it is in contact with two particles and one wall. Thus, its position is updated by position_update2w function.
-                            z_s, y_s, x_s, error_position_update2w = position_update2w(z, y, x, d, M_particle_steady, overlaps, walls)
-                            # Error value is 1 if the solver found the same new position of the considered particle twice.
-                            # The error is only displayed but the calculation is continued, as the position could be correct
-                            if error_position_update2w == 1:
-                                if display_detailed_progress == True:
-                                    print('Error in the error_position_update2w function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                            # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                            # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                            elif error_position_update2w == 2:
-                                if display_detailed_progress == True:
-                                    print('Error in the error_position_update2w function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                                z = z + STEPSIZE / 2
-                                continue
-                            # If particle is in contact with two steady state particle and one wall, it could be in steady state
-                            # Checking for overlaps after position update
-                            n_overlaps, overlaps2w, walls = calc_overlap(d, z_s, y_s, x_s, M_particle_steady, test_range)
-                            # If the particle overlaps with a second wall, its position is updated in the next loop
-                            if walls == 3 or walls == -3 or walls == 4 or walls == -4 or walls == 0:
-                                continue
-                            # If no overlaps occure, the particle is tested for steady state
-                            if n_overlaps == 0:
-                                # With three contact partners, the considered particle can be in a steady state, 
-                                # which is tested by the check_steadystate1w2 function
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i ,'is tested for steady state at two particles and one wall.')
-                                steady_check, overlaps, error_check_steadystate2w = check_steadystate2w(z_s, y_s, x_s, particle_i, M_particle_steady, overlaps, walls)
-                                # Error value is 1 if particles center is located below the triangle's centroid of the steady state particles
-                                if error_check_steadystate2w == 1:
-                                    if display_detailed_progress == True:
-                                        print('Error in the check_steadystate3 function. The center of the considered particle is located below the triangles centroid of the steady state particles.')
-                                    n_error = n_error + 1
-                                    continue
-                                # Error value is 2 if the triangle points of the steady state particles are collinear
-                                elif error_check_steadystate2w == 2:
-                                    if display_detailed_progress == True:
-                                        print('Error in the check_steadystate3 function. The triangle points of the steady state particles are collinear.')
-                                    n_error = n_error + 1
-                                    continue
-                                # Error value is 3 if the subdomain of the triangle could not be determined correctly
-                                elif error_check_steadystate2w == 3:
-                                    if display_detailed_progress == True:
-                                        print('Error in the check_steadystate3 function. The subdomain of the triangle could not be determined correctly.')
-                                    n_error = n_error + 1
-                                    continue
-                                # If steady_check == 0, the particle is in steady state
-                                if steady_check == 3:
-                                    # Assigning the coordinates of the considered partcile to its steady state coordinates at three particles
-                                    z = z_s
-                                    y = y_s
-                                    x = x_s
-                                    # If steady = 2, steady state position is reched at two particles and one wall
-                                    steady = 4
-                                # If steady_check == 1, the particle is not in steady state and would roll along one steady state particle
-                                # Thus its position is updated by position_update1 function
-                                elif steady_check == 1:
-                                    if display_detailed_progress == True:
-                                        print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along one particle')
-                                    # Function for position update at one contact.
-                                    y, x = position_update1(y, x, d, M_particle_steady, overlaps)
-                                # If steady_check == 2, the particle is not in steady state and would roll along two steady state particles
-                                # Thus its position is updated by position_update2 function                          
-                                elif steady_check == 2:
-                                    if display_detailed_progress == True:
-                                        print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along two particles')
-                                    # Function for position update at two contacts.
-                                    y_s, x_s, error_position_update2 = position_update2(z, y, x, d, M_particle_steady, overlaps)
-                                    # If no range error occurs, the new coordinates are assignet
-                                    if error_position_update2 == 0:
-                                        y = y_s
-                                        x = x_s
-                                    # Error value is 1 if the solver found the same new position of the considered particle twice.
-                                    # The error is only displayed but the calculation is continued, as the position could be correct
-                                    elif error_position_update2 == 1:
-                                        if display_detailed_progress == True:
-                                            print('Error in the error_position_update2 function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                                        y = y_s
-                                        x = x_s                    
-                                    # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                                    # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                                    elif error_position_update2 == 2:
-                                        if display_detailed_progress == True:
-                                            print('Error in the error_position_update2 function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                                        z = z + STEPSIZE / 2
-                                        continue
-                                    # Error value is 3 if the both solver solutions lies behind the simulation borders
-                                    elif error_position_update2 == 3:
-                                        if display_detailed_progress == True:
-                                            print('Error in the error_position_update1w function. The both solver solutions lies behind the simulation borders. ')
-                                        n_error = n_error + 1
-                                        continue
-                                # If steady_check == 4, the particle is not in steady state and would roll along one steady state particle and wall
-                                # Thus its position is updated by position_update1w function    
-                                elif steady_check == 4:
-                                    if display_detailed_progress == True:
-                                        print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along one particle and wall')
-                                    # Function for position update at two contacts.
-                                    y_s, x_s, error_position_update1w = position_update1w(z, y, x, d, M_particle_steady, overlaps, walls)
-                                    # If no range error occurs, the new coordinates are assignet
-                                    if error_position_update1w == 0:
-                                        y = y_s
-                                        x = x_s
-                                    # Error value is 1 if the solver found the same new position of the considered particle twice.
-                                    # The error is only displayed but the calculation is continued, as the position could be correct
-                                    elif error_position_update1w == 1:
-                                        if display_detailed_progress == True:
-                                            print('Error in the error_position_update1w function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                                        y = y_s
-                                        x = x_s                    
-                                    # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                                    # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                                    elif error_position_update1w == 2:
-                                        if display_detailed_progress == True:
-                                            print('Error in the error_position_update1w function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                                        z = z + STEPSIZE / 2
-                                        continue
-                                    # Error value is 3 if the both solver solutions lies behind the simulation borders
-                                    elif error_position_update1w == 3:
-                                        if display_detailed_progress == True:
-                                            print('Error in the error_position_update1w function. The both solver solutions lies behind the simulation borders. ')
-                                        n_error = n_error + 1
-                                        continue
-                                continue
-                            # If overlaps occur, the considered particle is moved 1/10 of the step upwards an handled again in the next loop.
-                            else:        
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i ,'collides with three steady state particles and two walls.')
-                                z = z + STEPSIZE / 2
-                                continue
-                        # Overlap with two walls
-                        elif walls == 3 or walls == -3 or walls == 4 or walls == -4:
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i ,'collides with one steady state particle and two walls.')
-                            # If the considered particle overlaps with one steady state particle and two walls after position update at one particle,
-                            # it is in contact with two particles and two walls. Thus, its position is updated by position_update2w function.
-                            z_s, y_s, x_s, error_position_update2w2 = position_update2w2(z, y, x, d, M_particle_steady, overlaps, walls)
-                            # Error value is 1 if the solver found the same new position of the considered particle twice.
-                            # The error is only displayed but the calculation is continued, as the position could be correct
-                            if error_position_update2w2 == 1:
-                                if display_detailed_progress == True:
-                                    print('Error in the error_position_update2w2 function. Solver found the same new position twice. Calculation proceeds with the found value.')                  
-                            # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                            # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                            elif error_position_update2w2 == 2:
-                                if display_detailed_progress == True:
-                                    print('Error in the error_position_update2w2 function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                                z = z + STEPSIZE / 2
-                                continue
-                            # If particle is in contact with two steady state particle and two walls, it could be in steady state
-                            # Checking for overlaps after position update
-                            n_overlaps, overlaps2w2, walls = calc_overlap(d, z_s, y_s, x_s, M_particle_steady, test_range)
-                            # If the particle overlaps with onlyone wall, its position is updated in the next loop
-                            if walls == 1 or walls == -1 or walls == 2 or walls == -2 or walls == 0:
-                                continue
-                            # If no overlaps occure, the particle is tested for steady state
-                            if n_overlaps == 0:
-                                # With three contact partners, the considered particle can be in a steady state, 
-                                # which is tested by the check_steadystate1w2 function
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i ,'is tested for steady state at two particles and two walls.')
-                                steady_check, overlaps, walls, error_check_steadystate2w2 = check_steadystate2w2(z_s, y_s, x_s, M_particle_steady, overlaps, walls)
-                                # Error value is 1 if particles center is located below the triangle's centroid of the steady state particles
-                                if error_check_steadystate2w2 == 1:
-                                    if display_detailed_progress == True:
-                                        print('Error in the check_steadystate3 function. The center of the considered particle is located below the triangles centroid of the steady state particles.')
-                                    n_error = n_error + 1
-                                    continue
-                                # Error value is 2 if the triangle points of the steady state particles are collinear
-                                elif error_check_steadystate2w2 == 2:
-                                    if display_detailed_progress == True:
-                                        print('Error in the check_steadystate3 function. The triangle points of the steady state particles are collinear.')
-                                    n_error = n_error + 1
-                                    continue
-                                # Error value is 3 if the subdomain of the triangle could not be determined correctly
-                                elif error_check_steadystate2w2 == 3:
-                                    if display_detailed_progress == True:
-                                        print('Error in the check_steadystate3 function. The subdomain of the triangle could not be determined correctly.')
-                                    n_error = n_error + 1
-                                    continue
-                                # If steady_check == 0, the particle is in steady state
-                                if steady_check == 3:
-                                    # Assigning the coordinates of the considered partcile to its steady state coordinates at three particles
-                                    z = z_s
-                                    y = y_s
-                                    x = x_s
-                                    # If steady = 2, steady state position is reched at two particles and two walls
-                                    steady = 7
-                                # If steady_check == 1, the particle is not in steady state and would roll along one steady state particle
-                                # Thus its position is updated by position_update1 function
-                                elif steady_check == 1:
-                                    if display_detailed_progress == True:
-                                        print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along one particle')
-                                    # Function for position update at one contact.
-                                    y, x = position_update1(y, x, d, M_particle_steady, overlaps)
-                                # If steady_check == 2, the particle is not in steady state and would roll along two steady state particles
-                                # Thus its position is updated by position_update2 function                          
-                                elif steady_check == 2:
-                                    if display_detailed_progress == True:
-                                        print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along two particles')
-                                    # Function for position update at two contacts.
-                                    y_s, x_s, error_position_update2 = position_update2(z, y, x, d, M_particle_steady, overlaps)
-                                    # If no range error occurs, the new coordinates are assignet
-                                    if error_position_update2 == 0:
-                                        y = y_s
-                                        x = x_s
-                                    # Error value is 1 if the solver found the same new position of the considered particle twice.
-                                    # The error is only displayed but the calculation is continued, as the position could be correct
-                                    elif error_position_update2 == 1:
-                                        if display_detailed_progress == True:
-                                            print('Error in the error_position_update2 function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                                        y = y_s
-                                        x = x_s                    
-                                    # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                                    # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                                    elif error_position_update2 == 2:
-                                        if display_detailed_progress == True:
-                                            print('Error in the error_position_update2 function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                                        z = z + STEPSIZE / 2
-                                        continue
-                                    # Error value is 3 if the both solver solutions lies behind the simulation borders
-                                    elif error_position_update2 == 3:
-                                        if display_detailed_progress == True:
-                                            print('Error in the error_position_update1w function. The both solver solutions lies behind the simulation borders. ')
-                                        n_error = n_error + 1
-                                        continue
-                                # If steady_check == 4, the particle is not in steady state and would roll along one steady state particle and wall
-                                # Thus its position is updated by position_update1w function    
-                                elif steady_check == 4:
-                                    if display_detailed_progress == True:
-                                        print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along one particle and wall')
-                                    # Function for position update at two contacts.
-                                    y_s, x_s, error_position_update1w = position_update1w(z, y, x, d, M_particle_steady, overlaps, walls)
-                                    # If no range error occurs, the new coordinates are assignet
-                                    if error_position_update1w == 0:
-                                        y = y_s
-                                        x = x_s
-                                    # Error value is 1 if the solver found the same new position of the considered particle twice.
-                                    # The error is only displayed but the calculation is continued, as the position could be correct
-                                    elif error_position_update1w == 1:
-                                        if display_detailed_progress == True:
-                                            print('Error in the error_position_update1w function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                                        y = y_s
-                                        x = x_s                    
-                                    # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                                    # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                                    elif error_position_update1w == 2:
-                                        if display_detailed_progress == True:
-                                            print('Error in the error_position_update1w function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                                        z = z + STEPSIZE / 2
-                                        continue
-                                    # Error value is 3 if the both solver solutions lies behind the simulation borders
-                                    elif error_position_update1w == 3:
-                                        if display_detailed_progress == True:
-                                            print('Error in the error_position_update1w function. The both solver solutions lies behind the simulation borders. ')
-                                        n_error = n_error + 1
-                                        continue
-                                continue
-                            # If overlaps occur, the considered particle is moved 1/10 of the step upwards an handled again in the next loop.
-                            else:        
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i ,'collides with three steady state particles and two walls.')
-                                z = z + STEPSIZE / 2
-                                continue
-                        # Checking for overlaps after position update
-                        n_overlaps, overlaps, walls = calc_overlap(d, z, y, x, M_particle_steady, test_range)
-                        # Three overlappings in total
-                        if n_overlaps == 1:
-                            # index of the third steady state particle, the considered particle collides with
-                            index3 = overlaps[0]
-                            # Update overlaps array
-                            overlaps = np.array([index1, index2, index3])
-                            # No overlappings with walls
-                            if walls == 0:
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i ,'collides with three steady state particles.')
-                                # If the considered particle overlaps with one steady state particle after position update at two particles,
-                                # it is in contact with three particles. Thus, its position is updated by position_update3 function.
-                                z_s, y_s, x_s, error_position_update3 = position_update3(z, y, x, d, M_particle_steady, overlaps)
-                                # Error value is 1 if the solver found the same new position of the considered particle twice.
-                                # The error is only displayed but the calculation is continued, as the position could be correct
-                                if error_position_update3 == 1:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update3 function. Solver found the same new position twice.  Calculation proceeds with the found value.')
-                                # Error value is 2 if the position of the considered particle is located below the centroid of the steady state particles
-                                elif error_position_update3 == 2:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update3 function. Position of the considered particle is located below the centroid of the steady state particles.')
-                                    z = z + STEPSIZE / 2
-                                    continue
-                                # Error value is 3 if the position of the first solver solution (the logical one) is not located on the intersection plane
-                                elif error_position_update3 == 3:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update3 function. The logical new position determnined by the solver is not located on the intersection plane. ')
-                                    z = z + STEPSIZE / 2
-                                    continue
-                            # If the considered particle overlaps with more than one wall after position update at three particles, 
-                            # it is moved 1/10 of the step upwards an handled again in the next loop.
-                            else:        
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i ,'collides with three steady state particles and walls.')
-                                z = z + STEPSIZE / 2
-                                continue
-                            # Checking for overlaps after position update 
-                            n_overlaps, overlaps3, error_overlap = calc_overlap(d, z_s, y_s, x_s, M_particle_steady, test_range)
-                            # If value is True, an error has occurred in the calc_overlapping function.
-                            # The considered particle is stored in the steady state array with NaN values.
-                            if error_overlap == True:
-                                if display_detailed_progress == True:
-                                    print('Error in the calc_overlapping function. Check calc_overlapping function.')
-                                fatal_error = True
-                                break
-                            # If no overlaps occure, the particle is tested for steady state
-                            if n_overlaps == 0:
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i ,'is checked for steady state at three steady state particles.')
-                                # With three contact partners, the considered particle can be in a steady state, 
-                                # which is tested by the check_steadystate3 function
-                                steady_check, overlaps, error_check_steadystate3 = check_steadystate3(z_s, y_s, x_s, M_particle_steady, overlaps)
-                                # Error value is 1 if particles center is located below the triangle's centroid of the steady state particles
-                                if error_check_steadystate3 == 1:
-                                    if display_detailed_progress == True:
-                                        print('Error in the check_steadystate3 function. The center of the considered particle is located below the triangles centroid of the steady state particles.')
-                                    n_error = n_error + 1
-                                    continue
-                                # Error value is 2 if the triangle points of the steady state particles are collinear
-                                elif error_check_steadystate3 == 2:
-                                    if display_detailed_progress == True:
-                                        print('Error in the check_steadystate3 function. The triangle points of the steady state particles are collinear.')
-                                    n_error = n_error + 1
-                                    continue
-                                # Error value is 3 if the subdomain of the triangle could not be determined correctly
-                                elif error_check_steadystate3 == 3:
-                                    if display_detailed_progress == True:
-                                        print('Error in the check_steadystate3 function. The subdomain of the triangle could not be determined correctly.')
-                                    n_error = n_error + 1
-                                    continue
-                                # If steady_check == 0, the particle is in steady state
-                                if steady_check == 3:
-                                    # Assigning the coordinates of the considered partcile to its steady state coordinates at three particles
-                                    z = z_s
-                                    y = y_s
-                                    x = x_s
-                                    # If steady = 2, steady state position is reched on three particles
-                                    steady = 2
-                                # If steady_check == 1, the particle is not in steady state and would roll along one steady state particle
-                                # Thus its position is updated by position_update1 function
-                                elif steady_check == 1:
-                                    if display_detailed_progress == True:
-                                        print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along one particle')
-                                    # Function for position update at one contact.
-                                    y, x = position_update1(y, x, d, M_particle_steady, overlaps)
-                                # If steady_check == 2, the particle is not in steady state and would roll along two steady state particles
-                                # Thus its position is updated by position_update2 function    
-                                elif steady_check == 2:
-                                    if display_detailed_progress == True:
-                                        print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along two particles')
-                                    # Function for position update at two contacts.
-                                    y_s, x_s, error_position_update2 = position_update2(z, y, x, d, M_particle_steady, overlaps)
-                                    # If no range error occurs, the new coordinates are assignet
-                                    if error_position_update2 == 0:
-                                        y = y_s
-                                        x = x_s
-                                    # Error value is 1 if the solver found the same new position of the considered particle twice.
-                                    # The error is only displayed but the calculation is continued, as the position could be correct
-                                    elif error_position_update2 == 1:
-                                        if display_detailed_progress == True:
-                                            print('Error in the error_position_update2 function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                                        y = y_s
-                                        x = x_s                    
-                                    # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                                    # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                                    elif error_position_update2 == 2:
-                                        if display_detailed_progress == True:
-                                            print('Error in the error_position_update2 function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                                        z = z + STEPSIZE / 2
-                                        continue
-                                    # Error value is 3 if the both solver solutions lies behind the simulation borders
-                                    elif error_position_update2 == 3:
-                                        if display_detailed_progress == True:
-                                            print('Error in the error_position_update1w function. The both solver solutions lies behind the simulation borders. ')
-                                        n_error = n_error + 1
-                                        continue
-                            # If the considered particle overlaps with more than one steady state particle after position update at three contacts,
-                            # it is in contact with more than four particles. 
-                            # In this case, the particle is moved 1/10 of the step upwards an handled again in the next loop.
-                            elif n_overlaps > 0:
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i, 'collides with more than three particles')
-                                z = z + STEPSIZE / 2
-                                continue
-                        # If the considered particle overlaps with more than two steady state particle after position update at two contacts,
-                        # it is in contact with more than four particles. 
-                        # In this case, the particle is moved 1/10 of the step upwards an handled again in the next loop.
-                        elif n_overlaps > 1:
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i, 'collides with more than three particles')
-                            z = z + STEPSIZE / 2
-                            continue
-                    # Overlap with three steady state particles in total
-                    elif n_overlaps == 2:
-                        # Indices of the second and third steady state particles, the considered particle collides with
-                        index2 = overlaps[0]
-                        index3 = overlaps[1]
-                        # Update overlaps array
-                        overlaps = np.array([index1, index2, index3])
-                        # No overlappings with walls
-                        if walls == 0:
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i ,'collides with three steady state particles.')
-                            # If the considered particle overlaps with two steady state particle after position update at one contact,
-                            # it is in contact with three particles. Thus, its position is updated by position_update3 function.
-                            z_s, y_s, x_s, error_position_update3 = position_update3(z, y, x, d, M_particle_steady, overlaps)
-                            # Error value is 1 if the solver found the same new position of the considered particle twice.
-                            # The error is only displayed but the calculation is continued, as the position could be correct
-                            if error_position_update3 == 1:
-                                if display_detailed_progress == True:
-                                    print('Error in the error_position_update3 function. Solver found the same new position twice.  Calculation proceeds with the found value.')
-                            # Error value is 2 if the position of the considered particle is located below the centroid of the steady state particles
-                            elif error_position_update3 == 2:
-                                if display_detailed_progress == True:
-                                    print('Error in the error_position_update3 function. Position of the considered particle is located below the centroid of the steady state particles.')
-                                z = z + STEPSIZE / 2
-                                continue
-                            # Error value is 3 if the position of the first solver solution (the logical one) is not located on the intersection plane
-                            elif error_position_update3 == 3:
-                                if display_detailed_progress == True:
-                                    print('Error in the error_position_update3 function. The logical new position determnined by the solver is not located on the intersection plane. ')
-                                z = z + STEPSIZE / 2
-                                continue
-                        # If the considered particle overlaps with more than one wall after position update at three particles, 
-                        # it is moved 1/10 of the step upwards an handled again in the next loop.
-                        else:        
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i ,'collides with three steady state particles and walls.')
-                            z = z + STEPSIZE / 2
-                            continue
-                        # Checking for overlaps after position update   
-                        n_overlaps, overlaps3, error_overlap = calc_overlap(d, z_s, y_s, x_s, M_particle_steady, test_range)
-                        # If value is True, an error has occurred in the calc_overlapping function.
-                        # The considered particle is stored in the steady state array with NaN values.
-                        if error_overlap == True:
-                            if display_detailed_progress == True:
-                                print('Error in the calc_overlapping function. Check calc_overlapping function.')
-                            fatal_error = True
-                            break
-                        # If no overlaps occure, the particle is tested for steady state
-                        if n_overlaps == 0:
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i ,'is checked for steady state at three steady state particles.')
-                            # With three contact partners, the considered particle can be in a steady state, 
-                            # which is tested by the check_steadystate3 function
-                            steady_check, overlaps, error_check_steadystate3 = check_steadystate3(z_s, y_s, x_s, M_particle_steady, overlaps)
-                            # Error value is 1 if particles center is located below the triangle's centroid of the steady state particles
-                            if error_check_steadystate3 == 1:
-                                if display_detailed_progress == True:
-                                    print('Error in the check_steadystate3 function. The center of the considered particle is located below the triangles centroid of the steady state particles.')
-                                n_error = n_error + 1
-                                continue
-                            # Error value is 2 if the triangle points of the steady state particles are collinear
-                            elif error_check_steadystate3 == 2:
-                                if display_detailed_progress == True:
-                                    print('Error in the check_steadystate3 function. The triangle points of the steady state particles are collinear.')
-                                n_error = n_error + 1
-                                continue
-                            # Error value is 3 if the subdomain of the triangle could not be determined correctly
-                            elif error_check_steadystate3 == 3:
-                                if display_detailed_progress == True:
-                                    print('Error in the check_steadystate3 function. The subdomain of the triangle could not be determined correctly.')
-                                n_error = n_error + 1
-                                continue
-                            # If steady_check == 0, the particle is in steady state
-                            if steady_check == 3:
-                                # Assigning the coordinates of the considered partcile to its steady state coordinates at three particles
-                                z = z_s
-                                y = y_s
-                                x = x_s
-                                # If steady = 2, steady state position is reched on three particles
-                                steady = 2
-                            # If steady_check == 1, the particle is not in steady state and would roll along one steady state particle
-                            # Thus its position is updated by position_update1 function
-                            elif steady_check == 1:
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along one particle')
-                                # Function for position update at one contact.
-                                y, x = position_update1(y, x, d, M_particle_steady, overlaps)
-                            # If steady_check == 2, the particle is not in steady state and would roll along two steady state particles
-                            # Thus its position is updated by position_update2 function    
-                            elif steady_check == 2:
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along two particles')
-                                # Function for position update at two contacts.
-                                y_s, x_s, error_position_update2 = position_update2(z, y, x, d, M_particle_steady, overlaps)
-                                # If no range error occurs, the new coordinates are assignet
-                                if error_position_update2 == 0:
-                                    y = y_s
-                                    x = x_s
-                                # Error value is 1 if the solver found the same new position of the considered particle twice.
-                                # The error is only displayed but the calculation is continued, as the position could be correct
-                                elif error_position_update2 == 1:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update2 function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                                    y = y_s
-                                    x = x_s                    
-                                # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                                # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                                elif error_position_update2 == 2:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update2 function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                                    z = z + STEPSIZE / 2
-                                    continue
-                                # Error value is 3 if the both solver solutions lies behind the simulation borders
-                                elif error_position_update2 == 3:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update1w function. The both solver solutions lies behind the simulation borders. ')
-                                    n_error = n_error + 1
-                                    continue
-                        # If the considered particle overlaps with more than one steady state particle after position update at three contacts,
-                        # it is in contact with more than four particles. 
-                        # In this case, the particle is moved 1/10 of the step upwards an handled again in the next loop.
-                        elif n_overlaps > 0:
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i, 'collides with more than three particles')
-                            z = z + STEPSIZE / 2
-                            continue
-                    # If the considered particle overlaps with more than three steady state particle after position update at one contact,
-                    # it is in contact with more than four particles. 
-                    # In this case, the particle is moved 1/10 of the step upwards an handled again in the next loop.
-                    elif n_overlaps > 2:
-                        if display_detailed_progress == True:
-                            print('Particle Nr.', particle_i, 'collides with more than three particles')
-                        z = z + STEPSIZE / 2    
-                        continue
-                # Two overlaps with steady state particles in total
-                elif n_overlaps == 2:
-                    # Indices of the second and third steady state particles, the considered particle collides with
-                    index1 = overlaps[0]
-                    index2 = overlaps[1]
-                    # No overlappings with walls
-                    if walls == 0:
-                        if display_detailed_progress == True:
-                            print('Particle Nr.', particle_i ,'collides with two steady state particles.')
-                        # If the considered particle overlaps with two steady state particles, 
-                        # its position is updated by position_update2 function.
-                        y_s, x_s, error_position_update2 = position_update2(z, y, x, d, M_particle_steady, overlaps)
-                        # If no range error occurs, the new coordinates are assignet
-                        if error_position_update2 == 0:
-                            y = y_s
-                            x = x_s
-                        # Error value is 1 if the solver found the same new position of the considered particle twice.
-                        # The error is only displayed but the calculation is continued, as the position could be correct
-                        elif error_position_update2 == 1:
-                            if display_detailed_progress == True:
-                                print('Error in the error_position_update2 function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                            y = y_s
-                            x = x_s                    
-                        # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                        # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                        elif error_position_update2 == 2:
-                            if display_detailed_progress == True:
-                                print('Error in the error_position_update2 function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                            z = z + STEPSIZE / 2
-                            continue
-                        # Error value is 3 if the both solver solutions lies behind the simulation borders
-                        elif error_position_update2 == 3:
-                            if display_detailed_progress == True:
-                                print('Error in the error_position_update1w function. The both solver solutions lies behind the simulation borders. ')
-                            n_error = n_error + 1
-                            continue
-                    # Overlap with one wall
-                    elif walls == 1 or walls == -1 or walls == 2 or walls == -2:
-                        if display_detailed_progress == True:
-                            print('Particle Nr.', particle_i ,'collides with two steady state particles and one wall.')
-                        # If the considered particle overlaps with one steady state particle and one wall after position update at one particle,
-                        # it is in contact with two particles and one wall. Thus, its position is updated by position_update2w function.
-                        z_s, y_s, x_s, error_position_update2w = position_update2w(z, y, x, d, M_particle_steady, overlaps, walls)
-                        # Error value is 1 if the solver found the same new position of the considered particle twice.
-                        # The error is only displayed but the calculation is continued, as the position could be correct
-                        if error_position_update2w == 1:
-                            if display_detailed_progress == True:
-                                print('Error in the error_position_update2w function. Solver found the same new position twice. Calculation proceeds with the found value.')                 
-                        # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                        # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                        elif error_position_update2w == 2:
-                            if display_detailed_progress == True:
-                                print('Error in the error_position_update2w function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                            z = z + STEPSIZE / 2
-                            continue
-                        # If particle is in contact with two steady state particle and one wall, it could be in steady state
-                        # Checking for overlaps after position update
-                        n_overlaps, overlaps2w, walls = calc_overlap(d, z_s, y_s, x_s, M_particle_steady, test_range)
-                        # If the particle overlaps with a second wall, its position is updated in the next loop
-                        if walls == 3 or walls == -3 or walls == 4 or walls == -4 or walls == 0:
-                            continue
-                        # If no overlaps occure, the particle is tested for steady state
-                        if n_overlaps == 0:
-                            # With three contact partners, the considered particle can be in a steady state, 
-                            # which is tested by the check_steadystate1w2 function
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i ,'is tested for steady state at two particles and one wall.')
-                            steady_check, overlaps, error_check_steadystate2w = check_steadystate2w(z_s, y_s, x_s, particle_i, M_particle_steady, overlaps, walls)
-                            # Error value is 1 if particles center is located below the triangle's centroid of the steady state particles
-                            if error_check_steadystate2w == 1:
-                                if display_detailed_progress == True:
-                                    print('Error in the check_steadystate3 function. The center of the considered particle is located below the triangles centroid of the steady state particles.')
-                                n_error = n_error + 1
-                                continue
-                            # Error value is 2 if the triangle points of the steady state particles are collinear
-                            elif error_check_steadystate2w == 2:
-                                if display_detailed_progress == True:
-                                    print('Error in the check_steadystate3 function. The triangle points of the steady state particles are collinear.')
-                                n_error = n_error + 1
-                                continue
-                            # Error value is 3 if the subdomain of the triangle could not be determined correctly
-                            elif error_check_steadystate2w == 3:
-                                if display_detailed_progress == True:
-                                    print('Error in the check_steadystate3 function. The subdomain of the triangle could not be determined correctly.')
-                                n_error = n_error + 1
-                                continue
-                            # If steady_check == 0, the particle is in steady state
-                            if steady_check == 3:
-                                # Assigning the coordinates of the considered partcile to its steady state coordinates at three particles
-                                z = z_s
-                                y = y_s
-                                x = x_s
-                                # If steady = 2, steady state position is reched at two particles and one wall
-                                steady = 4
-                            # If steady_check == 1, the particle is not in steady state and would roll along one steady state particle
-                            # Thus its position is updated by position_update1 function
-                            elif steady_check == 1:
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along one particle')
-                                # Function for position update at one contact.
-                                y, x = position_update1(y, x, d, M_particle_steady, overlaps)
-                            # If steady_check == 2, the particle is not in steady state and would roll along two steady state particles
-                            # Thus its position is updated by position_update2 function                          
-                            elif steady_check == 2:
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along two particles')
-                                # Function for position update at two contacts.
-                                y_s, x_s, error_position_update2 = position_update2(z, y, x, d, M_particle_steady, overlaps)
-                                # If no range error occurs, the new coordinates are assignet
-                                if error_position_update2 == 0:
-                                    y = y_s
-                                    x = x_s
-                                # Error value is 1 if the solver found the same new position of the considered particle twice.
-                                # The error is only displayed but the calculation is continued, as the position could be correct
-                                elif error_position_update2 == 1:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update2 function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                                    y = y_s
-                                    x = x_s                    
-                                # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                                # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                                elif error_position_update2 == 2:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update2 function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                                    z = z + STEPSIZE / 2
-                                    continue
-                                # Error value is 3 if the both solver solutions lies behind the simulation borders
-                                elif error_position_update2 == 3:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update1w function. The both solver solutions lies behind the simulation borders. ')
-                                    n_error = n_error + 1
-                                    continue
-                            # If steady_check == 4, the particle is not in steady state and would roll along one steady state particle and wall
-                            # Thus its position is updated by position_update1w function    
-                            elif steady_check == 4:
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along one particle and wall')
-                                # Function for position update at two contacts.
-                                y_s, x_s, error_position_update1w = position_update1w(z, y, x, d, M_particle_steady, overlaps, walls)
-                                # If no range error occurs, the new coordinates are assignet
-                                if error_position_update1w == 0:
-                                    y = y_s
-                                    x = x_s
-                                # Error value is 1 if the solver found the same new position of the considered particle twice.
-                                # The error is only displayed but the calculation is continued, as the position could be correct
-                                elif error_position_update1w == 1:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update1w function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                                    y = y_s
-                                    x = x_s                    
-                                # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                                # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                                elif error_position_update1w == 2:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update1w function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                                    z = z + STEPSIZE / 2
-                                    continue
-                                # Error value is 3 if the both solver solutions lies behind the simulation borders
-                                elif error_position_update1w == 3:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update1w function. The both solver solutions lies behind the simulation borders. ')
-                                    n_error = n_error + 1
-                                    continue
-                            continue
-                        # If overlaps occur, the considered particle is moved 1/10 of the step upwards an handled again in the next loop.
-                        else:        
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i ,'collides with three steady state particles and two walls.')
-                            z = z + STEPSIZE / 2
-                            continue
-                    # Overlap with two walls
-                    elif walls == 3 or walls == -3 or walls == 4 or walls == -4:
-                        if display_detailed_progress == True:
-                            print('Particle Nr.', particle_i ,'collides with one steady state particle and two walls.')
-                        # If the considered particle overlaps with one steady state particle and two walls after position update at one particle,
-                        # it is in contact with two particles and two walls. Thus, its position is updated by position_update2w function.
-                        z_s, y_s, x_s, error_position_update2w2 = position_update2w2(z, y, x, d, M_particle_steady, overlaps, walls)
-                        # Error value is 1 if the solver found the same new position of the considered particle twice.
-                        # The error is only displayed but the calculation is continued, as the position could be correct
-                        if error_position_update2w2 == 1:
-                            if display_detailed_progress == True:
-                                print('Error in the error_position_update2w2 function. Solver found the same new position twice. Calculation proceeds with the found value.')                  
-                        # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                        # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                        elif error_position_update2w2 == 2:
-                            if display_detailed_progress == True:
-                                print('Error in the error_position_update2w2 function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                            z = z + STEPSIZE / 2
-                            continue
-                        # If particle is in contact with two steady state particle and two walls, it could be in steady state
-                        # Checking for overlaps after position update
-                        n_overlaps, overlaps2w2, walls = calc_overlap(d, z_s, y_s, x_s, M_particle_steady, test_range)
-                        # If the particle overlaps with onlyone wall, its position is updated in the next loop
-                        if walls == 1 or walls == -1 or walls == 2 or walls == -2 or walls == 0:
-                            continue
-                        # If no overlaps occure, the particle is tested for steady state
-                        if n_overlaps == 0:
-                            # With three contact partners, the considered particle can be in a steady state, 
-                            # which is tested by the check_steadystate1w2 function
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i ,'is tested for steady state at two particles and two walls.')
-                            steady_check, overlaps, walls, error_check_steadystate2w2 = check_steadystate2w2(z_s, y_s, x_s, M_particle_steady, overlaps, walls)
-                            # Error value is 1 if particles center is located below the triangle's centroid of the steady state particles
-                            if error_check_steadystate2w2 == 1:
-                                if display_detailed_progress == True:
-                                    print('Error in the check_steadystate3 function. The center of the considered particle is located below the triangles centroid of the steady state particles.')
-                                n_error = n_error + 1
-                                continue
-                            # Error value is 2 if the triangle points of the steady state particles are collinear
-                            elif error_check_steadystate2w2 == 2:
-                                if display_detailed_progress == True:
-                                    print('Error in the check_steadystate3 function. The triangle points of the steady state particles are collinear.')
-                                n_error = n_error + 1
-                                continue
-                            # Error value is 3 if the subdomain of the triangle could not be determined correctly
-                            elif error_check_steadystate2w2 == 3:
-                                if display_detailed_progress == True:
-                                    print('Error in the check_steadystate3 function. The subdomain of the triangle could not be determined correctly.')
-                                n_error = n_error + 1
-                                continue
-                            # If steady_check == 0, the particle is in steady state
-                            if steady_check == 3:
-                                # Assigning the coordinates of the considered partcile to its steady state coordinates at three particles
-                                z = z_s
-                                y = y_s
-                                x = x_s
-                                # If steady = 2, steady state position is reched at two particles and two walls
-                                steady = 7
-                            # If steady_check == 1, the particle is not in steady state and would roll along one steady state particle
-                            # Thus its position is updated by position_update1 function
-                            elif steady_check == 1:
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along one particle')
-                                # Function for position update at one contact.
-                                y, x = position_update1(y, x, d, M_particle_steady, overlaps)
-                            # If steady_check == 2, the particle is not in steady state and would roll along two steady state particles
-                            # Thus its position is updated by position_update2 function                          
-                            elif steady_check == 2:
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along two particles')
-                                # Function for position update at two contacts.
-                                y_s, x_s, error_position_update2 = position_update2(z, y, x, d, M_particle_steady, overlaps)
-                                # If no range error occurs, the new coordinates are assignet
-                                if error_position_update2 == 0:
-                                    y = y_s
-                                    x = x_s
-                                # Error value is 1 if the solver found the same new position of the considered particle twice.
-                                # The error is only displayed but the calculation is continued, as the position could be correct
-                                elif error_position_update2 == 1:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update2 function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                                    y = y_s
-                                    x = x_s                    
-                                # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                                # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                                elif error_position_update2 == 2:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update2 function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                                    z = z + STEPSIZE / 2
-                                    continue
-                                # Error value is 3 if the both solver solutions lies behind the simulation borders
-                                elif error_position_update2 == 3:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update1w function. The both solver solutions lies behind the simulation borders. ')
-                                    n_error = n_error + 1
-                                    continue
-                            # If steady_check == 4, the particle is not in steady state and would roll along one steady state particle and wall
-                            # Thus its position is updated by position_update1w function    
-                            elif steady_check == 4:
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along one particle and wall')
-                                # Function for position update at two contacts.
-                                y_s, x_s, error_position_update1w = position_update1w(z, y, x, d, M_particle_steady, overlaps, walls)
-                                # If no range error occurs, the new coordinates are assignet
-                                if error_position_update1w == 0:
-                                    y = y_s
-                                    x = x_s
-                                # Error value is 1 if the solver found the same new position of the considered particle twice.
-                                # The error is only displayed but the calculation is continued, as the position could be correct
-                                elif error_position_update1w == 1:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update1w function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                                    y = y_s
-                                    x = x_s                    
-                                # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                                # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                                elif error_position_update1w == 2:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update1w function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                                    z = z + STEPSIZE / 2
-                                    continue
-                                # Error value is 3 if the both solver solutions lies behind the simulation borders
-                                elif error_position_update1w == 3:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update1w function. The both solver solutions lies behind the simulation borders. ')
-                                    n_error = n_error + 1
-                                    continue
-                            continue
-                        # If overlaps occur, the considered particle is moved 1/10 of the step upwards an handled again in the next loop.
-                        else:        
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i ,'collides with three steady state particles and two walls.')
-                            z = z + STEPSIZE / 2
-                            continue
-                    # Checking for overlaps after position update
-                    n_overlaps, overlaps, walls = calc_overlap(d, z, y, x, M_particle_steady, test_range)
-                    # Three overlaps with steady state particles in total
-                    if n_overlaps == 1:
-                        # Index of the second and third steady state particles, the considered particle collides with
-                        index3 = overlaps[0]
-                        # Update overlaps array
-                        overlaps = np.array([index1, index2, index3])
-                        # No overlappings with walls    
-                        if walls == 0:
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i ,'collides with three steady state particles.')
-                            # If the considered particle overlaps with one steady state particle after position update at two contacts,
-                            # it is in contact with three particles. Thus, its position is updated by position_update3 function.
-                            z_s, y_s, x_s, error_position_update3 = position_update3(z, y, x, d, M_particle_steady, overlaps)
-                            # Error value is 1 if the solver found the same new position of the considered particle twice.
-                            # The error is only displayed but the calculation is continued, as the position could be correct
-                            if error_position_update3 == 1:
-                                if display_detailed_progress == True:
-                                    print('Error in the error_position_update3 function. Solver found the same new position twice.  Calculation proceeds with the found value.')
-                            # Error value is 2 if the position of the considered particle is located below the centroid of the steady state particles
-                            elif error_position_update3 == 2:
-                                if display_detailed_progress == True:
-                                    print('Error in the error_position_update3 function. Position of the considered particle is located below the centroid of the steady state particles.')
-                                z = z + STEPSIZE / 2
-                                continue
-                            # Error value is 3 if the position of the first solver solution (the logical one) is not located on the intersection plane
-                            elif error_position_update3 == 3:
-                                if display_detailed_progress == True:
-                                    print('Error in the error_position_update3 function. The logical new position determnined by the solver is not located on the intersection plane. ')
-                                z = z + STEPSIZE / 2
-                                continue
-                        # If the considered particle overlaps with more than one wall after position update at three particles, 
-                        # it is moved 1/10 of the step upwards an handled again in the next loop.
-                        else:        
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i ,'collides with three steady state particles and walls.')
-                            z = z + STEPSIZE / 2
-                            continue
-                        # Checking for overlaps after position update   
-                        n_overlaps, overlaps3, error_overlap = calc_overlap(d, z_s, y_s, x_s, M_particle_steady, test_range)
-                        # If value is True, an error has occurred in the calc_overlapping function.
-                        # The considered particle is stored in the steady state array with NaN values.
-                        if error_overlap == True:
-                            if display_detailed_progress == True:
-                                print('Error in the calc_overlapping function. Check calc_overlapping function.')
-                            fatal_error = True
-                            break
-                        # If no overlaps occure, the particle is tested for steady state
-                        if n_overlaps == 0:
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i ,'is checked for steady state at three steady state particles.')
-                            # With three contact partners, the considered particle can be in a steady state, 
-                            # which is tested by the check_steadystate3 function
-                            steady_check, overlaps, error_check_steadystate3 = check_steadystate3(z_s, y_s, x_s, M_particle_steady, overlaps)
-                            # Error value is 1 if particles center is located below the triangle's centroid of the steady state particles
-                            if error_check_steadystate3 == 1:
-                                if display_detailed_progress == True:
-                                    print('Error in the check_steadystate3 function. The center of the considered particle is located below the triangles centroid of the steady state particles.')
-                                n_error = n_error + 1
-                                continue
-                            # Error value is 2 if the triangle points of the steady state particles are collinear
-                            elif error_check_steadystate3 == 2:
-                                if display_detailed_progress == True:
-                                    print('Error in the check_steadystate3 function. The triangle points of the steady state particles are collinear.')
-                                n_error = n_error + 1
-                                continue
-                            # Error value is 3 if the subdomain of the triangle could not be determined correctly
-                            elif error_check_steadystate3 == 3:
-                                if display_detailed_progress == True:
-                                    print('Error in the check_steadystate3 function. The subdomain of the triangle could not be determined correctly.')
-                                n_error = n_error + 1
-                                continue
-                            # If steady_check == 0, the particle is in steady state
-                            if steady_check == 3:
-                                # Assigning the coordinates of the considered partcile to its steady state coordinates at three particles
-                                z = z_s
-                                y = y_s
-                                x = x_s
-                                # If steady = 2, steady state position is reched on three particles
-                                steady = 2
-                            # If steady_check == 1, the particle is not in steady state and would roll along one steady state particle
-                            # Thus its position is updated by position_update1 function
-                            elif steady_check == 1:
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along one particle')
-                                # Function for position update at one contact.
-                                y, x = position_update1(y, x, d, M_particle_steady, overlaps)
-                            # If steady_check == 2, the particle is not in steady state and would roll along two steady state particles
-                            # Thus its position is updated by position_update2 function    
-                            elif steady_check == 2:
-                                if display_detailed_progress == True:
-                                    print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along two particles')
-                                # Function for position update at two contacts.
-                                y_s, x_s, error_position_update2 = position_update2(z, y, x, d, M_particle_steady, overlaps)
-                                # If no range error occurs, the new coordinates are assignet
-                                if error_position_update2 == 0:
-                                    y = y_s
-                                    x = x_s
-                                # Error value is 1 if the solver found the same new position of the considered particle twice.
-                                # The error is only displayed but the calculation is continued, as the position could be correct
-                                elif error_position_update2 == 1:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update2 function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                                    y = y_s
-                                    x = x_s                    
-                                # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                                # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                                elif error_position_update2 == 2:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update2 function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                                    z = z + STEPSIZE / 2
-                                    continue
-                                # Error value is 3 if the both solver solutions lies behind the simulation borders
-                                elif error_position_update2 == 3:
-                                    if display_detailed_progress == True:
-                                        print('Error in the error_position_update1w function. The both solver solutions lies behind the simulation borders. ')
-                                    n_error = n_error + 1
-                                    continue
-                        # If the considered particle overlaps with more than one steady state particle after position update at three contacts,
-                        # it is in contact with more than four particles. 
-                        # In this case, the particle is moved 1/10 of the step upwards an handled again in the next loop.
-                        elif n_overlaps > 0:
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i, 'collides with more than three particles')
-                            z = z + STEPSIZE / 2
-                            continue
-                    # If the considered particle overlaps with more than two steady state particle after position update at two contacts,
-                    # it is in contact with more than four particles. 
-                    # In this case, the particle is moved 1/10 of the step upwards an handled again in the next loop.
-                    elif n_overlaps > 1:
-                        if display_detailed_progress == True:
-                            print('Particle Nr.', particle_i, 'collides with more than four particles')
-                        z = z + STEPSIZE / 2
-                        continue
-                # Three overlaps with steady state particles in total
-                elif n_overlaps == 3:
-                    # Indices of the first, second and third steady state particles, the considered particle collides with
-                    index1 = overlaps[0]
-                    index2 = overlaps[1]
-                    index3 = overlaps[2]
-                    # No overlappings with walls
-                    if walls == 0:
-                        if display_detailed_progress == True:
-                            print('Particle Nr.', particle_i ,'collides with three steady state particles.')
-                        # If the considered particle overlaps with three steady state particles, 
-                        # its position is updated by position_update3 function.
-                        z_s, y_s, x_s, error_position_update3 = position_update3(z, y, x, d, M_particle_steady, overlaps)
-                        # Error value is 1 if the solver found the same new position of the considered particle twice.
-                        # The error is only displayed but the calculation is continued, as the position could be correct
-                        if error_position_update3 == 1:
-                            if display_detailed_progress == True:
-                                print('Error in the error_position_update3 function. Solver found the same new position twice.  Calculation proceeds with the found value.')
-                        # Error value is 2 if the position of the considered particle is located below the centroid of the steady state particles
-                        elif error_position_update3 == 2:
-                            if display_detailed_progress == True:
-                                print('Error in the error_position_update3 function. Position of the considered particle is located below the centroid of the steady state particles.')
-                            z = z + STEPSIZE / 2
-                            continue
-                        # Error value is 3 if the position of the first solver solution (the logical one) is not located on the intersection plane
-                        elif error_position_update3 == 3:
-                            if display_detailed_progress == True:
-                                print('Error in the error_position_update3 function. The logical new position determnined by the solver is not located on the intersection plane. ')
-                            z = z + STEPSIZE / 2
-                            continue
-                    # If the considered particle overlaps with more than one wall after position update at three particles, 
-                    # it is moved 1/10 of the step upwards an handled again in the next loop.
-                    else:        
-                        if display_detailed_progress == True:
-                            print('Particle Nr.', particle_i ,'collides with three steady state particles and walls.')
-                        z = z + STEPSIZE / 2
-                        continue
-                    # Checking for overlaps after position update   
-                    n_overlaps, overlaps3, error_overlap = calc_overlap(d, z_s, y_s, x_s, M_particle_steady, test_range)
-                    # If no overlaps occure, the particle is tested for steady state
-                    if n_overlaps == 0:
-                        if display_detailed_progress == True:
-                            print('Particle Nr.', particle_i ,'is checked for steady state at three steady state particles.')
-                        # With three contact partners, the considered particle can be in a steady state, 
-                        # which is tested by the check_steadystate3 function
-                        steady_check, overlaps, error_check_steadystate3 = check_steadystate3(z_s, y_s, x_s, M_particle_steady, overlaps)
-                        # Error value is 1 if particles center is located below the triangle's centroid of the steady state particles
-                        if error_check_steadystate3 == 1:
-                            if display_detailed_progress == True:
-                                print('Error in the check_steadystate3 function. The center of the considered particle is located below the triangles centroid of the steady state particles.')
-                            n_error = n_error + 1
-                            continue
-                        # Error value is 2 if the triangle points of the steady state particles are collinear
-                        elif error_check_steadystate3 == 2:
-                            if display_detailed_progress == True:
-                                print('Error in the check_steadystate3 function. The triangle points of the steady state particles are collinear.')
-                            n_error = n_error + 1
-                            continue
-                        # Error value is 3 if the subdomain of the triangle could not be determined correctly
-                        elif error_check_steadystate3 == 3:
-                            if display_detailed_progress == True:
-                                print('Error in the check_steadystate3 function. The subdomain of the triangle could not be determined correctly.')
-                            n_error = n_error + 1
-                            continue
-                        # If steady_check == 0, the particle is in steady state
-                        if steady_check == 3:
-                            # Assigning the coordinates of the considered partcile to its steady state coordinates at three particles
-                            z = z_s
-                            y = y_s
-                            x = x_s
-                            # If steady = 2, steady state position is reched on three particles
-                            steady = 2
-                        # If steady_check == 1, the particle is not in steady state and would roll along one steady state particle
-                        # Thus its position is updated by position_update1 function    
-                        elif steady_check == 1:
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along one particle')
-                            # Function for position update at one contact.
-                            y, x = position_update1(y, x, d, M_particle_steady, overlaps)
-                        # If steady_check == 2, the particle is not in steady state and would roll along two steady state particles
-                        # Thus its position is updated by position_update2 function
-                        elif steady_check == 2:
-                            if display_detailed_progress == True:
-                                print('Particle Nr.', particle_i, 'exits the steady state check and is now rolling along two particles')
-                            # Function for position update at two contacts.
-                            y_s, x_s, error_position_update2 = position_update2(z, y, x, d, M_particle_steady, overlaps)
-                            # If no range error occurs, the new coordinates are assignet
-                            if error_position_update2 == 0:
-                                y = y_s
-                                x = x_s
-                            # Error value is 1 if the solver found the same new position of the considered particle twice.
-                            # The error is only displayed but the calculation is continued, as the position could be correct
-                            elif error_position_update2 == 1:
-                                if display_detailed_progress == True:
-                                    print('Error in the error_position_update2 function. Solver found the same new position twice. Calculation proceeds with the found value.')
-                                y = y_s
-                                x = x_s                    
-                            # Error value is 2 if the distance between the centers of the two steady state particles and the position 
-                            # of the first solver solution (the logical one) are not in the same range as the sums of their radii
-                            elif error_position_update2 == 2:
-                                if display_detailed_progress == True:
-                                    print('Error in the error_position_update2 function. The logical solution of the new position determined by the solver lies not in the correct range. ')
-                                z = z + STEPSIZE / 2
-                                continue
-                            # Error value is 3 if the both solver solutions lies behind the simulation borders
-                            elif error_position_update2 == 3:
-                                if display_detailed_progress == True:
-                                    print('Error in the error_position_update1w function. The both solver solutions lies behind the simulation borders. ')
-                                n_error = n_error + 1
-                                continue
-                    # If the considered particle overlaps with more than one steady state particle after position update at three contacts,
-                    # it is in contact with more than four particles. 
-                    # In this case, the particle is moved 1/10 of the step upwards an handled again in the next loop.
-                    elif n_overlaps > 0:
-                        if display_detailed_progress == True:
-                            print('Particle Nr.', particle_i, 'collides with more than four particles')
-                        z = z + STEPSIZE / 2
-                        continue
-                # If the considered particle overlaps with more than four steady state particle,
-                # it is in contact with more than four particles. 
-                # In this case, the particle is moved 1/10 of the step upwards an handled again in the next loop.
-                elif n_overlaps > 3:
-                    if display_detailed_progress == True:
-                        print('Particle Nr.', particle_i, 'collides with more than four particles')
-                    z = z + STEPSIZE / 2
-                    continue
-                # If particle reaches the ground, it is stored in steady state array.
-                if z <= d / 2:
-                    z = d / 2
-            """ Display final particle status and store the steady state particle in the steady state array. """
-            # Display if the considered particle is in steady state
-            if display_detailed_progress == True:
-                if steady == 1: 
-                    print('Particle Nr.', particle_i, 'reaches the ground.')
-                elif steady == 2:
-                    print('Particle Nr.', particle_i, 'has reached steady state on three particles.')
-                elif steady == 3:
-                    print('Particle Nr.', particle_i, 'has reached steady state on three particles.')
-                elif steady == 4:
-                    print('Particle Nr.', particle_i, 'has reached steady state on two particles and one wall.')
-                elif steady == 5:
-                    print('Particle Nr.', particle_i, 'has reached steady state on three particles and one wall.')
-                elif steady == 6:
-                    print('Particle Nr.', particle_i, 'has reached steady state on one particles and two walls.')
-                elif steady == 7:
-                    print('Particle Nr.', particle_i, 'has reached steady state on two particles and two walls.')
-            # If error=True, timeout in the calc_sedimentation function occured. 
-            # The considered particle is stored in the steady state array with NaN values.
-            if fatal_error == True:
-                M_particle_steady = np.vstack([M_particle_steady, np.zeros((1, 8 + len(q0)))])
-                M_particle_steady[len(M_particle_steady) - 1, 0] = particle_i
-                M_particle_steady[len(M_particle_steady) - 1, 1] = np.nan
-                M_particle_steady[len(M_particle_steady) - 1, 2] = np.nan
-                M_particle_steady[len(M_particle_steady) - 1, 3] = np.nan
-                M_particle_steady[len(M_particle_steady) - 1, 4] = np.nan
-                M_particle_steady[len(M_particle_steady) - 1, 5] = d
-                M_particle_steady[len(M_particle_steady) - 1, 6] = index_i
-                M_particle_steady[len(M_particle_steady) - 1, 7] = class_i
-            # The considered particle is stored in the steady state array.
-            else:    
-                M_particle_steady = np.vstack([M_particle_steady, np.zeros((1, 8 + len(q0)))])
-                M_particle_steady[len(M_particle_steady) - 1, 0] = particle_i
-                M_particle_steady[len(M_particle_steady) - 1, 1] = z
-                M_particle_steady[len(M_particle_steady) - 1, 2] = y
-                M_particle_steady[len(M_particle_steady) - 1, 3] = x
-                M_particle_steady[len(M_particle_steady) - 1, 4] = steady
-                M_particle_steady[len(M_particle_steady) - 1, 5] = d
-                M_particle_steady[len(M_particle_steady) - 1, 6] = index_i
-                M_particle_steady[len(M_particle_steady) - 1, 7] = class_i 
-                # Store contact partners in the steady state array.
-                # Determine number of collisions
-                n_overlaps_final = len(overlaps)
-                if n_overlaps_final == 1:
-                    # Particle classes of the particles in contact
-                    class1 = int(M_particle_steady[overlaps[0], 7])
-                    # The classes of the particles in contact are assigned to the considered particle
-                    M_particle_steady[len(M_particle_steady) - 1, 8 + class1] = M_particle_steady[len(M_particle_steady) - 1, 8 + class1] + 1
-                    # The class of the considered particle is assigned to the particles in contact 
-                    M_particle_steady[overlaps[0], 8 + class_i] = M_particle_steady[overlaps[0], 8 + class_i] + 1
-                elif n_overlaps_final == 2:
-                    # Particle classes of the particles in contact
-                    class1 = int(M_particle_steady[overlaps[0], 7])
-                    class2 = int(M_particle_steady[overlaps[1], 7])
-                    # The classes of the particles in contact are assigned to the considered particle
-                    M_particle_steady[len(M_particle_steady) - 1, 8 + class1] = M_particle_steady[len(M_particle_steady) - 1, 8 + class1] + 1
-                    M_particle_steady[len(M_particle_steady) - 1, 8 + class2] = M_particle_steady[len(M_particle_steady) - 1, 8 + class2] + 1
-                    # The class of the considered particle is assigned to the particles in contact 
-                    M_particle_steady[overlaps[0], 8 + class_i] = M_particle_steady[overlaps[0], 8 + class_i] + 1
-                    M_particle_steady[overlaps[1], 8 + class_i] = M_particle_steady[overlaps[1], 8 + class_i] + 1
-                elif n_overlaps_final == 3:
-                    # Particle classes of the particles in contact
-                    class1 = int(M_particle_steady[overlaps[0], 7])
-                    class2 = int(M_particle_steady[overlaps[1], 7])
-                    class3 = int(M_particle_steady[overlaps[2], 7])
-                    # The classes of the particles in contact are assigned to the considered particle
-                    M_particle_steady[len(M_particle_steady) - 1, 8 + class1] = M_particle_steady[len(M_particle_steady) - 1, 8 + class1] + 1
-                    M_particle_steady[len(M_particle_steady) - 1, 8 + class2] = M_particle_steady[len(M_particle_steady) - 1, 8 + class2] + 1
-                    M_particle_steady[len(M_particle_steady) - 1, 8 + class3] = M_particle_steady[len(M_particle_steady) - 1, 8 + class3] + 1
-                    # The class of the considered particle is assigned to the particles in contact 
-                    M_particle_steady[overlaps[0], 8 + class_i] = M_particle_steady[overlaps[0], 8 + class_i] + 1
-                    M_particle_steady[overlaps[1], 8 + class_i] = M_particle_steady[overlaps[1], 8 + class_i] + 1
-                    M_particle_steady[overlaps[2], 8 + class_i] = M_particle_steady[overlaps[2], 8 + class_i] + 1
-                elif n_overlaps_final == 4:
-                    # Particle classes of the particles in contact
-                    class1 = int(M_particle_steady[overlaps[0], 7])
-                    class2 = int(M_particle_steady[overlaps[1], 7])
-                    class3 = int(M_particle_steady[overlaps[2], 7])
-                    class4 = int(M_particle_steady[overlaps[3], 7])
-                    # The classes of the particles in contact are assigned to the considered particle
-                    M_particle_steady[len(M_particle_steady) - 1, 8 + class1] = M_particle_steady[len(M_particle_steady) - 1, 8 + class1] + 1
-                    M_particle_steady[len(M_particle_steady) - 1, 8 + class2] = M_particle_steady[len(M_particle_steady) - 1, 8 + class2] + 1
-                    M_particle_steady[len(M_particle_steady) - 1, 8 + class3] = M_particle_steady[len(M_particle_steady) - 1, 8 + class3] + 1
-                    M_particle_steady[len(M_particle_steady) - 1, 8 + class4] = M_particle_steady[len(M_particle_steady) - 1, 8 + class4] + 1
-                    # The class of the considered particle is assigned to the particles in contact 
-                    M_particle_steady[overlaps[0], 8 + class_i] = M_particle_steady[overlaps[0], 8 + class_i] + 1
-                    M_particle_steady[overlaps[1], 8 + class_i] = M_particle_steady[overlaps[1], 8 + class_i] + 1
-                    M_particle_steady[overlaps[2], 8 + class_i] = M_particle_steady[overlaps[2], 8 + class_i] + 1
-                    M_particle_steady[overlaps[3], 8 + class_i] = M_particle_steady[overlaps[3], 8 + class_i] + 1   
-                # Update the progress bar
-                pbar.update(1)
-    # Find NaN values in the steady state array 
-    del_list = np.argwhere(np.isnan(M_particle_steady[:, 1]))
-    print('Number of particles that could not be placed:', len(del_list))
-    print('Diameter of particles that could not be placed:', M_particle_steady[del_list, 5])
-    # Delete NaN values from the steady state array 
-    M_particle_steady = np.delete(M_particle_steady, del_list, 0)
-    # Number of set particles
-    n_particles_placed = len(M_particle_steady)
-    """ Check the whole particle packing for particles outside the simulation room boarders. """
-    # Mask the values between the bounds in x direction
-    mask_x = (M_particle_steady[:, 3] <= A_0) & (M_particle_steady[:, 3] >= -A_0)
-    # Mask the values between the bounds in y direction
-    mask_y = (M_particle_steady[:, 2] <= A_0) & (M_particle_steady[:, 2] >= -A_0)
-    # Combine the results of both tests 
-    mask_xy = [False if not mask_x[i] else mask_y[i] for i in range(len(mask_x))]
-    # Extract particles within the compartiment
-    M_particle_filtered = M_particle_steady[mask_xy]
-    # Number of particles after test
-    n_particles_filtered = len(M_particle_filtered)
-    # Difference between before and after test
-    n_particles_delta = n_particles_placed - n_particles_filtered
-    print('Number of particles that were placed outside simulation boarders (deleted):', n_particles_delta)
-    """ Check the whole particle packing for overlappings. """
-    if final_test == True:
-        # Counting variable for overlaps
-        n_overlaps_total = 0
-        # Consider each particles in the packing
-        for i in range(len(M_particle_filtered)):
-            # Skip NaN values
-            if np.isnan(M_particle_filtered[i, 1]):
-                continue
-            # Retrieve information from the steady state array
-            # Coordinates
-            z1 = float(M_particle_filtered[i, 1])
-            y1 = float(M_particle_filtered[i, 2])
-            x1 = float(M_particle_filtered[i, 3])
-            # Diameter
-            d1 = float(M_particle_filtered[i, 5])
-            # Define particle under consideration
-            particle1 = z1, y1, x1, d1
-            """ Define region of interrest """
-            z_min = z1 - test_range
-            if z_min < 0:
-                z_min = 0
-            z_max = z1 + test_range
-            y_min = y1 - test_range
-            y_max = y1 + test_range
-            x_min = x1 - test_range
-            x_max = x1 + test_range
-            """ Checking particles within the testing range for possible overlappings """
-            # Arrays in x, y and z direction with indices of particles, which could overlap with the considered particle
-            x_array = np.argwhere(np.logical_and(M_particle_filtered[:, 3] > x_min, M_particle_filtered[:, 3] < x_max))
-            y_array = np.argwhere(np.logical_and(M_particle_filtered[:, 2] > y_min, M_particle_filtered[:, 2] < y_max))
-            z_array = np.argwhere(np.logical_and(M_particle_filtered[:, 1] > z_min, M_particle_filtered[:, 1] < z_max))
-            # Combine the arrays --> Particles to check
-            particle_to_check = reduce(np.intersect1d, (x_array, y_array, z_array))
-            # Convert array to list
-            particle_to_check = np.array(particle_to_check).tolist()
-            """ Check each particle that may overlap for an actual overlapping """
-            for j in range(len(particle_to_check)):
-                if i != j:
-                    # Retrieve information from the steady state array
-                    # Coordinates
-                    z2 = float(M_particle_filtered[j, 1])
-                    y2 = float(M_particle_filtered[j, 2])
-                    x2 = float(M_particle_filtered[j, 3])
-                    # Diameter
-                    d2 = float(M_particle_filtered[j, 5])
-                    # Define particle
-                    particle2 = z2, y2, x2, d2
-                    # Required min distance between particles
-                    dist_required = (d1 + d2) / 2
-                    # Actual distance between particles
-                    dist_actual = ((x2 - x1) ** 2 + (y2 - y1) ** 2 + (z2 - z1) ** 2) ** 0.5
-                    # If actual distanze is below the min distance, they overlap
-                    if (dist_required - dist_actual) > 1e-9:
-                        # Update counting variable
-                        n_overlaps_total = n_overlaps_total + 1
-        # Since every particle is checked with every particle, the overlaps are counted double 
-        n_overlaps_total = int(n_overlaps_total / 2)
-        print('Total overlaps after final check:', n_overlaps_total)
-    print('Random loose packing simulation time:')
-    print("--- %s seconds ---" % (time.time() - start_time_rlp))
-    # Time stamp
-    start_time_growing = time.time()
-    """ Delete upper part of particle packing with lower hold-up than average """
-    # z-coordinate of the top particle
-    z_particle_max = np.max(M_particle_filtered[:, 1])
-    # Number of testing compartiments
-    n_compartiments = int(round(z_particle_max / (D_32/2), 0))
-    # Testing compartment height
-    delta_z = z_particle_max / n_compartiments
-    # Volume of testing compartiment
-    volume_compartiment = delta_z * L_0**2
-    # Predefine a list to calculate compartiment holdups
-    holdup_compartiments = np.zeros(n_compartiments)
-    # Iterate through each comartiment and calculate its holdup
-    for i in range(n_compartiments):
-        # Mask the values between the defined compartiment bounds
-        mask_holdup = (M_particle_filtered[:, 1] <= ((n_compartiments-i) * delta_z)) & (M_particle_filtered[:, 1] >= ((n_compartiments-i-1) * delta_z))
-        # Extract particles within the compartiment
-        M_particle_compartiment = M_particle_filtered[mask_holdup]
-        # Volume of particles within the compartiment
-        volume_particles_holdup = np.sum((np.pi / 6) * M_particle_compartiment[:, 5]**3)
-        # Compartiment holdup 
-        holdup_compartiment_i = volume_particles_holdup / volume_compartiment
-        # Update list of compartiment holdups
-        holdup_compartiments[i] = holdup_compartiment_i
-    # Upper boandary of average holdup calculation
-    bound_up_holdup = int(round(n_compartiments * 0.3, 0))
-    # Average holdup in packing
-    holdup_average = np.mean(holdup_compartiments[bound_up_holdup:n_compartiments-1])
-    for holdup_boandary in range(n_compartiments):
-        if abs(holdup_average - holdup_compartiments[holdup_boandary]) < 0.05:
-            break
-    # Max. compartiment height
-    z_compartiment_max = (n_compartiments - holdup_boandary) * delta_z
-    # Extract final particles 
-    M_particle_final = M_particle_filtered[M_particle_filtered[:, 1] <= z_compartiment_max] 
-    # Final height of packing
-    z_packing = max(M_particle_final[:,1] + M_particle_final[:,5] / 2)
-    # Final number of particles in packing
-    n_particle_final_rlp = len(M_particle_final)
-    print('Final number of particles after deleting the top part of the packing with low holdup:', n_particle_final_rlp)
-    """ Graphically display particles in 3D """
-    if display_particles == True:
-        plot_particles(M_particle_final)
-    """ 
-    Tesselation.
-    From here the coordinates order is x,y,z since the tesselation package uses this order.
-    Perform Tesselation and get information from Tesselation.
-    The cells are polyhedrons.
-    The lists are defined as follows:
-    volumes_cells: cell volumes
-    cell_neighbors: cell neighbors 
-    cell_face_normals: normal vectors of cell faces
-    cell_vertices: cell verticies
-    vertices_indices: indices of the vertices of each cell face
-    """
-    sys.stdout.write("Tesselation...")
-    # Starting coordinates for Tesselation, x, y, z
-    tess_start = (-A_0, -A_0, 0)
-    # Final coordinates for Tesselation, x, y, z
-    tess_end = (A_0, A_0, z_packing)
-    # Create an array with coordinates for tesselation
-    M_particle_tess = M_particle_final[:, [3, 2, 1]]
-    # Initial particle diameters
-    diameters_0 = M_particle_final[:,5]
-    # Calculate tesselation
-    volumes_cells, cell_neighbors, cell_face_normals, cell_vertices, vertices_indices = voro_tess(M_particle_tess, tess_start, tess_end, diameters_0)
-    sys.stdout.write("completed\n")
-    # Convert list to array
-    volumes_cells = np.array(volumes_cells)
-    """ Calculate initial/max distances between particle center and polyhedron faces
-    and determine cell faces which are from beginning in contact with other cells due to initial particle contact """
-    sys.stdout.write("Determining initial particle conditions...")
-    # Predefine list for initial/max distances between particle center and polyhedron faces
-    distances_faces_max = [
-        [
-            [None for _ in sub_inner_tuple]
-            for sub_inner_tuple in inner_tuple
-        ]
-        for inner_tuple in cell_face_normals
-    ]
-    # Predefine list for cell faces which are from beginning in contact with other cells due to initial particle contact
-    cell_face_fixed = [
-        [
-            [None for _ in sub_inner_tuple]
-            for sub_inner_tuple in inner_tuple
-        ]
-        for inner_tuple in cell_face_normals
-    ]
-    # Check each particle from particles array
-    for particle_i in range(len(M_particle_tess)):
-        # List of fixed cell faces 
-        cell_face_fixed_i = []
-        # List of distances between particle center and faces at each iteration step for volume grow
-        distances_faces_max_i = []
-        # Particle center coordinates
-        center_particle_i = M_particle_tess[particle_i]
-        # Initial particle radius
-        radius_particle_i_0 = diameters_0[particle_i] / 2
-        # Verticies of the cell corresponding to the particle
-        cell_vertices_i = cell_vertices[particle_i]
-        # Indices of the vertices of each cell face corresponding to the particle.
-        vertices_indices_i = vertices_indices[particle_i]
-        # Normal vectors of cell faces corresponding to the particle
-        cell_face_normals_i = cell_face_normals[particle_i]
-        # Iterate through each face of the cell
-        for face in range(len(cell_face_normals_i)):
-            # Indices of the vertices of the considered cell face 
-            indices_cell_face = vertices_indices_i[face]
-            # Vector between the particle center and one point on the face area (vertex)
-            vector_center_vertex = center_particle_i - cell_vertices_i[indices_cell_face[0]]
-            # Distance between particle/cell center and the cell face
-            distance_face = np.abs(np.dot(vector_center_vertex, cell_face_normals_i[face]) / np.linalg.norm(cell_face_normals_i[face]))
-            # If the distance is less than or equal to the radius of the sphere, the face intersects with the sphere. 
-            # Thus it is set to fixed
-            if np.abs(np.abs(distance_face) - radius_particle_i_0) < 1e-9:
-                face_fixed = True
-            else: 
-                face_fixed = False
-            # Update lists
-            distances_faces_max_i.append(distance_face)
-            cell_face_fixed_i.append(face_fixed)
-        # Update lists
-        distances_faces_max[particle_i] = distances_faces_max_i
-        cell_face_fixed[particle_i] = cell_face_fixed_i
-    """ Calculate the contact probability of the initial state """
-    # Create a new list with cell neighbor diameters of each particle
-    # None values are walls
-    diameters_neighbors = [
-        [diameters_0[i] if i >= 0 else None for i in sublist]
-        for sublist in cell_neighbors
-    ]    
-    # List to label particles. Label is False if particle is in contact with walls at its initial state 
-    # or if the solver found no solution during the growing simulation
-    particle_status_0 = list([True] * len(diameters_0))
-    # Check conditions and update particle_status list accordingly
-    # If None values (walls) of the diameters_neighbors list are at the same position as True values (fixed faces) of the
-    # cell_face_fixed list, the particle are in contact with walls at their initial state. Thus their label is set to False
-    for i in range(len(particle_status_0)):
-        for j in range(len(diameters_neighbors[i])):
-            if diameters_neighbors[i][j] is None and cell_face_fixed[i][j]:
-                particle_status_0[i] = False
-    # Number of particles not in contact with walls 
-    n_particle_nowall = int(np.sum(particle_status_0))
-    # Volume of cells not in contact with walls
-    volumes_cells_nowall = volumes_cells[particle_status_0]
-    # Initial diameters of particles not in contact with walls
-    diameters_nowall_0 = diameters_0[particle_status_0]
-    # Initial volumes of particles not in contact with walls
-    volumes_particles_nowall_0 = (np.pi / 6) * diameters_nowall_0 **(3)
-    # Grow factor. This factor is defined by the lowest cell volume to particle volume ratio 
-    # since the volume of each particle can be increased max. to this value due to the required constant size distribution
-    # Only particles/cells not in contact with walls are considered
-    # Calculate the grow factor only where the particle_status_0 is True
-    grow_factor_limit = round(np.min((volumes_cells_nowall / volumes_particles_nowall_0) ** (1/3)), 2)
-    grow_factor_max = round(np.max((volumes_cells_nowall / volumes_particles_nowall_0) ** (1/3)), 2)
-    if grow_factor_limit <= 1.0:
-        print('Fatal error: Tesselation failed. Try another seed')
-        sys.exit()
-    # Volume of cube surrounding the packing
-    volume_cube = z_packing * L_0**2
-    # Final particle diameters at constant growing
-    diameters_final = diameters_0 * grow_factor_limit
-    # Final particle volumes at constant growing
-    volumes_particles_final = (np.pi / 6) * diameters_final **(3)
-    # Correct volumes
-    for i in range(len(volumes_particles_final)):
-        if volumes_particles_final[i] - volumes_cells[i] > 1e-9:
-            volumes_particles_final[i] = volumes_cells[i]
-    # Packing final holdup at constant growing
-    holdup_final = np.sum(volumes_particles_final) / volume_cube
-    # Starting holdup for grow simulation
-    holdup_start = 0.525
-    # Grow steps at constant dsd size increase
-    grow_steps_const = int(math.floor((holdup_final - holdup_start) / HOLDUP_DELTA))
-    # Grow steps at non-constant dsd size increase
-    grow_steps_nonconst = int(math.floor((1.0 - holdup_final) / 0.2))
-    # Total grow steps
-    grow_steps = grow_steps_const + grow_steps_nonconst
-    # Predefine list with grow factors
-    grow_factor_list=[]
-    # Grow factor the simulations starts from
-    for i in range(grow_steps+1):
-        if i <= grow_steps_const:
-            holdup_end = holdup_start + HOLDUP_DELTA * i
-        else:
-            holdup_end = 1.0 - (grow_steps + 1 - i) * 0.2
-        # Initial guess for solver
-        initial_guess = grow_factor_limit
-        # Bounds
-        bnds = [(1, grow_factor_max)]
-        # Solver method
-        solver_method = 'SLSQP'
-        # Perform the optimization
-        result = minimize(solver_grow_factor, initial_guess, args=(holdup_end, volume_cube, diameters_0, volumes_cells), method=solver_method, bounds=bnds, tol=1e-5)
-        # Obtain the value of the minimized function
-        minimized_value = result.fun
-        # Obtain grow factor value
-        grow_factor = result.x[0]
-        grow_factor_list.append(grow_factor)
-    # Predefine list of contact probabilities
-    contact_probabilities = [[[[] for _ in range(N_CLASSES)] for _ in range(N_CLASSES)] for _ in range(grow_steps+4)]
-    # Predefine list of average contacts
-    average_contacts = [[None] * N_CLASSES for _ in range(grow_steps+4)]
-    # Determine average contacts and contact probabilities of initial state and update the list
-    average_contacts, contact_probabilities = calc_contact_probabilities(average_contacts, contact_probabilities, cell_face_fixed, diameters_neighbors, diameters_0, 0, particle_status_0)
-    # Set all faces to fixed which is the case in final state
-    cell_face_fixed_final = [[True for _ in row] for row in cell_face_fixed]
-    # Determine average contacts and contact probabilities of final state and update the list
-    average_contacts, contact_probabilities = calc_contact_probabilities(average_contacts, contact_probabilities, cell_face_fixed_final, diameters_neighbors, diameters_0, grow_steps+3, particle_status_0)
-    # Final distribution after random loose packing simulation
-    q0_rlp, q3_rlp = calc_q(diameters_nowall_0, n_particle_nowall, d_classes)
-    # Equivalent particle diameters from cell volumes
-    diameters_cells = ((6/np.pi) * volumes_cells)**(1/3)
-    # Diameters of droplets not in contact with walls
-    diameters_cells_nowall = diameters_cells[particle_status_0]
-    # Max. diameter
-    d_cells_max = np.max(diameters_cells_nowall)
-    # Update distribution classes
-    if d_cells_max > d_95:
-        # Final number of classes 
-        n_classes_final = math.ceil((d_cells_max - d_05) / delta_class)
-        # Final class diameters
-        d_classes_final = np.array([d_05+delta_class/2 + i * delta_class for i in range(n_classes_final)])
-    else:
-        d_classes_final = d_classes
-    # Final distribution after growing simulation
-    q0_final, q3_final = calc_q(diameters_cells_nowall, n_particle_nowall, d_classes_final)
-    # Particle initial volumes
-    volumes_particles = (np.pi / 6) * diameters_0 **(3)
-    # Total particle volume
-    volume_particle_total = np.sum(volumes_particles)
-    # Predefine list of packing holdups
-    holdup_packing = np.zeros(grow_steps+4)
-    # Packing initial holdup
-    holdup_packing[0] = volume_particle_total / volume_cube
-    # Packing final holdup
-    holdup_packing[grow_steps+3] = 1.0
-    #
-    sys.stdout.write("completed\n")
-    # List to label particles. Label is False if particle's volume is smaller than its cell volume.
-    cell_status = list([False] * len(diameters_0))
-    # For loop to simulate growing
-    for i_grow in range(grow_steps + 2):
-        print('Particle grow simulation. Step:', i_grow+1, 'of', (grow_steps + 2))
-        # Increase diameters
-        if i_grow <= grow_steps_const:
-            diameters = diameters_0 * grow_factor_list[i_grow]
-        elif i_grow == (grow_steps_const + 1):
-            diameters = diameters_0 * grow_factor_limit
-        else:
-            diameters = diameters_0 * grow_factor_list[i_grow-1]
-        # Update volumes
-        volumes_particles = (np.pi / 6) * diameters **(3)
-        # Check and update cell status 
-        for i in range(len(cell_status)):
-            if volumes_particles[i] - volumes_cells[i] > 1e-9:
-                volumes_particles[i] = volumes_cells[i]
-                cell_status[i] = True
-        # Final total particle volume
-        volume_particle_total = np.sum(volumes_particles)
-        # Update packing holdup
-        holdup_packing[i_grow+1] = volume_particle_total / volume_cube
-        # List of numbers to be squared
-        particle_indices = list(range(0, len(M_particle_tess)))
-        sys.stdout.write("Initializing multiprocessing...")
-        # Initialize manager for shared data
-        with multiprocessing.Manager() as manager:
-            # Lists shared with manager
-            cell_face_fixed = manager.list([manager.list(sublist) for sublist in cell_face_fixed])
-            particle_status = manager.list(particle_status_0)
-            # Number of processes to use
-            if N_CORES == 0:
-                num_processes = multiprocessing.cpu_count()
-            else:
-                num_processes = N_CORES
-            # Create a pool of worker processes
-            with multiprocessing.Pool(processes=num_processes) as pool:
-                # Create partial function for parallel processing
-                calc_partial = partial(calc_grow,
-                                    volumes_particles = volumes_particles, 
-                                    diameters = diameters,
-                                    cell_face_fixed = cell_face_fixed, 
-                                    distances_faces_max = distances_faces_max, 
-                                    cell_face_normals = cell_face_normals, 
-                                    vertices_indices = vertices_indices, 
-                                    cell_vertices = cell_vertices, 
-                                    display_particle_cell = display_particle_cell,
-                                    particle_status = particle_status,
-                                    cell_status = cell_status)
-                # List to keep track of AsyncResult objects
-                async_results = []
-                # Distribute the tasks among the worker processes using apply_async
-                for index in particle_indices:
-                    result = pool.apply_async(calc_partial, (index,))
-                    async_results.append(result)
-                sys.stdout.write("completed\n")
-                # Use tqdm to track progress
-                with tqdm(total=len(async_results), desc='Particle grow simulation progress', unit='particle') as pbar:
-                    results = []
-                    for result in async_results:
-                        results.append(result.get())
-                        # Update progress bar after each task completes
-                        pbar.update(1)  
-            # No need to close the pool and wait, 'with' statement handles it
-            # Convert shared lists to regular lists for further processing 
-            cell_face_fixed = [list(sublist) for sublist in cell_face_fixed]
-            particle_status = list(particle_status)
-        # Determine contact probabilities and update the list
-        average_contacts, contact_probabilities = calc_contact_probabilities(average_contacts, contact_probabilities, cell_face_fixed, diameters_neighbors, diameters_0, i_grow+1, particle_status)
-    # Final number of particles considered for the growing simulation
-    n_particle_final_gs = int(np.sum(particle_status))
-    """ Save results as Excel sheet. """
-    # Convert the arrays into dataframes
-    holdup_packing_df = pd.DataFrame(100*holdup_packing)
-    average_contacts_df = pd.DataFrame(average_contacts)
-    contact_probabilities_df = pd.DataFrame(contact_probabilities)
-    q0_initial_df = pd.DataFrame(100*q0_initial)
-    q0_rlp_df = pd.DataFrame(100*q0_rlp)
-    q0_final_df = pd.DataFrame(100*q0_final)
-    q3_initial_df = pd.DataFrame(100*q3_initial)
-    q3_rlp_df = pd.DataFrame(100*q3_rlp)
-    q3_final_df = pd.DataFrame(100*q3_final)
-    # Round class diameters 
-    np.round(d_classes_final, decimals=0, out=d_classes_final)
-    d_classes_final_df = pd.DataFrame(d_classes_final)
-    # Define headers
-    headers_simulation = ['Seed', 'Final number of particles']
-    headers_holdup_packing = ['Grow step', 'Hold-up']
-    headers_d_classes = ['Class', 'Class diameter']
-    header_average_contacts = ['Grow step']
-    headers_dsd = ['q0_initial', 'q0_rlp', 'q0_final', 'q3_initial', 'q3_rlp', 'q3_final']
-    for i in range(N_CLASSES):
-        header_average_contacts.append(f'Class {i+1}')
-    header_contact_probabilities = []
-    for i in range(N_CLASSES):
-        header_contact_probabilities.append(f'Class {i+1}')
-    # Create a new Excel workbook
-    workbook = xlwt.Workbook()
-    # Create a new Excel sheet for the holdup data
-    sheet_main = workbook.add_sheet('Main')
-    # Write simulation settings headers to Excel
-    for row_index, header in enumerate(headers_simulation):
-        sheet_main.write(row_index, 0, header)
-    # Write simulation settings data to Excel
-    sheet_main.write(0, 1, SEED)
-    sheet_main.write(1, 1, n_particle_final_rlp)
-    sheet_main.write(1, 2, n_particle_nowall)
-    sheet_main.write(1, 3, n_particle_final_gs)
-    # Write holdup headers to Excel
-    for row_index, header in enumerate(headers_holdup_packing):
-        sheet_main.write(row_index + 3, 0, header)
-    # Write holdup data to Excel
-    for col_index, value in enumerate(holdup_packing_df.values):
-        sheet_main.write(3, col_index + 1, col_index)
-        sheet_main.write(4, col_index + 1, value[0])
-    # Write classes headers to Excel
-    for row_index, header in enumerate(headers_d_classes):
-        sheet_main.write(row_index + 6, 0, header)
-    # Write classes data to Excel
-    for col_index in range(len(d_classes_final)):
-        sheet_main.write(6, col_index + 1, col_index + 1)
-    for col_index, value in enumerate(d_classes_final_df.values):
-        sheet_main.write(7, col_index + 1, value[0])
-    # Write DSD headers to Excel
-    for row_index, header in enumerate(headers_dsd):
-        sheet_main.write(row_index + 9, 0, header)
-    # Write DSD data to Excel
-    for col_index, value in enumerate(q0_initial_df.values):
-        sheet_main.write(9, col_index + 1, value[0])
-    for col_index, value in enumerate(q0_rlp_df.values):
-        sheet_main.write(10, col_index + 1, value[0])
-    for col_index, value in enumerate(q0_final_df.values):
-        sheet_main.write(11, col_index + 1, value[0])
-    for col_index, value in enumerate(q3_initial_df.values):
-        sheet_main.write(12, col_index + 1, value[0])
-    for col_index, value in enumerate(q3_rlp_df.values):
-        sheet_main.write(13, col_index + 1, value[0])
-    for col_index, value in enumerate(q3_final_df.values):
-        sheet_main.write(14, col_index + 1, value[0])
-    # Create a new Excel sheet for the contacts data
-    sheet_contacts = workbook.add_sheet('Contacts')
-    # Write contacts headers to Excel
-    for col_index, header in enumerate(header_average_contacts):
-        sheet_contacts.write(0, col_index, header)
-    # Write contacts data to Excel
-    for row_index, values in enumerate(average_contacts_df.values):
-        sheet_contacts.write(row_index + 1, 0, row_index)
-        for col_index, value in enumerate(values):
-            sheet_contacts.write(row_index + 1, col_index + 1, value)
-    # Iterate through the nested dataframe of contact probabilities
-    for sheet_index, sheet_data in enumerate(contact_probabilities_df.values):
-        # Create a new Excel sheet for each inner list
-        sheet_probabilities = workbook.add_sheet(f'Probabilities Step {sheet_index}')  
-        # Write probabilities headers to Excel
-        for col_index, header in enumerate(header_contact_probabilities):
-            sheet_probabilities.write(0, col_index + 1, header)
-            sheet_probabilities.write(col_index + 1, 0, header)
-        # Iterate through each sublist 
-        for row_index, sublist in enumerate(sheet_data):
-            # Write contact probabilities to Excel
-            for col_index, value in enumerate(sublist):
-                sheet_probabilities.write(row_index + 1, col_index + 1, value)
-    # Save the workbook to a file
-    file_name = os.path.join(ROOT_DIR, NAME_EXCEL_FILE)
-    workbook.save(file_name)
-    """ Graphically display particles in 3D """
-    if display_particles == True:
-        plot_particles(M_particle_final)
-    print('Particle growing simulation time:')
-    print("--- %s seconds ---" % (time.time() - start_time_growing))
-    print('Total simulation time:')
-    print("--- %s seconds ---" % (time.time() - start_time_rlp))
-    sss = 1
\ No newline at end of file