diff --git a/Local installation b/Local installation
index 3be5553700d0bea57a10af9c69ba2cd2421ec951..9faf06c4eb8eece00df4a16373b3638ebdbbd424 100644
--- a/Local installation	
+++ b/Local installation	
@@ -1,6 +1,7 @@
 # Install Spark:
 
 # Install scala
+
 sudo apt install scala
 # Download Spark from: https://spark.apache.org/downloads.html
 tar xvf spark-3.2.1-bin-hadoop3.2.tgz
@@ -16,6 +17,9 @@ sudo apt-get update
 sudo apt-get install -y mongodb-org
 
 # Start mongoDB
+
 sudo service mongod start
+
 # Check mongoDB status
+
 sudo service mongod status
diff --git a/VS2N.py b/VS2N.py
index 9a63eae6e93288d0df661b65858dc0677cfc6295..13671d2a0edb617fb96a74a2c02c7905ce7203bf 100755
--- a/VS2N.py
+++ b/VS2N.py
@@ -3,319 +3,317 @@ Main VS2N class
 
 """
 
+import _thread
+import hashlib
+import importlib
+import logging
+import os
+import sqlite3
+import time
+import traceback
+import webbrowser
+
 import dash
 import dash_bootstrap_components as dbc
 import flask
-import hashlib
-from flask import Flask, request, render_template
 import flask_login
-from werkzeug.serving import run_simple
+from flask import Flask, render_template, request
 from werkzeug.middleware.dispatcher import DispatcherMiddleware
+from werkzeug.serving import run_simple
+
 from src.Global_Var import Global_Var
 from src.Modules.General.layout import layout
-import os
-import traceback
-import sqlite3
-import importlib
-import time
-import _thread
-import webbrowser
-import logging
-
-# Print only errors in console ---------------------------------------
-log = logging.getLogger('werkzeug')
-log.setLevel(logging.ERROR)
-# --------------------------------------------------------------------
-modulesNbr = 0
-mongo_exists = False
-app_vis = None
-users = dict()
-g = Global_Var()
-__name__ = "__main__"
-url = g.config.VS2N_HOST
-port = g.config.VS2N_PORT
-
-# Test port availability ---------------------------------------------
-if(not g.testPort(port)):
-    port += 1
-    print("Loading another instance of VS2N")
-# --------------------------------------------------------------------
-
-# Create flask app instance ------------------------------------------
-flask_app = Flask(__name__, template_folder=os.path.curdir +
-                  "/src/templates", static_folder=os.path.curdir + "/src/static")
-flask_app.secret_key = g.config.SECRET_KEY
-
-login_manager = flask_login.LoginManager()
-login_manager.init_app(flask_app)
-login_manager.session_protection = "strong"
-
-# Flask user preperation ---------------------------------------------
-
-flask_app.config['LOGIN_DISABLED'] = False
-
-try:
-    conn = sqlite3.connect("info.db")
-    users = dict([u for u in conn.execute("SELECT * FROM users")])
-    conn.close()
-except Exception as e:
-    # If users database is not generated, disable login 
-    flask_app.config['LOGIN_DISABLED'] = True
 
 class User(flask_login.UserMixin):
-    pass
-
-# BDD connection -----------------------------------------------------
-mongo_exists = g.mongoConnect()
-# --------------------------------------------------------------------
-
-# Login manager ------------------------------------------------------
-
-
-@login_manager.unauthorized_handler
-def unauthorized_handler():
-    """ Redirect any unauthorized access to login screen.
-
-    Returns:
-        redirection to login
-    """
-    return flask.redirect(flask.url_for('login'))
-
-
-@flask_app.errorhandler(404)
-def page_not_found(e):
-    """ Handle the 404 error.
-
-    Returns:
-        redirection to login
-    """
-    return flask.redirect(flask.url_for('login'))
-
-
-@login_manager.user_loader
-def user_loader(email):
-    """ Load user from email address.
-
-    Args:
-        email (String): user email
-
-    Returns:
-        user instance
-    """
-    try:
-        users[email]
-    except Exception as e:
-        print("exception : user_loader")
-        return None
-
-    user = User()
-    user.id = email
-    return user
-
-
-@login_manager.request_loader
-def request_loader(request):
-    """ Process request for authentication.
-
-    Args:
-        request
-
-    Returns:
-        user instance
-    """
-    try:
-        email = request.form.get('email')
-        email = email if email == None else hashlib.sha512(
-            bytes(email, encoding='utf-8')).hexdigest()
-        users[email]
-    except Exception as e:
-        return
-    user = User()
-    user.id = email
-
-    user.is_authenticated = hashlib.sha512(
-        bytes(request.form['password'], encoding='utf-8')).hexdigest() == users[email]
-    return user
-
-# --------------------------------------------------------------------
-
-# VS2N Rooting -------------------------------------------------------
-
-
-@flask_app.route('/login', methods=['GET', 'POST'])
-def login():
-    """ Handle requests to '/login' page.
+            pass
 
-    Returns:
-        redirection to login if not connected otherwise to home screen.
-    """
-    if flask_app.config['LOGIN_DISABLED']:
-        return flask.redirect(flask.url_for('home'))
-    else:
-        if flask.request.method == 'GET':
-            return render_template('login.html')
+class VS2N():
+    
+    def __init__(self):
+        # Print only errors in console ---------------------------------------
+        log = logging.getLogger('werkzeug')
+        log.setLevel(logging.ERROR)
+        # --------------------------------------------------------------------
+        self.modulesNbr = 0
+        self.mongo_exists = False
+        self.app_vis = None
+        self.users = dict()
+        self.g = Global_Var()
+        self.url = self.g.config.VS2N_HOST
+        self.port = self.g.config.VS2N_PORT
+        
+        # Test port availability ---------------------------------------------
+        if(not self.g.testPort(self.port)):
+            self.port += 1
+            print("Loading another instance of VS2N")
+        # --------------------------------------------------------------------
+
+        # Create flask app instance ------------------------------------------
+        self.flask_app = Flask(__name__, template_folder=os.path.curdir +
+                        "/src/templates", static_folder=os.path.curdir + "/src/static")
+        self.flask_app.secret_key = self.g.config.SECRET_KEY
+
+        # Flask user preperation ---------------------------------------------
+
+        self.flask_app.config['LOGIN_DISABLED'] = False
 
-        email = hashlib.sha512(
-            bytes(flask.request.form['email'], encoding='utf-8')).hexdigest()
         try:
-            if users[email] == hashlib.sha512(bytes(flask.request.form['password'], encoding='utf-8')).hexdigest():
-                user = User()
-                user.id = email
-                flask_login.login_user(user)
-                return flask.redirect(flask.url_for('home'))
+            conn = sqlite3.connect("info.db")
+            self.users = dict([u for u in conn.execute("SELECT * FROM users")])
+            conn.close()
         except Exception as e:
-            print("Authentication failed !")
-            pass
-
-        return flask.redirect(flask.url_for('login'))
-
-
-@flask_app.route('/logout')
-def logout():
-    """ Logout from VS2N.
-
-    Returns:
-        redirection to login screen.
-    """
-    if flask_app.config['LOGIN_DISABLED']:
-        return flask.redirect(flask.url_for('home'))
-    else:
-        flask_login.logout_user()
-        return flask.redirect(flask.url_for('login'))
-
-
-@flask_app.route('/')
-def main():
-    """ Handle incoming requests to VS2N and check authentication status.
-
-    Returns:
-        redirection to home if user is authenticated otherwise to login screen.
-    """
-    if flask_app.config['LOGIN_DISABLED']:
-            return flask.redirect(flask.url_for('home'))
-    else:
-        if not flask_login.current_user.is_authenticated:
-            return render_template('login.html')
-        else:
-            return flask.redirect(flask.url_for('home'))
-
-
-@flask_app.route('/home')
-@flask_login.login_required
-def home():
-    """ Get existing modules and check MongoDB availability.
-
-    Returns:
-        main screen if everything is ok, otherwise show exception.
-    """
-    global mongo_exists
-
-    # init all variables
-    g.__init__()
-    # if MongoDB exist
-    if mongo_exists:
-        m = os.listdir(os.path.curdir + "/src/Modules")
-        simulations = []
-        g.db = g.client.list_database_names()
-
-        # Load existing simulations
-        for bdd in g.db:
-            if bdd != "local" and bdd != "admin" and bdd != "config":
-                simulations.append(bdd)
-        i = 1
-
-        # get modules names
-        modules = []
-        for module in m:
-            if ".pyc" not in module and "__" not in module and "General" not in module:
-                modules.append(module.rsplit(".", 1)[0])
-                i = i + 1
-        sparkVersion = g.createSparkSession()
-        # return main screen with list of modules and existing simulations
-        return render_template('index.html', modules=modules, Simulations=simulations, mongodb=g.client.server_info()["version"], vs2n=g.config.VS2N_VERSION, spark=sparkVersion)
-    else:
-        mongo_exists = g.mongoConnect()
-        return render_template('exception.html')
-
-
-def Module(n, g):
-    """ Helper function to execute each module sparks pre processing (if exist).
-
-    Args:
-        n (String): module name
-        g (Global_Var): reference to access global variables
-    """
-    importlib.import_module("src.Modules." + n + ".spark").init(g)
-
-
-def LoadingModules():
-    """ Run spark operation for the selected modules
-    """
-    try:
-        # Spark & MongoDB tasks launch
-        g.CreateIndexes(g.name)
-        for module in g.modules:
-            _thread.start_new_thread(Module, (module, g,))
-
-    except Exception as e:
-        print("LoadingModules:" + str(e))
-
-
-@flask_app.route('/processVis', methods=['POST'])
-@flask_login.login_required
-def processing():
-    """ Start processing and load modules into dashboard.
-    """
-    g.name = request.get_json()["name"]
-    g.modules = request.get_json()["modules"]
-    g.updateInterval = float(request.get_json()["updateInterval"])
-
-    g.modules.append("General")
-    g.modulesNbr = len(g.modules)
-
-    LoadingModules()
+            # If users database is not generated, disable login
+            self.flask_app.config['LOGIN_DISABLED'] = True
+
+        self.login_manager = flask_login.LoginManager()
+        self.login_manager.init_app(self.flask_app)
+        self.login_manager.session_protection = "strong"
+
+        # BDD connection -----------------------------------------------------
+        if __name__ == '__main__':
+            self.mongo_exists = self.g.mongoConnect()
+        # --------------------------------------------------------------------
+
+        # Create dash app instance -------------------------------------------
+        if __name__ == '__main__':
+            self.app_vis = dash.Dash(
+                __name__,
+                requests_pathname_prefix='/vis/',
+                external_stylesheets=[dbc.themes.BOOTSTRAP, dbc.icons.FONT_AWESOME],
+                suppress_callback_exceptions=True,
+                title="VS2N")
+            self.app_vis.enable_dev_tools(debug=self.g.config.DEBUG)
+
+        # Login manager ------------------------------------------------------
+
+        @self.login_manager.unauthorized_handler
+        def unauthorized_handler():
+            """ Redirect any unauthorized access to login screen
+
+            Returns:
+                redirection to login
+            """
+            return flask.redirect(flask.url_for('login'))
+
+
+        @self.flask_app.errorhandler(404)
+        def page_not_found(e):
+            """ Handle the 404 error
+
+            Returns:
+                redirection to login
+            """
+            return flask.redirect(flask.url_for('login'))
+
+
+        @self.login_manager.user_loader
+        def user_loader(email):
+            """ Load user from email address
+
+            Args:
+                email (String): user email
+
+            Returns:
+                user instance
+            """
+            try:
+                self.users[email]
+            except Exception as e:
+                print("exception : user_loader")
+                return None
+
+            user = User()
+            user.id = email
+            return user
+
+
+        @self.login_manager.request_loader
+        def request_loader(request):
+            """ Process request for authentication
+
+            Args:
+                request
+
+            Returns:
+                user instance
+            """
+            try:
+                email = request.form.get('email')
+                email = email if email == None else hashlib.sha512(
+                    bytes(email, encoding='utf-8')).hexdigest()
+                self.users[email]
+            except Exception as e:
+                return
+            user = User()
+            user.id = email
+
+            user.is_authenticated = hashlib.sha512(
+                bytes(request.form['password'], encoding='utf-8')).hexdigest() == self.users[email]
+            return user
+
+        # --------------------------------------------------------------------
+
+        # VS2N Rooting -------------------------------------------------------
+
+
+        @self.flask_app.route('/login', methods=['GET', 'POST'])
+        def login():
+            """ Handle requests to '/login' page
+
+            Returns:
+                redirection to login if not connected otherwise to home screen
+            """
+            if self.flask_app.config['LOGIN_DISABLED']:
+                return flask.redirect(flask.url_for('home'))
+            else:
+                if flask.request.method == 'GET':
+                    return render_template('login.html')
+
+                email = hashlib.sha512(
+                    bytes(flask.request.form['email'], encoding='utf-8')).hexdigest()
+                try:
+                    if self.users[email] == hashlib.sha512(bytes(flask.request.form['password'], encoding='utf-8')).hexdigest():
+                        user = User()
+                        user.id = email
+                        flask_login.login_user(user)
+                        return flask.redirect(flask.url_for('home'))
+                except Exception as e:
+                    print("Authentication failed !")
+                    pass
+
+                return flask.redirect(flask.url_for('login'))
+
+
+        @self.flask_app.route('/logout')
+        def logout():
+            """ Logout from VS2N.
+
+            Returns:
+                redirection to login screen.
+            """
+            if self.flask_app.config['LOGIN_DISABLED']:
+                return flask.redirect(flask.url_for('home'))
+            else:
+                flask_login.logout_user()
+                return flask.redirect(flask.url_for('login'))
 
-    while (g.modulesNbr != 0):
-        time.sleep(1)
-        continue
 
-    # Loading Modules to Dashboard
-    layout().load(app_vis, g)
-    return "done"
+        @self.flask_app.route('/')
+        def main():
+            """ Handle incoming requests to VS2N and check authentication status
 
