diff --git a/Geo/MElement.cpp b/Geo/MElement.cpp
index 2900eaa8acda5f5a4270c976d16b5bea0533504f..2194c5a07c962bc2b1a14e25de0fe72b0d0e98a5 100644
--- a/Geo/MElement.cpp
+++ b/Geo/MElement.cpp
@@ -1415,6 +1415,21 @@ MElement *MElementFactory::create(int type, std::vector<MVertex*> &v,
   case MSH_PRI_6:   return new MPrism(v, num, part);
   case MSH_PRI_15:  return new MPrism15(v, num, part);
   case MSH_PRI_18:  return new MPrism18(v, num, part);
+  case MSH_PRI_40:  return new MPrismN(v, 3, num, part);
+  case MSH_PRI_75:  return new MPrismN(v, 4, num, part);
+  case MSH_PRI_126:  return new MPrismN(v, 5, num, part);
+  case MSH_PRI_196:  return new MPrismN(v, 6, num, part);
+  case MSH_PRI_288:  return new MPrismN(v, 7, num, part);
+  case MSH_PRI_405:  return new MPrismN(v, 8, num, part);
+  case MSH_PRI_550:  return new MPrismN(v, 9, num, part);
+  case MSH_PRI_24:  return new MPrismN(v, 3, num, part);
+  case MSH_PRI_33:  return new MPrismN(v, 4, num, part);
+  case MSH_PRI_42:  return new MPrismN(v, 5, num, part);
+  case MSH_PRI_51:  return new MPrismN(v, 6, num, part);
+  case MSH_PRI_60:  return new MPrismN(v, 7, num, part);
+  case MSH_PRI_69:  return new MPrismN(v, 8, num, part);
+  case MSH_PRI_78:  return new MPrismN(v, 9, num, part);
+  case MSH_PRI_1:  return new MPrismN(v, 0, num, part);
   case MSH_TET_20:  return new MTetrahedronN(v, 3, num, part);
   case MSH_TET_35:  return new MTetrahedronN(v, 4, num, part);
   case MSH_TET_56:  return new MTetrahedronN(v, 5, num, part);
diff --git a/Geo/MPrism.cpp b/Geo/MPrism.cpp
index beda50e53c21cf6f846fc010720235dbd20e8c48..3f11e25a5ea75b20db1fcab238572541227fe923 100644
--- a/Geo/MPrism.cpp
+++ b/Geo/MPrism.cpp
@@ -176,6 +176,10 @@ void MPrism18::getEdgeRep(int num, double *x, double *y, double *z, SVector3 *n)
   _myGetEdgeRep(this, num, x, y, z, n, CTX::instance()->mesh.numSubEdges);
 }
 
+void MPrismN::getEdgeRep(int num, double *x, double *y, double *z, SVector3 *n) {
+  _myGetEdgeRep(this, num, x, y, z, n, CTX::instance()->mesh.numSubEdges);
+}
+
 int MPrism15::getNumEdgesRep() {
   return 9 * CTX::instance()->mesh.numSubEdges;
 }
@@ -184,6 +188,10 @@ int MPrism18::getNumEdgesRep() {
   return 9 * CTX::instance()->mesh.numSubEdges;
 }
 
+int MPrismN::getNumEdgesRep() {
+  return 9 * CTX::instance()->mesh.numSubEdges;
+}
+
 
 void _myGetFaceRep(MPrism *pri, int num, double *x, double *y, double *z,
                           SVector3 *n, int numSubEdges)
@@ -413,6 +421,11 @@ void MPrism18::getFaceRep(int num, double *x, double *y, double *z, SVector3 *n)
   _myGetFaceRep(this, num, x, y, z, n, CTX::instance()->mesh.numSubEdges);
 }
 
+void MPrismN::getFaceRep(int num, double *x, double *y, double *z, SVector3 *n)
+{
+  _myGetFaceRep(this, num, x, y, z, n, CTX::instance()->mesh.numSubEdges);
+}
+
 int MPrism15::getNumFacesRep() {
   return 4 * (CTX::instance()->mesh.numSubEdges * CTX::instance()->mesh.numSubEdges * 2);
 }
