diff --git a/models/boneload.py b/models/boneload.py
index e688ac6b5de983480398c58ec99fac817518d6e8..e55fd33f57443099610b0676b904e1cac85e62f9 100644
--- a/models/boneload.py
+++ b/models/boneload.py
@@ -15,6 +15,7 @@
 import os
 import math
 import vtk
+import time
 colors = vtk.vtkNamedColors()
 # import numpy
 # from vtk.util.numpy_support import vtk_to_numpy
@@ -129,7 +130,7 @@ def load_off(meshfile):
     return nodes, tris
 
 
-def identify_nodes(coords, all_no, all_coords, eps=1e-3):
+def identify_nodes_brutal(coords, all_no, all_coords, eps=1e-3):
     """identify the nodes based on their coordinates "coords"
     among the list of coordinates "all_coords"
     all_no, given as argument, is an array of tags for the nodes defined in all_coords
@@ -141,6 +142,9 @@ def identify_nodes(coords, all_no, all_coords, eps=1e-3):
 
     returns ntags, a list of node tags of the identified nodes
     """
+    cpu_start = time.perf_counter()
+    print(f'identify_nodes_brutal...')
+
     notfound = []
     ntags = []
     for n in coords:  # for all nodes
@@ -161,11 +165,97 @@ def identify_nodes(coords, all_no, all_coords, eps=1e-3):
         if not found:
             notfound.append(n)
 
+    cpu_stop = time.perf_counter()
+
+    # print stats
+    print(f'identify_nodes_brutal done in {cpu_stop-cpu_start} seconds')
+
     if(len(notfound) != 0):
         print('WARNING: these nodes have not been identified:')
         for n in notfound:
             print(f'\tn={n}')
-        # raise Exception(f'ERROR: {len(notfound)} nodes have not been found!')
+        raise Exception(f'ERROR: {len(notfound)} nodes have not been found!')
+    return ntags
+
+
+def identify_nodes_SaP(coords, all_no, all_coords, eps=1e-3):
+    """identify the nodes based on their coordinates "coords"
+    among the list of coordinates "all_coords"
+    all_no, given as argument, is an array of tags for the nodes defined in all_coords
+
+    The algorithm used is "sweep & prune" along X.
+
+    Comparision with the brutal approach: (6567 nodes, 12696 triangles)
+        identify_nodes_brutal done in 26.600111499999997 seconds
+        sweep and prune done in 0.30412259999999947 seconds
+        (48500 tests instead of 818707890)
+
+    returns ntags, a list of node tags of the identified nodes
+    """
+    print('identify_nodes_SaP...')
+    cpu_start = time.perf_counter()
+
+    # define nod structure
+    class Nod:
+        def __init__(self, x, idx=-1):  # selected points have  a -1 index
+            self.x = x
+            self.idx = idx
+
+        def __str__(self):
+            return f'{self.idx}: {self.x}'
+
+    # build node list
+    nods = []
+    for i, p in enumerate(all_coords):
+        nods.append(Nod(p, all_no[i]))
+    for p in coords:
+        nods.append(Nod(p, -1))
+
+    # sort nodes according to their x coordinate
+    nods.sort(key=lambda nod: nod.x[0])
+
+    ntags = []
+
+    eps = 1e-3    # geometrical tolerance
+    ntests = 0
+    nnods = len(nods)
+    i = 0
+    while i < nnods:
+        n = nods[i]
+        x = n.x[0]
+        i2 = i
+        while True:
+            i2 += 1
+            if i2 == nnods:
+                break
+            n2 = nods[i2]
+            x2 = n2.x[0]
+            if abs(x2-x) > eps:  # test X
+                break
+            ntests += 1
+            if n.idx >= 0 and n2.idx >= 0:  # nodes from different sets
+                continue
+            if abs(n.x[1]-n2.x[1]) > eps:  # test Y
+                continue
+            if abs(n.x[2]-n2.x[2]) > eps:  # test Z
+                continue
+            no = max(n.idx, n2.idx)
+            ntags.append(no)
+            break # hyp: a max of 1 pair should be found per node
+            # print(f'{no} is selected')
+        i += 1
+
+    cpu_stop = time.perf_counter()
+
+    # print stats
+    print(f'sweep and prune done in {cpu_stop-cpu_start} seconds')
+    print(f'({ntests} tests instead of {len(coords)*len(all_coords)})')
+    print(f'{len(coords)} to be identified')
+    print(f'{len(ntags)} successfully identified')
+    if(len(coords) != len(ntags)):
+        print('WARNING: some nodes have not been identified!')
+        raise Exception(f'ERROR: in identify_nodes_SaP!')
+
     return ntags
 
 
