Example #1
0
Eigen::ArrayXXi distmesh::utils::findUniqueEdges(Eigen::Ref<Eigen::ArrayXXi const> const triangulation) {
    // find all unique combinations
    auto const combinations = nOverK(triangulation.cols(), 2);

    // find unique edges for all combinations
    // guarantee direction of edges with lower node index to higher index
    std::set<std::array<int, 2>> uniqueEdges;
    std::array<int, 2> edge = {{0, 0}};
    for (int combination = 0; combination < combinations.rows(); ++combination)
    for (int triangle = 0; triangle < triangulation.rows(); ++triangle) {
        edge[0] = triangulation(triangle, combinations(combination, 0));
        edge[1] = triangulation(triangle, combinations(combination, 1));

        edge = edge[1] < edge[0] ? std::array<int, 2>{edge[1], edge[0]} : edge;

        uniqueEdges.insert(edge);
    }

    // copy set to eigen array
    Eigen::ArrayXXi edgeIndices(uniqueEdges.size(), 2);
    int index = 0;
    for (auto const& edge : uniqueEdges) {
        edgeIndices(index, 0) = edge[0];
        edgeIndices(index, 1) = edge[1];

        index++;
    }

    return edgeIndices;
}
Example #2
0
Eigen::ArrayXXi distmesh::utils::getTriangulationEdgeIndices(
    Eigen::Ref<Eigen::ArrayXXi const> const triangulation,
    Eigen::Ref<Eigen::ArrayXXi const> const edges) {
    // find indices for each edge of triangulation in edge index array
    Eigen::ArrayXXi edgeIndices(triangulation.rows(), triangulation.cols());
    for (int element = 0; element < triangulation.rows(); ++element)
    for (int node = 0; node < triangulation.cols(); ++node) {
        // create edge with direction from node with lower index
        // to node with higher index
        auto const edge = (Eigen::ArrayXi(2) << triangulation(element, node), triangulation(element, (node + 1) % triangulation.cols())).finished();

        // check if edge is in edges list, and get index
        int edgeIndex = 0;
        if (((edges.rowwise() - edge.transpose()).square().rowwise().sum().minCoeff(&edgeIndex) == 0) ||
            ((edges.rowwise() - edge.transpose().reverse()).square().rowwise().sum().minCoeff(&edgeIndex) == 0)) {
            edgeIndices(element, node) = edgeIndex;
        }
    }

    return edgeIndices;
}
Example #3
0
void Expand(const TriMeshWithTopology& in,Real distance,int divs,TriMesh& m)
{
  Assert(in.tris.size()==in.triNeighbors.size());
  Assert(in.verts.size()==in.incidentTris.size());
  if(divs > 1) {
    FatalError("Only support 1 division yet");
  }
  Assert(divs <= 1);
  vector<TriMeshEdge> edges;
  vector<IntTriple> edgeIndices(in.tris.size());
  GetPairedEdges(in,edges);
  for(size_t i=0;i<edges.size();i++) {
    edgeIndices[edges[i].t1][edges[i].e1] = i;
    edgeIndices[edges[i].t2][edges[i].e2] = i;
  }

  vector<Vector3> tripts(in.tris.size()*3);
  vector<Vector3> vertpts(in.verts.size());
  vector<Vector3> edgepts(edges.size()*2);
  for(size_t i=0;i<in.tris.size();i++) {
    Vector3 n=in.TriangleNormal(i)*distance;
    tripts[i*3] = in.TriangleVertex(i,0) + n;
    tripts[i*3+1] = in.TriangleVertex(i,1) + n;
    tripts[i*3+2] = in.TriangleVertex(i,2) + n;
  }
  for(size_t i=0;i<in.verts.size();i++) {
    Vector3 n(Zero);
    for(size_t j=0;j<in.incidentTris[i].size();j++) 
      n+=in.TriangleNormal(in.incidentTris[i][j]);
    n.inplaceNormalize();
    vertpts[i] = in.verts[i] + n * distance;
  }
  if(divs >= 1) {
    for(size_t i=0;i<edges.size();i++) {
      Vector3 n=in.TriangleNormal(edges[i].t1)+in.TriangleNormal(edges[i].t2);
      n.inplaceNormalize();
      edgepts[i*2] = in.verts[edges[i].v1] + n * distance;
      edgepts[i*2+1] = in.verts[edges[i].v2] + n * distance;
    }
  }
  m.verts = tripts;
  ArrayUtils::concat(m.verts,vertpts);
  ArrayUtils::concat(m.verts,edgepts);
  m.tris.resize(0);
  m.tris.reserve(in.tris.size() + 2*(1+divs)*edges.size() + in.verts.size());
  //triangle faces
  for(size_t i=0;i<in.tris.size();i++) 
    m.tris.push_back(IntTriple(i*3,i*3+1,i*3+2));
  //vertex faces
  int k1=(int)tripts.size();
  int k2=(int)tripts.size()+(int)vertpts.size();
  for(size_t i=0;i<in.verts.size();i++) {
    if(divs==1) {
      for(size_t j=0;j<in.incidentTris[i].size();j++) {
	int t=in.incidentTris[i][j];
	int vi=in.tris[t].getIndex(i);
	Assert(vi >= 0);
	int e1,e2;
	edgeIndices[in.incidentTris[i][j]].getCompliment(vi,e1,e2);
	int flip1 = (edges[e1].v1 == (int)i? 0 : 1);
	int flip2 = (edges[e2].v1 == (int)i? 0 : 1);
	m.tris.push_back(IntTriple(k1+i,t*3+vi,k2+e1*2+flip1));
	m.tris.push_back(IntTriple(k1+i,k2+e2*2+flip2,t*3+vi));
      }
    }
    else {
      vector<int> ring; ring.reserve(in.incidentTris[i].size());
      int t0=in.incidentTris[i][0];
      while(ring.size() < in.incidentTris[i].size()) {
	ring.push_back(t0);
	int vi=in.tris[t0].getIndex(i);
	t0=in.triNeighbors[t0][(vi+1)%3];
	if(ring.size() >= 2)
	  Assert(t0 != ring[ring.size()-2]);
      }
      vector<int> v(ring.size());
      for(size_t j=0;j<ring.size();j++) {
	int t=ring[j];
	int vi=in.tris[t].getIndex(i);
	Assert(vi >= 0);
	v[j] = t*3+vi;
      }
      for(size_t j=0;j<ring.size();j++) {
	int vi=v[j];
	int vn=v[(j+1)%ring.size()];
	m.tris.push_back(IntTriple(k1+i,vi,vn));
      }
    }
  }
  //edge faces
  for(size_t i=0;i<edges.size();i++) {
    int v11=(edges[i].e1+1)%3,v12=(edges[i].e1+2)%3;
    int v21=(edges[i].e2+1)%3,v22=(edges[i].e2+2)%3;
    int t1=edges[i].t1;
    int t2=edges[i].t2;
    int a=t1*3+v11,b=t1*3+v12,c=t2*3+v21,d=t2*3+v22;
    if(divs==0) {
      m.tris.push_back(IntTriple(a,c,b));
      m.tris.push_back(IntTriple(a,d,c));
    }
    else {
      int e=k2+i*2;
      int f=k2+i*2+1;
      m.tris.push_back(IntTriple(a,f,b));
      m.tris.push_back(IntTriple(a,e,f));
      m.tris.push_back(IntTriple(e,c,f));
      m.tris.push_back(IntTriple(e,d,c));
    }
  }
}
Example #4
0
bool MeshTopologyTests::testEntityConstraints()
{
  bool success = true;

  // make two simple meshes
  MeshTopologyPtr mesh2D = makeRectMesh(0.0, 0.0, 2.0, 1.0,
                                        2, 1);
  MeshTopologyPtr mesh3D = makeHexMesh(0.0, 0.0, 0.0, 2.0, 4.0, 3.0,
                                       2, 2, 1);

  unsigned vertexDim = 0;
  unsigned edgeDim = 1;
  unsigned faceDim = 2;

  // first, check that unconstrained edges and faces are unconstrained

  set< unsigned > boundaryEdges;
  set< unsigned > internalEdges;

  for (unsigned cellIndex=0; cellIndex<mesh2D->cellCount(); cellIndex++)
  {
    CellPtr cell = mesh2D->getCell(cellIndex);
    unsigned sideCount = cell->getSideCount();

    for (unsigned sideOrdinal=0; sideOrdinal<sideCount; sideOrdinal++)
    {
      unsigned edgeIndex = cell->entityIndex(edgeDim, sideOrdinal);
      unsigned numCells = mesh2D->getActiveCellCount(edgeDim,edgeIndex);
      if (numCells == 1)   // boundary edge
      {
        boundaryEdges.insert(edgeIndex);
      }
      else if (numCells == 2)
      {
        internalEdges.insert(edgeIndex);
      }
      else
      {
        success = false;
        cout << "testEntityConstraints: In initial 2D mesh, edge " << edgeIndex << " has active cell count of " << numCells << ".\n";
      }
    }
  }
  if (internalEdges.size() != 1)
  {
    success = false;
    cout << "testEntityConstraints: In initial 2D mesh, there are " << internalEdges.size() << " internal edges (expected 1).\n";
  }
  for (set<unsigned>::iterator edgeIt=internalEdges.begin(); edgeIt != internalEdges.end(); edgeIt++)
  {
    unsigned edgeIndex = *edgeIt;
    unsigned constrainingEntityIndex = mesh2D->getConstrainingEntity(edgeDim,edgeIndex).first;
    if (constrainingEntityIndex != edgeIndex)
    {
      success = false;
      cout << "testEntityConstraints: In initial 2D mesh, internal edge is constrained by a different edge.\n";
    }
  }

  set<unsigned> boundaryFaces;
  set<unsigned> internalFaces;
  map<unsigned, vector<unsigned> > faceToEdges;
  for (unsigned cellIndex=0; cellIndex<mesh3D->cellCount(); cellIndex++)
  {
    CellPtr cell = mesh3D->getCell(cellIndex);
    unsigned sideCount = cell->getSideCount();

    for (unsigned sideOrdinal=0; sideOrdinal<sideCount; sideOrdinal++)
    {
      unsigned faceIndex = cell->entityIndex(faceDim, sideOrdinal);
      unsigned numCells = mesh3D->getActiveCellCount(faceDim,faceIndex);
      if (numCells == 1)   // boundary face
      {
        boundaryFaces.insert(faceIndex);
      }
      else if (numCells == 2)
      {
        internalFaces.insert(faceIndex);
      }
      else
      {
        success = false;
        cout << "testEntityConstraints: In initial 3D mesh, face " << faceIndex << " has active cell count of " << numCells << ".\n";
      }

      if (faceToEdges.find(faceIndex) == faceToEdges.end())
      {
        CellTopoPtr faceTopo = cell->topology()->getSubcell(faceDim, sideOrdinal);
        unsigned numEdges = faceTopo->getSubcellCount(edgeDim);
        vector<unsigned> edgeIndices(numEdges);
        for (unsigned edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
        {
          edgeIndices[edgeOrdinal] = mesh3D->getFaceEdgeIndex(faceIndex, edgeOrdinal);
        }
      }
    }
  }

  if (internalFaces.size() != 4)
  {
    success = false;
    cout << "testEntityConstraints: In initial 3D mesh, there are " << internalFaces.size() << " internal faces (expected 4).\n";
  }
  for (set<unsigned>::iterator faceIt=internalFaces.begin(); faceIt != internalFaces.end(); faceIt++)
  {
    unsigned faceIndex = *faceIt;
    unsigned constrainingEntityIndex = mesh3D->getConstrainingEntity(faceDim,faceIndex).first;
    if (constrainingEntityIndex != faceIndex)
    {
      success = false;
      cout << "testEntityConstraints: In initial 3D mesh, internal face is constrained by a different face.\n";
    }
  }

  // now, make a single refinement in each mesh:
  unsigned cellToRefine2D = 0, cellToRefine3D = 3;
  mesh2D->refineCell(cellToRefine2D, RefinementPattern::regularRefinementPatternQuad(), mesh2D->cellCount());
  mesh3D->refineCell(cellToRefine3D, RefinementPattern::regularRefinementPatternHexahedron(), mesh3D->cellCount());

//  printMeshInfo(mesh2D);

  // figure out which faces/edges were refined and add the corresponding

  map<unsigned,pair<IndexType,unsigned> > expectedEdgeConstraints2D;
  set<unsigned> refinedEdges;
  for (set<unsigned>::iterator edgeIt=boundaryEdges.begin(); edgeIt != boundaryEdges.end(); edgeIt++)
  {
    set<unsigned> children = mesh2D->getChildEntitiesSet(edgeDim, *edgeIt);
    if (children.size() > 0)
    {
      refinedEdges.insert(*edgeIt);
      boundaryEdges.insert(children.begin(), children.end());
    }
  }
  for (set<unsigned>::iterator edgeIt=internalEdges.begin(); edgeIt != internalEdges.end(); edgeIt++)
  {
    set<unsigned> children = mesh2D->getChildEntitiesSet(edgeDim, *edgeIt);
    if (children.size() > 0)
    {
      refinedEdges.insert(*edgeIt);
      internalEdges.insert(children.begin(), children.end());
      for (set<unsigned>::iterator childIt = children.begin(); childIt != children.end(); childIt++)
      {
        unsigned childIndex = *childIt;
        expectedEdgeConstraints2D[childIndex] = make_pair(*edgeIt, edgeDim);
      }
    }
  }
  // 1 quad refined: expect 4 refined edges
  if (refinedEdges.size() != 4)
  {
    success = false;
    cout << "After initial refinement, 2D mesh has " << refinedEdges.size() << " refined edges (expected 4).\n";
  }
  checkConstraints(mesh2D, edgeDim, expectedEdgeConstraints2D);

  set<unsigned> refinedFaces;
  map<unsigned,pair<IndexType,unsigned> > expectedFaceConstraints3D;
  map<unsigned,pair<IndexType,unsigned> > expectedEdgeConstraints3D;

  for (set<unsigned>::iterator faceIt=boundaryFaces.begin(); faceIt != boundaryFaces.end(); faceIt++)
  {
    set<unsigned> children = mesh3D->getChildEntitiesSet(faceDim, *faceIt);
    if (children.size() > 0)
    {
      refinedFaces.insert(*faceIt);
      boundaryFaces.insert(children.begin(), children.end());
    }
  }

  for (set<unsigned>::iterator faceIt=internalFaces.begin(); faceIt != internalFaces.end(); faceIt++)
  {
    vector<unsigned> children = mesh3D->getChildEntities(faceDim, *faceIt);
    if (children.size() > 0)
    {
      refinedFaces.insert(*faceIt);
      internalFaces.insert(children.begin(), children.end());
      for (unsigned childOrdinal = 0; childOrdinal < children.size(); childOrdinal++)
      {
        unsigned childIndex = children[childOrdinal];
        expectedFaceConstraints3D[childIndex] = make_pair(*faceIt, faceDim);
        unsigned numEdges = 4;
        unsigned internalEdgeCount = 0; // for each child of a quad, we expect to have 2 internal edges
        for (unsigned edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
        {
          unsigned edgeIndex = mesh3D->getFaceEdgeIndex(childIndex, edgeOrdinal);
          unsigned activeCellCount = mesh3D->getActiveCellCount(edgeDim, edgeIndex);
          if (activeCellCount==2)
          {
            internalEdgeCount++;
            expectedEdgeConstraints3D[edgeIndex] = make_pair(*faceIt, faceDim);
          }
          else if (activeCellCount==1)     // hanging edge
          {
            if (! mesh3D->entityHasParent(edgeDim, edgeIndex))
            {
              cout << "Hanging edge with edgeIndex " << edgeIndex << " (in face " << childIndex << ") does not have a parent edge.\n";
              cout << "Edge vertices:\n";
              mesh3D->printEntityVertices(edgeDim, edgeIndex);
              cout << "Face vertices:\n";
              mesh3D->printEntityVertices(faceDim, childIndex);
              success = false;
            }
            else
            {
              unsigned edgeParentIndex = mesh3D->getEntityParent(edgeDim, edgeIndex);
              expectedEdgeConstraints3D[edgeIndex] = make_pair(edgeParentIndex, edgeDim);
            }
          }
          else
          {
            cout << "Unexpected number of active cells: " << activeCellCount << endl;
          }
        }
        if (internalEdgeCount != 2)
        {
          cout << "Expected internalEdgeCount to be 2; was " << internalEdgeCount << endl;
          success = false;
        }
      }
    }
  }
  // 1 hex refined: expect 6 refined faces
  if (refinedFaces.size() != 6)
  {
    success = false;
    cout << "After initial refinement, 3D mesh has " << refinedFaces.size() << " refined faces (expected 6).\n";
  }
  if (! checkConstraints(mesh3D, faceDim, expectedFaceConstraints3D, "refined 3D mesh") )
  {
    cout << "Failed face constraint check for refined 3D mesh." << endl;
    success = false;
  }
  if (! checkConstraints(mesh3D, edgeDim, expectedEdgeConstraints3D, "refined 3D mesh") )
  {
    cout << "Failed edge constraint check for refined 3D mesh." << endl;
    success = false;
  }

  // now, we refine one of the children of the refined cells in each mesh, to produce a 2-level constraint
  set<unsigned> edgeChildren2D;
  set<unsigned> cellsForEdgeChildren2D;
  for (map<unsigned,pair<IndexType,unsigned> >::iterator edgeConstraint=expectedEdgeConstraints2D.begin();
       edgeConstraint != expectedEdgeConstraints2D.end(); edgeConstraint++)
  {
    edgeChildren2D.insert(edgeConstraint->first);
    unsigned cellIndex = mesh2D->getActiveCellIndices(edgeDim, edgeConstraint->first).begin()->first;
    cellsForEdgeChildren2D.insert(cellIndex);
//    cout << "cellsForEdgeChildren2D: " << cellIndex << endl;
  }

  // one of these has (1,0) as one of its vertices.  Let's figure out which one:
  unsigned vertexIndex;
  if (! mesh2D->getVertexIndex(makeVertex(1, 0), vertexIndex) )
  {
    cout << "Error: vertex not found.\n";
    success = false;
  }

  vector< pair<unsigned,unsigned> > cellsForVertex = mesh2D->getActiveCellIndices(vertexDim, vertexIndex);
  if (cellsForVertex.size() != 2)
  {
    cout << "cellsForVertex should have 2 entries; has " << cellsForVertex.size() << endl;
    success = false;
  }
  unsigned childCellForVertex, childCellConstrainedEdge;
  set<unsigned> childNewlyConstrainingEdges; // the two interior edges that we break
  for (vector< pair<unsigned,unsigned> >::iterator cellIt=cellsForVertex.begin(); cellIt != cellsForVertex.end(); cellIt++)
  {
//    cout << "cellsForVertex: " << cellIt->first << endl;
    if ( cellsForEdgeChildren2D.find( cellIt->first ) != cellsForEdgeChildren2D.end() )
    {
      // found match
      childCellForVertex = cellIt->first;
      // now, figure out which of the "edgeChildren2D" is shared by this cell:
      CellPtr cell = mesh2D->getCell(childCellForVertex);
      unsigned numEdges = cell->getSideCount();
      for (unsigned edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
      {
        unsigned edgeIndex = cell->entityIndex(edgeDim, edgeOrdinal);
        if (edgeChildren2D.find(edgeIndex) != edgeChildren2D.end())
        {
          childCellConstrainedEdge = edgeIndex;
        }
        else if ( mesh2D->getActiveCellCount(edgeDim, edgeIndex) == 2 )
        {
          childNewlyConstrainingEdges.insert(edgeIndex);
        }
      }
    }
  }
  if (childNewlyConstrainingEdges.size() != 2)
  {
    cout << "Expected 2 newly constraining edges after 2nd refinement of 2D mesh, but found " << childNewlyConstrainingEdges.size() << endl;
    success = false;
  }

  // refine the cell that matches (1,0):
  mesh2D->refineCell(childCellForVertex, RefinementPattern::regularRefinementPatternQuad(), mesh2D->cellCount());

  // now, fix the expected edge constraints, then check them...
  set<unsigned> childEdges = mesh2D->getChildEntitiesSet(edgeDim, childCellConstrainedEdge);
  if (childEdges.size() != 2)
  {
    cout << "Expected 2 child edges, but found " << childEdges.size() << ".\n";
    success = false;
  }
  for (set<unsigned>::iterator edgeIt = childEdges.begin(); edgeIt != childEdges.end(); edgeIt++)
  {
    expectedEdgeConstraints2D[*edgeIt] = expectedEdgeConstraints2D[childCellConstrainedEdge];
  }
  expectedEdgeConstraints2D.erase(childCellConstrainedEdge);
  for (set<unsigned>::iterator edgeIt = childNewlyConstrainingEdges.begin(); edgeIt != childNewlyConstrainingEdges.end(); edgeIt++)
  {
    set<unsigned> newChildEdges = mesh2D->getChildEntitiesSet(edgeDim, *edgeIt);
    for (set<unsigned>::iterator newEdgeIt = newChildEdges.begin(); newEdgeIt != newChildEdges.end(); newEdgeIt++)
    {
      expectedEdgeConstraints2D[*newEdgeIt] = make_pair(*edgeIt,edgeDim);
    }
  }

  if (! checkConstraints(mesh2D, edgeDim, expectedEdgeConstraints2D, "twice-refined 2D mesh") )
  {
    cout << "Failed constraint check for twice-refined 2D mesh." << endl;
    success = false;
  }

  // now, do a second level of refinement for 3D mesh
  // one of these has (1,2,0) as one of its vertices.  Let's figure out which one:
  if (! mesh3D->getVertexIndex(makeVertex(1, 2, 0), vertexIndex) )
  {
    cout << "Error: vertex not found.\n";
    success = false;
  }

  cellsForVertex = mesh3D->getActiveCellIndices(vertexDim, vertexIndex);
  if (cellsForVertex.size() != 4)
  {
    cout << "cellsForVertex should have 4 entries; has " << cellsForVertex.size() << endl;
    success = false;
  }

  vector<unsigned> justCellsForVertex;
  for (vector< pair<unsigned,unsigned> >::iterator entryIt = cellsForVertex.begin(); entryIt != cellsForVertex.end(); entryIt++)
  {
    justCellsForVertex.push_back(entryIt->first);
  }
  vector<unsigned> childCellIndices = mesh3D->getCell(cellToRefine3D)->getChildIndices(mesh3D);
  std::sort(childCellIndices.begin(), childCellIndices.end());
  vector<unsigned> matches(childCellIndices.size() + cellsForVertex.size());
  vector<unsigned>::iterator matchEnd = std::set_intersection(justCellsForVertex.begin(), justCellsForVertex.end(), childCellIndices.begin(), childCellIndices.end(), matches.begin());
  matches.resize(matchEnd-matches.begin());

  if (matches.size() != 1)
  {
    cout << "matches should have exactly one entry, but has " << matches.size();
    success = false;
  }
  unsigned childCellIndex = matches[0];
  CellPtr childCell = mesh3D->getCell(childCellIndex);
  set<unsigned> childInteriorUnconstrainedFaces;
  set<unsigned> childInteriorConstrainedFaces;
  unsigned faceCount = childCell->getSideCount();
  for (unsigned faceOrdinal=0; faceOrdinal<faceCount; faceOrdinal++)
  {
    unsigned faceIndex = childCell->entityIndex(faceDim, faceOrdinal);
    if (mesh3D->getActiveCellCount(faceDim, faceIndex) == 1)
    {
      // that's an interior constrained face, or a boundary face
      if (expectedFaceConstraints3D.find(faceIndex) != expectedFaceConstraints3D.end())
      {
        // constrained face
        childInteriorConstrainedFaces.insert(faceIndex);
      }
    }
    else if (mesh3D->getActiveCellCount(faceDim, faceIndex) == 2)
    {
      // an interior unconstrained face
      childInteriorUnconstrainedFaces.insert(faceIndex);
    }
    else
    {
      cout << "Error: unexpected active cell count.  Expected 1 or 2, but was " << mesh3D->getActiveCellCount(faceDim, faceIndex) << endl;
      success = false;
    }
  }
//  Camellia::print("childInteriorUnconstrainedFaces", childInteriorUnconstrainedFaces);
//  Camellia::print("childInteriorConstrainedFaces", childInteriorConstrainedFaces);

  mesh3D->refineCell(childCellIndex, RefinementPattern::regularRefinementPatternHexahedron(), mesh3D->cellCount());

  // update expected face and edge constraints
//  set<unsigned> edgeConstraintsToDrop;
  for (set<unsigned>::iterator faceIt=childInteriorConstrainedFaces.begin(); faceIt != childInteriorConstrainedFaces.end(); faceIt++)
  {
    unsigned faceIndex = *faceIt;
    set<unsigned> newChildFaces = mesh3D->getChildEntitiesSet(faceDim, faceIndex);
    for (set<unsigned>::iterator newChildIt=newChildFaces.begin(); newChildIt != newChildFaces.end(); newChildIt++)
    {
      unsigned newChildIndex = *newChildIt;
      expectedFaceConstraints3D[newChildIndex] = expectedFaceConstraints3D[faceIndex];
//      cout << "Expecting two-level face constraint: face " << newChildIndex << " constrained by face " << expectedFaceConstraints3D[newChildIndex].first << endl;
    }
    unsigned numEdges = mesh3D->getSubEntityCount(faceDim, faceIndex, edgeDim);
    set<IndexType> childEdgesOnParentBoundary;
    for (unsigned edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
    {
      unsigned edgeIndex = mesh3D->getSubEntityIndex(faceDim, faceIndex, edgeDim, edgeOrdinal);
      set<unsigned> newChildEdges = mesh3D->getChildEntitiesSet(edgeDim, edgeIndex);
      for (set<unsigned>::iterator newChildIt=newChildEdges.begin(); newChildIt != newChildEdges.end(); newChildIt++)
      {
        unsigned newChildIndex = *newChildIt;
        expectedEdgeConstraints3D[newChildIndex] = expectedEdgeConstraints3D[edgeIndex];
//        cout << "Expecting two-level edge constraint: edge " << newChildIndex << " constrained by ";
//        cout << typeString(expectedEdgeConstraints3D[newChildIndex].second) << " " << expectedEdgeConstraints3D[newChildIndex].first << endl;
        childEdgesOnParentBoundary.insert(newChildIndex);
//        edgeConstraintsToDrop.insert(edgeIndex);
      }
    }

    for (set<unsigned>::iterator newChildIt=newChildFaces.begin(); newChildIt != newChildFaces.end(); newChildIt++)
    {
      unsigned newChildFaceIndex = *newChildIt;
      int numEdges = mesh3D->getSubEntityCount(faceDim, newChildFaceIndex, edgeDim);
      for (unsigned edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
      {
        unsigned newChildEdgeIndex = mesh3D->getSubEntityIndex(faceDim, newChildFaceIndex, edgeDim, edgeOrdinal);
        if (childEdgesOnParentBoundary.find(newChildEdgeIndex) == childEdgesOnParentBoundary.end())
        {
          expectedEdgeConstraints3D[newChildEdgeIndex] = expectedFaceConstraints3D[faceIndex];
        }
      }
    }

    expectedFaceConstraints3D.erase(faceIndex);
  }
//  for (set<unsigned>::iterator edgeToDropIt=edgeConstraintsToDrop.begin(); edgeToDropIt != edgeConstraintsToDrop.end(); edgeToDropIt++) {
//    expectedEdgeConstraints3D.erase(*edgeToDropIt);
//  }
  for (set<unsigned>::iterator faceIt=childInteriorUnconstrainedFaces.begin(); faceIt != childInteriorUnconstrainedFaces.end(); faceIt++)
  {
    unsigned faceIndex = *faceIt;
    set<unsigned> newChildFaces = mesh3D->getChildEntitiesSet(faceDim, faceIndex);
    for (set<unsigned>::iterator newChildIt=newChildFaces.begin(); newChildIt != newChildFaces.end(); newChildIt++)
    {
      unsigned newChildIndex = *newChildIt;
      expectedFaceConstraints3D[newChildIndex] = make_pair(faceIndex, faceDim);
    }
    expectedFaceConstraints3D.erase(faceIndex);
    unsigned numEdges = mesh3D->getSubEntityCount(faceDim, faceIndex, edgeDim);
    set<IndexType> childEdgesOnParentBoundary;
    for (unsigned edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
    {
      unsigned edgeIndex = mesh3D->getSubEntityIndex(faceDim, faceIndex, edgeDim, edgeOrdinal);
      set<unsigned> newChildEdges = mesh3D->getChildEntitiesSet(edgeDim, edgeIndex);
      for (set<unsigned>::iterator newChildIt=newChildEdges.begin(); newChildIt != newChildEdges.end(); newChildIt++)
      {
        unsigned newChildIndex = *newChildIt;
        if (expectedEdgeConstraints3D.find(newChildIndex) == expectedEdgeConstraints3D.end())   // only impose edge constraint if there is not one already present
        {
          expectedEdgeConstraints3D[newChildIndex] = make_pair(edgeIndex,edgeDim);
        }
        childEdgesOnParentBoundary.insert(newChildIndex);
      }
    }
    for (set<unsigned>::iterator newChildIt=newChildFaces.begin(); newChildIt != newChildFaces.end(); newChildIt++)
    {
      unsigned newChildFaceIndex = *newChildIt;
      int numEdges = mesh3D->getSubEntityCount(faceDim, newChildFaceIndex, edgeDim);
      for (unsigned edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
      {
        unsigned newChildEdgeIndex = mesh3D->getSubEntityIndex(faceDim, newChildFaceIndex, edgeDim, edgeOrdinal);
        if (childEdgesOnParentBoundary.find(newChildEdgeIndex) == childEdgesOnParentBoundary.end())
        {
          if (expectedEdgeConstraints3D.find(newChildEdgeIndex) == expectedEdgeConstraints3D.end())   // only impose edge constraint if there is not one already present
          {
            expectedEdgeConstraints3D[newChildEdgeIndex] = make_pair(faceIndex, faceDim);
          }
        }
      }
    }
  }

  if (! checkConstraints(mesh3D, edgeDim, expectedEdgeConstraints3D, "twice-refined 3D mesh") )
  {
    cout << "Failed edge constraint check for twice-refined 3D mesh." << endl;
    success = false;
  }

  if (! checkConstraints(mesh3D, faceDim, expectedFaceConstraints3D, "twice-refined 3D mesh") )
  {
    cout << "Failed face constraint check for twice-refined 3D mesh." << endl;
    success = false;
  }

  return success;
}