@@ -420,3 +433,88 @@ int MPrism15::getNumFacesRep() {
 int MPrism18::getNumFacesRep() {
   return 4 * (CTX::instance()->mesh.numSubEdges * CTX::instance()->mesh.numSubEdges * 2);
 }
+
+int MPrismN::getNumFacesRep() {
+  return 4 * (CTX::instance()->mesh.numSubEdges * CTX::instance()->mesh.numSubEdges * 2);
+}
+
+namespace {
+
+void _addEdgeNodes(int num, bool reverse, int order, const std::vector<MVertex*> &vs,
+                   int &ind, std::vector<MVertex*> &v)
+{
+
+  const int nNode = order-1, startNode = num*nNode, endNode = startNode+nNode-1;
+
+  if (reverse)
+    for (int i=endNode; i>=startNode; i--, ind++) v[ind] = vs[i];
+  else
+    for (int i=startNode; i<=endNode; i++, ind++) v[ind] = vs[i];
+
+}
+
+void _addFaceNodes(int num, int order, const std::vector<MVertex*> &vs,
+                   int &ind, std::vector<MVertex*> &v)
+{
+
+  const int nNodeEd = order-1, nNodeTri = (order-2)*(order-1)/2;
+
+  int startNode, endNode;
+  if (num < 2) {
+    startNode = 9*nNodeEd+num*nNodeTri;
+    endNode = startNode+nNodeTri;
+  }
+  else {
+    const int nNodeQuad = (order-1)*(order-1);
+    startNode = 9*nNodeEd+2*nNodeTri+(num-2)*nNodeQuad;
+    endNode = startNode+nNodeQuad;
+  }
+
+  for (int i=startNode; i<endNode; i++, ind++) v[ind] = vs[i];
+
+}
+
+}
+
+// To be tested
+void MPrismN::getFaceVertices(const int num, std::vector<MVertex*> &v) const
+{
+
+  static const int edge[5][4] = {
+      {1, 3, 0, -1},
+      {6, 8, 7, -1},
+      {0, 4, 6, 2},
+      {2, 7, 5, 1},
+      {3, 5, 8, 4}
+  };
+  static const bool reverse[5][4] = {
+      {false, true, true, false},
+      {false, false, true, false},
+      {false, false, true, true},
+      {false, false, true, true},
+      {false, false, true, true}
+  };
+
+  int nNodeTotal, nEdge;
+  if (num < 2) {                            // Triangular face
+    nNodeTotal = (_order+1)*(_order+2)/2;
+    nEdge = 3;
+  }
+  else {                                    // Quad face
+    nNodeTotal = (_order+1)*(_order+1);
+    nEdge = 4;
+  }
+
+  v.resize(nNodeTotal);
+
+  // Add corner nodes (there are nEdge corner nodes)
+  MPrism::_getFaceVertices(num, v);
+  int ind = nEdge;
+
+  // Add edge nodes
+  for (int iE=0; iE<nEdge; iE++) _addEdgeNodes(edge[num][iE],reverse[num][iE],_order,_vs,ind,v);
+
+  // Add face nodes
+  _addFaceNodes(num,_order,_vs,ind,v);
+
+}
diff --git a/Geo/MPrism.h b/Geo/MPrism.h
index 964d8dd9d3056c37328ae200c057abed7825068a..2e27dc44979ac6065f67698857faaf05473d3e8e 100644
--- a/Geo/MPrism.h
+++ b/Geo/MPrism.h
@@ -385,4 +385,108 @@ class MPrism18 : public MPrism {
   }
 };
 