@@ -973,82 +1063,38 @@ if __name__ == "__main__":
     # print(help(vtk.vtkCellArray))
     # import sys; sys.exit()
 
-    # basic implementation of "sweep & prune" algorithm.
+    # test of the "sweep & prune" algorithm.
+
+    npts = 10
 
     # build a list of points with random coordinates
     import random
-    all_pts = []
-    for i in range(10):
-        all_pts.append(Pt([random.random(), random.random(), random.random()]))
+    all_coords = []
+    all_no = []
+    for i in range(npts):
+        all_coords.append([random.random(), random.random(), random.random()])
+        all_no.append(i)
 
     # build a list of selected points
-    pts = []
-    pts.append(all_pts[1])
-    pts.append(all_pts[2])
-    pts.append(all_pts[5])
-    pts.append(all_pts[8])
+    coords = []
+    coords.append(all_coords[1])
+    coords.append(all_coords[2])
+    coords.append(all_coords[5])
+    coords.append(all_coords[8])
 
     # add some variants of the selected pts
-    for p in pts[0:3]:
-        all_pts.append(Pt([p.x[0], 0.0, 1.0]))
-        all_pts.append(Pt([p.x[0], p.x[1], 1.0]))
-
+    for i, p in enumerate(all_coords[0:3]):
+        all_coords.append([p[0], 2.0, 2.0]); all_no.append(npts); npts+=1
+        all_coords.append([p[0], p[1], 2.0]); all_no.append(npts); npts+=1
+        
+    # display all points
     print(f'all points:')
-    for no, p in enumerate(all_pts):
+    for no, p in zip(all_no, all_coords):
         print(f'\t{no}: {p}')
 
-    print(f'selected:')
-    for p in pts:
+    # display selected coordinates
+    print(f'selected coordinates:')
+    for p in coords:
         print(f'\t{p}')
 
-    class Nod:
-        def __init__(self, x, idx=-1):  # selected points have  a -1 index
-            self.x = x
-            self.idx = idx
-
-        def __str__(self):
-            return f'{self.idx}: {self.x}'
-
-    # build node list
-    nods = []
-    for no, p in enumerate(all_pts):
-        nods.append(Nod(p.x, no))
-    for p in pts:
-        nods.append(Nod(p.x, -1))
-
-    # sort nodes according to their x coordinate
-    nods.sort(key=lambda nod: nod.x[0])
-
-    print('sorted list:')
-    for n in nods:
-        print(f'\t{n}')
-
-    print('sweep and prune:')
-    eps = 1e-3    # geometrical tolerance
-    ntests = 0
-    nnods = len(nods)
-    i = 0
-    while i < nnods:
-        n = nods[i]
-        x = n.x[0]
-        i2 = i
-        while True:
-            i2 += 1
-            if i2 == nnods:
-                break
-            n2 = nods[i2]
-            x2 = n2.x[0]
-            if abs(x2-x) > eps:  # test X
-                break
-            ntests += 1
-            if n.idx >= 0 and n2.idx >= 0:  # nodes from different sets
-                continue
-            if abs(n.x[1]-n2.x[1]) > eps:  # test Y
-                continue
-            if abs(n.x[2]-n2.x[2]) > eps:  # test Z
-                continue
-            no = max(n.idx, n2.idx)
-            print(f'{no} is selected')
-        i += 1
-    print(
-        f'sweep and prune done ({ntests} tests instead of {len(pts)*len(all_pts)})')
+    identify_nodes_SaP(coords, all_no, all_coords, eps=1e-3)
diff --git a/models/mandiblemodel.py b/models/mandiblemodel.py
index e86639ed17e37162cf35348e778e45c306c0cfdb..ce67332ea4de8202075545e505630400fb39c835 100644
--- a/models/mandiblemodel.py
+++ b/models/mandiblemodel.py
@@ -314,7 +314,8 @@ def create_group(mesh_file_or_coord, all_no, all_coords, domain, groups, gname=N
     else:
         raise Exception(f'Data is not understood: {mesh_file_or_coord}')
     # identify nodes of the mesh among the given vertices of the main mesh
-    ntags = boneload.identify_nodes(nodes, all_no, all_coords)
+    # ntags = boneload.identify_nodes_brutal(nodes, all_no, all_coords)
+    ntags = boneload.identify_nodes_SaP(nodes, all_no, all_coords)
 
     # create a metafor group
     groupset = domain.getGeometry().getGroupSet()