diff --git a/models/bonemodel.py b/models/bonemodel.py
index d2624db217d9e0ab9ce0345e78de724ac934126c..00d1c7acbeed271bc9f1346e22175ce0b007bf4b 100644
--- a/models/bonemodel.py
+++ b/models/bonemodel.py
@@ -18,23 +18,18 @@ def parms(d={}):
     """
     p = {}
 
-    # Geomagic/MeshLab input files
+    # folder containing meshes
     path = 'dolicorhynchops/10k'
+
     # mandible mesh (.ply/.geo/.msh)
-    p['bone'] = f'{path}/mandible.ply' 
-    # one or several vertices (.ply/.off) (used if p['food']=='fixteeth')
-    p['contact_pts'] = f'{path}/teeth.off'
-    # left temporomandibular joint - 1 vertex (.ply/.off/coordinates)
-    p['axis_pt1'] = [0.1458893, -73.13895, 227.3909]
-    # right temporomandibular joint - 1 vertex (.ply/.off/coordinates) 
-    p['axis_pt2'] = f'{path}/RTMJ.off'
+    p['bone'] = f'{path}/mandible.ply'
     
     p['muscles'] = [
         { 
             'file': f'{path}/Lmuscle.ply', 
             'force': 100., 
             'focalpt': f'{path}/LmuscleF.off', 
-            'method': 'T'                       # loading model: 'U', 'T', 'T+N'
+            'method': 'T' # loading model: 'U', 'T', 'T+N', 'D' or 'N'
         },
         { 
             'file': f'{path}/Rmuscle.off', 
@@ -43,12 +38,35 @@ def parms(d={}):
             'method':'T'
         }
     ]
+
+    # list of fixed nodes
+    #   with "name" a description string used for output purposes,
+    #        "nodes" a list of nodes (coordinates) or a mesh file,
+    #        "direction" a list of constrained degrees of freedom
+
+    p['fixations'] = [
+        {
+            'name': 'contact_pts',
+            'nodes': [[-10.20362, -17.46838, -229.9061],
+                        [-11.92466, 26.3042, -229.5354]],
+            'direction': 'x'
+        },
+        {
+            'name': 'axis_pt1',
+            'nodes': f'{path}/LTMJ.off',
+            'direction': ['x', 'y', 'z']
+        },
+        {
+            'name': 'axis_pt2',
+            'nodes': [-8.716309, 79.13171, 233.8385],
+            'direction': ['x', 'z']
+        }
+    ]
+
+    p['loads'] = [] # additional loads
+
     p['food'] = 'fixteeth'  # type of food: 'fixteeth' or 'cylinder'
-    p['fixations'] = {  
-        'contact_pts': ['x','y','z'],  # constrained degrees of freedom
-        'axis_pt1': ['x','y','z'],
-        'axis_pt2': ['x','y','z']
-    }
+    
     # rigid cylinder (used if p['food']=='cylinder') 
     R = 20
     p['cylinder'] = {
@@ -90,13 +108,12 @@ def getMetafor(p={}):
     # load main mandible mesh (volume or surface - it will be meshed in 3D)
     import_mesh(domain, p['bone'], p['use_gmshOld'])
 
-    # define groups
+    # -- define groups
 
     # group #1 is the group containing all volume elements
     groupset = geometry.getGroupSet()    
     groups = {}
     groups['all'] = groupset(1)
-
     print("the mandible mesh has %d nodes" % groups['all'].getNumberOfMeshPoints())
 
     # extract external surface of the mesh => groups['surf']
@@ -106,14 +123,10 @@ def getMetafor(p={}):
     groups['surf'].addMeshPointsFromObject(groups['all'], BoundarySelector())
     nods_no, nods_pos = extract_nodes_from_group(groups['surf'])
 
-    # load other surface meshes  => groups['axis_pt1'], ...
-    for meshname in ['axis_pt1', 'axis_pt2']:
-        create_group(p[meshname], nods_no, nods_pos, domain, groups, meshname)
-
     # load muscle groups
     mgroups = {}                    # stores muscle group data and loads
     for muscle in p['muscles']:
-        # load focal point
+        # load focal point / loading direction
         if isinstance(muscle['focalpt'], str):
             fullpath = os.path.join(os.path.dirname(__file__), muscle['focalpt'])
             focalnodes, _ = boneload.load_msh(fullpath)
@@ -128,7 +141,11 @@ def getMetafor(p={}):
             total_force=muscle['force'], target=focalnodes[0], method=muscle['method'])
         # store data in a structure
         mgroups[name] = MuscleGroup(nodes, tris, ntags, loads)
-        
+
+    # create groups of fixed nodes
+    for fix in p['fixations']:
+        create_group(fix['nodes'], nods_no, nods_pos, domain, groups, fix['name'])
+
     # --------------------------------------------------------------------------
 
     # material 
@@ -152,13 +169,13 @@ def getMetafor(p={}):
     fct.setData(0.0, 0.0)
     fct.setData(1.0, 1.0)
 
-    txt2key = { 'x': TX, 'y':TY, 'z':TZ }
-
-    # axis of rotation
-    for gname in ['axis_pt1', 'axis_pt2']:
-        for d in p['fixations'][gname]:
-            domain.getLoadingSet().define(groups[gname], Field1D(txt2key[d],RE), 0.0)
+    # add Dirichlet conditions (fixations)
+    txt2key = { 'x': TX, 'y': TY, 'z': TZ }
+    for fix in p['fixations']:
+        for d in fix['direction']:
+            domain.getLoadingSet().define(groups[fix['name']], Field1D(txt2key[d],RE), 0.0)
 
+    # add Neumann conditions (loads) from muscle loads
     mshpts = geometry.getMesh().getPointSet()
     for name, gr in mgroups.items():
         # print(f'len(gr.ntags)={len(gr.ntags)}')
@@ -169,6 +186,16 @@ def getMetafor(p={}):
             domain.getLoadingSet().define(target, Field1D(TY,GF1), load.x[1], fct)
             domain.getLoadingSet().define(target, Field1D(TZ,GF1), load.x[2], fct)
 
+    # add Neumann conditions (loads) from external loads
+    dirs = [TX,TY,TZ]
+    for load in p['loads']:
+        name, nodes, tris, ntags = \
+            create_group(load['nodes'], nods_no, nods_pos, domain, groups, load['name'])
+        for ntag in ntags:
+            for i in range(3):
+                target = mshpts(ntag)
+                domain.getLoadingSet().define(target, Field1D(dirs[i],GF1), load['values'][i], fct)
+
     # type of food
 
     if p['food']=='cylinder':
@@ -193,10 +220,7 @@ def getMetafor(p={}):
         ci.addProperty(prp2)
         domain.getInteractionSet().add(ci)
     elif p['food']=='fixteeth':
-        # teeth are fixed along X
-        create_group(p['contact_pts'], nods_no, nods_pos, domain, groups, 'contact_pts')
-        for d in p['fixations']['contact_pts']:
-            domain.getLoadingSet().define(groups['contact_pts'], Field1D(txt2key[d],RE), 0.0)
+        pass
     else:
         # p['food'] is a filename
         ply2mtf(domain, p['food'])
diff --git a/models/bonemodel2.py b/models/bonemodel2.py
index 3e62d531e80a855a030a699c6b44dad57cc16103..1bc8373ee05dd0d72b6641093eb99c7018c4574d 100644
--- a/models/bonemodel2.py
+++ b/models/bonemodel2.py
@@ -18,7 +18,7 @@ def parms(d={}):
     """
     p = {}
 