+/*
+ * MPrismN
+ */
+class MPrismN : public MPrism {
+ protected:
+  std::vector<MVertex *> _vs;
+  const char _order;
+ public :
+  MPrismN(MVertex *v0, MVertex *v1, MVertex *v2, MVertex *v3, MVertex *v4,
+           MVertex *v5, const std::vector<MVertex*> &v, char order, int num=0, int part=0)
+    : MPrism(v0, v1, v2, v3, v4, v5, num, part), _vs(v), _order(order)
+  {
+    for(unsigned int i = 0; i < _vs.size(); i++) _vs[i]->setPolynomialOrder(_order);
+  }
+  MPrismN(const std::vector<MVertex*> &v, char order, int num=0, int part=0)
+    : MPrism(v, num, part), _order(order)
+  {
+    for (unsigned int i = 6; i < v.size(); i++) _vs.push_back(v[i]);
+    for (unsigned int i = 0; i < _vs.size(); i++) _vs[i]->setPolynomialOrder(2);
+  }
+  ~MPrismN(){}
+  virtual int getPolynomialOrder() const { return _order; }
+  virtual int getNumVertices() const { return 6+_vs.size(); }
+  virtual MVertex *getVertex(int num){ return num < 6 ? _v[num] : _vs[num-6]; }
+  virtual const MVertex *getVertex(int num) const{ return num < 6 ? _v[num] : _vs[num-6]; }
+  virtual int getNumEdgeVertices() const { return 9*(_order-1); }
+  virtual int getNumFaceVertices() const { int n = _order-1; return n*((n-1)+3*n); }
+  virtual int getNumVolumeVertices() const { int n = _order-1; return _vs.size()-n*(9+(n-1)+3*n); }
+  virtual int getNumEdgesRep();
+  virtual void getEdgeRep(int num, double *x, double *y, double *z, SVector3 *n);
+  virtual void getEdgeVertices(const int num, std::vector<MVertex*> &v) const
+  {
+    v.resize(_order+1);
+    MPrism::_getEdgeVertices(num, v);
+    const int n = _order-1;
+    for (int i=0; i<n; i++) v[2+i] = _vs[num*n+i];
+  }
+  virtual int getNumFacesRep();
+  virtual void getFaceRep(int num, double *x, double *y, double *z, SVector3 *n);
+  virtual void getFaceVertices(const int num, std::vector<MVertex*> &v) const;
+  virtual int getTypeForMSH() const {
+    switch (_order) {
+    case 0:
+      return MSH_PRI_1;
+    case 1:
+      return MSH_PRI_6;
+    case 2:
+      if (_vs.size() == 12) return MSH_PRI_18;
+      else if (_vs.size() == 9) return MSH_PRI_15;
+      break;
+    case 3:
+      if (_vs.size() == 34) return MSH_PRI_40;
+      else if (_vs.size() == 18) return MSH_PRI_24;
+      break;
+    case 4:
+      if (_vs.size() == 69) return MSH_PRI_75;
+      else if (_vs.size() == 27) return MSH_PRI_33;
+      break;
+    case 5:
+      if (_vs.size() == 120) return MSH_PRI_126;
+      else if (_vs.size() == 36) return MSH_PRI_42;
+      break;
+    case 6:
+      if (_vs.size() == 190) return MSH_PRI_196;
+      else if (_vs.size() == 45) return MSH_PRI_51;
+      break;
+    case 7:
+      if (_vs.size() == 282) return MSH_PRI_288;
+      else if (_vs.size() == 54) return MSH_PRI_60;
+      break;
+    case 8:
+      if (_vs.size() == 399) return MSH_PRI_405;
+      else if (_vs.size() == 63) return MSH_PRI_69;
+      break;
+    case 9:
+      if (_vs.size() == 544) return MSH_PRI_550;
+      else if (_vs.size() == 72) return MSH_PRI_78;
+      break;
+    }
+    Msg::Error("No tag matches a p%d prism with %d vertices", _order, 6+_vs.size());
+    return 0;
+  }
+  virtual const char *getStringForPOS() const {
+    switch (_order) {
+    case 0: return "SI0";
+    case 1: return "SI1";
+    case 2: return "SI2";
+    case 3: return "SI3";
+    case 4: return "SI4";
+    case 5: return "SI5";
+    case 6: return "SI6";
+    case 7: return "SI7";
+    case 8: return "SI8";
+    case 9: return "SI9";
+    }
+    return "";
+  }
+  virtual void getNode(int num, double &u, double &v, double &w) const
+  {
+    const fullMatrix<double> &p = getFunctionSpace()->points;
+    u = p(num,0); v = p(num,1); w = p(num,2);
+  }
+};
+
 #endif