-# Create dash app instance -------------------------------------------
+            Returns:
+                redirection to home if user is authenticated otherwise to login screen
+            """
+            if self.flask_app.config['LOGIN_DISABLED']:
+                return flask.redirect(flask.url_for('home'))
+            else:
+                if not flask_login.current_user.is_authenticated:
+                    return render_template('login.html')
+                else:
+                    return flask.redirect(flask.url_for('home'))
+
+
+        @self.flask_app.route('/home')
+        @flask_login.login_required
+        def home():
+            """ Get existing modules and check MongoDB availability
+
+            Returns:
+                main screen if everything is ok, otherwise show exception
+            """
+
+            # if MongoDB exist
+            if self.mongo_exists:
+                m = os.listdir(os.path.curdir + "/src/Modules")
+                simulations = []
+                self.g.db = self.g.client.list_database_names()
+
+                # Load existing simulations
+                for bdd in self.g.db:
+                    if bdd != "local" and bdd != "admin" and bdd != "config":
+                        simulations.append(bdd)
+                i = 1
+
+                # get modules names
+                modules = []
+                for module in m:
+                    if ".pyc" not in module and "__" not in module and "General" not in module:
+                        modules.append(module.rsplit(".", 1)[0])
+                        i = i + 1
+                sparkVersion = self.g.createSparkSession()
+                # return main screen with list of modules and existing simulations
+                return render_template('index.html', modules=modules, Simulations=simulations, mongodb=self.g.client.server_info()["version"], vs2n=self.g.config.VS2N_VERSION, spark=sparkVersion)
+            else:
+                self.mongo_exists = self.g.mongoConnect()
+                return render_template('exception.html')
+        
+        @self.flask_app.route('/processVis', methods=['POST'])
+        @flask_login.login_required
+        def processing():
+            """ Start processing and load modules into dashboard
+            """
+            self.g.name = request.get_json()["name"]
+            self.g.modules = request.get_json()["modules"]
+            self.g.updateInterval = float(request.get_json()["updateInterval"])
+
+            self.g.modules.append("General")
+            self.g.modulesNbr = len(self.g.modules)
+
+            self.LoadingModules()
+
+            while (self.g.modulesNbr != 0):
+                time.sleep(1)
+                continue
+
+            # Loading Modules to Dashboard
+            layout().load(self.app_vis, self.g)
+        
+    def Module(self, n, g):
+        """ Helper function to execute each module sparks pre processing (if exist)
+
+        Args:
+            n (String): module name
+            g (Global_Var): reference to access global variables
+        """
+        importlib.import_module("src.Modules." + n + ".spark").init(g)
+
+
+    def LoadingModules(self):
+        """ Run spark operation for the selected modules
+        """
+        try:
+            # Spark & MongoDB tasks launch
+            self.g.CreateIndexes(self.g.name)
+            for module in self.g.modules:
+                _thread.start_new_thread(self.Module, (module, self.g,))
 
-app_vis = dash.Dash(
-    __name__,
-    requests_pathname_prefix='/vis/',
-    external_stylesheets=[dbc.themes.BOOTSTRAP],
-    suppress_callback_exceptions=True,
-    title="VS2N")
+        except Exception as e:
+            print("LoadingModules:" + str(e))
 
-app_vis.enable_dev_tools(debug=g.config.DEBUG)
 
-# Start VS2N ---------------------------------------------------------
+    # Start VS2N ---------------------------------------------------------
 
-def start_VS2N():
-    """ Start the virtual server on the web browser .
 
-    Returns:
-        [type]: [description]
-    """
-    print("Starting VS2N")
-    app_vis.serve_reload_hash
+    def start_VS2N(self):
+        """ Start the virtual server on the web browser
 
-    webbrowser.open("http://"+url+":"+str(port))
-    return app_vis.server
+        Returns:
+            Dash server
+        """
+        print("Starting VS2N")
+        self.app_vis.serve_reload_hash
 
+        webbrowser.open("http://"+self.url+":"+str(self.port))
+        return self.app_vis.server
 
-application = DispatcherMiddleware(flask_app, {
-    '/vis': start_VS2N()})
+    # Start server -------------------------------------------------------
 
-# Start server -------------------------------------------------------
 
 if __name__ == '__main__':
     try:
-        run_simple(url, port, application, use_debugger=g.config.DEBUG)
+        vs2n = VS2N()
+        application = DispatcherMiddleware(vs2n.flask_app, {'/vis': vs2n.start_VS2N()})
+        run_simple(vs2n.url, vs2n.port, application, use_debugger=vs2n.g.config.DEBUG)
     except Exception as e:
-        traceback.print_exc()
\ No newline at end of file
+        traceback.print_exc()
diff --git a/__init__.py b/__init__.py
new file mode 100755
index 0000000000000000000000000000000000000000..0519ecba6ea913e21689ec692e81e9e4973fbf73
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1 @@
+ 
\ No newline at end of file
diff --git a/src/Global_Var.py b/src/Global_Var.py
index ce8d739856f5660c0e50e2576f6bed3570d7c9cc..747e8cb3474dcd68a908450b0d34de2a36c34df8 100755
--- a/src/Global_Var.py
+++ b/src/Global_Var.py
@@ -1,15 +1,17 @@
 """ This class contains global variables and functions.
 """
 
-from pyspark.sql import SparkSession
-from pymongo import MongoClient
+import socket
+import traceback
 from datetime import timedelta
-from pyspark import SparkConf
-from config import config
+
 import numpy as np
 import pymongo
-import socket
-import time
+from pymongo import MongoClient
+from pyspark import SparkConf
+from pyspark.sql import SparkSession
+
+from config import config
 
 
 class Global_Var():
@@ -27,10 +29,12 @@ class Global_Var():
     updateInterval = 1.0  # 1 second
     stepMax = 0
     Max = 0
+    nbrClasses = 0
 
     # General network information ------------------------------------
     LayersNeuronsInfo = []
     Layer_Neuron = None
+    NeuronsSize = {}
     NeuronsNbr = 0
     LayersNbr = 0
     Dataset = ""
@@ -46,9 +50,8 @@ class Global_Var():
         """
         self.stepMax = 0
         self.Max = 0
-
         self.LayersNeuronsInfo = []
-        self.NeuronsSize = {"x":0,"y":0}
+        self.NeuronsSize = {"x": 0, "y": 0}
         self.Layer_Neuron = None
         self.NeuronsNbr = 0
         self.LayersNbr = 0
@@ -81,11 +84,11 @@ class Global_Var():
                                           authMechanism='SCRAM-SHA-1')
                 self.MONGODBURL = config().MONGODBURL
 
-            self.client.server_info() # will throw an exception if mongodb is not detected
+            self.client.server_info()  # will throw an exception if mongodb is not detected
             self.db = self.client.list_database_names()
             return 1
-        except Exception as e:
-            print("mongoConnect:" + str(e))
+        except Exception:
+            print("mongoConnect:" + traceback.format_exc())
             return 0
 
     def checkExistance(self, app, component):
@@ -117,29 +120,33 @@ class Global_Var():
         if ('labels' in self.db.list_collection_names()):
             col = pymongo.collection.Collection(self.db, 'labels')
             col.create_index([("T", 1)])
-            print("Labels index done")
+            if self.config.DEBUG:
+                print("Labels index done")
 
         if ('spikes' in self.db.list_collection_names()):
             col = pymongo.collection.Collection(self.db, 'spikes')
             col.create_index([("T", 1)])
             col.create_index([("i.L", 1)])
             col.create_index([("i.N", 1)])
-            print("Spikes index done")
-        
+            if self.config.DEBUG:
+                print("Spikes index done")
+
         if ('potential' in self.db.list_collection_names()):
             col = pymongo.collection.Collection(self.db, 'potential')
             col.create_index([("T", 1)])
             col.create_index([("L", 1)])
             col.create_index([("N", 1)])
-            print("Potential index done")
-        
+            if self.config.DEBUG:
+                print("Potential index done")
+
         if ('synapseWeight' in self.db.list_collection_names()):
             col = pymongo.collection.Collection(self.db, 'synapseWeight')
             col.create_index([("T", 1)])
             col.create_index([("C", 1)])
             col.create_index([("L", 1)])
             col.create_index([("To", 1)])
-            print("Synapses index done")
+            if self.config.DEBUG:
+                print("Synapses index done")
 
     def getLabelTime(self, step, value):
         """ Return a string that represent time .
@@ -175,12 +182,13 @@ class Global_Var():
 
         return x, y
 
-    def createHeatMap(self, x, y, data, depth):
+    def createHeatMap(self, x, y, data, depth, rotation):
         """ Create a heatmap from a set of synapses values .
 
         Args:
             x , y (int): shape 2D dimensions
             data (Array): synapses values
+            rotation: if to rotate the heatmap 90°
 
         Returns:
             Array: Heatmap vector
@@ -189,22 +197,24 @@ class Global_Var():
             heatmap = np.zeros((x, y))
             heatmap[:] = -1
             data = data.to_numpy()
-            
-            if(depth == 0): # single depth
+            if(depth == 0):  # single depth
                 for d in data:
-                    heatmap[int(d[0])][int(d[1])] = d[3]
-            else: # multiple dimensions
+                    if rotation:
+                        heatmap[int(d[0])][int(d[1])] = d[3]
+                    else:
+                        heatmap[int(d[1])][int(d[0])] = d[3]
+            else:  # multiple dimensions
                 for d in data:
                     if(d[2] == 0):
-                    #if(heatmap[int(d[0])][int(d[1])] == -1):
-                        heatmap[int(d[0])][int(d[1])] = d[3]
-                    #else:
-                    #    heatmap[int(d[0])][int(d[1])] = np.mean(heatmap[int(d[0])][int(d[1])]+d[3])
+                        if rotation:
+                            heatmap[int(d[0])][int(d[1])] = d[3]
+                        else:
+                            heatmap[int(d[1])][int(d[0])] = d[3]
 
             return heatmap
-        
-        except Exception as e:
-            print("createHeatMap: "+str(e))
+
+        except Exception:
+            print("createHeatMap: " + traceback.format_exc())
 
     def createVerticalHeatMap(self, data):
         """ Create a vertical heat map from a given data array .
@@ -246,7 +256,7 @@ class Global_Var():
             heatMapsZ = [(v * 100)/maxHeatMaps for v in heatMapsZ]
             return [heatMapsZ, heatMapsZLabel]
         else:
-            return [heatMapsZ,heatMapsZ]
+            return [heatMapsZ, heatMapsZ]
 
     def norm(data, Max):
         """ Normalize data to be between 0 and 1 .
@@ -275,14 +285,13 @@ class Global_Var():
             self.sparkSession = SparkSession.builder.config(conf=conf) \
                 .config('spark.jars.packages', 'org.mongodb.spark:mongo-spark-connector_2.12:2.4.4') \
                 .getOrCreate()
-            
+
             if not self.config.DEBUG:
                 self.sparkSession.sparkContext.setLogLevel("Error")
             return self.sparkSession.version
-        except Exception as e:
-            print("createSparkSession:" + str(e))
+        except Exception:
+            print("createSparkSession:" + traceback.format_exc())
             return ""
-        
 
     def testPort(self, port):
         """Test if a given port is currently in use .
diff --git a/src/Modules/General/callbacks.py b/src/Modules/General/callbacks.py
index 4aa190e2689ff57baf385cd0a58a90e1b3293ca2..38c1a476e1af7b8de5e22067e918f74569da6f88 100755
--- a/src/Modules/General/callbacks.py
+++ b/src/Modules/General/callbacks.py
@@ -4,14 +4,13 @@
 """
 
 from collections import deque
-import dash
 import pymongo
+import traceback
 from bson.json_util import dumps
 from bson.json_util import loads
 import plotly.graph_objects as go
-from dash.exceptions import PreventUpdate
+from dash import (no_update, Input, Output, State, ALL, callback_context)
 from plotly.subplots import make_subplots
-from dash.dependencies import Input, Output, State, MATCH
 from src.templates.callbacksOp import callbacksOp
 
 class callbacks(callbacksOp):
@@ -208,8 +207,8 @@ class callbacks(callbacksOp):
                         
                         return fig
 
-                    except Exception as e:
-                        print("processGeneralGraph "+str(e))
+                    except Exception:
+                        print("processGeneralGraph " + traceback.format_exc())
 
                 def processLabelInfoTreemap(data):
                     """ Return a Treemap visualization of actual inputs
@@ -252,8 +251,8 @@ class callbacks(callbacksOp):
                                 uirevision='no reset of zoom',
                                 margin={'l': 0, 'r': 0, 't': 30, 'b': 0},
                             )}
-                    except Exception as e:
-                        print("processLabelInfoTreemap:"+str(e))
+                    except Exception:
+                        print("processLabelInfoTreemap:"+ traceback.format_exc())
 
                 # ----------------------------------------------------
                 # Callbacks
@@ -286,7 +285,7 @@ class callbacks(callbacksOp):
                         array of outputs that are selected in the callback
                     """
                     try:
