diff --git a/src/mesh_utils/cohesive_element_inserter_helper.cc b/src/mesh_utils/cohesive_element_inserter_helper.cc
index b50fea3d3..22f2777fa 100644
--- a/src/mesh_utils/cohesive_element_inserter_helper.cc
+++ b/src/mesh_utils/cohesive_element_inserter_helper.cc
@@ -1,947 +1,948 @@
 /**
  * @file   cohesive_element_inserter_helper.cc
  *
  * @author Nicolas Richart
  *
  * @date creation  jeu sep 03 2020
  *
  * @brief A Documented file.
  *
  * @section LICENSE
  *
  * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne)
  * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
  *
  * Akantu is free  software: you can redistribute it and/or  modify it under the
  * terms  of the  GNU Lesser  General Public  License as  published by  the Free
  * Software Foundation, either version 3 of the License, or (at your option) any
  * later version.
  *
  * Akantu is  distributed in the  hope that it  will be useful, but  WITHOUT ANY
  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  * A  PARTICULAR PURPOSE. See  the GNU  Lesser General  Public License  for more
  * details.
  *
  * You should  have received  a copy  of the GNU  Lesser General  Public License
  * along with Akantu. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 /* -------------------------------------------------------------------------- */
 #include "cohesive_element_inserter_helper.hh"
 /* -------------------------------------------------------------------------- */
 #include "data_accessor.hh"
 #include "element_synchronizer.hh"
 #include "fe_engine.hh"
 #include "mesh_accessor.hh"
 /* -------------------------------------------------------------------------- */
 #include <queue>
 /* -------------------------------------------------------------------------- */
 
 namespace akantu {
 
 /* -------------------------------------------------------------------------- */
 CohesiveElementInserterHelper::CohesiveElementInserterHelper(
     Mesh & mesh, const ElementTypeMapArray<bool> & facet_insertion)
     : doubled_nodes(0, 2, "doubled_nodes"), mesh(mesh),
       mesh_facets(mesh.getMeshFacets()) {
 
   auto spatial_dimension = mesh_facets.getSpatialDimension();
 
   for (auto gt : ghost_types) {
     for (auto type : mesh_facets.elementTypes(_ghost_type = gt)) {
       nb_new_facets(type, gt) = mesh_facets.getNbElement(type, gt);
     }
   }
 
   std::array<Int, 2> nb_facet_to_insert{0, 0};
   // creates a vector of the facets to insert
   std::vector<Element> potential_facets_to_double;
   for (auto gt_facet : ghost_types) {
     for (auto type_facet :
          mesh_facets.elementTypes(spatial_dimension - 1, gt_facet)) {
       const auto & f_insertion = facet_insertion(type_facet, gt_facet);
       auto & counter = nb_facet_to_insert[gt_facet == _not_ghost ? 0 : 1];
 
       for (auto && data : enumerate(f_insertion)) {
         if (std::get<1>(data)) {
           UInt el = std::get<0>(data);
           potential_facets_to_double.push_back({type_facet, el, gt_facet});
           ++counter;
         }
       }
     }
   }
 
   // Defines a global order of insertion oof cohesive element to ensure node
   // doubling appens in the smae order, this is necessary for the global node
   // numbering
   if (mesh_facets.isDistributed()) {
     const auto & comm = mesh_facets.getCommunicator();
 
     ElementTypeMapArray<Int> global_orderings;
 
     global_orderings.initialize(mesh_facets,
                                 _spatial_dimension = spatial_dimension - 1,
                                 _with_nb_element = true, _default_value = -1);
 
     auto starting_index = nb_facet_to_insert[0];
     comm.exclusiveScan(starting_index);
 
     // define the local numbers for facet to insert
     for (auto gt_facet : ghost_types) {
       for (auto type_facet :
            mesh_facets.elementTypes(spatial_dimension - 1, gt_facet)) {
         for (auto data : zip(facet_insertion(type_facet, gt_facet),
                              global_orderings(type_facet, gt_facet))) {
           if (std::get<0>(data)) {
             std::get<1>(data) = starting_index;
             ++starting_index;
           }
         }
       }
     }
 
     // retreives the oorder number from neighoring processors
     auto && synchronizer = mesh_facets.getElementSynchronizer();
     SimpleElementDataAccessor<Int> data_accessor(
         global_orderings, SynchronizationTag::_ce_insertion_order);
     synchronizer.synchronizeOnce(data_accessor,
                                  SynchronizationTag::_ce_insertion_order);
 
     // sort the facets to double based on this global ordering
     std::sort(potential_facets_to_double.begin(),
               potential_facets_to_double.end(),
               [&global_orderings](auto && el1, auto && el2) {
                 return global_orderings(el1) < global_orderings(el2);
               });
   }
 
   for (auto d : arange(spatial_dimension)) {
     facets_to_double_by_dim[d] = std::make_unique<Array<Element>>(
         0, 2, "facets_to_double_" + std::to_string(d));
   }
 
   auto & facets_to_double = *facets_to_double_by_dim[spatial_dimension - 1];
 
   MeshAccessor mesh_accessor(mesh_facets);
   auto & elements_to_subelements = mesh_accessor.getElementToSubelement();
 
   for (auto && facet_to_double : potential_facets_to_double) {
     auto gt_facet = facet_to_double.ghost_type;
     auto type_facet = facet_to_double.type;
     auto & elements_to_facets = elements_to_subelements(type_facet, gt_facet);
 
     auto & elements_to_facet = elements_to_facets(facet_to_double.element);
     if (elements_to_facet[1].type == _not_defined
 #if defined(AKANTU_COHESIVE_ELEMENT)
         || elements_to_facet[1].kind() == _ek_cohesive
 #endif
     ) {
       AKANTU_DEBUG_WARNING("attempt to double a facet on the boundary");
       continue;
     }
 
     auto new_facet = nb_new_facets(type_facet, gt_facet)++;
 
     facets_to_double.push_back(Vector<Element>{
         facet_to_double, Element{type_facet, new_facet, gt_facet}});
 
     /// update facet_to_element vector
     auto & element_to_update = elements_to_facet[1];
     auto el = element_to_update.element;
 
-    auto & facets_to_elements = mesh_facets.getSubelementToElement(
+    const auto & facets_to_elements = mesh_facets.getSubelementToElement(
         element_to_update.type, element_to_update.ghost_type);
     auto facets_to_element = Vector<Element>(
         make_view(facets_to_elements, facets_to_elements.getNbComponent())
             .begin()[el]);
     auto facet_to_update = std::find(facets_to_element.begin(),
                                      facets_to_element.end(), facet_to_double);
 
     AKANTU_DEBUG_ASSERT(facet_to_update != facets_to_element.end(),
                         "Facet not found");
 
     facet_to_update->element = new_facet;
 
     /// update elements connected to facet
     const auto & first_facet_list = elements_to_facet;
     elements_to_facets.push_back(first_facet_list);
 
     /// set new and original facets as boundary facets
     elements_to_facets(new_facet)[0] = elements_to_facets(new_facet)[1];
     elements_to_facets(new_facet)[1] = ElementNull;
 
     elements_to_facets(facet_to_double.element)[1] = ElementNull;
   }
 }
 
 /* -------------------------------------------------------------------------- */
 inline auto
 CohesiveElementInserterHelper::hasElement(const Vector<UInt> & nodes_element,
                                           const Vector<UInt> & nodes) -> bool {
   // if one of the nodes of "nodes" is not in "nodes_element" it stops
   auto it = std::mismatch(nodes.begin(), nodes.end(), nodes_element.begin(),
                           [&](auto && node, auto && /*node2*/) -> bool {
                             auto it = std::find(nodes_element.begin(),
                                                 nodes_element.end(), node);
                             return (it != nodes_element.end());
                           });
 
   // true if all "nodes" where found in "nodes_element"
   return (it.first == nodes.end());
 }
 
 /* -------------------------------------------------------------------------- */
 inline auto CohesiveElementInserterHelper::removeElementsInVector(
     const std::vector<Element> & elem_to_remove,
     std::vector<Element> & elem_list) -> bool {
   if (elem_list.size() <= elem_to_remove.size()) {
     return true;
   }
 
   auto el_it = elem_to_remove.begin();
   auto el_last = elem_to_remove.end();
   std::vector<Element>::iterator el_del;
 
   UInt deletions = 0;
 
   for (; el_it != el_last; ++el_it) {
     el_del = std::find(elem_list.begin(), elem_list.end(), *el_it);
 
     if (el_del != elem_list.end()) {
       elem_list.erase(el_del);
       ++deletions;
     }
   }
 
   AKANTU_DEBUG_ASSERT(deletions == 0 || deletions == elem_to_remove.size(),
                       "Not all elements have been erased");
 
   return deletions == 0;
 }
 
 /* -------------------------------------------------------------------------- */
 void CohesiveElementInserterHelper::updateElementalConnectivity(
     Mesh & mesh, UInt old_node, UInt new_node,
     const std::vector<Element> & element_list,
     const std::vector<Element> * facet_list) {
   AKANTU_DEBUG_IN();
 
-  for (auto & element : element_list) {
+  for (const auto & element : element_list) {
     if (element.type == _not_defined) {
       continue;
     }
 
     Vector<UInt> connectivity = mesh.getConnectivity(element);
 
     if (element.kind() == _ek_cohesive) {
       AKANTU_DEBUG_ASSERT(
           facet_list != nullptr,
           "Provide a facet list in order to update cohesive elements");
 
       const Vector<Element> facets =
           mesh_facets.getSubelementToElement(element);
 
       auto facet_nb_nodes = connectivity.size() / 2;
 
       /// loop over cohesive element's facets
       for (const auto & facet : enumerate(facets)) {
         /// skip facets if not present in the list
         if (std::find(facet_list->begin(), facet_list->end(),
                       std::get<1>(facet)) == facet_list->end()) {
           continue;
         }
 
         auto n = std::get<0>(facet);
 
-        auto begin = connectivity.begin() + static_cast<UInt>(n * facet_nb_nodes);
+        auto begin =
+            connectivity.begin() + static_cast<UInt>(n * facet_nb_nodes);
         auto end = begin + facet_nb_nodes;
 
         auto it = std::find(begin, end, old_node);
         AKANTU_DEBUG_ASSERT(it != end, "Node not found in current element");
 
         *it = new_node;
       }
     } else {
       auto it = std::find(connectivity.begin(), connectivity.end(), old_node);
       AKANTU_DEBUG_ASSERT(it != connectivity.end(),
                           "Node not found in current element");
 
       /// update connectivity
       *it = new_node;
     }
   }
 
   AKANTU_DEBUG_OUT();
 }
 
 /* -------------------------------------------------------------------------- */
 void CohesiveElementInserterHelper::updateSubelementToElement(UInt dim,
                                                               bool facet_mode) {
   auto & facets_to_double = *facets_to_double_by_dim[dim];
-  auto & facets_to_subfacets =
-      elementsOfDimToElementsOfDim(dim + static_cast<decltype(dim)>(facet_mode), dim);
+  auto & facets_to_subfacets = elementsOfDimToElementsOfDim(
+      dim + static_cast<decltype(dim)>(facet_mode), dim);
 
   for (auto && data :
        zip(make_view(facets_to_double, 2), facets_to_subfacets)) {
     const auto & old_subfacet = std::get<0>(data)[0];
     const auto & new_subfacet = std::get<0>(data)[1];
     auto & facet_to_subfacets = std::get<1>(data);
 
     MeshAccessor mesh_accessor(mesh_facets);
     for (auto & facet : facet_to_subfacets) {
       Vector<Element> subfacets = mesh_accessor.getSubelementToElement(facet);
 
       auto && subfacet_to_update_it =
           std::find(subfacets.begin(), subfacets.end(), old_subfacet);
 
       AKANTU_DEBUG_ASSERT(subfacet_to_update_it != subfacets.end(),
                           "Subfacet not found");
 
       *subfacet_to_update_it = new_subfacet;
     }
   }
 }
 
 /* -------------------------------------------------------------------------- */
 void CohesiveElementInserterHelper::updateElementToSubelement(UInt dim,
                                                               bool facet_mode) {
   auto & facets_to_double = *facets_to_double_by_dim[dim];
   auto & facets_to_subfacets = elementsOfDimToElementsOfDim(
       dim + static_cast<decltype(dim)>(facet_mode), dim);
 
   MeshAccessor mesh_accessor(mesh_facets);
   // resize the arrays
   mesh_accessor.getElementToSubelement().initialize(
       mesh_facets, _spatial_dimension = dim, _with_nb_element = true);
 
   for (auto && data :
        zip(make_view(facets_to_double, 2), facets_to_subfacets)) {
     const auto & new_facet = std::get<0>(data)[1];
     mesh_accessor.getElementToSubelement(new_facet) = std::get<1>(data);
   }
 }
 
 /* -------------------------------------------------------------------------- */
 void CohesiveElementInserterHelper::updateQuadraticSegments(UInt dim) {
   AKANTU_DEBUG_IN();
   auto spatial_dimension = mesh.getSpatialDimension();
   auto & facets_to_double = *facets_to_double_by_dim[dim];
 
   MeshAccessor mesh_accessor(mesh_facets);
   auto & connectivities = mesh_accessor.getConnectivities();
 
   /// this ones matter only for segments in 3D
   Array<std::vector<Element>> * element_to_subfacet_double = nullptr;
   Array<std::vector<Element>> * facet_to_subfacet_double = nullptr;
 
   if (dim == spatial_dimension - 2) {
     element_to_subfacet_double = &elementsOfDimToElementsOfDim(dim + 2, dim);
     facet_to_subfacet_double = &elementsOfDimToElementsOfDim(dim + 1, dim);
   }
 
-  auto & element_to_subelement = mesh_facets.getElementToSubelement();
+  const auto & element_to_subelement = mesh_facets.getElementToSubelement();
   std::vector<UInt> middle_nodes;
 
   for (auto && facet_to_double : make_view(facets_to_double, 2)) {
     auto & old_facet = facet_to_double[0];
     if (old_facet.type != _segment_3) {
       continue;
     }
 
     auto node = connectivities(old_facet, 2);
     if (not mesh.isPureGhostNode(node)) {
       middle_nodes.push_back(node);
     }
   }
 
   auto n = doubled_nodes.size();
   doubleNodes(middle_nodes);
 
   for (auto && data : enumerate(make_view(facets_to_double, 2))) {
     auto facet = std::get<0>(data);
     auto & old_facet = std::get<1>(data)[0];
     if (old_facet.type != _segment_3) {
       continue;
     }
 
     auto old_node = connectivities(old_facet, 2);
 
     if (mesh.isPureGhostNode(old_node)) {
       continue;
     }
 
     auto new_node = doubled_nodes(n, 1);
 
     auto & new_facet = std::get<1>(data)[1];
     connectivities(new_facet, 2) = new_node;
 
     if (dim == spatial_dimension - 2) {
       updateElementalConnectivity(mesh_facets, old_node, new_node,
                                   element_to_subelement(new_facet, 0));
 
       updateElementalConnectivity(mesh, old_node, new_node,
                                   (*element_to_subfacet_double)(facet),
                                   &(*facet_to_subfacet_double)(facet));
     } else {
       updateElementalConnectivity(mesh, old_node, new_node,
                                   element_to_subelement(new_facet, 0));
     }
 
     ++n;
   }
 
   AKANTU_DEBUG_OUT();
 }
 
 /* -------------------------------------------------------------------------- */
 UInt CohesiveElementInserterHelper::insertCohesiveElement() {
   auto nb_new_facets = insertFacetsOnly();
   if (nb_new_facets == 0) {
     return 0;
   }
 
   updateCohesiveData();
   return nb_new_facets;
 }
 
 /* -------------------------------------------------------------------------- */
 UInt CohesiveElementInserterHelper::insertFacetsOnly() {
   UInt spatial_dimension = mesh.getSpatialDimension();
 
   if (facets_to_double_by_dim[spatial_dimension - 1]->empty()) {
     return 0;
   }
 
   if (spatial_dimension == 1) {
     doublePointFacet();
   } else if (spatial_dimension == 2) {
     doubleFacets<1>();
     findSubfacetToDouble<1>();
 
     doubleSubfacet<2>();
 
   } else if (spatial_dimension == 3) {
     doubleFacets<2>();
     findSubfacetToDouble<2>();
 
     doubleFacets<1>();
     findSubfacetToDouble<1>();
 
     doubleSubfacet<3>();
   }
 
   return facets_to_double_by_dim[spatial_dimension - 1]->size();
 }
 
 /* -------------------------------------------------------------------------- */
 template <UInt dim> void CohesiveElementInserterHelper::doubleFacets() {
   AKANTU_DEBUG_IN();
   NewElementsEvent new_facets;
   auto spatial_dimension = mesh_facets.getSpatialDimension();
 
   auto & facets_to_double = *facets_to_double_by_dim[dim];
   MeshAccessor mesh_accessor(mesh_facets);
   for (auto && facet_to_double : make_view(facets_to_double, 2)) {
     auto && old_facet = facet_to_double[0];
     auto && new_facet = facet_to_double[1];
 
     auto & facets_connectivities =
         mesh_accessor.getConnectivity(old_facet.type, old_facet.ghost_type);
     facets_connectivities.resize(
         nb_new_facets(old_facet.type, old_facet.ghost_type));
 
     auto facets_connectivities_begin =
         make_view(facets_connectivities, facets_connectivities.getNbComponent())
             .begin();
 
     // copy the connectivities
     Vector<UInt> new_conn(facets_connectivities_begin[new_facet.element]);
     Vector<UInt> old_conn(facets_connectivities_begin[old_facet.element]);
     new_conn = old_conn;
 
     // this will fail if multiple facet types exists for a given element type
     // \TODO handle multiple sub-facet types
     auto nb_subfacet_per_facet = Mesh::getNbFacetsPerElement(old_facet.type);
 
     auto & subfacets_to_facets = mesh_accessor.getSubelementToElementNC(
         old_facet.type, old_facet.ghost_type);
 
     subfacets_to_facets.resize(
         nb_new_facets(old_facet.type, old_facet.ghost_type), ElementNull);
 
     auto subfacets_to_facets_begin =
         make_view(subfacets_to_facets, nb_subfacet_per_facet).begin();
 
     // copy the subfacet to facets information
     Vector<Element> old_subfacets_to_facet(
         subfacets_to_facets_begin[old_facet.element]);
     Vector<Element> new_subfacet_to_facet(
         subfacets_to_facets_begin[new_facet.element]);
     new_subfacet_to_facet = old_subfacets_to_facet;
 
     for (auto & subfacet : old_subfacets_to_facet) {
       if (subfacet == ElementNull) {
         continue;
       }
 
       /// update facet_to_subfacet array
       mesh_accessor.getElementToSubelement(subfacet).push_back(new_facet);
     }
 
     new_facets.getList().push_back(new_facet);
   }
 
   /// update facet_to_subfacet and _segment_3 facets if any
   if (dim != spatial_dimension - 1) {
     updateSubelementToElement(dim, true);
     updateElementToSubelement(dim, true);
     updateQuadraticSegments(dim);
   } else {
     updateQuadraticSegments(dim);
   }
 
   mesh_facets.sendEvent(new_facets);
   AKANTU_DEBUG_OUT();
 }
 
 /* -------------------------------------------------------------------------- */
 template <UInt dim> void CohesiveElementInserterHelper::findSubfacetToDouble() {
   AKANTU_DEBUG_IN();
 
   UInt spatial_dimension = mesh_facets.getSpatialDimension();
 
   MeshAccessor mesh_accessor(mesh_facets);
 
   auto & facets_to_double = *facets_to_double_by_dim[spatial_dimension - 1];
   auto & subfacets_to_facets = mesh_facets.getSubelementToElement();
   auto & elements_to_facets = mesh_accessor.getElementToSubelement();
 
   /// loop on every facet
   for (auto f : arange(2)) {
     for (auto && facet_to_double : make_view(facets_to_double, 2)) {
       auto old_facet = facet_to_double[f];
       auto new_facet = facet_to_double[1 - f];
 
       const auto & starting_element = elements_to_facets(new_facet, 0)[0];
       auto current_facet = old_facet;
 
       Vector<Element> subfacets_to_facet = subfacets_to_facets.get(old_facet);
       /// loop on every subfacet
       for (auto & subfacet : subfacets_to_facet) {
         if (subfacet == ElementNull) {
           continue;
         }
 
         if (dim == spatial_dimension - 2) {
           Vector<Element> subsubfacets_to_subfacet =
               subfacets_to_facets.get(subfacet);
           /// loop on every subsubfacet
           for (auto & subsubfacet : subsubfacets_to_subfacet) {
             if (subsubfacet == ElementNull) {
               continue;
             }
 
             Vector<UInt> subsubfacet_connectivity =
                 mesh_facets.getConnectivity(subsubfacet);
 
             std::vector<Element> element_list;
             std::vector<Element> facet_list;
             std::vector<Element> subfacet_list;
 
             /// check if subsubfacet is to be doubled
             if (findElementsAroundSubfacet(
                     starting_element, current_facet, subsubfacet_connectivity,
                     element_list, facet_list, &subfacet_list) == false and
                 removeElementsInVector(
                     subfacet_list, elements_to_facets(subsubfacet)) == false) {
               Element new_subsubfacet{
                   subsubfacet.type,
                   nb_new_facets(subsubfacet.type, subsubfacet.ghost_type)++,
                   subsubfacet.ghost_type};
               facets_to_double_by_dim[dim - 1]->push_back(
                   Vector<Element>{subsubfacet, new_subsubfacet});
               elementsOfDimToElementsOfDim(dim - 1, dim - 1)
                   .push_back(subfacet_list);
-              elementsOfDimToElementsOfDim(dim, dim - 1)
-                  .push_back(facet_list);
+              elementsOfDimToElementsOfDim(dim, dim - 1).push_back(facet_list);
               elementsOfDimToElementsOfDim(dim + 1, dim - 1)
                   .push_back(element_list);
             }
           }
         } else {
           std::vector<Element> element_list;
           std::vector<Element> facet_list;
 
           Vector<UInt> subfacet_connectivity =
               mesh_facets.getConnectivity(subfacet);
 
           /// check if subfacet is to be doubled
           if (findElementsAroundSubfacet(starting_element, current_facet,
                                          subfacet_connectivity, element_list,
                                          facet_list) == false and
               removeElementsInVector(facet_list,
                                      elements_to_facets(subfacet)) == false) {
             Element new_subfacet{
                 subfacet.type,
                 nb_new_facets(subfacet.type, subfacet.ghost_type)++,
                 subfacet.ghost_type};
             facets_to_double_by_dim[dim - 1]->push_back(
                 Vector<Element>{subfacet, new_subfacet});
 
             elementsOfDimToElementsOfDim(dim, dim - 1).push_back(facet_list);
             elementsOfDimToElementsOfDim(dim + 1, dim - 1)
                 .push_back(element_list);
           }
         }
       }
     }
   }
 }
 
 /* -------------------------------------------------------------------------- */
 void CohesiveElementInserterHelper::doubleNodes(
     const std::vector<UInt> & old_nodes) {
   auto & position = mesh.getNodes();
   auto spatial_dimension = mesh.getSpatialDimension();
 
   auto old_nb_nodes = position.size();
   position.reserve(old_nb_nodes + old_nodes.size());
   doubled_nodes.reserve(doubled_nodes.size() + old_nodes.size());
 
   auto position_begin = position.begin(spatial_dimension);
   for (auto && data : enumerate(old_nodes)) {
     auto n = std::get<0>(data);
     auto old_node = std::get<1>(data);
     decltype(old_node) new_node = old_nb_nodes + n;
 
     /// store doubled nodes
     doubled_nodes.push_back(Vector<UInt>{old_node, new_node});
 
     /// update position
     Vector<Real> coords = Vector<Real>(position_begin[old_node]);
     position.push_back(coords);
   }
 }
 
 /* -------------------------------------------------------------------------- */
 bool CohesiveElementInserterHelper::findElementsAroundSubfacet(
     const Element & starting_element, const Element & end_facet,
     const Vector<UInt> & subfacet_connectivity,
     std::vector<Element> & element_list, std::vector<Element> & facet_list,
     std::vector<Element> * subfacet_list) {
   bool facet_matched = false;
 
   element_list.push_back(starting_element);
 
   std::queue<Element> elements_to_check;
   elements_to_check.push(starting_element);
 
   /// keep going as long as there are elements to check
   while (not elements_to_check.empty()) {
     /// check current element
     Element & current_element = elements_to_check.front();
 
     const Vector<Element> facets_to_element =
         mesh_facets.getSubelementToElement(current_element);
 
     // for every facet of the element
     for (auto & current_facet : facets_to_element) {
       if (current_facet == ElementNull) {
         continue;
       }
 
       if (current_facet == end_facet) {
         facet_matched = true;
       }
 
       auto find_facet =
           std::find(facet_list.begin(), facet_list.end(), current_facet);
 
       // facet already listed or subfacet_connectivity is not in the
       // connectivity of current_facet;
       if ((find_facet != facet_list.end()) or
           not hasElement(mesh_facets.getConnectivity(current_facet),
                          subfacet_connectivity)) {
         continue;
       }
 
       facet_list.push_back(current_facet);
 
       if (subfacet_list != nullptr) {
         const Vector<Element> subfacets_of_facet =
             mesh_facets.getSubelementToElement(current_facet);
 
         /// check subfacets
         for (const auto & current_subfacet : subfacets_of_facet) {
           if (current_subfacet == ElementNull) {
             continue;
           }
 
           auto find_subfacet = std::find(
               subfacet_list->begin(), subfacet_list->end(), current_subfacet);
           if ((find_subfacet != subfacet_list->end()) or
               not hasElement(mesh_facets.getConnectivity(current_subfacet),
                              subfacet_connectivity)) {
             continue;
           }
 
           subfacet_list->push_back(current_subfacet);
         }
       }
 
       /// consider opposing element
       const auto & elements_to_facet =
           mesh_facets.getElementToSubelement(current_facet);
       UInt opposing = 0;
       if (elements_to_facet[0] == current_element) {
         opposing = 1;
       }
 
       auto & opposing_element = elements_to_facet[opposing];
 
       /// skip null elements since they are on a boundary
       if (opposing_element == ElementNull) {
         continue;
       }
 
       /// skip this element if already added
       if (std::find(element_list.begin(), element_list.end(),
                     opposing_element) != element_list.end()) {
         continue;
       }
 
       /// only regular elements have to be checked
       if (opposing_element.kind() == _ek_regular) {
         elements_to_check.push(opposing_element);
       }
 
       element_list.push_back(opposing_element);
 
       AKANTU_DEBUG_ASSERT(hasElement(mesh.getConnectivity(opposing_element),
                                      subfacet_connectivity),
                           "Subfacet doesn't belong to this element");
     }
 
     /// erased checked element from the list
     elements_to_check.pop();
   }
 
   return facet_matched;
 }
 
 /* -------------------------------------------------------------------------- */
 void CohesiveElementInserterHelper::updateCohesiveData() {
 
   UInt spatial_dimension = mesh.getSpatialDimension();
   bool third_dimension = spatial_dimension == 3;
 
   MeshAccessor mesh_accessor(mesh);
   MeshAccessor mesh_facets_accessor(mesh_facets);
 
   for (auto ghost_type : ghost_types) {
     for (auto cohesive_type : mesh.elementTypes(_element_kind = _ek_cohesive)) {
       auto nb_cohesive_elements = mesh.getNbElement(cohesive_type, ghost_type);
       nb_new_facets(cohesive_type, ghost_type) = nb_cohesive_elements;
 
       mesh_facets_accessor.getSubelementToElement(cohesive_type, ghost_type);
     }
   }
 
   auto & facets_to_double = *facets_to_double_by_dim[spatial_dimension - 1];
 
   new_elements.reserve(new_elements.size() + facets_to_double.size());
 
   std::array<Element, 2> facets;
 
   auto & element_to_facet = mesh_facets_accessor.getElementToSubelement();
   auto & facets_to_cohesive_elements =
       mesh_facets_accessor.getSubelementToElement();
 
   for (auto && facet_to_double : make_view(facets_to_double, 2)) {
     auto & old_facet = facet_to_double[0];
 
     /// (in 3D cohesive elements connectivity is inverted)
     facets[third_dimension ? 1 : 0] = old_facet;
     facets[third_dimension ? 0 : 1] = facet_to_double[1];
 
     auto type_cohesive = FEEngine::getCohesiveElementType(old_facet.type);
 
     auto & facet_connectivities =
         mesh_facets.getConnectivity(old_facet.type, old_facet.ghost_type);
     auto facet_connectivity_it =
         facet_connectivities.begin(facet_connectivities.getNbComponent());
 
     auto cohesive_element = Element{
         type_cohesive, nb_new_facets(type_cohesive, old_facet.ghost_type)++,
         old_facet.ghost_type};
 
     auto & cohesives_connectivities =
         mesh_accessor.getConnectivity(type_cohesive, old_facet.ghost_type);
     Matrix<UInt> connectivity(facet_connectivities.getNbComponent(), 2);
     Vector<Element> facets_to_cohesive_element(2);
 
     for (auto s : arange(2)) {
       /// store doubled facets
       facets_to_cohesive_element[s] = facets[s];
 
       // update connectivities
       connectivity(s) = Vector<UInt>(facet_connectivity_it[facets[s].element]);
 
       /// update element_to_facet vectors
       element_to_facet(facets[s], 0)[1] = cohesive_element;
     }
 
     cohesives_connectivities.push_back(connectivity);
 
     facets_to_cohesive_elements(type_cohesive, old_facet.ghost_type)
         .push_back(facets_to_cohesive_element);
 
     /// add cohesive element to the element event list
     new_elements.push_back(cohesive_element);
   }
 }
 
 /* -------------------------------------------------------------------------- */
 void CohesiveElementInserterHelper::doublePointFacet() {
   UInt spatial_dimension = mesh.getSpatialDimension();
   if (spatial_dimension != 1) {
     return;
   }
 
   NewElementsEvent new_facets_event;
 
   auto & facets_to_double = *facets_to_double_by_dim[spatial_dimension - 1];
-  auto & element_to_facet = mesh_facets.getElementToSubelement();
+  const auto & element_to_facet = mesh_facets.getElementToSubelement();
   auto & position = mesh.getNodes();
   MeshAccessor mesh_accessor(mesh_facets);
 
   for (auto ghost_type : ghost_types) {
     for (auto facet_type : nb_new_facets.elementTypes(
              spatial_dimension - 1, ghost_type, _ek_regular)) {
       auto nb_new_element = nb_new_facets(facet_type, ghost_type);
       auto & connectivities =
           mesh_accessor.getConnectivity(facet_type, ghost_type);
-      connectivities.resize(connectivities.size() + nb_new_element);
+      connectivities.resize(nb_new_element);
     }
   }
 
+  position.reserve(position.size() + facets_to_double.size());
   for (auto facet_to_double : make_view(facets_to_double, 2)) {
     auto & old_facet = facet_to_double[0];
     auto & new_facet = facet_to_double[1];
 
     auto element = element_to_facet(new_facet)[0];
     auto & facet_connectivities =
         mesh_accessor.getConnectivity(old_facet.type, old_facet.ghost_type);
     auto old_node = facet_connectivities(old_facet.element);
     auto new_node = position.size();
 
     /// update position
     position.push_back(position(old_node));
     facet_connectivities(new_facet.element) = new_node;
 
     Vector<UInt> segment_conectivity = mesh.getConnectivity(element);
 
     /// update facet connectivity
     auto it = std::find(segment_conectivity.begin(), segment_conectivity.end(),
                         old_node);
     *it = new_node;
 
     doubled_nodes.push_back(Vector<UInt>{old_node, new_node});
     new_facets_event.getList().push_back(new_facet);
   }
 
   mesh_facets.sendEvent(new_facets_event);
 }
 
 /* -------------------------------------------------------------------------- */
 template <UInt spatial_dimension>
 void CohesiveElementInserterHelper::doubleSubfacet() {
   if (spatial_dimension == 1) {
     return;
   }
 
   NewElementsEvent new_facets_event;
 
   std::vector<UInt> nodes_to_double;
   MeshAccessor mesh_accessor(mesh_facets);
   auto & connectivities = mesh_accessor.getConnectivities();
 
   auto & facets_to_double = *facets_to_double_by_dim[0];
   ElementTypeMap<Int> nb_new_elements;
   for (auto && facet_to_double : make_view(facets_to_double, 2)) {
     auto && old_element = facet_to_double[0];
     nb_new_elements(old_element.type, old_element.ghost_type) = 0;
   }
 
   for (auto && facet_to_double : make_view(facets_to_double, 2)) {
     auto && old_element = facet_to_double[0];
     ++nb_new_elements(old_element.type, old_element.ghost_type);
   }
 
   for (auto ghost_type : ghost_types) {
     for (auto facet_type :
          nb_new_elements.elementTypes(0, ghost_type, _ek_regular)) {
       auto & connectivities =
           mesh_accessor.getConnectivity(facet_type, ghost_type);
       connectivities.resize(connectivities.size() +
                             nb_new_elements(facet_type, ghost_type));
     }
   }
 
   for (auto && facet_to_double : make_view(facets_to_double, 2)) {
     auto & old_facet = facet_to_double(0);
     // auto & new_facet = facet_to_double(1);
 
     AKANTU_DEBUG_ASSERT(
         old_facet.type == _point_1,
         "Only _point_1 subfacet doubling is supported at the moment");
 
     /// list nodes double
     nodes_to_double.push_back(connectivities(old_facet));
   }
 
   auto old_nb_doubled_nodes = doubled_nodes.size();
   doubleNodes(nodes_to_double);
 
   auto double_nodes_view = make_view(doubled_nodes, 2);
 
   for (auto && data :
        zip(make_view(facets_to_double, 2),
            range(double_nodes_view.begin() + old_nb_doubled_nodes,
                  double_nodes_view.end()),
            arange(facets_to_double.size()))) {
     // auto & old_facet = std::get<0>(data)[0];
     auto & new_facet = std::get<0>(data)[1];
 
     new_facets_event.getList().push_back(new_facet);
 
     auto & nodes = std::get<1>(data);
     auto old_node = nodes(0);
     auto new_node = nodes(1);
 
     auto f = std::get<2>(data);
 
     /// add new nodes in connectivity
     connectivities(new_facet) = new_node;
 
     updateElementalConnectivity(mesh, old_node, new_node,
                                 elementsOfDimToElementsOfDim(2, 0)(f),
                                 &elementsOfDimToElementsOfDim(1, 0)(f));
 
     updateElementalConnectivity(mesh_facets, old_node, new_node,
                                 elementsOfDimToElementsOfDim(1, 0)(f));
 
     if (spatial_dimension == 3) {
       updateElementalConnectivity(mesh_facets, old_node, new_node,
                                   elementsOfDimToElementsOfDim(0, 0)(f));
     }
   }
 
   updateSubelementToElement(0, spatial_dimension == 2);
   updateElementToSubelement(0, spatial_dimension == 2);
 
   mesh_facets.sendEvent(new_facets_event);
 }
 
 } // namespace akantu
diff --git a/src/model/model.cc b/src/model/model.cc
index bc948054c..02c111a8e 100644
--- a/src/model/model.cc
+++ b/src/model/model.cc
@@ -1,347 +1,349 @@
 /**
  * @file   model.cc
  *
  * @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
  * @author David Simon Kammer <david.kammer@epfl.ch>
  * @author Nicolas Richart <nicolas.richart@epfl.ch>
  *
  * @date creation: Mon Oct 03 2011
  * @date last modification: Tue Feb 20 2018
  *
  * @brief  implementation of model common parts
  *
  *
  * Copyright (©)  2010-2018 EPFL (Ecole Polytechnique Fédérale de Lausanne)
  * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
  *
  * Akantu is free  software: you can redistribute it and/or  modify it under the
  * terms  of the  GNU Lesser  General Public  License as published by  the Free
  * Software Foundation, either version 3 of the License, or (at your option) any
  * later version.
  *
  * Akantu is  distributed in the  hope that it  will be useful, but  WITHOUT ANY
  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  * A PARTICULAR PURPOSE. See  the GNU  Lesser General  Public License  for more
  * details.
  *
  * You should  have received  a copy  of the GNU  Lesser General  Public License
  * along with Akantu. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
 /* -------------------------------------------------------------------------- */
 #include "model.hh"
 #include "communicator.hh"
 #include "data_accessor.hh"
 #include "element_group.hh"
 #include "element_synchronizer.hh"
 #include "synchronizer_registry.hh"
 /* -------------------------------------------------------------------------- */
 
 namespace akantu {
 
 /* -------------------------------------------------------------------------- */
 Model::Model(Mesh & mesh, const ModelType & type,
              std::shared_ptr<DOFManager> dof_manager, UInt dim, const ID & id,
              const MemoryID & memory_id)
     : Memory(id, memory_id),
-      ModelSolver(mesh, type, id, memory_id, dof_manager), mesh(mesh),
+      ModelSolver(mesh, type, id, memory_id, std::move(dof_manager)),
+      mesh(mesh),
       spatial_dimension(dim == _all_dimensions ? mesh.getSpatialDimension()
                                                : dim),
       parser(getStaticParser()) {
-  AKANTU_DEBUG_IN();
-
   this->mesh.registerEventHandler(*this, _ehp_model);
-
-  AKANTU_DEBUG_OUT();
 }
 
 /* -------------------------------------------------------------------------- */
 Model::Model(Mesh & mesh, const ModelType & type, UInt dim, const ID & id,
              const MemoryID & memory_id)
-    : Model(mesh, type, nullptr, dim, id, memory_id) {}
+    : Memory(id, memory_id), ModelSolver(mesh, type, id, memory_id), mesh(mesh),
+      spatial_dimension(dim == _all_dimensions ? mesh.getSpatialDimension()
+                                               : dim),
+      parser(getStaticParser()) {
+  this->mesh.registerEventHandler(*this, _ehp_model);
+}
 
 /* -------------------------------------------------------------------------- */
 Model::~Model() = default;
 
 /* -------------------------------------------------------------------------- */
 void Model::initFullImpl(const ModelOptions & options) {
   AKANTU_DEBUG_IN();
 
   method = options.analysis_method;
   if (!this->hasDefaultSolver()) {
     this->initNewSolver(this->method);
   }
 
   initModel();
 
   initFEEngineBoundary();
 
   AKANTU_DEBUG_OUT();
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::initNewSolver(const AnalysisMethod & method) {
   ID solver_name;
   TimeStepSolverType tss_type;
   std::tie(solver_name, tss_type) = this->getDefaultSolverID(method);
 
   if (not this->hasSolver(solver_name)) {
     ModelSolverOptions options = this->getDefaultSolverOptions(tss_type);
     this->getNewSolver(solver_name, tss_type, options.non_linear_solver_type);
 
     for (auto && is_type : options.integration_scheme_type) {
       if (!this->hasIntegrationScheme(solver_name, is_type.first)) {
         this->setIntegrationScheme(solver_name, is_type.first, is_type.second,
                                    options.solution_type[is_type.first]);
       }
     }
   }
 
   this->method = method;
   this->setDefaultSolver(solver_name);
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::initFEEngineBoundary() {
   FEEngine & fem_boundary = getFEEngineBoundary();
   fem_boundary.initShapeFunctions(_not_ghost);
   fem_boundary.initShapeFunctions(_ghost);
 
   fem_boundary.computeNormalsOnIntegrationPoints(_not_ghost);
   fem_boundary.computeNormalsOnIntegrationPoints(_ghost);
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::dumpGroup(const std::string & group_name) {
   ElementGroup & group = mesh.getElementGroup(group_name);
   group.dump();
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::dumpGroup(const std::string & group_name,
                       const std::string & dumper_name) {
   ElementGroup & group = mesh.getElementGroup(group_name);
   group.dump(dumper_name);
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::dumpGroup() {
   for (auto & group : mesh.iterateElementGroups()) {
     group.dump();
   }
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::setGroupDirectory(const std::string & directory) {
   for (auto & group : mesh.iterateElementGroups()) {
     group.setDirectory(directory);
   }
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::setGroupDirectory(const std::string & directory,
                               const std::string & group_name) {
   ElementGroup & group = mesh.getElementGroup(group_name);
   group.setDirectory(directory);
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::setGroupBaseName(const std::string & basename,
                              const std::string & group_name) {
   ElementGroup & group = mesh.getElementGroup(group_name);
   group.setBaseName(basename);
 }
 
 /* -------------------------------------------------------------------------- */
 DumperIOHelper & Model::getGroupDumper(const std::string & group_name) {
   ElementGroup & group = mesh.getElementGroup(group_name);
   return group.getDumper();
 }
 
 /* -------------------------------------------------------------------------- */
 // DUMPER stuff
 /* -------------------------------------------------------------------------- */
 void Model::addDumpGroupFieldToDumper(const std::string & field_id,
                                       std::shared_ptr<dumpers::Field> field,
                                       DumperIOHelper & dumper) {
 #ifdef AKANTU_USE_IOHELPER
   dumper.registerField(field_id, std::move(field));
 #endif
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::addDumpField(const std::string & field_id) {
 
   this->addDumpFieldToDumper(mesh.getDefaultDumperName(), field_id);
 }
 /* -------------------------------------------------------------------------- */
 
 void Model::addDumpFieldVector(const std::string & field_id) {
 
   this->addDumpFieldVectorToDumper(mesh.getDefaultDumperName(), field_id);
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::addDumpFieldTensor(const std::string & field_id) {
 
   this->addDumpFieldTensorToDumper(mesh.getDefaultDumperName(), field_id);
 }
 
 /* -------------------------------------------------------------------------- */
 
 void Model::setBaseName(const std::string & field_id) {
 
   mesh.setBaseName(field_id);
 }
 /* -------------------------------------------------------------------------- */
 
 void Model::setBaseNameToDumper(const std::string & dumper_name,
                                 const std::string & basename) {
   mesh.setBaseNameToDumper(dumper_name, basename);
 }
 /* -------------------------------------------------------------------------- */
 
 void Model::addDumpFieldToDumper(const std::string & dumper_name,
                                  const std::string & field_id) {
 
   this->addDumpGroupFieldToDumper(dumper_name, field_id, "all", _ek_regular,
                                   false);
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::addDumpGroupField(const std::string & field_id,
                               const std::string & group_name) {
 
   ElementGroup & group = mesh.getElementGroup(group_name);
   this->addDumpGroupFieldToDumper(group.getDefaultDumperName(), field_id,
                                   group_name, _ek_regular, false);
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::removeDumpGroupField(const std::string & field_id,
                                  const std::string & group_name) {
   ElementGroup & group = mesh.getElementGroup(group_name);
   this->removeDumpGroupFieldFromDumper(group.getDefaultDumperName(), field_id,
                                        group_name);
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::removeDumpGroupFieldFromDumper(const std::string & dumper_name,
                                            const std::string & field_id,
                                            const std::string & group_name) {
   ElementGroup & group = mesh.getElementGroup(group_name);
   group.removeDumpFieldFromDumper(dumper_name, field_id);
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::addDumpFieldVectorToDumper(const std::string & dumper_name,
                                        const std::string & field_id) {
   this->addDumpGroupFieldToDumper(dumper_name, field_id, "all", _ek_regular,
                                   true);
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::addDumpGroupFieldVector(const std::string & field_id,
                                     const std::string & group_name) {
   ElementGroup & group = mesh.getElementGroup(group_name);
   this->addDumpGroupFieldVectorToDumper(group.getDefaultDumperName(), field_id,
                                         group_name);
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::addDumpGroupFieldVectorToDumper(const std::string & dumper_name,
                                             const std::string & field_id,
                                             const std::string & group_name) {
   this->addDumpGroupFieldToDumper(dumper_name, field_id, group_name,
                                   _ek_regular, true);
 }
 /* -------------------------------------------------------------------------- */
 
 void Model::addDumpFieldTensorToDumper(const std::string & dumper_name,
                                        const std::string & field_id) {
   this->addDumpGroupFieldToDumper(dumper_name, field_id, "all", _ek_regular,
                                   true);
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::addDumpGroupFieldToDumper(const std::string & dumper_name,
                                       const std::string & field_id,
                                       const std::string & group_name,
                                       ElementKind element_kind,
                                       bool padding_flag) {
   this->addDumpGroupFieldToDumper(dumper_name, field_id, group_name,
                                   this->spatial_dimension, element_kind,
                                   padding_flag);
 }
 
 /* -------------------------------------------------------------------------- */
 void Model::addDumpGroupFieldToDumper(const std::string & dumper_name,
                                       const std::string & field_id,
                                       const std::string & group_name,
                                       UInt spatial_dimension,
                                       ElementKind element_kind,
                                       bool padding_flag) {
 
 #ifdef AKANTU_USE_IOHELPER
   std::shared_ptr<dumpers::Field> field;
 
   if (!field) {
     field = this->createNodalFieldReal(field_id, group_name, padding_flag);
   }
   if (!field) {
     field = this->createNodalFieldUInt(field_id, group_name, padding_flag);
   }
   if (!field) {
     field = this->createNodalFieldBool(field_id, group_name, padding_flag);
   }
   if (!field) {
     field = this->createElementalField(field_id, group_name, padding_flag,
                                        spatial_dimension, element_kind);
   }
   if (!field) {
     field = this->mesh.createFieldFromAttachedData<UInt>(field_id, group_name,
                                                          element_kind);
   }
   if (!field) {
     field = this->mesh.createFieldFromAttachedData<Real>(field_id, group_name,
                                                          element_kind);
   }
 
 #ifndef AKANTU_NDEBUG
   if (!field) {
     AKANTU_DEBUG_WARNING("No field could be found based on name: " << field_id);
   }
 #endif
   if (field) {
     DumperIOHelper & dumper = mesh.getGroupDumper(dumper_name, group_name);
     this->addDumpGroupFieldToDumper(field_id, field, dumper);
   }
 
 #endif
 }
 
 /* -------------------------------------------------------------------------- */
 
 void Model::dump() { mesh.dump(); }
 
 /* -------------------------------------------------------------------------- */
 
 void Model::setDirectory(const std::string & directory) {
   mesh.setDirectory(directory);
 }
 
 /* -------------------------------------------------------------------------- */
 
 void Model::setDirectoryToDumper(const std::string & dumper_name,
                                  const std::string & directory) {
   mesh.setDirectoryToDumper(dumper_name, directory);
 }
 
 /* -------------------------------------------------------------------------- */
 
 void Model::setTextModeToDumper() { mesh.setTextModeToDumper(); }
 
 /* -------------------------------------------------------------------------- */
 
 } // namespace akantu
diff --git a/src/model/solid_mechanics/solid_mechanics_model.cc b/src/model/solid_mechanics/solid_mechanics_model.cc
index 73c9c27c6..f35e1755b 100644
--- a/src/model/solid_mechanics/solid_mechanics_model.cc
+++ b/src/model/solid_mechanics/solid_mechanics_model.cc
@@ -1,1242 +1,1242 @@
 /**
  * @file   solid_mechanics_model.cc
  *
  * @author Ramin Aghababaei <ramin.aghababaei@epfl.ch>
  * @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
  * @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch>
  * @author David Simon Kammer <david.kammer@epfl.ch>
  * @author Daniel Pino Muñoz <daniel.pinomunoz@epfl.ch>
  * @author Nicolas Richart <nicolas.richart@epfl.ch>
  * @author Clement Roux <clement.roux@epfl.ch>
  * @author Marco Vocialta <marco.vocialta@epfl.ch>
  *
  * @date creation: Tue Jul 27 2010
  * @date last modification: Wed Feb 21 2018
  *
  * @brief  Implementation of the SolidMechanicsModel class
  *
  *
  * Copyright (©)  2010-2018 EPFL (Ecole Polytechnique Fédérale de Lausanne)
  * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
  *
  * Akantu is free  software: you can redistribute it and/or  modify it under the
  * terms  of the  GNU Lesser  General Public  License as published by  the Free
  * Software Foundation, either version 3 of the License, or (at your option) any
  * later version.
  *
  * Akantu is  distributed in the  hope that it  will be useful, but  WITHOUT ANY
  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  * A PARTICULAR PURPOSE. See  the GNU  Lesser General  Public License  for more
  * details.
  *
  * You should  have received  a copy  of the GNU  Lesser General  Public License
  * along with Akantu. If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
 /* -------------------------------------------------------------------------- */
 #include "solid_mechanics_model.hh"
 #include "integrator_gauss.hh"
 #include "shape_lagrange.hh"
 #include "solid_mechanics_model_tmpl.hh"
 
 #include "communicator.hh"
 #include "element_synchronizer.hh"
 #include "sparse_matrix.hh"
 #include "synchronizer_registry.hh"
 
 #include "dumpable_inline_impl.hh"
 #ifdef AKANTU_USE_IOHELPER
 #include "dumper_iohelper_paraview.hh"
 #endif
 
 #include "material_non_local.hh"
 /* -------------------------------------------------------------------------- */
 
 namespace akantu {
 
 /* -------------------------------------------------------------------------- */
 /**
  * A solid mechanics model need a mesh  and a dimension to be created. the model
  * by it  self can not  do a lot,  the good init  functions should be  called in
  * order to configure the model depending on what we want to do.
  *
  * @param  mesh mesh  representing  the model  we  want to  simulate
  * @param dim spatial  dimension of the problem, if dim =  0 (default value) the
  * dimension of the problem is assumed to be the on of the mesh
  * @param id an id to identify the model
  * @param memory_id the id of the memory
  * @param model_type this is an internal parameter for inheritance purposes
  */
 
 SolidMechanicsModel::SolidMechanicsModel(
     Mesh & mesh, UInt dim, const ID & id, const MemoryID & memory_id,
     std::shared_ptr<DOFManager> dof_manager, const ModelType model_type)
-    : Model(mesh, model_type, dof_manager, dim, id, memory_id),
+    : Model(mesh, model_type, std::move(dof_manager), dim, id, memory_id),
       BoundaryCondition<SolidMechanicsModel>(),
       material_index("material index", id, memory_id),
       material_local_numbering("material local numbering", id, memory_id) {
   AKANTU_DEBUG_IN();
 
   this->registerFEEngineObject<MyFEEngineType>("SolidMechanicsFEEngine", mesh,
                                                Model::spatial_dimension);
 
 #if defined(AKANTU_USE_IOHELPER)
   this->mesh.registerDumper<DumperParaview>("solid_mechanics_model", id, true);
   this->mesh.addDumpMesh(mesh, Model::spatial_dimension, _not_ghost,
                          _ek_regular);
 #endif
 
   material_selector = std::make_shared<DefaultMaterialSelector>(material_index);
 
   this->registerDataAccessor(*this);
 
   if (this->mesh.isDistributed()) {
     auto & synchronizer = this->mesh.getElementSynchronizer();
     this->registerSynchronizer(synchronizer, SynchronizationTag::_material_id);
     this->registerSynchronizer(synchronizer, SynchronizationTag::_smm_mass);
     this->registerSynchronizer(synchronizer, SynchronizationTag::_smm_stress);
     this->registerSynchronizer(synchronizer, SynchronizationTag::_for_dump);
   }
 
   AKANTU_DEBUG_OUT();
 }
 
 /* -------------------------------------------------------------------------- */
 SolidMechanicsModel::~SolidMechanicsModel() = default;
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::setTimeStep(Real time_step, const ID & solver_id) {
   Model::setTimeStep(time_step, solver_id);
 
 #if defined(AKANTU_USE_IOHELPER)
   this->mesh.getDumper().setTimeStep(time_step);
 #endif
 }
 
 /* -------------------------------------------------------------------------- */
 /* Initialization                                                             */
 /* -------------------------------------------------------------------------- */
 /**
  * This function groups  many of the initialization in on  function. For most of
  * basics  case the  function should  be  enough. The  functions initialize  the
  * model, the internal  vectors, set them to 0, and  depending on the parameters
  * it also initialize the explicit or implicit solver.
  *
  * @param options
  * \parblock
  * contains the different options to initialize the model
  * \li \c analysis_method specify the type of solver to use
  * \endparblock
  */
 void SolidMechanicsModel::initFullImpl(const ModelOptions & options) {
   material_index.initialize(mesh, _element_kind = _ek_not_defined,
                             _default_value = UInt(-1), _with_nb_element = true);
   material_local_numbering.initialize(mesh, _element_kind = _ek_not_defined,
                                       _with_nb_element = true);
 
   Model::initFullImpl(options);
 
   // initialize the materials
   if (not this->parser.getLastParsedFile().empty()) {
     this->instantiateMaterials();
     this->initMaterials();
   }
 
   this->initBC(*this, *displacement, *displacement_increment, *external_force);
 }
 
 /* -------------------------------------------------------------------------- */
 TimeStepSolverType SolidMechanicsModel::getDefaultSolverType() const {
   return TimeStepSolverType::_dynamic_lumped;
 }
 
 /* -------------------------------------------------------------------------- */
 ModelSolverOptions SolidMechanicsModel::getDefaultSolverOptions(
     const TimeStepSolverType & type) const {
   ModelSolverOptions options;
 
   switch (type) {
   case TimeStepSolverType::_dynamic_lumped: {
     options.non_linear_solver_type = NonLinearSolverType::_lumped;
     options.integration_scheme_type["displacement"] =
         IntegrationSchemeType::_central_difference;
     options.solution_type["displacement"] = IntegrationScheme::_acceleration;
     break;
   }
   case TimeStepSolverType::_static: {
     options.non_linear_solver_type = NonLinearSolverType::_newton_raphson;
     options.integration_scheme_type["displacement"] =
         IntegrationSchemeType::_pseudo_time;
     options.solution_type["displacement"] = IntegrationScheme::_not_defined;
     break;
   }
   case TimeStepSolverType::_dynamic: {
     if (this->method == _explicit_consistent_mass) {
       options.non_linear_solver_type = NonLinearSolverType::_newton_raphson;
       options.integration_scheme_type["displacement"] =
           IntegrationSchemeType::_central_difference;
       options.solution_type["displacement"] = IntegrationScheme::_acceleration;
     } else {
       options.non_linear_solver_type = NonLinearSolverType::_newton_raphson;
       options.integration_scheme_type["displacement"] =
           IntegrationSchemeType::_trapezoidal_rule_2;
       options.solution_type["displacement"] = IntegrationScheme::_displacement;
     }
     break;
   }
   default:
     AKANTU_EXCEPTION(type << " is not a valid time step solver type");
   }
 
   return options;
 }
 
 /* -------------------------------------------------------------------------- */
 std::tuple<ID, TimeStepSolverType>
 SolidMechanicsModel::getDefaultSolverID(const AnalysisMethod & method) {
   switch (method) {
   case _explicit_lumped_mass: {
     return std::make_tuple("explicit_lumped",
                            TimeStepSolverType::_dynamic_lumped);
   }
   case _explicit_consistent_mass: {
     return std::make_tuple("explicit", TimeStepSolverType::_dynamic);
   }
   case _static: {
     return std::make_tuple("static", TimeStepSolverType::_static);
   }
   case _implicit_dynamic: {
     return std::make_tuple("implicit", TimeStepSolverType::_dynamic);
   }
   default:
     return std::make_tuple("unknown", TimeStepSolverType::_not_defined);
   }
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::initSolver(TimeStepSolverType time_step_solver_type,
                                      NonLinearSolverType /*unused*/) {
   auto & dof_manager = this->getDOFManager();
 
   /* ------------------------------------------------------------------------ */
   // for alloc type of solvers
   this->allocNodalField(this->displacement, spatial_dimension, "displacement");
   this->allocNodalField(this->previous_displacement, spatial_dimension,
                         "previous_displacement");
   this->allocNodalField(this->displacement_increment, spatial_dimension,
                         "displacement_increment");
   this->allocNodalField(this->internal_force, spatial_dimension,
                         "internal_force");
   this->allocNodalField(this->external_force, spatial_dimension,
                         "external_force");
   this->allocNodalField(this->blocked_dofs, spatial_dimension, "blocked_dofs");
   this->allocNodalField(this->current_position, spatial_dimension,
                         "current_position");
 
   // initialize the current positions
   this->current_position->copy(this->mesh.getNodes());
 
   /* ------------------------------------------------------------------------ */
   if (!dof_manager.hasDOFs("displacement")) {
     dof_manager.registerDOFs("displacement", *this->displacement, _dst_nodal);
     dof_manager.registerBlockedDOFs("displacement", *this->blocked_dofs);
     dof_manager.registerDOFsIncrement("displacement",
                                       *this->displacement_increment);
     dof_manager.registerDOFsPrevious("displacement",
                                      *this->previous_displacement);
   }
 
   /* ------------------------------------------------------------------------ */
   // for dynamic
   if (time_step_solver_type == TimeStepSolverType::_dynamic ||
       time_step_solver_type == TimeStepSolverType::_dynamic_lumped) {
     this->allocNodalField(this->velocity, spatial_dimension, "velocity");
     this->allocNodalField(this->acceleration, spatial_dimension,
                           "acceleration");
 
     if (!dof_manager.hasDOFsDerivatives("displacement", 1)) {
       dof_manager.registerDOFsDerivative("displacement", 1, *this->velocity);
       dof_manager.registerDOFsDerivative("displacement", 2,
                                          *this->acceleration);
     }
   }
 }
 
 /* -------------------------------------------------------------------------- */
 /**
  * Initialize the model,basically it  pre-compute the shapes, shapes derivatives
  * and jacobian
  */
 void SolidMechanicsModel::initModel() {
   /// \todo add  the current position  as a parameter to  initShapeFunctions for
   /// large deformation
   getFEEngine().initShapeFunctions(_not_ghost);
   getFEEngine().initShapeFunctions(_ghost);
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::assembleResidual() {
   AKANTU_DEBUG_IN();
 
   /* ------------------------------------------------------------------------ */
   // computes the internal forces
   this->assembleInternalForces();
 
   /* ------------------------------------------------------------------------ */
   this->getDOFManager().assembleToResidual("displacement",
                                            *this->external_force, 1);
   this->getDOFManager().assembleToResidual("displacement",
                                            *this->internal_force, 1);
 
   AKANTU_DEBUG_OUT();
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::assembleResidual(const ID & residual_part) {
   AKANTU_DEBUG_IN();
 
   if ("external" == residual_part) {
     this->getDOFManager().assembleToResidual("displacement",
                                              *this->external_force, 1);
     AKANTU_DEBUG_OUT();
     return;
   }
 
   if ("internal" == residual_part) {
     this->assembleInternalForces();
     this->getDOFManager().assembleToResidual("displacement",
                                              *this->internal_force, 1);
     AKANTU_DEBUG_OUT();
     return;
   }
 
   AKANTU_CUSTOM_EXCEPTION(
       debug::SolverCallbackResidualPartUnknown(residual_part));
 
   AKANTU_DEBUG_OUT();
 }
 
 /* -------------------------------------------------------------------------- */
 MatrixType SolidMechanicsModel::getMatrixType(const ID & matrix_id) {
   // \TODO check the materials to know what is the correct answer
   if (matrix_id == "C") {
     return _mt_not_defined;
   }
 
   if (matrix_id == "K") {
     auto matrix_type = _unsymmetric;
 
     for (auto & material : materials) {
       matrix_type = std::max(matrix_type, material->getMatrixType(matrix_id));
     }
   }
   return _symmetric;
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::assembleMatrix(const ID & matrix_id) {
   if (matrix_id == "K") {
     this->assembleStiffnessMatrix();
   } else if (matrix_id == "M") {
     this->assembleMass();
   }
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::assembleLumpedMatrix(const ID & matrix_id) {
   if (matrix_id == "M") {
     this->assembleMassLumped();
   }
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::beforeSolveStep() {
   for (auto & material : materials) {
     material->beforeSolveStep();
   }
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::afterSolveStep(bool converged) {
   for (auto & material : materials) {
     material->afterSolveStep(converged);
   }
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::predictor() { ++displacement_release; }
 
 /* -------------------------------------------------------------------------- */
   void SolidMechanicsModel::corrector() { ++displacement_release; }
 
 /* -------------------------------------------------------------------------- */
 /**
  * This function computes the internal forces as \f$F_{int} = \int_{\Omega} N
  * \sigma d\Omega@\f$
  */
 void SolidMechanicsModel::assembleInternalForces() {
   AKANTU_DEBUG_IN();
 
   AKANTU_DEBUG_INFO("Assemble the internal forces");
 
   this->internal_force->zero();
 
   // compute the stresses of local elements
   AKANTU_DEBUG_INFO("Compute local stresses");
   for (auto & material : materials) {
     material->computeAllStresses(_not_ghost);
   }
 
   /* ------------------------------------------------------------------------ */
   /* Computation of the non local part */
   if (this->non_local_manager) {
     this->non_local_manager->computeAllNonLocalStresses();
   }
 
   // communicate the stresses
   AKANTU_DEBUG_INFO("Send data for residual assembly");
   this->asynchronousSynchronize(SynchronizationTag::_smm_stress);
 
   // assemble the forces due to local stresses
   AKANTU_DEBUG_INFO("Assemble residual for local elements");
   for (auto & material : materials) {
     material->assembleInternalForces(_not_ghost);
   }
 
   // finalize communications
   AKANTU_DEBUG_INFO("Wait distant stresses");
   this->waitEndSynchronize(SynchronizationTag::_smm_stress);
 
   // assemble the stresses due to ghost elements
   AKANTU_DEBUG_INFO("Assemble residual for ghost elements");
   for (auto & material : materials) {
     material->assembleInternalForces(_ghost);
   }
 
   AKANTU_DEBUG_OUT();
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::assembleStiffnessMatrix(bool need_to_reassemble) {
   AKANTU_DEBUG_IN();
 
   AKANTU_DEBUG_INFO("Assemble the new stiffness matrix.");
 
   // Check if materials need to recompute the matrix
   /*bool need_to_reassemble = true;*/
 
   //bool need_to_reassemble = false;
 
   for (auto & material : materials) {
     need_to_reassemble |= material->hasMatrixChanged("K");
   }
 
   if (need_to_reassemble) {
     this->getDOFManager().getMatrix("K").zero();
 
     // call compute stiffness matrix on each local elements
     for (auto & material : materials) {
       material->assembleStiffnessMatrix(_not_ghost);
     }
   }
 
   AKANTU_DEBUG_OUT();
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::updateCurrentPosition() {
   if (this->current_position_release == this->displacement_release) {
     return;
   }
 
   this->current_position->copy(this->mesh.getNodes());
 
   auto cpos_it = this->current_position->begin(Model::spatial_dimension);
   auto cpos_end = this->current_position->end(Model::spatial_dimension);
   auto disp_it = this->displacement->begin(Model::spatial_dimension);
 
   for (; cpos_it != cpos_end; ++cpos_it, ++disp_it) {
     *cpos_it += *disp_it;
   }
 
   this->current_position_release = this->displacement_release;
 }
 
 /* -------------------------------------------------------------------------- */
 const Array<Real> & SolidMechanicsModel::getCurrentPosition() {
   this->updateCurrentPosition();
   return *this->current_position;
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::updateDataForNonLocalCriterion(
     ElementTypeMapReal & criterion) {
   const ID field_name = criterion.getName();
   for (auto & material : materials) {
     if (!material->isInternal<Real>(field_name, _ek_regular)) {
       continue;
     }
 
     for (auto ghost_type : ghost_types) {
       material->flattenInternal(field_name, criterion, ghost_type, _ek_regular);
     }
   }
 }
 
 /* -------------------------------------------------------------------------- */
 /* Information                                                                */
 /* -------------------------------------------------------------------------- */
 Real SolidMechanicsModel::getStableTimeStep() {
   AKANTU_DEBUG_IN();
 
   Real min_dt = getStableTimeStep(_not_ghost);
 
   /// reduction min over all processors
   mesh.getCommunicator().allReduce(min_dt, SynchronizerOperation::_min);
 
   AKANTU_DEBUG_OUT();
   return min_dt;
 }
 
 /* -------------------------------------------------------------------------- */
 Real SolidMechanicsModel::getStableTimeStep(GhostType ghost_type) {
   AKANTU_DEBUG_IN();
 
   Real min_dt = std::numeric_limits<Real>::max();
 
   this->updateCurrentPosition();
 
   Element elem;
   elem.ghost_type = ghost_type;
 
   for (auto type :
        mesh.elementTypes(Model::spatial_dimension, ghost_type, _ek_regular)) {
     elem.type = type;
     UInt nb_nodes_per_element = mesh.getNbNodesPerElement(type);
     UInt nb_element = mesh.getNbElement(type);
 
     auto mat_indexes = material_index(type, ghost_type).begin();
     auto mat_loc_num = material_local_numbering(type, ghost_type).begin();
 
     Array<Real> X(0, nb_nodes_per_element * Model::spatial_dimension);
     FEEngine::extractNodalToElementField(mesh, *current_position, X, type,
                                          _not_ghost);
 
     auto X_el = X.begin(Model::spatial_dimension, nb_nodes_per_element);
 
     for (UInt el = 0; el < nb_element;
          ++el, ++X_el, ++mat_indexes, ++mat_loc_num) {
       elem.element = *mat_loc_num;
       Real el_h = getFEEngine().getElementInradius(*X_el, type);
       Real el_c = this->materials[*mat_indexes]->getCelerity(elem);
       Real el_dt = el_h / el_c;
 
       min_dt = std::min(min_dt, el_dt);
     }
   }
 
   AKANTU_DEBUG_OUT();
   return min_dt;
 }
 
 /* -------------------------------------------------------------------------- */
 Real SolidMechanicsModel::getKineticEnergy() {
   AKANTU_DEBUG_IN();
 
   Real ekin = 0.;
   UInt nb_nodes = mesh.getNbNodes();
 
   if (this->getDOFManager().hasLumpedMatrix("M")) {
     auto m_it = this->mass->begin(Model::spatial_dimension);
     auto m_end = this->mass->end(Model::spatial_dimension);
     auto v_it = this->velocity->begin(Model::spatial_dimension);
 
     for (UInt n = 0; m_it != m_end; ++n, ++m_it, ++v_it) {
       const auto & v = *v_it;
       const auto & m = *m_it;
 
       Real mv2 = 0.;
       auto is_local_node = mesh.isLocalOrMasterNode(n);
       // bool is_not_pbc_slave_node = !isPBCSlaveNode(n);
       auto count_node = is_local_node; // && is_not_pbc_slave_node;
       if (count_node) {
         for (UInt i = 0; i < Model::spatial_dimension; ++i) {
           if (m(i) > std::numeric_limits<Real>::epsilon()) {
             mv2 += v(i) * v(i) * m(i);
           }
         }
       }
 
       ekin += mv2;
     }
   } else if (this->getDOFManager().hasMatrix("M")) {
     Array<Real> Mv(nb_nodes, Model::spatial_dimension);
     this->getDOFManager().assembleMatMulVectToArray("displacement", "M",
                                                     *this->velocity, Mv);
 
     for (auto && data : zip(arange(nb_nodes), make_view(Mv, spatial_dimension),
                             make_view(*this->velocity, spatial_dimension))) {
       ekin += std::get<2>(data).dot(std::get<1>(data)) *
               static_cast<Real>(mesh.isLocalOrMasterNode(std::get<0>(data)));
     }
   } else {
     AKANTU_ERROR("No function called to assemble the mass matrix.");
   }
 
   mesh.getCommunicator().allReduce(ekin, SynchronizerOperation::_sum);
 
   AKANTU_DEBUG_OUT();
   return ekin * .5;
 }
 
 /* -------------------------------------------------------------------------- */
 Real SolidMechanicsModel::getKineticEnergy(ElementType type, UInt index) {
   AKANTU_DEBUG_IN();
 
   UInt nb_quadrature_points = getFEEngine().getNbIntegrationPoints(type);
 
   Array<Real> vel_on_quad(nb_quadrature_points, Model::spatial_dimension);
   Array<UInt> filter_element(1, 1, index);
 
   getFEEngine().interpolateOnIntegrationPoints(*velocity, vel_on_quad,
                                                Model::spatial_dimension, type,
                                                _not_ghost, filter_element);
 
   auto vit = vel_on_quad.begin(Model::spatial_dimension);
   auto vend = vel_on_quad.end(Model::spatial_dimension);
 
   Vector<Real> rho_v2(nb_quadrature_points);
 
   Real rho = materials[material_index(type)(index)]->getRho();
 
   for (UInt q = 0; vit != vend; ++vit, ++q) {
     rho_v2(q) = rho * vit->dot(*vit);
   }
 
   AKANTU_DEBUG_OUT();
 
   return .5 * getFEEngine().integrate(rho_v2, type, index);
 }
 
 /* -------------------------------------------------------------------------- */
 Real SolidMechanicsModel::getExternalWork() {
   AKANTU_DEBUG_IN();
 
   auto ext_force_it = external_force->begin(Model::spatial_dimension);
   auto int_force_it = internal_force->begin(Model::spatial_dimension);
   auto boun_it = blocked_dofs->begin(Model::spatial_dimension);
 
   decltype(ext_force_it) incr_or_velo_it;
   if (this->method == _static) {
     incr_or_velo_it =
         this->displacement_increment->begin(Model::spatial_dimension);
   } else {
     incr_or_velo_it = this->velocity->begin(Model::spatial_dimension);
   }
 
   Real work = 0.;
 
   UInt nb_nodes = this->mesh.getNbNodes();
 
   for (UInt n = 0; n < nb_nodes;
        ++n, ++ext_force_it, ++int_force_it, ++boun_it, ++incr_or_velo_it) {
     const auto & int_force = *int_force_it;
     const auto & ext_force = *ext_force_it;
     const auto & boun = *boun_it;
     const auto & incr_or_velo = *incr_or_velo_it;
 
     bool is_local_node = this->mesh.isLocalOrMasterNode(n);
     // bool is_not_pbc_slave_node = !this->isPBCSlaveNode(n);
     bool count_node = is_local_node; // && is_not_pbc_slave_node;
 
     if (count_node) {
       for (UInt i = 0; i < Model::spatial_dimension; ++i) {
         if (boun(i)) {
           work -= int_force(i) * incr_or_velo(i);
         } else {
           work += ext_force(i) * incr_or_velo(i);
         }
       }
     }
   }
 
   mesh.getCommunicator().allReduce(work, SynchronizerOperation::_sum);
 
   if (this->method != _static) {
     work *= this->getTimeStep();
   }
 
   AKANTU_DEBUG_OUT();
   return work;
 }
 
 /* -------------------------------------------------------------------------- */
 Real SolidMechanicsModel::getEnergy(const std::string & energy_id) {
   AKANTU_DEBUG_IN();
 
   if (energy_id == "kinetic") {
     return getKineticEnergy();
   }
 
   if (energy_id == "external work") {
     return getExternalWork();
   }
 
   Real energy = 0.;
   for (auto & material : materials) {
     energy += material->getEnergy(energy_id);
   }
 
   /// reduction sum over all processors
   mesh.getCommunicator().allReduce(energy, SynchronizerOperation::_sum);
 
   AKANTU_DEBUG_OUT();
   return energy;
 }
 /* -------------------------------------------------------------------------- */
 Real SolidMechanicsModel::getEnergy(const std::string & energy_id,
                                     ElementType type, UInt index) {
   AKANTU_DEBUG_IN();
 
   if (energy_id == "kinetic") {
     return getKineticEnergy(type, index);
   }
 
   UInt mat_index = this->material_index(type, _not_ghost)(index);
   UInt mat_loc_num = this->material_local_numbering(type, _not_ghost)(index);
   Real energy =
       this->materials[mat_index]->getEnergy(energy_id, type, mat_loc_num);
 
   AKANTU_DEBUG_OUT();
   return energy;
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::onElementsAdded(const Array<Element> & element_list,
                                           const NewElementsEvent & event) {
   AKANTU_DEBUG_IN();
 
   this->material_index.initialize(mesh, _element_kind = _ek_not_defined,
                                   _with_nb_element = true,
                                   _default_value = UInt(-1));
   this->material_local_numbering.initialize(
       mesh, _element_kind = _ek_not_defined, _with_nb_element = true,
       _default_value = UInt(-1));
 
   ElementTypeMapArray<UInt> filter("new_element_filter", this->getID(),
                                    this->getMemoryID());
 
   for (const auto & elem : element_list) {
     if (mesh.getSpatialDimension(elem.type) != spatial_dimension) {
       continue;
     }
 
     if (!filter.exists(elem.type, elem.ghost_type)) {
       filter.alloc(0, 1, elem.type, elem.ghost_type);
     }
     filter(elem.type, elem.ghost_type).push_back(elem.element);
   }
 
   // this fails in parallel if the event is sent on facet between constructor
   // and initFull \todo: to debug...
   this->assignMaterialToElements(&filter);
 
   for (auto & material : materials) {
     material->onElementsAdded(element_list, event);
   }
 
   AKANTU_DEBUG_OUT();
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::onElementsRemoved(
     const Array<Element> & element_list,
     const ElementTypeMapArray<UInt> & new_numbering,
     const RemovedElementsEvent & event) {
   for (auto & material : materials) {
     material->onElementsRemoved(element_list, new_numbering, event);
   }
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::onNodesAdded(const Array<UInt> & nodes_list,
                                        const NewNodesEvent & event) {
   AKANTU_DEBUG_IN();
   UInt nb_nodes = mesh.getNbNodes();
 
   if (displacement) {
     displacement->resize(nb_nodes, 0.);
     ++displacement_release;
   }
   if (mass) {
     mass->resize(nb_nodes, 0.);
   }
   if (velocity) {
     velocity->resize(nb_nodes, 0.);
   }
   if (acceleration) {
     acceleration->resize(nb_nodes, 0.);
   }
   if (external_force) {
     external_force->resize(nb_nodes, 0.);
   }
   if (internal_force) {
     internal_force->resize(nb_nodes, 0.);
   }
   if (blocked_dofs) {
     blocked_dofs->resize(nb_nodes, false);
   }
   if (current_position) {
     current_position->resize(nb_nodes, 0.);
   }
 
   if (previous_displacement) {
     previous_displacement->resize(nb_nodes, 0.);
   }
   if (displacement_increment) {
     displacement_increment->resize(nb_nodes, 0.);
   }
 
   for (auto & material : materials) {
     material->onNodesAdded(nodes_list, event);
   }
 
   need_to_reassemble_lumped_mass = true;
   need_to_reassemble_mass = true;
 
   AKANTU_DEBUG_OUT();
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::onNodesRemoved(const Array<UInt> & /*element_list*/,
                                          const Array<UInt> & new_numbering,
                                          const RemovedNodesEvent & /*event*/) {
   if (displacement) {
     mesh.removeNodesFromArray(*displacement, new_numbering);
     ++displacement_release;
   }
   if (mass) {
     mesh.removeNodesFromArray(*mass, new_numbering);
   }
   if (velocity) {
     mesh.removeNodesFromArray(*velocity, new_numbering);
   }
   if (acceleration) {
     mesh.removeNodesFromArray(*acceleration, new_numbering);
   }
   if (internal_force) {
     mesh.removeNodesFromArray(*internal_force, new_numbering);
   }
   if (external_force) {
     mesh.removeNodesFromArray(*external_force, new_numbering);
   }
   if (blocked_dofs) {
     mesh.removeNodesFromArray(*blocked_dofs, new_numbering);
   }
 
   // if (increment_acceleration)
   //   mesh.removeNodesFromArray(*increment_acceleration, new_numbering);
   if (displacement_increment) {
     mesh.removeNodesFromArray(*displacement_increment, new_numbering);
   }
 
   if (previous_displacement) {
     mesh.removeNodesFromArray(*previous_displacement, new_numbering);
   }
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::printself(std::ostream & stream, int indent) const {
   std::string space(indent, AKANTU_INDENT);
 
   stream << space << "Solid Mechanics Model [" << std::endl;
   stream << space << " + id                : " << id << std::endl;
   stream << space << " + spatial dimension : " << Model::spatial_dimension
          << std::endl;
 
   stream << space << " + fem [" << std::endl;
   getFEEngine().printself(stream, indent + 2);
   stream << space << " ]" << std::endl;
 
   stream << space << " + nodals information [" << std::endl;
   displacement->printself(stream, indent + 2);
   if (velocity) {
     velocity->printself(stream, indent + 2);
   }
   if (acceleration) {
     acceleration->printself(stream, indent + 2);
   }
   if (mass) {
     mass->printself(stream, indent + 2);
   }
   external_force->printself(stream, indent + 2);
   internal_force->printself(stream, indent + 2);
   blocked_dofs->printself(stream, indent + 2);
   stream << space << " ]" << std::endl;
 
   stream << space << " + material information [" << std::endl;
   material_index.printself(stream, indent + 2);
   stream << space << " ]" << std::endl;
 
   stream << space << " + materials [" << std::endl;
   for (const auto & material : materials) {
     material->printself(stream, indent + 2);
   }
   stream << space << " ]" << std::endl;
 
   stream << space << "]" << std::endl;
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::initializeNonLocal() {
   this->non_local_manager->synchronize(*this, SynchronizationTag::_material_id);
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::insertIntegrationPointsInNeighborhoods(
     GhostType ghost_type) {
   for (auto & mat : materials) {
     MaterialNonLocalInterface * mat_non_local;
     if ((mat_non_local =
              dynamic_cast<MaterialNonLocalInterface *>(mat.get())) == nullptr) {
       continue;
     }
 
     ElementTypeMapArray<Real> quadrature_points_coordinates(
         "quadrature_points_coordinates_tmp_nl", this->id, this->memory_id);
     quadrature_points_coordinates.initialize(this->getFEEngine(),
                                              _nb_component = spatial_dimension,
                                              _ghost_type = ghost_type);
 
     for (const auto & type : quadrature_points_coordinates.elementTypes(
              Model::spatial_dimension, ghost_type)) {
       this->getFEEngine().computeIntegrationPointsCoordinates(
           quadrature_points_coordinates(type, ghost_type), type, ghost_type);
     }
 
     mat_non_local->initMaterialNonLocal();
 
     mat_non_local->insertIntegrationPointsInNeighborhoods(
         ghost_type, quadrature_points_coordinates);
   }
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::computeNonLocalStresses(GhostType ghost_type) {
   for (auto & mat : materials) {
     if (not aka::is_of_type<MaterialNonLocalInterface>(*mat)) {
       continue;
     }
 
     auto & mat_non_local = dynamic_cast<MaterialNonLocalInterface &>(*mat);
     mat_non_local.computeNonLocalStresses(ghost_type);
   }
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::updateLocalInternal(
     ElementTypeMapReal & internal_flat, GhostType ghost_type,
     ElementKind kind) {
   const ID field_name = internal_flat.getName();
   for (auto & material : materials) {
     if (material->isInternal<Real>(field_name, kind)) {
       material->flattenInternal(field_name, internal_flat, ghost_type, kind);
     }
   }
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::updateNonLocalInternal(
     ElementTypeMapReal & internal_flat, GhostType ghost_type,
     ElementKind kind) {
 
   const ID field_name = internal_flat.getName();
 
   for (auto & mat : materials) {
     if (not aka::is_of_type<MaterialNonLocalInterface>(*mat)) {
       continue;
     }
 
     auto & mat_non_local = dynamic_cast<MaterialNonLocalInterface &>(*mat);
     mat_non_local.updateNonLocalInternals(internal_flat, field_name, ghost_type,
                                           kind);
   }
 }
 
 /* -------------------------------------------------------------------------- */
 FEEngine & SolidMechanicsModel::getFEEngineBoundary(const ID & name) {
   return getFEEngineClassBoundary<MyFEEngineType>(name);
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::splitElementByMaterial(
     const Array<Element> & elements,
     std::vector<Array<Element>> & elements_per_mat) const {
   for (const auto & el : elements) {
     Element mat_el = el;
     mat_el.element = this->material_local_numbering(el);
     elements_per_mat[this->material_index(el)].push_back(mat_el);
   }
 }
 
 /* -------------------------------------------------------------------------- */
 UInt SolidMechanicsModel::getNbData(const Array<Element> & elements,
                                     const SynchronizationTag & tag) const {
   AKANTU_DEBUG_IN();
 
   UInt size = 0;
   UInt nb_nodes_per_element = 0;
 
   for (const Element & el : elements) {
     nb_nodes_per_element += Mesh::getNbNodesPerElement(el.type);
   }
 
   switch (tag) {
   case SynchronizationTag::_material_id: {
     size += elements.size() * sizeof(UInt);
     break;
   }
   case SynchronizationTag::_smm_mass: {
     size += nb_nodes_per_element * sizeof(Real) *
             Model::spatial_dimension; // mass vector
     break;
   }
   case SynchronizationTag::_smm_for_gradu: {
     size += nb_nodes_per_element * Model::spatial_dimension *
             sizeof(Real); // displacement
     break;
   }
   case SynchronizationTag::_smm_boundary: {
     // force, displacement, boundary
     size += nb_nodes_per_element * Model::spatial_dimension *
             (2 * sizeof(Real) + sizeof(bool));
     break;
   }
   case SynchronizationTag::_for_dump: {
     // displacement, velocity, acceleration, residual, force
     size += nb_nodes_per_element * Model::spatial_dimension * sizeof(Real) * 5;
     break;
   }
   default: {
   }
   }
 
   if (tag != SynchronizationTag::_material_id) {
     splitByMaterial(elements, [&](auto && mat, auto && elements) {
       size += mat.getNbData(elements, tag);
     });
   }
 
   AKANTU_DEBUG_OUT();
   return size;
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::packData(CommunicationBuffer & buffer,
                                    const Array<Element> & elements,
                                    const SynchronizationTag & tag) const {
   AKANTU_DEBUG_IN();
 
   switch (tag) {
   case SynchronizationTag::_material_id: {
     packElementalDataHelper(
         material_index, buffer, elements, false, getFEEngine());
     break;
   }
   case SynchronizationTag::_smm_mass: {
     packNodalDataHelper(*mass, buffer, elements, mesh);
     break;
   }
   case SynchronizationTag::_smm_for_gradu: {
     packNodalDataHelper(*displacement, buffer, elements, mesh);
     break;
   }
   case SynchronizationTag::_for_dump: {
     packNodalDataHelper(*displacement, buffer, elements, mesh);
     packNodalDataHelper(*velocity, buffer, elements, mesh);
     packNodalDataHelper(*acceleration, buffer, elements, mesh);
     packNodalDataHelper(*internal_force, buffer, elements, mesh);
     packNodalDataHelper(*external_force, buffer, elements, mesh);
     break;
   }
   case SynchronizationTag::_smm_boundary: {
     packNodalDataHelper(*external_force, buffer, elements, mesh);
     packNodalDataHelper(*velocity, buffer, elements, mesh);
     packNodalDataHelper(*blocked_dofs, buffer, elements, mesh);
     break;
   }
   default: {
   }
   }
 
   if (tag != SynchronizationTag::_material_id) {
     splitByMaterial(elements, [&](auto && mat, auto && elements) {
       mat.packData(buffer, elements, tag);
     });
   }
   AKANTU_DEBUG_OUT();
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::unpackData(CommunicationBuffer & buffer,
                                      const Array<Element> & elements,
                                      const SynchronizationTag & tag) {
   AKANTU_DEBUG_IN();
 
   switch (tag) {
   case SynchronizationTag::_material_id: {
     for (auto && element : elements) {
       UInt recv_mat_index;
       buffer >> recv_mat_index;
       UInt & mat_index = material_index(element);
       if (mat_index != UInt(-1)) {
         continue;
       }
 
       // add ghosts element to the correct material
       mat_index = recv_mat_index;
       UInt index = materials[mat_index]->addElement(element);
       material_local_numbering(element) = index;
     }
     break;
   }
   case SynchronizationTag::_smm_mass: {
     unpackNodalDataHelper(*mass, buffer, elements, mesh);
     break;
   }
   case SynchronizationTag::_smm_for_gradu: {
     unpackNodalDataHelper(*displacement, buffer, elements, mesh);
     break;
   }
   case SynchronizationTag::_for_dump: {
     unpackNodalDataHelper(*displacement, buffer, elements, mesh);
     unpackNodalDataHelper(*velocity, buffer, elements, mesh);
     unpackNodalDataHelper(*acceleration, buffer, elements, mesh);
     unpackNodalDataHelper(*internal_force, buffer, elements, mesh);
     unpackNodalDataHelper(*external_force, buffer, elements, mesh);
     break;
   }
   case SynchronizationTag::_smm_boundary: {
     unpackNodalDataHelper(*external_force, buffer, elements, mesh);
     unpackNodalDataHelper(*velocity, buffer, elements, mesh);
     unpackNodalDataHelper(*blocked_dofs, buffer, elements, mesh);
     break;
   }
   default: {
   }
   }
 
   if (tag != SynchronizationTag::_material_id) {
     splitByMaterial(elements, [&](auto && mat, auto && elements) {
       mat.unpackData(buffer, elements, tag);
     });
   }
 
   AKANTU_DEBUG_OUT();
 }
 
 /* -------------------------------------------------------------------------- */
 UInt SolidMechanicsModel::getNbData(const Array<UInt> & dofs,
                                     const SynchronizationTag & tag) const {
   AKANTU_DEBUG_IN();
 
   UInt size = 0;
   //  UInt nb_nodes = mesh.getNbNodes();
 
   switch (tag) {
   case SynchronizationTag::_smm_uv: {
     size += sizeof(Real) * Model::spatial_dimension * 2;
     break;
   }
   case SynchronizationTag::_smm_res: /* FALLTHRU */
   case SynchronizationTag::_smm_mass: {
     size += sizeof(Real) * Model::spatial_dimension;
     break;
   }
   case SynchronizationTag::_for_dump: {
     size += sizeof(Real) * Model::spatial_dimension * 5;
     break;
   }
   default: {
     AKANTU_ERROR("Unknown ghost synchronization tag : " << tag);
   }
   }
 
   AKANTU_DEBUG_OUT();
   return size * dofs.size();
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::packData(CommunicationBuffer & buffer,
                                    const Array<UInt> & dofs,
                                    const SynchronizationTag & tag) const {
   AKANTU_DEBUG_IN();
 
   switch (tag) {
   case SynchronizationTag::_smm_uv: {
     packDOFDataHelper(*displacement, buffer, dofs);
     packDOFDataHelper(*velocity, buffer, dofs);
     break;
   }
   case SynchronizationTag::_smm_res: {
     packDOFDataHelper(*internal_force, buffer, dofs);
     break;
   }
   case SynchronizationTag::_smm_mass: {
     packDOFDataHelper(*mass, buffer, dofs);
     break;
   }
   case SynchronizationTag::_for_dump: {
     packDOFDataHelper(*displacement, buffer, dofs);
     packDOFDataHelper(*velocity, buffer, dofs);
     packDOFDataHelper(*acceleration, buffer, dofs);
     packDOFDataHelper(*internal_force, buffer, dofs);
     packDOFDataHelper(*external_force, buffer, dofs);
     break;
   }
   default: {
     AKANTU_ERROR("Unknown ghost synchronization tag : " << tag);
   }
   }
 
   AKANTU_DEBUG_OUT();
 }
 
 /* -------------------------------------------------------------------------- */
 void SolidMechanicsModel::unpackData(CommunicationBuffer & buffer,
                                      const Array<UInt> & dofs,
                                      const SynchronizationTag & tag) {
   AKANTU_DEBUG_IN();
 
   switch (tag) {
   case SynchronizationTag::_smm_uv: {
     unpackDOFDataHelper(*displacement, buffer, dofs);
     unpackDOFDataHelper(*velocity, buffer, dofs);
     break;
   }
   case SynchronizationTag::_smm_res: {
     unpackDOFDataHelper(*internal_force, buffer, dofs);
     break;
   }
   case SynchronizationTag::_smm_mass: {
     unpackDOFDataHelper(*mass, buffer, dofs);
     break;
   }
   case SynchronizationTag::_for_dump: {
     unpackDOFDataHelper(*displacement, buffer, dofs);
     unpackDOFDataHelper(*velocity, buffer, dofs);
     unpackDOFDataHelper(*acceleration, buffer, dofs);
     unpackDOFDataHelper(*internal_force, buffer, dofs);
     unpackDOFDataHelper(*external_force, buffer, dofs);
     break;
   }
   default: {
     AKANTU_ERROR("Unknown ghost synchronization tag : " << tag);
   }
   }
 
   AKANTU_DEBUG_OUT();
 }
 
 } // namespace akantu