diff --git a/Mesh/HighOrder.cpp b/Mesh/HighOrder.cpp
index 69d6b8c09bd9379da24d79c0df670ecb0227d883..e8fd5ae08c6f12a608884ab5f4049f18f8a5de61 100644
--- a/Mesh/HighOrder.cpp
+++ b/Mesh/HighOrder.cpp
@@ -585,9 +585,7 @@ static void getRegionVertices(GRegion *gr, MElement *incomplete, MElement *ele,
     switch (nPts){
     case 0: return;
     case 1: return;
-    case 2:
-      //BasisFactory::getNodalBasis(MSH_TET_20)->points.print();
-      points = BasisFactory::getNodalBasis(MSH_TET_20)->points; break;
+    case 2: points = BasisFactory::getNodalBasis(MSH_TET_20)->points; break;
     case 3: points = BasisFactory::getNodalBasis(MSH_TET_35)->points; break;
     case 4: points = BasisFactory::getNodalBasis(MSH_TET_56)->points; break;
     case 5: points = BasisFactory::getNodalBasis(MSH_TET_84)->points; break;
@@ -599,7 +597,7 @@ static void getRegionVertices(GRegion *gr, MElement *incomplete, MElement *ele,
       Msg::Error("getRegionVertices not implemented for order %i", nPts+1);
       break;
     }
-    start = ((nPts+2)*(nPts+3)*(nPts+4)-(nPts-2)*(nPts-1)*(nPts))/6;
+    start = ((nPts+2)*(nPts+3)*(nPts+4)-(nPts-2)*(nPts-1)*(nPts))/6;  // = 4+6*(p-1)+4*(p-2)*(p-1)/2 = 4*(p+1)*(p+2)/2-6*(p-1)-2*4 = 2*p*p+2
     break;
   case TYPE_HEX :
     switch (nPts){
@@ -616,7 +614,24 @@ static void getRegionVertices(GRegion *gr, MElement *incomplete, MElement *ele,
       Msg::Error("getRegionVertices not implemented for order %i", nPts+1);
       break;
     }
-    start = (nPts+2)*(nPts+2)*(nPts+2) - (nPts)*(nPts)*(nPts) ;
+    start = (nPts+2)*(nPts+2)*(nPts+2) - (nPts)*(nPts)*(nPts) ; // = 6*(p-1)*(p-1)+12*(p-1)+8 = 6*(p+1)*(p+1)-12*(p-1)-2*8 = 6*p*p+2
+    break;
+  case TYPE_PRI :
+    switch (nPts){
+    case 0: return;
+    case 1: return;
+    case 2: points = BasisFactory::getNodalBasis(MSH_PRI_40)->points; break;
+    case 3: points = BasisFactory::getNodalBasis(MSH_PRI_75)->points; break;
+    case 4: points = BasisFactory::getNodalBasis(MSH_PRI_126)->points; break;
+    case 5: points = BasisFactory::getNodalBasis(MSH_PRI_196)->points; break;
+    case 6: points = BasisFactory::getNodalBasis(MSH_PRI_288)->points; break;
+    case 7: points = BasisFactory::getNodalBasis(MSH_PRI_405)->points; break;
+    case 8: points = BasisFactory::getNodalBasis(MSH_PRI_550)->points; break;
+    default:
+      Msg::Error("getRegionVertices not implemented for order %i", nPts+1);
+      break;
+    }
+    start = 4*(nPts+1)*(nPts+1)+2;  // = 4*p*p+2 = 6+9*(p-1)+2*(p-2)*(p-1)/2+3*(p-1)*(p-1) = 2*(p+1)*(p+2)/2+3*(p+1)*(p+1)-9*(p-1)-2*6
     break;
   case TYPE_PYR:
     switch (nPts){
@@ -857,8 +872,9 @@ static MPrism *setHighOrder(MPrism *p, GRegion *gr,
                           0, p->getPartition());
     }
     else{
-      Msg::Error("PrismN generation not implemented");
-      return 0;
+      return new MPrismN(p->getVertex(0), p->getVertex(1), p->getVertex(2),
+                         p->getVertex(3), p->getVertex(4), p->getVertex(5),
+                         ve, nPts + 1, 0, p->getPartition());
     }
   }
   else{
@@ -871,8 +887,20 @@ static MPrism *setHighOrder(MPrism *p, GRegion *gr,
                           0, p->getPartition());
     }
     else {
-      Msg::Error("PrismN generation not implemented");
-      return 0;
+      // create serendipity element to place internal vertices (we used to
+      // saturate face vertices also, but the corresponding function spaces do
+      // not exist anymore, and there is no theoretical justification for doing
+      // it either way)
+      MPrismN incpl(p->getVertex(0), p->getVertex(1), p->getVertex(2),
+                    p->getVertex(3), p->getVertex(4), p->getVertex(5),
+                    ve, nPts + 1, 0, p->getPartition());
+      getFaceVertices(gr, p, vf, faceVertices, edgeVertices, linear, nPts);
+      ve.insert(ve.end(), vf.begin(), vf.end());
+      getRegionVertices(gr, &incpl, p, vr, linear, nPts);
+      ve.insert(ve.end(), vr.begin(), vr.end());
+      return new MPrismN(p->getVertex(0), p->getVertex(1), p->getVertex(2),
+                         p->getVertex(3), p->getVertex(4), p->getVertex(5),
+                         ve, nPts + 1, 0, p->getPartition());
     }
   }
 }