-                        context = dash.callback_context.triggered[0]['prop_id'].split('.')[
+                        context = callback_context.triggered[0]['prop_id'].split('.')[
                             0]
                         # update interval value if changed
                         if(g.updateInterval != float(updateInterval)):
@@ -330,7 +329,7 @@ class callbacks(callbacksOp):
                                 super.clearData()
                                 clearGraphs = not clearGraphs
                             else:
-                                if context == "btn-next" and dash.callback_context.triggered[0]['value'] != None:
+                                if context == "btn-next" and callback_context.triggered[0]['value'] != None:
                                     if(sliderValue < g.stepMax):
                                         sliderValue = sliderValue + 1
 
@@ -364,8 +363,8 @@ class callbacks(callbacksOp):
 
                                 return [super.label, sliderValue, 1, g.stepMax, sliderValue, clearGraphs, g.updateInterval]
 
-                    except Exception as e:
-                        print("progress:" + str(e))
+                    except Exception:
+                        print("progress:" + traceback.format_exc())
                 # Callback to controle play/stop button
                 @app.callback(
                     [Output("btnControle", "children"), Output("btnControle", "className"),Output("vis-update", "disabled")],
@@ -374,6 +373,7 @@ class callbacks(callbacksOp):
                     """ This is the callback function. It is called when play/stop button is clicked.
 
                     Args:
+                        visUpdateInterval : interval instance that will cause this function to be called each step
                         playButton (int): number of clicks on the start/stop button
                         sliderValue (int): value of the slider
                         playButtonText (String): text on the start/stop button
@@ -381,7 +381,7 @@ class callbacks(callbacksOp):
                     Returns:
                         array of outputs that are selected in the callback
                     """
-                    if dash.callback_context.triggered[0]['prop_id'].split('.')[0] == "btnControle":
+                    if callback_context.triggered[0]['prop_id'].split('.')[0] == "btnControle":
                         if playButtonText == "Start":
                             if(int(g.stepMax) <= sliderValue):
                                 super.visStopped = True
@@ -394,13 +394,13 @@ class callbacks(callbacksOp):
                                 super.visStopped = True
                                 return ["Start", "btn btn-success", True]
                             else:
-                                raise PreventUpdate
+                                return no_update
                     else:
                         if(int(g.stepMax) <= sliderValue):
                                 super.visStopped = True
                                 return ["Start", "btn btn-success", True]
                         else:
-                            raise PreventUpdate
+                            return no_update
 
                 # Callback to handle general graph content
                 @app.callback(
@@ -418,14 +418,14 @@ class callbacks(callbacksOp):
                         generalGraphSwitchIsOn (bool): general graph switch value
 
                     Raises:
-                        PreventUpdate: in case we don't want to update the content we rise this execption 
+                        no_update: in case we don't want to update the content we rise this execption 
 
                     Returns:
                         content of the graph that contains general information on the network activity
                     """
                     if generalGraphSwitchIsOn:
                         if len(super.xAxisLabel) > 0 and "["+g.getLabelTime(g.updateInterval, sliderValue)+","+g.getLabelTime(g.updateInterval, sliderValue+1)+"]" == super.xAxisLabel[-1]:
-                            raise PreventUpdate
+                            return no_update
 
                         if(not super.visStopped):
                             generalData = GeneralModuleData(
@@ -442,7 +442,7 @@ class callbacks(callbacksOp):
 
                         else:
                             if(sliderValue > g.stepMax):
-                                raise PreventUpdate
+                                return no_update
                             else:
                                 generalData = GeneralModuleData(
                                     int(sliderValue)*float(updateInterval), generalGraphFilter, generalLayerFilter)
@@ -450,7 +450,7 @@ class callbacks(callbacksOp):
                                     generalData, int(sliderValue), generalGraphFilter, generalLayerFilter)
                                 return [generalGraph]
                     else:
-                        raise PreventUpdate
+                        return no_update
 
                 # Callback to handle label graph content
                 @app.callback(
@@ -495,7 +495,7 @@ class callbacks(callbacksOp):
                                 labelData)
                             return [labelInfoTreemap]
                     else:
-                        raise PreventUpdate
+                        return no_update
 
                 # Callback to update the speed of visualization
                 @app.callback(
@@ -513,12 +513,12 @@ class callbacks(callbacksOp):
                     try:
                         if(speedValue < 0.25):
                            speedValue = 0.25
-                        if (speedValue > 5):
-                           speedValue = 5
+                        if (speedValue > 120):
+                           speedValue = 120
 
                         return [speedValue * 1000,speedValue]
-                    except Exception as e:
-                        print("speedControle:" + str(e))
+                    except Exception:
+                        print("speedControle:" + traceback.format_exc())
 
                 # Callback to handle information tab (open or close)
                 @app.callback(
@@ -535,15 +535,109 @@ class callbacks(callbacksOp):
                         if information tab should be opened or closed
                     """
                     try:
-                        if dash.callback_context.triggered[0]["value"] != None:
+                        if callback_context.triggered[0]["value"] != None:
                             return [not isTabOpen]
                         else:
                             return [isTabOpen]
-                    except Exception as e:
-                        print("informationTabController:" + str(e))
+                    except Exception:
+                        print("informationTabController:" + traceback.format_exc())
+
+                # Callback to handle the 2D view spiking visualization
+                @app.callback(
+                    Output("cytoscape-compound", "elements"),Output('spikes_info', 'children'),Output({"index": ALL, "type": '2DView-heatmap'},'figure'),Output({"index": ALL, "type": 'SpikesActivityPerInput'},'figure'),
+                    Input("vis-update", "n_intervals"),Input("v-step", "children"),Input('cytoscape-compound', 'mouseoverNodeData'),
+                    State("interval", "value"),State('cytoscape-compound', 'elements'),State("2DViewLayerFilter", "value"))
+                def animation2DView(visUpdateInterval,sliderValue, mouseOverNodeData, updateInterval, elements, Layer2DViewFilter):
+                    """ Function called each step to update the 2D view
 
-            except Exception as e:
-                print("Done loading:"+str(e))
+                    Args:
+                        sliderValue (int): value of the slider
+                        updateInterval (int): update Interval (s)
+                        visUpdateInterval : interval instance that will cause this function to be called each step
+                        mouseOverNodeData : contains data of the hovered node
+                        elements : nodes description
+                        heatmapData : heatmap data
+                        Layer2DViewFilter : selected layers
+
+                    Returns:
+                        if information tab should be opened or closed
+                    """
+                    try:
+                        elements = super.Spikes2D
+                        matrix = {}
+                        indices = {}
+                        if callback_context.triggered[0]['prop_id'].split('.')[0] in ["v-step","vis-update"]:
+                            super.SpikesActivityPerInput = {i:[[0 for j in range(g.nbrClasses+1)] for i in range(g.Layer_Neuron[i])] for i in g.Layer_Neuron if i != "Input"}
+                            for element in elements:
+                                    if element["data"]['spiked'] != -1:
+                                        element["data"]["spiked"] = 0
+                                        element["data"]["spikes"] = 0
+
+                            spikes = getSpike(int(sliderValue)*float(updateInterval), g.updateInterval,Layer2DViewFilter,True)
+                            
+                            for layer in Layer2DViewFilter:
+                                #neurons = [[0 for j in range(g.nbrClasses)] for i in range(g.Layer_Neuron[layer])]
+                                try:
+                                    layerSpikes = [list(list(list(s.values())[0].values())[0].values())[0] for s in spikes if list(s.keys())[0] == layer]
+                                except Exception:
+                                    layerSpikes = []
+
+                                if spikes and layerSpikes:
+                                    maxSpike = max(layerSpikes) 
+                                    for spike in spikes:
+                                        if list(spike.keys())[0] == layer:
+                                            # update the spikes neurons
+                                            i = 0
+                                            for element in elements:
+                                                if element["data"]['spiked'] != -1:
+                                                    if (element["data"]["id"] == layer+str(list(list(spike.values())[0].keys())[0])) and (element["data"]["label"] == str(list(list(spike.values())[0].keys())[0])):
+                                                        spk = list(list(list(spike.values())[0].values())[0].values())[0]
+                                                        element["data"]["spiked"] = round(spk / maxSpike,2)
+                                                        element["data"]["spikes"] = spk
+                                                        super.AccumulatedSpikes2D[layer][int(element["data"]["label"])] += spk
+                                                        super.SpikesActivityPerInput[layer][list(list(spike.values())[0].keys())[0]][list(list(list(spike.values())[0].values())[0].keys())[0]] = spk
+                                                    i+=1
+
+                                matrix[layer] = super.toMatrix(super.AccumulatedSpikes2D[layer])
+                                indices[layer] = super.toMatrix([i for i in range(0,len(super.AccumulatedSpikes2D[layer]))])
+
+
+
+                            if len(Layer2DViewFilter) != len(super.AccumulatedSpikes2D):
+                                for layer in super.AccumulatedSpikes2D:
+                                    if layer not in matrix:
+                                        matrix[layer] = []
+                                        indices[layer] = []
+
+                            heatmaps = [{"data":[go.Heatmap(z = matrix[layer], colorscale= 'Reds', customdata = indices[layer], hovertemplate=('Neuron: %{customdata} <br>Spikes: %{z} <extra></extra>'),xgap=4,ygap=4)],"layout":{"xaxis":dict(showgrid = False, zeroline = False),"yaxis":dict(autorange = 'reversed',scaleanchor = 'x',showgrid = False, zeroline = False),"margin":{'l': 0, 'r': 0, 't': 5, 'b': 0},"uirevision":'no reset of zoom', "hoverlabel_align": 'right'}} for layer in super.AccumulatedSpikes2D]
+
+                            SpikesActivityPerInput = [{"data":[go.Heatmap(z=super.SpikesActivityPerInput[layer], colorscale= 'Reds',hovertemplate=('Class: %{x} <br>Neuron: %{y} <br>Spikes: %{z} <extra></extra>'),xgap=4,ygap=4)],"layout":{"xaxis":dict(title="Class",tickmode="array",zeroline = False,tickvals=[i for i in range(g.nbrClasses+1)]),"yaxis":dict(title="Neuron",zeroline = False),"margin":{'r': 0,'t': 0},"uirevision":'no reset of zoom'}} for layer in super.SpikesActivityPerInput]
+                            return [elements,[],heatmaps,SpikesActivityPerInput]
+                        else:
+                            
+                            try:
+                                for layer in Layer2DViewFilter:
+                                    matrix[layer] = super.toMatrix(super.AccumulatedSpikes2D[layer])
+                                    indices[layer] = super.toMatrix([i for i in range(0,len(super.AccumulatedSpikes2D[layer]))])
+
+                                if len(Layer2DViewFilter) != len(super.AccumulatedSpikes2D):
+                                    for layer in super.AccumulatedSpikes2D:
+                                        if layer not in matrix:
+                                            matrix[layer] = []
+                                            indices[layer] = []
+
+                                heatmaps = [{"data":[go.Heatmap(z = matrix[layer], colorscale= 'Reds', customdata = indices[layer], hovertemplate=('Neuron: %{customdata} <br>Spikes: %{z} <extra></extra>'),xgap=4,ygap=4)],"layout":{"xaxis":dict(showgrid = False, zeroline = False),"yaxis":dict(autorange = 'reversed',scaleanchor = 'x',showgrid = False, zeroline = False),"margin":{'l': 0, 'r': 0, 't': 5, 'b': 0},"uirevision":'no reset of zoom', "hoverlabel_align": 'right'}} for layer in super.AccumulatedSpikes2D]
+
+                                SpikesActivityPerInput = [{"data":[go.Heatmap(z=super.SpikesActivityPerInput[layer], colorscale= 'Reds',hovertemplate=('Class: %{x} <br>Neuron: %{y} <br>Spikes: %{z} <extra></extra>'),xgap=4,ygap=4)],"layout":{"xaxis":dict(title="Class",tickmode="array",zeroline = False,tickvals=[i for i in range(g.nbrClasses+1)]),"yaxis":dict(title="Neuron",zeroline = False),"margin":{'r': 0,'t': 0},"uirevision":'no reset of zoom'}} for layer in super.SpikesActivityPerInput]
+                                return [elements,f"Neuron {mouseOverNodeData['label']} : {mouseOverNodeData['spikes']}" if 'spikes' in mouseOverNodeData else "", heatmaps,SpikesActivityPerInput]
+                            except Exception:
+                                print("OnHover:"+traceback.format_exc())
+                                return no_update
+                    except Exception:
+                        print("animation2DViewController:" + traceback.format_exc())
+
+            except Exception:
+                print("Done loading:"+traceback.format_exc())
 
         try:
 
@@ -577,9 +671,9 @@ class callbacks(callbacksOp):
                 for f in filter:
                     if f == "Spikes":
                         if res == []:
-                            res = [getSpike(timestamp, g.updateInterval,layers)]
+                            res = [getSpike(timestamp, g.updateInterval,layers,False)]
                         else:
-                            res.append(getSpike(timestamp, g.updateInterval,layers))
+                            res.append(getSpike(timestamp, g.updateInterval,layers,False))
                     if f == "Synapses":
                         if res == []:
                             res = [getSynapse(timestamp, g.updateInterval,layers)]
@@ -621,7 +715,7 @@ class callbacks(callbacksOp):
                     }},
                     {"$group": {"_id": "$L", "C": {"$sum": 1}, "G": {"$max": "$G"}}},
                     {"$sort": {"_id": 1}}
-                ])
+                ], allowDiskUse = True)
 
                 # ToJson ---------------------------------------------
                 labels = loads(dumps(labels))
@@ -631,40 +725,47 @@ class callbacks(callbacksOp):
                 for i in labels:
                     Max = max(Max, i["G"])
 
-                L = dict({i["_id"]: 0 for i in labels})
-
                 if not labels:
                     return None
 
-                for i in labels:
-                    L[i["_id"]] = L[i["_id"]] + i["C"]
+                L = dict({i["_id"]: i["C"] for i in labels})
 
                 return [L, Max]
 
-            def getSpike(timestamp, interval, layer):
+            def getSpike(timestamp, interval, layer, perNeuron):
                 """ Get spikes activity in a given interval.
 
                 Args:
                     timestamp (int): timestamp value
                     interval (int): interval value
                     layers (array): array of selected layers
+                    perNeuron (boolean): return global or perNeuron Spikes
 
                 Returns:
                     array contains spikes
                 """
                 # MongoDB---------------------
                 col = pymongo.collection.Collection(g.db, 'spikes')
-                spikes = col.aggregate([
+
+                if perNeuron:
+                    spikes = col.aggregate([
+                                        {"$match": {"$and": [{"T": {'$gt': timestamp, '$lte': (timestamp+interval)}},{"i.L": {'$in': layer}}]}},
+                                        {"$group": {"_id": {"L":"$i.L","N":"$i.N","Input":"$Input"},"spikes": {"$sum":1}}},{"$sort": {"_id": 1}}
+                                       ], allowDiskUse = True)
+                else:
+                    spikes = col.aggregate([
                                         {"$match": {"$and": [{"T": {'$gt': timestamp, '$lte': (timestamp+interval)}},{"i.L": {'$in': layer}}]}},
                                         {"$group": {"_id": "$i.L","spikes": {"$sum":1}}},{"$sort": {"_id": 1}}
-                                       ])
-                    
-                # ----------------------------
+                                       ], allowDiskUse = True)
 
                 # ToJson----------------------
                 spikes = loads(dumps(spikes))
                 # ----------------------------
-                spikes = {s["_id"]:s for s in spikes}
+                if perNeuron:
+                    spikes = [{s["_id"]["L"]:{s["_id"]["N"]:{s["_id"]["Input"]:s["spikes"]}}} for s in spikes]
+                else:
+                    spikes = {s["_id"]:s for s in spikes}
+                
                 if not spikes:
                     return None
 
@@ -686,7 +787,7 @@ class callbacks(callbacksOp):
                 synapse = col.aggregate([
                                         {"$match": {"$and": [{"T": {'$gt': timestamp, '$lte': (timestamp+interval)}},{"L": {'$in': layer}}]}},
                                         {"$group": {"_id": "$L","synapseUpdate": {"$sum":1}}},{"$sort": {"_id": 1}}
-                                       ])
+                                       ], allowDiskUse = True)
                 
                 # ----------------------------
 