-    # Geomagic/MeshLab input folder
+    # folder containing meshes
     path = 'dolicorhynchops/10k'
 
     # mandible mesh (.ply/.geo/.msh)
@@ -29,7 +29,7 @@ def parms(d={}):
             'file': f'{path}/Lmuscle.ply',
             'force': 100.,
             'focalpt': f'{path}/LmuscleF.off',
-            'method': 'T'                       # loading model: 'U', 'T', 'T+N'
+            'method': 'T'   # loading model: 'U', 'T', 'T+N', 'D' or 'N'
         },
         {
             'file': f'{path}/Rmuscle.off',
@@ -63,7 +63,7 @@ def parms(d={}):
         }
     ]
 
-    p['loads'] = []
+    p['loads'] = [] # additional loads
 
     # material properties
     p['Young'] = 17000.      # [MPa] elastic modulus - bone: 17-20 GPa
@@ -80,15 +80,15 @@ def solve(p={}):
 
     p = parms(p)
 
-    # import json
-    # print(f'parameters={json.dumps(p, indent=4, sort_keys=True)}')
+    import json
+    print(f'parameters={json.dumps(p, indent=4, sort_keys=True)}')
 
     pbl = fem.Problem()
 
     # load main mandible mesh (volume or surface - it will be meshed in 3D)
     import_mesh(p['bone'])
 
-    # define groups
+    # -- define groups
 
     # group (3,1)='all' is the group containing all volume elements
     groups = {}
@@ -97,19 +97,18 @@ def solve(p={}):
     print(f"the mandible mesh has {len(nodeTags)} nodes")
 
     # extract external surface of the mesh => groups['surf']
-    # #   nods_no: tags of the nodes
-    # #   nods_pos: coordinates of these nodes
+    #   nods_no: tags of the nodes
+    #   nods_pos: coordinates of these nodes
     groups['surf'] = (2, 1)
     nods_no, nods_pos = gmsh.model.mesh.getNodesForPhysicalGroup(
         *groups['surf'])
     print(f"the mandible surface has {len(nods_no)} nodes")
     nods_pos = np.reshape(nods_pos, (len(nods_no), 3))
-    # print(f'nods_pos={nods_pos}')
 
     # load muscle groups
     mgroups = {}                    # stores muscle group data and loads
     for muscle in p['muscles']:
-        # load focal point
+        # load focal point / loading direction
         if isinstance(muscle['focalpt'], str):
             fullpath = os.path.join(
                 os.path.dirname(__file__), muscle['focalpt'])