diff --git a/DCNumPro_1.0.py b/DCNumPro_1.0.py
new file mode 100644
index 0000000000000000000000000000000000000000..9c9917ea33d44dab7e46df67c46ccb0cea659596
--- /dev/null
+++ b/DCNumPro_1.0.py
@@ -0,0 +1,4132 @@
+""" 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