@@ -715,7 +816,7 @@ class callbacks(callbacksOp):
                 potential = col.aggregate([
                                         {"$match": {"$and": [{"T": {'$gt': timestamp, '$lte': (timestamp+interval)}},{"L": {'$in': layer}}]}},
                                         {"$group": {"_id": "$L","potential": {"$sum":1}}},{"$sort": {"_id": 1}}
-                                       ])
+                                       ], allowDiskUse = True)
                 # ----------------------------
 
                 # ToJson----------------------
@@ -764,5 +865,5 @@ class callbacks(callbacksOp):
                 return min(100,round((loss*100) / len(spikes), 2))
             # ---------------------------------------------------------------------
 
-        except Exception as e:
-            print("Helper functions and MongoDB operations: "+str(e))
+        except Exception:
+            print("Helper functions and MongoDB operations: "+ traceback.format_exc())
\ No newline at end of file
diff --git a/src/Modules/General/layout.py b/src/Modules/General/layout.py
index 72f7c1b5ef54fd7b35cfb41a75bff47e219d7a10..db02ae245e43c2c78e97128431e90463f7f1e8ca 100755
--- a/src/Modules/General/layout.py
+++ b/src/Modules/General/layout.py
@@ -2,6 +2,8 @@
 """
 
 import importlib
+import traceback
+import math
 import dash_daq as daq
 from collections import deque
 import dash_cytoscape as cyto
@@ -28,12 +30,13 @@ class layout(layoutOp):
     MaxSpike = dict()
     MaxPotential = dict()
     MaxSynapse = dict()
+    AccumulatedSpikes2D = []
+    SpikesActivityPerInput = []
+    Spikes2D = dict()
     # LabelPie Data --------------------------------------------------
     Label = [[], []]
     Max = 0
     # ----------------------------------------------------------------
-    Nodes = []
-    Edges = []
     tabs = []
     label = " "
     visStopped = True
@@ -43,26 +46,29 @@ class layout(layoutOp):
 
     # 2D view --------------------------------------------------------
 
-    def generate2DView(self, Nodes, Edges, g):
+    def generate2DView(self, g):
         """ Generates a 2D View of the neural network
 
         Args:
-            Nodes (list): neurons list
-            Edges (list): synapses list
             g (Global_Var): reference to access global variables
         """
+        Nodes = []
         # Create the neurones and layers
         for L in g.LayersNeuronsInfo:
-            Nodes.append({
-                'data': {'id': L["layer"], 'label': L["layer"]}
-            })
+            Nodes.append({'data': {'id': L["layer"], 'label': L["layer"], 'spiked': -1}})
             for i in range(L["neuronNbr"]):
-                Nodes.append(
-                    {'data': {'id': L["layer"]+str(i), 'label': str(i), 'parent': L["layer"]}, 'position': {'x': 25*i, 'y': 0}})
+                Nodes.append({'data': {'id': L["layer"]+str(i), 'label': str(i), 'parent': L["layer"], 'spiked': 0.0, 'spikes': 0},'position': {'x': (i % int(math.sqrt(L["neuronNbr"]))) * 70, 'y': (i // int(math.sqrt(L["neuronNbr"]))) * 70},'height': 20,'width': 20})
         
         # Add connections
-        
-
+        return Nodes
+    
+    def toMatrix(self, l):
+        """ 1D array to 2D
+        """
+        n = int(math.sqrt(len(l)))
+        Matrix = [l[i:i+n] for i in range(0, len(l), n)]
+        return Matrix
+    
     def clearData(self):
         """ Clear the data when moved forward or backward for more than one step
         """
@@ -73,12 +79,13 @@ class layout(layoutOp):
         self.PotentialGraphY.clear()
         self.xAxisLabel.clear()
         self.Label.clear()
-        self.MaxPotential = dict()
-        self.MaxSpike = dict()
-        self.MaxSynapse = dict()
+        self.MaxPotential.clear()
+        self.MaxSpike.clear()
+        self.MaxSynapse.clear()
+        self.Spikes2D = self.generate2DView(self.g)
+        self.AccumulatedSpikes2D = {i:[0 for n in self.Spikes2D if n["data"]["spiked"] != -1 and i == n["data"]["parent"]] for i in self.g.Layer_Neuron if i != "Input"}
+        self.SpikesActivityPerInput = {i:[[0 for j in range(self.g.nbrClasses+1)] for i in range(self.g.Layer_Neuron[i])] for i in self.g.Layer_Neuron if i != "Input"}
         self.Max = 0
-        self.Nodes = []
-        self.Edges = []
 
     def Vis(self):
         """ Create layer components
@@ -102,9 +109,6 @@ class layout(layoutOp):
                 "Accuracy", style={"width": "25%", "fontWeight": "500"}), html.Td(str(self.g.Accuracy)+" %", style={"width": "25%"})])
         ]
 
-        # Generate 2D View -------------------------------------------
-        self.generate2DView(self.Nodes, self.Edges, self.g)
-
         # Tabs content -----------------------------------------------
         info_vis = dbc.Card(
             dbc.CardBody([dbc.Card([dbc.CardHeader(
@@ -126,13 +130,15 @@ class layout(layoutOp):
                                 html.Div([html.Div([
                                     html.Div([
                                         html.Div([
-                                            html.Div([daq.PowerButton(
-                                                id="general-graph-switch",
-                                                on='True',
-                                                size=30,
-                                                color="#28a745",
-                                                style={"marginLeft": "10px"}
-                                            ),
+                                                html.Div([daq.PowerButton(
+                                                    id="general-graph-switch",
+                                                    on='True',
+                                                    size=30,
+                                                    color="#28a745",
+                                                    style={"marginLeft": "10px"}
+                                                ),
+                                                html.P("Graphs: ", style={
+                                                   "textAlign": "start", "marginLeft": "10px", "marginTop": "4px"}),
                                                 # Graphs filter
                                                 dcc.Dropdown(
                                                 id='GeneralGraphFilter',
@@ -140,65 +146,142 @@ class layout(layoutOp):
                                                     'label': "Neurons potential", 'value': "Potentials"}],
                                                 value=["Spikes","Synapses","Potentials"],
                                                 multi=True,
-                                                style={"minWidth": "20%", "marginLeft": "10px", "textAlign": "start"}),
+                                                style={"minWidth": "20%", "marginLeft": "5px", "textAlign": "start"}),
                                                 # Layers filter
+                                                html.P("Layers: ", style={
+                                                   "textAlign": "start", "marginLeft": "20px", "marginTop": "4px"}),
                                                 dcc.Dropdown(
                                                 id='GeneralLayerFilter',
                                                 options=[{'label': str(i), 'value': str(i)} for i in (
-                                                    i for i in self.g.Layer_Neuron if ("Input" not in i and "pool" not in i))],
+                                                    i for i in self.g.Layer_Neuron if i != "Input")],
                                                 value=[str(i) for i in (
-                                                    i for i in self.g.Layer_Neuron if ("Input" not in i and "pool" not in i))],
+                                                    i for i in self.g.Layer_Neuron if i != "Input")],
                                                 multi=True,
-                                                style={"minWidth": "20%", "marginLeft": "15px", "textAlign": "start"})], className="d-flex", style={"paddingLeft": "20px", 'width': '100%'})
+                                                style={"minWidth": "20%","marginLeft": "5px", "textAlign": "start"})], className="d-flex", style={"paddingLeft": "20px", 'width': '100%'})
                                         ], className="col-12")
                                     ], className="d-flex"),
                                     html.Div([dcc.Graph(id='general-graph', config={"displaylogo": False})])], className="col-lg-9 col-sm-12 col-xs-12" if(self.g.labelsExistance) else "col-lg-12 col-sm-12 col-xs-12"),
                                     html.Div([
                                         html.Div([
-                                            html.P("Inputs", style={
-                                                   "textAlign": "start", "marginLeft": "20px", "marginTop": "4px"}),
                                             daq.PowerButton(
                                                 id="label-graph-switch",
                                                 on='True',
                                                 size=30,
                                                 color="#28a745",
-                                                style={"marginLeft": "10px"}
-                                            )], className="d-flex"),
+                                                style={"marginLeft": "20px"}
+                                            ),
+                                            html.P("Inputs", style={"textAlign": "start", "marginLeft": "10px", "marginTop": "4px"})], className="d-flex"),
                                         dcc.Graph(id='label-graph', config={"displaylogo": False})], className="col-lg-3 col-sm-12 col-xs-12") if(self.g.labelsExistance) else []], className="row")
                             ], style={"padding": "5px"})), label="General information", value="General information"),
+                        # 2D view
                         dcc.Tab(dbc.Card(
                             dbc.CardBody([
-
+                                html.Div([
+                                        # Layers filter
+                                        html.P("Layers: ", style={
+                                                   "textAlign": "start", "marginRight": "10px", "marginTop": "4px"}),
+                                        dcc.Dropdown(
+                                        id='2DViewLayerFilter',
+                                        options=[{'label': str(i), 'value': str(i)} for i in (
+                                            i for i in self.g.Layer_Neuron if i != "Input")],
+                                        value=[str(i) for i in (
+                                            i for i in self.g.Layer_Neuron if i != "Input")],
+                                        multi=True,
+                                        style={"minWidth": "80%", "textAlign": "start"}),
+                                    ], style={"textAlign": "start", },className="d-flex col-lg-12 col-sm-12 col-xs-12"),
+                                html.Div([
+                                            html.Div([html.P("Accumulated Spikes", style={"margin":"0px"})]),
+                                            # Accumulated Spikes HeatMap
+                                            dcc.Tabs([dcc.Tab(dbc.Card(dbc.CardBody([
+                                                    dcc.Graph(id={"type":"2DView-heatmap","index":i}, config={"displaylogo": False})
+                                                ])),label=i, value='2Dview-'+str(x)) for x, i in enumerate(self.g.Layer_Neuron) if i != "Input"],value="2Dview-1"),
+                                            ], style={"textAlign": "start", }, className="col-lg-3 col-sm-12 col-xs-12")
+                                ,
                                 html.Div(
                                     [
+                                        html.Div([html.P("2D Space", style={"margin":"0px"})]),
+                                        html.Div([
                                         cyto.Cytoscape(
                                             id='cytoscape-compound',
-                                            layout={'name': 'grid'},
+                                            layout={'name': 'preset'},
+                                            boxSelectionEnabled=False,
                                             style={'width': '100%',
                                                    'height': '100%'},
                                             stylesheet=[
                                                 {
                                                     'selector': 'node',
-                                                    'style': {'content': 'data(label)'}
+                                                    'style': {'label': 'data(label)'}
+                                                },                                          
+                                                {
+                                                    'selector': '[spiked <= 1.0]',
+                                                    'style': {
+                                                        'background-color': 'rgb(227,70,70)',
+                                                        'height': 45,
+                                                        'width': 45
+                                                    }
                                                 },
                                                 {
-                                                    'selector': '.layers',
-                                                    'style': {'width': 5}
+                                                    'selector': '[spiked < 0.8]',
+                                                    'style': {
+                                                        'background-color': 'rgb(227,100,100)',
+                                                        'height': 40,
+                                                        'width': 40
+                                                    }
+                                                }, 
+                                                {
+                                                    'selector': '[spiked < 0.6]',
+                                                    'style': {
+                                                        'background-color': 'rgb(227,130,130)',
+                                                        'height': 35,
+                                                        'width': 35
+                                                    }
+                                                }, 
+                                                {
+                                                    'selector': '[spiked < 0.4]',
+                                                    'style': {
+                                                        'background-color': 'rgb(227,160,160)',
+                                                        'height': 30,
+                                                        'width': 30
+                                                    }
+                                                }, 
+                                                {
+                                                    'selector': '[spiked < 0.2]',
+                                                    'style': {
+                                                        'background-color': 'rgb(227,190,190)',
+                                                        'height': 25,
+                                                        'width': 25
+                                                    }
+                                                },  
+                                                {
+                                                    'selector': '[spiked = 0.0]',
+                                                    'style': {
+                                                        'background-color': 'rgb(199,197,197)',
+                                                        'height': 20,
+                                                        'width': 20
+                                                    }
                                                 },
                                                 {
-                                                    'selector': '.neurons',
-                                                    'style': {'line-style': 'dashed'}
+                                                    'selector': '[spiked = -1]',
+                                                    'style': {
+                                                        'background-color': 'rgb(227,227,227)'
+                                                    }
                                                 }
                                             ],
-                                            elements=self.Nodes + self.Edges
-                                        )
+                                            elements=self.Spikes2D
+                                        )],style={'position': 'absolute','width': '100%','height': '100%','z-index': 999,"background": "rgba(68, 71, 99, 0.05)"}),
+                                        html.P(id="spikes_info", style={"padding": "8px","margin":"0px"})
 
-                                    ], style={"background": "rgb(227, 245, 251)", "height": "60vh", "textAlign": "start", "padding": "0px", "width":"70%"}),
-                                    html.Div(
-                                    [
-
-                                    ], style={"height": "60vh", "textAlign": "start", "padding": "0px", "width":"30%"})
-                                    ])), label="2D view", value="2Dview")], id="tabinfo", value="General information"),
+                                    ], style={ "height": "50vh","textAlign": "start", "padding": "0px"},className="col-lg-5 col-sm-12 col-xs-12"),
+                                    
+                                    # 3D destribution
+                                    html.Div([
+                                        html.Div([html.P("Spikes Activity Per Input", style={"margin":"0px"})]),
+                                        dcc.Tabs([dcc.Tab(dbc.Card(dbc.CardBody([
+                                                dcc.Graph(id={"type":"SpikesActivityPerInput","index":i}, config={"displaylogo": False})
+                                            ])),label=i, value='SpikesActivityPerInput-'+str(x)) for x, i in enumerate(self.g.Layer_Neuron) if i != "Input"],value="SpikesActivityPerInput-1")
+                                    ], style={"textAlign": "start", },className="col-lg-4 col-sm-12 col-xs-12")
+                                    
+                                    ], className="row")), label="2D view", value="2Dview")], id="tabinfo", value="General information"),
 
             ]
             ),
@@ -212,8 +295,8 @@ class layout(layoutOp):
             try:
                 tabs.append(dcc.Tab(importlib.import_module(
                     ".Modules."+m+".layout", package="src").layout().load(self.app, self.g), label=m))
-            except Exception as e:
-                print("Tabs appending:"+str(e))
+            except Exception:
+                print("Tabs appending:"+traceback.format_exc())
 
         # App layout
         self.app.layout = html.Div([
@@ -232,16 +315,16 @@ class layout(layoutOp):
                 dcc.Tabs(tabs, id="tabs"),
                 # Control Layout
                 dbc.Row([
-                    html.Div([dbc.Button("<", id="btn-back", className="btn btn-default", style={"marginTop": "12px", "fontWeight": "500", "backgroundColor": "rgb(68, 71, 99)"}),
+                    html.Div([dbc.Button(html.I(className="fa-solid fa-angle-left"), id="btn-back", className="btn btn-default", style={"marginTop": "12px", "fontWeight": "500", "backgroundColor": "rgb(68, 71, 99)"}),
                               dbc.Button("Start", id="btnControle", className="btn btn-success", style={
                                          "marginTop": "12px", "marginLeft": "5px", "width": "100px", "fontWeight": "500"}),
-                              dbc.Button(">", id="btn-next", className="btn btn-default", style={"marginTop": "12px", "marginLeft": "5px", "fontWeight": "500", "backgroundColor": "rgb(68, 71, 99)"})], className="col-md-4 col-sm-12 col-lg-4"),
+                              dbc.Button(html.I(className="fa-solid fa-angle-right"), id="btn-next", className="btn btn-default", style={"marginTop": "12px", "marginLeft": "5px", "fontWeight": "500", "backgroundColor": "rgb(68, 71, 99)"})], className="col-md-4 col-sm-12 col-lg-4"),
                     html.Div([
                         html.Div([
                             html.Span("Update Speed (s)",
                                       className="input-group-text")
                         ], className="input-group-prepend"),
-                        dbc.Input(type="number", id="speed", value=1, min=0.25, max=5, step=0.25, style={
+                        dbc.Input(type="number", id="speed", value=1, min=0.25, max=120, step=0.25, style={
                                   "width": "30%", "textAlign": "center"})
                     ], className="input-group col-md-12 col-sm-12 col-lg-4", style={"height": "38px", "paddingTop": "12px"}),
 
diff --git a/src/Modules/General/spark.py b/src/Modules/General/spark.py
index cb1cf8608e1bbfffb0898436a5d1b857f0b19f0c..eb175ab21d45eba707a0eedd9a5c2eb12e83e735 100755
--- a/src/Modules/General/spark.py
+++ b/src/Modules/General/spark.py
@@ -56,6 +56,8 @@ class spark(sparkOp):
                 if ('spikes' in self.g.db.list_collection_names()):
                     M = max(M, pymongo.collection.Collection(
                         self.g.db, 'spikes').find_one(sort=[("T", -1)])["T"])
+                    self.g.nbrClasses = pymongo.collection.Collection(
+                        self.g.db, 'spikes').find_one(sort=[("Input", -1)])["Input"]
                 if ('potential' in self.g.db.list_collection_names()):
                     M = max(M, pymongo.collection.Collection(
                         self.g.db, 'potential').find_one(sort=[("T", -1)])["T"])
@@ -69,8 +71,8 @@ class spark(sparkOp):
                 else:
                     print("No labels")
             
-            except Exception as e:
-                print("MongoError:" + str(e))
+            except Exception:
+                print("GlobalSparkMongoError:" + traceback.format_exc())
                 pass
             self.g.Max = M
             self.g.stepMax = int(M/self.g.updateInterval)+1
@@ -78,22 +80,22 @@ class spark(sparkOp):
             try:
                 data = next(col, None)
                 self.g.Accuracy = data["Acc"]
-            except Exception as e:
+            except Exception:
                 self.g.Accuracy = "--"
                 print("No accuracy recorded")
                 pass
             
             try:
                 self.g.finalLabels = data["NLabel"]
-            except Exception as e:
+            except Exception:
                 print("No output neuron classes recorded")
                 self.g.finalLabels = None
                 pass
 
             # ------------------------------------------------------------
-            print("done ", self.MODULE_NAME)
+            if self.g.config.DEBUG:
+                print("done ", self.MODULE_NAME)
 
-        except Exception as e:
-            print("Error:" + str(e))
-            traceback.print_exc()
+        except Exception:
+            print("GlobalSpark:" + traceback.format_exc())
             pass
diff --git a/src/Modules/MultiLayer/callbacks.py b/src/Modules/MultiLayer/callbacks.py
index 89f40c7496202fd719aa1415de7ccf277b8a4eb9..d9065d6384c2b142e02eb4912838db2d2a38037f 100755
--- a/src/Modules/MultiLayer/callbacks.py
+++ b/src/Modules/MultiLayer/callbacks.py
@@ -11,6 +11,7 @@ import plotly.graph_objects as go
 from dash import dcc, html
 from bson.json_util import dumps, loads
 from dash.exceptions import PreventUpdate
+import traceback
 from dash.dependencies import ALL, MATCH, Input, Output, State
 from src.templates.callbacksOp import callbacksOp
 
@@ -49,11 +50,11 @@ class callbacks(callbacksOp):
                             super.clearData()
 
                         return [display, clear]
-                except Exception as e:
-                    print("display:"+str(e))
+                except Exception:
+                    print("display:" + traceback.format_exc())
 
-            except Exception as e:
-                print("CallbackName:" + str(e))
+            except Exception:
+                print("CallbackName:" + traceback.format_exc())
 
             # ----------------------------------------------------------------
             # Callback related Data retreiving functions
@@ -67,5 +68,5 @@ class callbacks(callbacksOp):
 
                 print("Add MongoDB functions here")
 
-            except Exception as e:
-                print("Data process loading:"+str(e))
\ No newline at end of file
+            except Exception:
+                print("Data process loading:" + traceback.format_exc())
\ No newline at end of file
diff --git a/src/Modules/MultiLayer/layout.py b/src/Modules/MultiLayer/layout.py
index 07af6ad30c26f69c9c9333038917f5ec0bef8ba5..275cf2cb3ab237def4a0309bddd261907640bfcc 100755
--- a/src/Modules/MultiLayer/layout.py
+++ b/src/Modules/MultiLayer/layout.py
@@ -3,6 +3,7 @@
 
 from collections import deque
 from dash import dcc, html
+import traceback
 import dash_bootstrap_components as dbc
 from .callbacks import callbacks
 from src.templates.layoutOp import layoutOp
@@ -49,5 +50,5 @@ class layout(layoutOp):
             # callbacks(self.app, self.g)
             # Return the Layer
             return layer
-        except Exception as e:
-            print("MultiLayer:" + str(e))
\ No newline at end of file
+        except Exception:
+            print("MultiLayer:" + traceback.format_exc())
\ No newline at end of file
diff --git a/src/Modules/MultiLayer/spark.py b/src/Modules/MultiLayer/spark.py
index cf2195eef34f09798c0a44436bc3b292068b1847..835b30725336698c947e8f934e5f1caebf513e4c 100755
--- a/src/Modules/MultiLayer/spark.py
+++ b/src/Modules/MultiLayer/spark.py
@@ -54,7 +54,6 @@ class spark(sparkOp):
                     self.g.modules = [module for module in self.g.modules if module != MODULE_NAME]
             print("done ", MODULE_NAME)
 
-        except Exception as e:
-            print("Error:" + str(e))
-            traceback.print_exc()
+        except Exception:
+            print("Error:" + traceback.format_exc())
             pass
\ No newline at end of file
diff --git a/src/Modules/Neuron/callbacks.py b/src/Modules/Neuron/callbacks.py
index 766871b1b508916cfeb56d4f025de3de238e3dd8..eb92f737e47bada1c5c1b5032e48e6ea877a108b 100755
--- a/src/Modules/Neuron/callbacks.py
+++ b/src/Modules/Neuron/callbacks.py
@@ -6,14 +6,15 @@
 import dash
 import pymongo
 import statistics
+import traceback
 import dash_daq as daq
 from collections import deque
 import plotly.graph_objects as go
 from dash import dcc, html
 from bson.json_util import dumps, loads
-from dash.exceptions import PreventUpdate
 from dash.dependencies import ALL, MATCH, Input, Output, State
 from src.templates.callbacksOp import callbacksOp
+from dash import no_update
 class callbacks(callbacksOp):
     """ Callbacks class
     """
@@ -101,8 +102,8 @@ class callbacks(callbacksOp):
                             if(len(graphsArea) == 0):
                                 graphsArea.append(addSpaceHolder(app))
                             return graphsArea
-            except Exception as e:
-                print("InsertRemoveNewElement: " + str(e))
+            except Exception:
+                print("InsertRemoveNewElement: " + traceback.format_exc())
 
 
             try:
@@ -121,8 +122,8 @@ class callbacks(callbacksOp):
                         return [[{'label': str(i), 'value': str(i)} for i in range(g.Layer_Neuron[selectedLayer])], ""]
                     else:
                         return [[], ""]
-            except Exception as e:
-                print("NeuronSelection: " + str(e))
+            except Exception:
+                print("NeuronSelection: " + traceback.format_exc())
 
             try:
                 # Callback to handle filtering when 3D view clicked
@@ -140,8 +141,8 @@ class callbacks(callbacksOp):
                         return [super.SpikesSameClass(filterClass, g), {"visibility": "hidden"}]
                     else:
                         return [super.SpikesSameClass(filterClass["points"][0], g), {"visibility": "visible"}]
-            except Exception as e:
-                print("NeuronFreqFilter: " + str(e))
+            except Exception:
+                print("NeuronFreqFilter: " + traceback.format_exc())
 
             try:
                 # Callback to handle clearing content when received a clear signal
@@ -165,8 +166,8 @@ class callbacks(callbacksOp):
                         super.clearData(existingIndexes)
 
                     return [displayContent, clearGraphs]
-            except Exception as e:
-                print("clearProcess: "+str(e))
+            except Exception:
+                print("clearProcess: "+ traceback.format_exc())
             try:
                 # Callback to handle spikes per interval graph and spike class representation
                 @app.callback([Output({"index": MATCH, "layer": MATCH, "neuron": MATCH, "type": "FreqGraph"}, "figure"),
@@ -184,7 +185,7 @@ class callbacks(callbacksOp):
                         isOnAcc (bool): whether spikes class graph is active or not
 
                     Raises:
-                        PreventUpdate: in case we don't want to update the content we rise this execption
+                        no_update: in case we don't want to update the content we rise this execption
 
                     Returns:
                         three graphs of the selected neuron
@@ -212,7 +213,7 @@ class callbacks(callbacksOp):
 
                                 return output
                             else:
-                                raise PreventUpdate
+                                return no_update
                         else:
                             if(sliderValue == 0):
                                 data = getSpikesOfNeuron(
@@ -228,7 +229,7 @@ class callbacks(callbacksOp):
                                 
                                 return output
                             else:
-                                raise PreventUpdate
+                                return no_update
                     else:
                         # after adding to the screen
                         if(selectedItem["index"] not in super.xAxisSpikeNbrLabel):
@@ -247,9 +248,9 @@ class callbacks(callbacksOp):
                             
                             return output
                         else:
-                            raise PreventUpdate
-            except Exception as e:
-                print("processSpikeRelatedGraphs: " + str(e))
+                            return no_update
+            except Exception:
+                print("processSpikeRelatedGraphs: " + traceback.format_exc())
 
             try:
                 # Callback to handle neuron potential graph
@@ -266,7 +267,7 @@ class callbacks(callbacksOp):
                         isOn (bool): whether this graph is active or not
 
                     Raises:
-                        PreventUpdate: in case we don't want to update the content we rise this execption
+return no_update: in case we don't want to update the content we rise this execption
 
                     Returns:
                         neuron potential graph of the selected neuron
@@ -292,7 +293,7 @@ class callbacks(callbacksOp):
                                 output = [neuronPotentialDrawGraph(selectedItem["index"], data, super.xAxisPotentialGraph[selectedItem["index"]], super.xAxisPotentialLabel[selectedItem["index"]], super.yAxisPotentialGraph, isOn)]
                                 return output
                             else:
-                                raise PreventUpdate
+                                return no_update
                         else:
                             if(sliderValue == 0):
                                 data = getPotentialOfNeuron(
@@ -307,7 +308,7 @@ class callbacks(callbacksOp):
                                 
                                 return output
                             else:
-                                raise PreventUpdate
+                                return no_update
                     else:
                         # after adding to the screen
                         if(selectedItem["index"] not in super.xAxisPotentialLabel):
@@ -325,9 +326,9 @@ class callbacks(callbacksOp):
                             
                             return output
                         else:
-                            raise PreventUpdate
-            except Exception as e:
-                print("processPotential: " + str(e))
+                            return no_update
+            except Exception:
+                print("processPotential: " + traceback.format_exc())
 
         try:
 
@@ -359,7 +360,7 @@ class callbacks(callbacksOp):
                             color="#28a745",
                             style={"marginLeft": "10px","marginRight": "10px"}
                             ),
-                            html.Div("−", className="btn-danger btn-circle", style={"fontWeight": "500","fontSize":"16px"}, id={"index": str(index), "type": "DeleteComponent"}),html.Div([html.Span(layer+" N: "+neuron, style={"fontSize": "13px", "paddingTop": "10px", "marginRight": "10px"}, className="badge alert-info")], className="d-flex", style={"direction": "rtl", "width": "100%"})], className="d-flex", style={"height": "35px"}),
+                            html.Button(html.I(className="fa-solid fa-trash"), style={"fontWeight": "500","fontSize":"16px", "marginLeft": "10px","color":"red","backgroundColor":"#00110000","border":"0"}, id={"index": str(index), "type": "DeleteComponent"}),html.Div([html.Span(layer+" N: "+neuron, style={"fontSize": "13px", "paddingTop": "10px", "marginRight": "10px"}, className="badge alert-info")], className="d-flex", style={"direction": "rtl", "width": "100%"})], className="d-flex", style={"height": "35px"}),
                             dcc.Graph(id={"index": str(index), "type": "FreqGraph", "layer": layer, "neuron": neuron}, style={
                             "width": "100%", "height": "290px"}, className="col-12", animate=False, config={"displaylogo": False}),
                         ], className="col-lg-4 col-sm-12 col-xs-12" if(g.finalLabels != None) else "col-lg-6 col-sm-12 col-xs-12"),
@@ -386,7 +387,7 @@ class callbacks(callbacksOp):
                             color="#28a745",
                             style={"marginLeft": "10px"})], className="row", style={"height": "35px"}),
                             dcc.Graph(id={"index": str(index), "type": "AccuracyGraph", "layer": layer, "neuron": neuron}, style={
-                            "width": "100%", "height": "290px"}, className="col-12", animate=False, config={"displaylogo": False})], className="col-lg-4 col-sm-6 col-xs-6" if(g.finalLabels != None) else "", style={} if(g.finalLabels != None) else {'display': 'none'})],className="row")], style={"background": "rgb(242, 248, 255)", "paddingBottom": "10px", "marginBottom": "10px", "margin": "5px", "borderRadius": "10px"}, id="VisComponent"+str(index))
+                            "width": "100%", "height": "290px"}, className="col-12", animate=False, config={"displaylogo": False})], className="col-lg-4 col-sm-6 col-xs-6" if(g.finalLabels != None) else "", style={} if(g.finalLabels != None) else {'display': 'none'})],className="row")], style={"background": "rgb(242, 248, 255)", "paddingBottom": "10px", "marginBottom": "10px", "padding": "5px", "borderRadius": "10px"}, id="VisComponent"+str(index))
 
             def addSpaceHolder(app):
                 """ Adds a space holder area when no graphs are selected.
@@ -492,8 +493,8 @@ class callbacks(callbacksOp):
                             paper_bgcolor= "rgba(255, 255, 255,0)",
                             plot_bgcolor= "rgba(255, 255, 255,0)"
                         )}
-                except Exception as e:
-                    print("spikeCountDrawGraph: "+str(e))
+                except Exception:
+                    print("spikeCountDrawGraph: "+ traceback.format_exc())
 
             def neuronPotentialDrawGraph(index, data, xAxis, xAxisLabel, yAxisList, isOn):
                 """ Create scatter plot for selected neuron, that contains 
@@ -595,8 +596,8 @@ class callbacks(callbacksOp):
                             paper_bgcolor= "rgba(255, 255, 255,0)",
                             plot_bgcolor= "rgba(255, 255, 255,0)"
                         )}
-                except Exception as e:
-                    print("neuronPotentialDrawGraph: "+str(e))
+                except Exception:
+                    print("neuronPotentialDrawGraph: "+ traceback.format_exc())
 
             def classDrawGraph(index, data, isOn, labels):
                 """ Create scatter polar plot for selected neuron, that represents 
@@ -690,8 +691,8 @@ class callbacks(callbacksOp):
                             margin={'l': 0, 'r': 0, 't': 25, 'b': 30},
                         )
                         }
-                except Exception as e:
-                    print("classDrawGraph: "+str(e))
+                except Exception:
+                    print("classDrawGraph: "+ traceback.format_exc())
 
             # ------------------------------------------------------------
             # MongoDB operations
@@ -753,5 +754,5 @@ class callbacks(callbacksOp):
                     return None
                 return FreqForNeuron
 
-        except Exception as e:
-            print("Helper functions: "+str(e))
\ No newline at end of file
+        except Exception:
+            print("Helper functions: "+ traceback.format_exc())
\ No newline at end of file
diff --git a/src/Modules/Neuron/layout.py b/src/Modules/Neuron/layout.py
index b0679f189c213a9f237e1bf14ccd273c3d7e485d..747874158c968582ddaa89f99a63a8da39a201a1 100755
--- a/src/Modules/Neuron/layout.py
+++ b/src/Modules/Neuron/layout.py
@@ -4,6 +4,7 @@
 import pymongo
 from itertools import product
 from collections import deque
+import traceback
 from .callbacks import callbacks
 from bson.json_util import dumps
 from bson.json_util import loads
@@ -22,7 +23,6 @@ class layout(layoutOp):
     xAxisPotentialGraph = dict()
     yAxisSpikeNbrGraph = dict()
     yAxisPotentialGraph = dict()
-    SpikePerNeuron = None
 
     def clearData(self, indexes):
         """ Clear the data when moved forward or backward for more than one step
@@ -56,8 +56,8 @@ class layout(layoutOp):
         """
         try:
             self.clearData([])
-            print("neuron-vis")
-            self.SpikePerNeuron = self.getSpikePerNeuron(self.g)
+            if self.g.config.DEBUG:
+                print("neuron-vis")
             layer = dbc.Card(
                 dbc.CardBody(
                     [
@@ -75,7 +75,7 @@ class layout(layoutOp):
                                     options=[],
                                     multi=False,
                                     style={'width': '150px', "marginLeft": "10px", "textAlign": "start"}),
-                                dbc.Button("+", id="AddComponentNeuron", n_clicks=0, style={
+                                dbc.Button(html.I(className="fa-solid fa-plus"), id="AddComponentNeuron", n_clicks=0, style={
                                     "fontWeight": "500", "marginLeft": "20px", "height": "36px", "backgroundColor": "rgb(68, 71, 99)", "borderColor": "rgb(68, 71, 99)"}), html.Div(id='clear-Neuron', children="False", style={'display': 'none'}), html.Div(id='display-Neuron', children="False", style={'display': 'none'})
                             ], className="d-flex"),
                             html.Div(id={'type': "GraphsAreaNeuron"}, children=[html.Div(id={'type': "OutputNeurons"}, children=[dcc.Graph(id="SpikePerNeuronFreq", figure=self.SpikePerNeuron3D(self.g), config={"displaylogo": False}, className="col-6"),
@@ -87,8 +87,8 @@ class layout(layoutOp):
             callbacks(self,self.app, self.g)
             # Return the Layer
             return layer
-        except Exception as e:
-            print("NeuronLayer: " + str(e))
+        except Exception:
+            print("NeuronLayer: " + traceback.format_exc())
 
     # ----------------------------------------------------------------
     # Helper functions
@@ -113,7 +113,7 @@ class layout(layoutOp):
                 'title': 'No Labels detected'}}
         else:
 
-            data = self.SpikePerNeuron
+            data = self.getSpikePerNeuron(self.g)
 
             total = 0
 
@@ -122,11 +122,9 @@ class layout(layoutOp):
 
             xx = [N["i"]["N"] for N in data]
             yy = [(count["count"]/total) for count in data]
-            zz = [int(c["Label"]) for i, c in product(data, g.finalLabels) if (
-                int(c["N"]) == int(i["i"]["N"]) and c["L"] == i["i"]["L"])]
+            zz = [int(c["Label"]) for i, c in product(data, g.finalLabels) if (int(c["N"]) == int(i["i"]["N"]) and c["L"] == i["i"]["L"])]
 
             labels = list(dict.fromkeys(zz))
-
             items = [[[item[0], item[1], item[2]]
                       for item in zip(xx, yy, zz) if item[2] == x] for x in labels]
 
@@ -179,13 +177,11 @@ class layout(layoutOp):
             return {'data': [],
                     'layout': {'margin': {'l': 0, 'r': 0, 't': 30, 'b': 0}}}
         else:
-            data = self.SpikePerNeuron
-
-            data = [d for d, l in product(data, g.finalLabels) if (
-                int(l["N"]) == d['i']['N'] and int(l["Label"]) == filteredClass["z"])]
+            data = self.getSpikePerNeuron(self.g)
+            data = [d for d, l in product(data, g.finalLabels) 
+                    if (l["N"] == d['i']['N'] and l["Label"] == filteredClass["z"])]
             xx = [N["i"]["N"] for N in data]
-            yy = [(count["count"]) for count in data]
-
+            yy = [count["count"] for count in data]
             graph = {'data': [go.Bar(
                 x=[x for x in range(len(xx))],
                 y=yy,
diff --git a/src/Modules/Neuron/spark.py b/src/Modules/Neuron/spark.py
index 89dd65c52e621af4013f8fac958657cbc9748b8c..54dafba7cc68f4572e9155fd13dca0fa29a63f9d 100755
--- a/src/Modules/Neuron/spark.py
+++ b/src/Modules/Neuron/spark.py
@@ -53,9 +53,9 @@ class spark(sparkOp):
                 if(not self.DOCUMENT_NAME in self.g.db.list_collection_names()):
                     print(self.DOCUMENT_NAME, "not found")
                     self.g.modules = [module for module in self.g.modules if module != self.MODULE_NAME]
-            print("done ", self.MODULE_NAME)
+            if self.g.config.DEBUG:
+                print("done ", self.MODULE_NAME)
 
-        except Exception as e:
-            print("Error:" + str(e))
-            traceback.print_exc()
+        except Exception:
+            print("Error:" + traceback.format_exc())
             pass
diff --git a/src/Modules/Synapse/callbacks.py b/src/Modules/Synapse/callbacks.py
index 6d73376175a3a3322817a0bae883610728ef8a3e..8e66f73f913ba0201d9f385ca6c9bd497a375977 100755
--- a/src/Modules/Synapse/callbacks.py
+++ b/src/Modules/Synapse/callbacks.py
@@ -3,20 +3,17 @@
     Dash callbacks are the responsible on updating graphs each step.
 """
 
-import time
 from dash.dependencies import Input, Output, State, MATCH, ALL
 import dash
 import pymongo
+import traceback
 from dash import dcc, html
 import dash_daq as daq
 from bson.json_util import loads
-from bson.json_util import dumps
-from dash.exceptions import PreventUpdate
+from dash import no_update
 import plotly.graph_objects as go
 from plotly.subplots import make_subplots
 from collections import deque
-import statistics
-from itertools import groupby
 import numpy as np
 import pandas as pd
 from src.templates.callbacksOp import callbacksOp
@@ -44,10 +41,8 @@ class callbacks(callbacksOp):
                               [Input("AddComponentSynapse", "n_clicks"), Input(
                                   {"type": "DeleteComponent", "index": ALL}, "n_clicks")],
                               [State({"type": "GraphsAreaSynapse"}, "children"), State({"type": "GlobalHeatMapAreaSynapse"}, "children"),
-                               State("LayerFilterSynapse", "value"), State(
-                                  "NeuronFilterSynapse", "value"), State("HeatMapX", "value"),
-                               State("HeatMapY", "value")])
-                def InsertRemoveNewElement(addButtonClick, deleteButtonsClick, graphsArea, GlobalHeatmap, selectedLayer, selectedNeuron, HeatMapX, HeatMapY):
+                               State("LayerFilterSynapse", "value"), State("NeuronFilterSynapse", "value"),State("heatmapEditInfo", "data")])
+                def InsertRemoveNewElement(addButtonClick, deleteButtonsClick, graphsArea, GlobalHeatmap, selectedLayer, selectedNeuron, heatmapEditInfo):
                     """ Insert of remove element.
 
                     Args:
@@ -57,15 +52,15 @@ class callbacks(callbacksOp):
                         GlobalHeatmap (list): list of GLobalHeatma component children
                         selectedLayer (String): name of the selected layer
                         selectedNeuron (int): id of selected neuron
-                        HeatMapX (int): X dimension for heatmaps
-                        HeatMapY (int): Y dimension for heatmaps
+                        heatmapEditInfo (Array): heatmap edit stats
 
                     Returns:
                         the updated content of 'GraphsAreaSynapse' and 'GlobalHeatMapAreaSynapse'
                     """
                     context = dash.callback_context.triggered
-
                     if "AddComponentSynapse" in context[0]['prop_id']:
+                        if context[0]['value'] == 0:
+                            selectedNeuron = None
                         # Add item
                         if(selectedLayer != None and (selectedNeuron != None and len(selectedNeuron) != 0)):
                             if len(selectedNeuron) > 0:
@@ -78,7 +73,7 @@ class callbacks(callbacksOp):
                             else:
                                 return [graphsArea, GlobalHeatmap]
                         else:
-                            data = None
+                            data = pd.DataFrame({})
                             if selectedLayer != None:
                                 if(selectedLayer in super.globalHeatMap):
                                     data = super.globalHeatMap[selectedLayer]
@@ -86,7 +81,7 @@ class callbacks(callbacksOp):
                                     data = self.getGlobalSynapseWeights(g,selectedLayer)
                                     super.globalHeatMap[selectedLayer] = data
 
-                            return [graphsArea, self.AddGlobalHeatmap(g, selectedLayer, data, HeatMapX, HeatMapY) if not data.empty else []]
+                            return [graphsArea, self.AddGlobalHeatmap(g, selectedLayer, data, heatmapEditInfo) if not data.empty else []]
                     else:
                         if(context[0]['prop_id'] != "."):
                             # Delete item
@@ -119,8 +114,8 @@ class callbacks(callbacksOp):
                             if(len(graphsArea) == 0):
                                 graphsArea.append(self.addSpaceHolder(g,app))
                             return [graphsArea, GlobalHeatmap]
-            except Exception as e:
-                print("InsertRemoveNewElement: " + str(e))
+            except Exception:
+                print("InsertRemoveNewElement: "+ traceback.format_exc())
 
             try:
                 # Callback to get neurons of seleced layer
@@ -138,8 +133,8 @@ class callbacks(callbacksOp):
                         return [[{'label': str(i), 'value': str(i)} for i in range(g.Layer_Neuron[selectedLayer])], ""]
                     else:
                         return [[], ""]
-            except Exception as e:
-                print("NeuronSelection: " + str(e))
+            except Exception:
+                print("NeuronSelection: " + traceback.format_exc())
 
             try:
                 # Callback to handle clearing content when received a clear signal
@@ -163,8 +158,8 @@ class callbacks(callbacksOp):
                         super.clearData(existingIndexes)
 
                     return [displayContent, clearGraphs]
-            except Exception as e:
-                print("clearProcess: "+str(e))
+            except Exception:
+                print("clearProcess: "+ traceback.format_exc())
 
             try:
                 # Callback to handle spikes per interval graph and spike class representation
@@ -173,9 +168,8 @@ class callbacks(callbacksOp):
                     [Input("display-Synapse", "children")], [State("v-step", "children"), State("interval", "value"),
                     State({"index": MATCH, "layer": MATCH, "neuron": MATCH, "type": "SynapseFreqGraph"}, "id"),
                     State({"index": MATCH, "layer": MATCH, "neuron": MATCH, "type": 'Synapse-frequency-switch'}, 'on'),
-                    State({"index": MATCH, "layer": MATCH, "neuron": MATCH, "type": 'Synapse-HeatMap-switch'}, 'on'),
-                    State("HeatMapX", "value"), State("HeatMapY", "value")])
-                def processSynapseData(displayContent, sliderValue, updateInterval, selectedItem, isOnSynapseFreqGraph, isOnHeatmap, heatMapX, heatMapY):
+                    State({"index": MATCH, "layer": MATCH, "neuron": MATCH, "type": 'Synapse-HeatMap-switch'}, 'on'),State("heatmapEditInfo", "data")])
+                def processSynapseData(displayContent, sliderValue, updateInterval, selectedItem, isOnSynapseFreqGraph, isOnHeatmap, heatmapEditInfo):
                     """ Create two graphs using neuron synapses ('SynapseFreqGraph' and 'Heatmap')
 
                     Args:
@@ -185,11 +179,10 @@ class callbacks(callbacksOp):
                         selectedItem (list): selected neuron
                         isOnSynapseFreqGraph (bool): whether SynapseFreq graph is active or not
                         isOnHeatmap (bool): whether Heatmap graph is active or not
-                        heatMapX (int): X dimension for heatmaps
-                        heatMapY (int): Y dimension for heatmaps
+                        heatmapEditInfo (Array): heatmap edit stats
 
                     Raises:
-                        PreventUpdate: in case we don't want to update the content we rise this execption
+                        no_update: in case we don't want to update the content we rise this execption
 
                     Returns:
                         synapse frequency graph and heatmap content (data and layout)
@@ -210,11 +203,11 @@ class callbacks(callbacksOp):
                                 output = [self.synapseFreqDrawGraph(g, selectedItem["index"],
                                             data, super.xAxisSynapseFreqGraph[selectedItem["index"]], super.xAxisLabel[selectedItem["index"]], super.yAxisSynapseFreqGraph,
                                             super.HeatMapSynapseFreqGraph, isOnSynapseFreqGraph),
-                                            self.heatMap(g, selectedItem["index"], data, super.heatmaps, isOnHeatmap, heatMapX, heatMapY)]
+                                            self.heatMap(g, selectedItem["index"], data, super.heatmaps, isOnHeatmap, heatmapEditInfo)]
                                 
                                 return output
                             else:
-                                raise PreventUpdate
+                                return no_update
                         else:
                             if(sliderValue == 0):
                                 data = self.getSynapseWeights(int(sliderValue)*float(updateInterval), g, selectedItem)
@@ -226,11 +219,11 @@ class callbacks(callbacksOp):
                                 output = [self.synapseFreqDrawGraph(g, selectedItem["index"],
                                             data, super.xAxisSynapseFreqGraph[selectedItem["index"]], super.xAxisLabel[selectedItem["index"]], super.yAxisSynapseFreqGraph,
                                             super.HeatMapSynapseFreqGraph, isOnSynapseFreqGraph),
-                                            self.heatMap(g, selectedItem["index"], data, super.heatmaps, isOnHeatmap, heatMapX, heatMapY)]
+                                            self.heatMap(g, selectedItem["index"], data, super.heatmaps, isOnHeatmap, heatmapEditInfo)]
                                 
                                 return output
                             else:
-                                raise PreventUpdate
+                                return no_update
                     else:
                         # after adding to the screen
                         if(selectedItem["index"] not in super.xAxisLabel):
@@ -246,13 +239,13 @@ class callbacks(callbacksOp):
                             output = [self.synapseFreqDrawGraph(g, selectedItem["index"],
                                         data, super.xAxisSynapseFreqGraph[selectedItem["index"]], super.xAxisLabel[selectedItem["index"]], super.yAxisSynapseFreqGraph,
                                         super.HeatMapSynapseFreqGraph, isOnSynapseFreqGraph),
-                                        self.heatMap(g, selectedItem["index"], data, super.heatmaps, isOnHeatmap, heatMapX, heatMapY)]
+                                        self.heatMap(g, selectedItem["index"], data, super.heatmaps, isOnHeatmap, heatmapEditInfo)]
                             
                             return output
                         else:
-                            raise PreventUpdate
-            except Exception as e:
-                print("processSynapseData: " + str(e))
+                            return no_update
+            except Exception:
+                print("processSynapseData: "+ traceback.format_exc())
 
 
             try:
@@ -273,9 +266,37 @@ class callbacks(callbacksOp):
                         return [not isTabOpen]
                     else:
                         return [isTabOpen]
-            except Exception as e:
-                print("GlobalHeatMapTab: " + str(e))
+            except Exception:
+                print("GlobalHeatMapTab: "+ traceback.format_exc())
+            
+            try:
+                # Callback to handle heatmap flipping if needed
+                @app.callback([Output("heatmapEditInfo", "data"),Output("AddComponentSynapse", "n_clicks")],[Input('FlipHeatmapH', 'n_clicks'),Input('FlipHeatmapV', 'n_clicks'),Input('RotateHeatmap', 'n_clicks'),State("heatmapEditInfo", "data")])
+                def HeatmapFlip(FlipHeatmapH, FlipHeatmapV, RotateHeatmap, heatmapEditInfo):
+                    """ Function called when flip button clicked
+
+                    Args:
+                        FlipHeatmapH (int): number of clicks on the FlipHeatmapH button
+                        FlipHeatmapV (int): number of clicks on the FlipHeatmapV button
+                        RotateHeatmap (int): number of clicks on the RotateHeatmap button
+                        heatmapEditInfo (Array): heatmap edit stats
 
+                    Returns:
+                        if global heatmaps tab should be opened or closed
+                    """
+                    context = dash.callback_context.triggered
+                    
+                    if "FlipHeatmapH" in context[0]['prop_id']:
+                        heatmapEditInfo[0] = not heatmapEditInfo[0]
+                    elif "FlipHeatmapV" in context[0]['prop_id']:
+                        heatmapEditInfo[1] = not heatmapEditInfo[1]
+                    else:
+                        heatmapEditInfo[2] = not heatmapEditInfo[2]
+
+                    return [heatmapEditInfo,0]
+            except Exception:
+                print("HeatmapFlip: "+ traceback.format_exc())
+            
     try:
 
         # ------------------------------------------------------------
@@ -302,7 +323,7 @@ class callbacks(callbacksOp):
                             id={"index": str(index), "layer": layer, "neuron": neuron,
                                 "type": "Synapse-frequency-switch"},
                             on='True', size=25, color="#28a745", style={"marginLeft": "10px"}),
-                            html.Div("−", className="btn-danger btn-circle", style={"fontWeight": "500","fontSize":"16px", "marginLeft": "10px"}, id={"index": str(index), "type": "DeleteComponent"}),
+                            html.Button(html.I(className="fa-solid fa-trash"), style={"fontWeight": "500","fontSize":"16px", "marginLeft": "10px","color":"red","backgroundColor":"#00110000","border":"0"}, id={"index": str(index), "type": "DeleteComponent"}),
                             html.Div([html.Span(layer+" N: "+neuron, style={"fontSize": "13px", "paddingTop": "10px", "marginRight": "10px"}, className="badge alert-info")], className="d-flex", style={"direction": "rtl", "width": "100%"})], className="d-flex", style={"height": "35px"}),
                         dcc.Graph(id={"index": str(index), "type": "SynapseFreqGraph", "layer": layer, "neuron": neuron}, style={
                             "width": "100%", "height": "290px"}, className="col-12", animate=False, config={"displaylogo": False}),
@@ -318,7 +339,7 @@ class callbacks(callbacksOp):
                             )], className="d-flex", style={"height": "35px", "direction": "rtl", "paddingRight": "30px"}),
                         dcc.Graph(id={"index": str(index), "type": "Heatmap", "layer": layer, "neuron": neuron}, style={"width": "100%", "height": "290px"}, className="col-12", animate=False, config={"displaylogo": False}),
                     ], className="col-lg-6 col-sm-12 col-xs-12")
-                ], className="row", style={"margin": "10px"})
+                ], className="row", style={"padding": "10px"})
 
             ], style={"background": "rgb(242, 248, 255)", "paddingBottom": "10px", "marginBottom": "10px",
                       "margin": "5px", "borderRadius": "10px"},
@@ -340,15 +361,14 @@ class callbacks(callbacksOp):
                 id="spaceHolder")
 
 
-        def AddGlobalHeatmap(self, g, selectedLayer, data, heatMapX, heatMapY):
+        def AddGlobalHeatmap(self, g, selectedLayer, data, heatmapEditInfo):
             """ Add global heatmap for selected layer.
 
             Args:
                 g (Global_Var): reference to access global variables
                 selectedLayer (String): layer name
                 data (dataframe): synapses weights of all neurons in the layer
-                heatMapX (int): X dimension for heatmaps
-                heatMapY (int): Y dimension for heatmaps
+                heatmapEditInfo (Array): heatmap edit stats
 
             Returns:
                 html component that contains the global heatmaps
@@ -379,11 +399,14 @@ class callbacks(callbacksOp):
                                 zmin=0,
                                 zmax=1,
                                 z=g.createHeatMap(
-                                    heatMapX, heatMapY, data[data.To == index],depth),
+                                    heatMapX, heatMapY, data[data.To == index],depth, heatmapEditInfo[2]),
                                 colorscale='jet',
                                 name=str(index)),
                             row=xx, col=yy)
-                        fig.update_yaxes(autorange="reversed", row=xx, col=yy)
+                        if heatmapEditInfo[0]:
+                            fig.update_xaxes(autorange="reversed")
+                        if heatmapEditInfo[1]:
+                            fig.update_yaxes(autorange="reversed")
                         fig.update_layout(height=80, width=80)
                         index += 1
 
@@ -553,10 +576,10 @@ class callbacks(callbacksOp):
                         plot_bgcolor= "rgba(255, 255, 255,0)",
                         xaxis={'title': 'Step'}
                     )}
-            except Exception as e:
-                print("synapseFreqDrawGraph: "+str(e))
+            except Exception:
+                print("synapseFreqDrawGraph: "+ traceback.format_exc())
 
-        def heatMap(self, g, index, data, heatmaps, isOn, heatMapX, heatMapY):
+        def heatMap(self, g, index, data, heatmaps, isOn, heatmapEditInfo):
             """ Create heatmap for selected neuron synapses.
 
             Args:
@@ -565,51 +588,51 @@ class callbacks(callbacksOp):
                 data (list): data to be added to the graph
                 heatmaps (list): list of heatmaps created
                 isOn (bool): whether this graph is active or not
-                heatMapX (int): X dimension for heatmaps
-                heatMapY (int): Y dimension for heatmaps
+                heatmapEditInfo (Array): heatmap edit stats
 
             Returns:
                 heatmap content (data and layout)
             """
             try:
-
                 heatMapWithIndexs = []
                 layout = go.Layout(
                             margin={'l': 0, 'r': 0, 't': 0, 'b': 25},
                             uirevision='no reset of zoom',
-                            yaxis={"autorange": "reversed"},
-                            xaxis={'showticklabels': False,"ticks":"inside"},
+                            xaxis={'showticklabels': False,"ticks":"inside","autorange": "reversed"} if heatmapEditInfo[0] else {'showticklabels': False,"ticks":"inside"},
+                            yaxis={"autorange": "reversed"} if heatmapEditInfo[1] else {},
                             width=400, height=300,
                             paper_bgcolor= "rgba(255, 255, 255,0)",
                             plot_bgcolor= "rgba(255, 255, 255,0)")
                 if isOn:
                     if not data.empty:
+                        heatMapX = data["x"].max() + 1
+                        heatMapY = data["y"].max() + 1
+
+                        # update heatmap in heatmaps
+                        if (str(index) not in heatmaps) or (type(heatmaps[str(index)]) == type(None)):
+                            heatmaps[str(index)] = [np.zeros((heatMapX,heatMapY)),heatMapX,heatMapY]
+
                         depth = data["C"].max()
                         data = data.sort_values(["T"])
                         data =  data.groupby(["C","x","y"], as_index=False).agg({"To":"last","T":"max","L":"last","V":"last"})
 
-                        # update heatmap in heatmaps
-                        if str(index) not in heatmaps:
-                            heatmaps[str(index)] = np.zeros(
-                                (heatMapX, heatMapY))
                         for X,Y,V,C in zip(data["x"],data["y"],data["V"],data["C"]):
                             try:
                                 # TODO: multi-D
-                                heatmaps[str(index)][X][Y] = V
+                                heatmaps[str(index)][0][X][Y] = V
                                 heatMapWithIndexs.append([X, Y, C, V])
-                            except Exception as e:
-                                print("heatMap1: "+str(e))
-
+                            except Exception:
+                                print("heatMap1: "+ traceback.format_exc())
                         return {'data': [
                             go.Heatmap(
-                                z=g.createHeatMap(heatMapX, heatMapY, pd.DataFrame(heatMapWithIndexs), depth),
+                                z=g.createHeatMap(heatMapX, heatMapY, pd.DataFrame(heatMapWithIndexs), depth, heatmapEditInfo[2]),
                                 zmin=0,
                                 zmax=1,
                                 colorscale="jet")],
                                 'layout': layout}
                     else:
                         if str(index) not in heatmaps:
-                            heatmaps[str(index)] = np.empty((heatMapX, heatMapY))
+                            heatmaps[str(index)] = None
                         
                         return {'data': [
                             go.Heatmap(
@@ -623,12 +646,12 @@ class callbacks(callbacksOp):
                             go.Heatmap(
                                 zmin=0,
                                 zmax=1,
-                                z=heatmaps[str(index)] if str(
+                                z=heatmaps[str(index)][0] if str(
                                     index) in heatmaps else [],
                                 colorscale="jet")],
                                 'layout': layout}
-            except Exception as e:
-                print("heatMap2: "+str(e))
+            except Exception:
+                print("heatMap2: "+ traceback.format_exc())
 
         # ------------------------------------------------------------
         # MongoDB operations
@@ -656,7 +679,7 @@ class callbacks(callbacksOp):
                     {"L": {'$in': layer}},
                     {"To": {'$in': neuron}},
                     {"T": {'$gt': timestamp, '$lte': (timestamp+g.updateInterval)}} ]}
-                }])
+                }], allowDiskUse = True)
 
             # ToDF----------------------
             SynapseWeight = pd.DataFrame(list(SynapseWeight))
@@ -680,7 +703,7 @@ class callbacks(callbacksOp):
             # MongoDB---------------------
             col = pymongo.collection.Collection(g.db, 'synapseWeightFinal')
 
-            globalSynapseWeights = col.aggregate([{"$match": { "_id.L": {"$eq": layer}}}])
+            globalSynapseWeights = col.aggregate([{"$match": { "_id.L": {"$eq": layer}}}], allowDiskUse = True)
 
             # ToDF----------------------
             globalSynapseWeights = pd.DataFrame(list(globalSynapseWeights))
@@ -695,5 +718,5 @@ class callbacks(callbacksOp):
             # ----------------------------
             return globalSynapseWeights
 
-    except Exception as e:
-        print("Data process loading: "+str(e))
\ No newline at end of file
+    except Exception:
+        print("Data process loading: "+ traceback.format_exc())
\ No newline at end of file
diff --git a/src/Modules/Synapse/layout.py b/src/Modules/Synapse/layout.py
index 7d253987e418c3c90278cbb1a930d928e3384426..11863c87257158471128f4ffcff784bcc1c42409 100755
--- a/src/Modules/Synapse/layout.py
+++ b/src/Modules/Synapse/layout.py
@@ -3,6 +3,7 @@
 
 from collections import deque
 from dash import dcc, html
+import traceback
 import dash_bootstrap_components as dbc
 from .callbacks import callbacks
 from src.templates.layoutOp import layoutOp
@@ -55,6 +56,7 @@ class layout(layoutOp):
                     [
                         html.Div(id="synapse-vis", children=[
                         html.Div([
+                        dcc.Store(id="heatmapEditInfo",data=[False,False,False]),
                         dcc.Dropdown(
                         id='LayerFilterSynapse',
                         options=[{'label': str(i), 'value': str(i)} for i in (
@@ -63,18 +65,16 @@ class layout(layoutOp):
                         dcc.Dropdown(
                         id='NeuronFilterSynapse', options=[], multi=False,
                         style={'width': '150px', "marginLeft": "10px", "textAlign": "start"}),
-                        dbc.Button("+", id="AddComponentSynapse", n_clicks=0, style={
+                        dbc.Button(html.I(className="fa-solid fa-plus"), id="AddComponentSynapse", n_clicks=0, style={
                         "fontWeight": "500", "marginLeft": "20px", "height": "36px",
                         "backgroundColor":"rgb(68, 71, 99)","borderColor":"rgb(68, 71, 99)"}),
-                        html.Div([
-                        dbc.Input(type="number",id="HeatMapX",value=self.g.NeuronsSize["x"]+1, min=0, step=1,
-                        style={"width":"80px","marginLeft":"10px","textAlign":"center"}),
-                        html.P("X",style={"padding":"5px"}),
-                        dbc.Input(type="number",id="HeatMapY",value=self.g.NeuronsSize["y"]+1, min=0, step=1,
-                        style={"width":"80px","textAlign":"center"})], className="d-flex",style={"marginLeft":"3px"})          
-                        ,html.Div(id='clear-Synapse',children="False", style={'display': 'none'})
-                        ,html.Div(id='display-Synapse',children="False", style={'display': 'none'})
-                    ], className="d-flex"),
+                        html.P("Heatmap Editor:", style={"textAlign": "start", "marginLeft": "20px", "marginTop": "4px"}),
+                        dbc.Button(html.I(className="fa-solid fa-left-right"), id="FlipHeatmapH", n_clicks=0, style={"fontWeight": "500", "marginLeft": "20px", "height": "36px","backgroundColor":"rgb(68, 71, 99)","borderColor":"rgb(68, 71, 99)", "color":"white"}),
+                        dbc.Button(html.I(className="fa-solid fa-up-down"), id="FlipHeatmapV", n_clicks=0, style={"fontWeight": "500", "marginLeft": "20px", "height": "36px","backgroundColor":"rgb(68, 71, 99)","borderColor":"rgb(68, 71, 99)", "color":"white"}),
+                        dbc.Button(html.I(className="fa-solid fa-rotate"), id="RotateHeatmap", n_clicks=0, style={"fontWeight": "500", "marginLeft": "20px", "height": "36px","backgroundColor":"rgb(68, 71, 99)","borderColor":"rgb(68, 71, 99)", "color":"white"}),
+                        html.Div(id='clear-Synapse',children="False", style={'display': 'none'}),
+                        html.Div(id='display-Synapse',children="False", style={'display': 'none'})
+                    ], className="d-flex", style={"paddingBottom": "10px"}),
                         dbc.Card( dbc.CardBody([dbc.Card([dbc.CardHeader(
                         dbc.Button( "Layer HeatMap", color="none",
                         id="group-GlobalHeatMapAreaSynapse-toggle",
@@ -93,5 +93,5 @@ class layout(layoutOp):
             callbacks(self,self.app,self.g)
             # Return the Layer
             return layer
-        except Exception as e:
-            print("SynapseLayer: " + str(e))
\ No newline at end of file
+        except Exception:
+            print("SynapseLayer: " + traceback.format_exc())
\ No newline at end of file
diff --git a/src/Modules/Synapse/spark.py b/src/Modules/Synapse/spark.py
index aef9ded093a26020097bf2a774c2d856bac4a514..c6de5e96170568b22bf46cadfa44a67e6b640687 100755
--- a/src/Modules/Synapse/spark.py
+++ b/src/Modules/Synapse/spark.py
@@ -34,7 +34,7 @@ class spark(sparkOp):
                     self.g.createSparkSession()
                 # --------------------------------------------------
                 col = pymongo.collection.Collection(self.g.db, self.DOCUMENT_NAME)
-                globalSynapseWeights = col.aggregate([{ "$sort": { "T": 1 } },{"$group" : { "_id" : {"To":'$To', "C":'$C', "index":'$index', "L":'$L'}, "T" : { "$last": '$T'},"V" : { "$last": '$V'} } }])
+                globalSynapseWeights = col.aggregate([{ "$sort": { "T": 1 } },{"$group" : { "_id" : {"To":'$To', "C":'$C', "index":'$index', "L":'$L'}, "T" : { "$last": '$T'},"V" : { "$last": '$V'} } }], allowDiskUse = True)
              
                 # Data save into MongoDB ---------------------------------
                 col = pymongo.collection.Collection(self.g.db, self.OUTPUT_DOCUMENT_NAME)
@@ -59,12 +59,12 @@ class spark(sparkOp):
                 
             # get dimensions for the heatmap
             col = pymongo.collection.Collection(self.g.db, 'synapseWeightFinal')
-            globalSynapseWeights = pd.DataFrame(list(col.aggregate([{"$group": {"_id":None, "x":{"$max":"$_id.index.x"}, "y":{"$max":"$_id.index.y"}}}])))
+            globalSynapseWeights = pd.DataFrame(list(col.aggregate([{"$group": {"_id":None, "x":{"$max":"$_id.index.x"}, "y":{"$max":"$_id.index.y"}}}], allowDiskUse = True)))
             self.g.NeuronsSize = {"x":globalSynapseWeights["x"].max(),"y":globalSynapseWeights["y"].max()}
-                
-            print("done", self.MODULE_NAME)
 
-        except Exception as e:
-            print("Error:" + str(e))
-            traceback.print_exc()
+            if self.g.config.DEBUG:
+                print("done", self.MODULE_NAME)
+
+        except Exception:
+            print("Error:" + traceback.format_exc())
             pass
\ No newline at end of file
diff --git a/src/static/css/app.css b/src/static/css/app.css
index 84e0d9b84bc80642a94116909e30b03d3a63d0d0..ece06d339e9887aa08be7dabc4f33e38df434ba3 100755
--- a/src/static/css/app.css
+++ b/src/static/css/app.css
@@ -23,14 +23,14 @@ background-color: red !important;
   background-color:  rgb(219, 232, 246) !important;
 }
 .row-sim{
-  background-color: rgb(242, 248, 255);
+  border: 1px solid rgba(0,0,0,.125);
   margin: 30px;
   border-radius: 10px;
   text-align: center;
   padding: 50px;
 }
 .panel{
-  background-color: rgb(242, 248, 255);
+  background-color: rgba(68, 71, 99, 0.2);
   margin: 5%;
   border-radius: 10px;
   text-align: center;
@@ -41,27 +41,32 @@ background-color: red !important;
   background-color: #dc3545; 
   color: white;
   font-weight: 500;
-  width: 100%;
+  width: 100px;
+  height: 100px;
   margin-bottom: 5px;
+  margin-right: 5px;
   border:none;
 }
 .btn-module:focus{
   background-color: #d42b3c; 
   color: white;
-  width: 100%;
+  width: 100px;
+  height: 100px;
   border:none;
   box-shadow: #d42b3c; 
 }
 .btn-module:hover{
   background-color: #d42b3c; 
   color: white;
-  width: 100%;
+  width: 100px;
+  height: 100px;
   border:none;
 }
 .btn-module:active{
   background-color: #d42b3c; 
   color: white;
-  width: 100%;
+  width: 100px;
+  height: 100px;
   border:none;
 }
 .btn-group-vertical{
diff --git a/src/templates/index.html b/src/templates/index.html
index 01ee2b45dd2801637b7def107f3e19fd1753a7d0..61fd8e2b66888a7b3d6d9d9c43588cda6981e354 100755
--- a/src/templates/index.html
+++ b/src/templates/index.html
@@ -33,17 +33,17 @@
     <div class="col-10 m-5">
     <div class="card">
     <div class="card-body">
-        VS2N version : <strong style="color: rgb(68, 71, 99);">{{ vs2n }}</strong><br><br>
-        MongoDB version : <strong style="color: rgb(68, 71, 99);">{{ mongodb }}</strong><br><br>
-        Apache Spark version : <strong style="color: rgb(68, 71, 99);">{{ spark }}</strong>
+        VS2N : <strong style="color: rgb(68, 71, 99);">{{ vs2n }}</strong><br><br>
+        MongoDB : <strong style="color: rgb(68, 71, 99);">{{ mongodb }}</strong><br><br>
+        Apache Spark : <strong style="color: rgb(68, 71, 99);">{{ spark }}</strong>
     </div>
     </div>
     </div>
     </div>
-</div>
+
     <div class="row d-flex justify-content-center">
         <div class="col-md-10 col-lg-5 col-sm-10 row-sim">
-            <h5 style="text-align: start;color: rgb(68, 71, 99);">Settings</h5>
+            <h5 style="text-align: start;">Settings</h5>
             <div class="input-group" style="padding-top: 18px;">
                 <div class="input-group-append" style="width: 180px;">
                   <span class="input-group-text"  style="width: 180px;">Simulation</span>
@@ -66,9 +66,8 @@
             </div>
 
         <div class="col-md-10 col-lg-5 col-sm-10 row-sim">
-            <h5 style="text-align: start;color: rgb(68, 71, 99);">Modules</h5>
-            <div class="col-12 btn-group-vertical" data-toggle="buttons" style="padding-left: 0px;padding-right: 0px;">
-
+            <h5 style="text-align: start;">Modules</h5>
+            <div class="col-12" data-toggle="buttons" style="padding-left: 0px;padding-right: 0px;">
                 {% for module in modules %}
 
                 <button type="button" class="btn btn-primary btn-module" data-value="{{ module }}" 
@@ -80,23 +79,12 @@
             </div>
         </div>
     </div>
-    <div class="loading" style="text-align: center;visibility: hidden;">
-      <h5 style="padding-bottom: 5px;">Pre-processing, please wait ...</h5>  
-      <div class="spinner-grow spinner-grow-sm" style="color: rgb(68, 71, 99)" role="status"></div>
-      <div class="spinner-grow spinner-grow-sm" style="color: rgb(68, 71, 99)" role="status"></div>
-      <div class="spinner-grow spinner-grow-sm" style="color: rgb(68, 71, 99)" role="status"></div>
-      <div class="spinner-grow spinner-grow-sm" style="color: rgb(68, 71, 99)" role="status"></div>
-      <div class="spinner-grow spinner-grow-sm" style="color: rgb(68, 71, 99)" role="status"></div>
-      <div class="spinner-grow spinner-grow-sm" style="color: rgb(68, 71, 99)" role="status"></div>
-      <div class="spinner-grow spinner-grow-sm" style="color: rgb(68, 71, 99)" role="status"></div>
-      <div class="spinner-grow spinner-grow-sm" style="color: rgb(68, 71, 99)" role="status"></div>
-      <div class="spinner-grow spinner-grow-sm" style="color: rgb(68, 71, 99)" role="status"></div>
-      <div class="spinner-grow spinner-grow-sm" style="color: rgb(68, 71, 99)" role="status"></div>
-    </div>
     <div style="width:100%;text-align:center;margin-bottom: 25px;margin-top: 10px;">  <a id="btn_start" class="btn btn-success" style="color: white;width:50%;font-weight:500;" href="#" role="button">
             Start Analysis</a>
      </div>
-     </div>
+     <div class="loading" style="text-align: center;visibility: hidden;">
+        <h5 style="padding-bottom: 8px;">Pre-processing, please wait ...</h5>  
+        <div class="spinner-border" style="color: rgb(68, 71, 99)" role="status"></div>
+      </div>
 </body>
-
 </html>
\ No newline at end of file
diff --git a/tests/Modules/test_General_callbacks.py b/tests/Modules/test_General_callbacks.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/Modules/test_General_layout.py b/tests/Modules/test_General_layout.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/Modules/test_General_spark.py b/tests/Modules/test_General_spark.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/Modules/test_Neuron_callbacks.py b/tests/Modules/test_Neuron_callbacks.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/Modules/test_Neuron_layout.py b/tests/Modules/test_Neuron_layout.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/Modules/test_Neuron_spark.py b/tests/Modules/test_Neuron_spark.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/Modules/test_Synapse_callbacks.py b/tests/Modules/test_Synapse_callbacks.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/Modules/test_Synapse_layout.py b/tests/Modules/test_Synapse_layout.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/Modules/test_Synapse_spark.py b/tests/Modules/test_Synapse_spark.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100755
index 0000000000000000000000000000000000000000..0519ecba6ea913e21689ec692e81e9e4973fbf73
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1 @@
+ 
\ No newline at end of file
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000000000000000000000000000000000000..0243da429ccbfaab1a617e71f8987c71ac9c5439
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,24 @@
+import pytest
+from VS2N import flask_app
+
+@pytest.fixture
+def flask_app():
+    yield flask_app
+
+#@pytest.fixture
+#def dashApp():
+#    # Init dash app
+#    app_vis = dash.Dash(
+#        __name__,
+#        requests_pathname_prefix='/vis/',
+#        external_stylesheets=[VS2N.dbc.themes.BOOTSTRAP, VS2N.dbc.icons.FONT_AWESOME],
+#        suppress_callback_exceptions=True,
+#        title="VS2N")
+#    
+#    app_vis.enable_dev_tools(debug=VS2N.g.config.DEBUG)
+#
+#    # Loading modules
+#
+#    request = {'name': 'csnn-23-3-2023-16:32:48', 'modules': ['Neuron', 'Synapse'], 'updateInterval': '1'}
+#    
+#    yield app_vis
\ No newline at end of file
diff --git a/tests/test_Global_var.py b/tests/test_Global_var.py
new file mode 100644
index 0000000000000000000000000000000000000000..214fecb347260be2a898a284c50e2b6095f7aa44
--- /dev/null
+++ b/tests/test_Global_var.py
@@ -0,0 +1,4 @@
+from flask import Flask
+
+def test_test(flask_app: Flask):
+    assert isinstance(flask_app,Flask)
\ No newline at end of file