Esempio n. 1
0
bool MeshTestUtility::neighborBasesAgreeOnSides(Teuchos::RCP<Mesh> mesh, Epetra_MultiVector &globalSolutionCoefficients)
{
  bool success = true;
  MeshTopologyViewPtr meshTopo = mesh->getTopology();
  int spaceDim = meshTopo->getDimension();

  set<IndexType> activeCellIndices = meshTopo->getActiveCellIndices();
  for (set<IndexType>::iterator cellIt=activeCellIndices.begin(); cellIt != activeCellIndices.end(); cellIt++)
  {
    IndexType cellIndex = *cellIt;
    CellPtr cell = meshTopo->getCell(cellIndex);

    BasisCachePtr fineCellBasisCache = BasisCache::basisCacheForCell(mesh, cellIndex);
    ElementTypePtr fineElemType = mesh->getElementType(cellIndex);
    DofOrderingPtr fineElemTrialOrder = fineElemType->trialOrderPtr;

    FieldContainer<double> fineSolutionCoefficients(fineElemTrialOrder->totalDofs());
    mesh->globalDofAssignment()->interpretGlobalCoefficients(cellIndex, fineSolutionCoefficients, globalSolutionCoefficients);
//    if ((cellIndex==0) || (cellIndex==2)) {
//      cout << "MeshTestUtility: local coefficients for cell " << cellIndex << ":\n" << fineSolutionCoefficients;
//    }

    unsigned sideCount = cell->getSideCount();
    for (unsigned sideOrdinal=0; sideOrdinal<sideCount; sideOrdinal++)
    {
      FieldContainer<double> fineSideRefPoints, fineCellRefPoints, coarseSideRefPoints, coarseCellRefPoints;
      bool hasCoarserNeighbor = determineRefTestPointsForNeighbors(meshTopo, cell, sideOrdinal, fineSideRefPoints, fineCellRefPoints, coarseSideRefPoints, coarseCellRefPoints);
      if (!hasCoarserNeighbor) continue;

      pair<GlobalIndexType, unsigned> neighborInfo = cell->getNeighborInfo(sideOrdinal, meshTopo);

      CellPtr neighborCell = meshTopo->getCell(neighborInfo.first);

      unsigned numTestPoints = coarseCellRefPoints.dimension(0);

      //        cout << "testing neighbor agreement between cell " << cellIndex << " and " << neighborCell->cellIndex() << endl;

      // if we get here, the cell has a neighbor on this side, and is at least as fine as that neighbor.

      BasisCachePtr fineSideBasisCache = fineCellBasisCache->getSideBasisCache(sideOrdinal);

      fineCellBasisCache->setRefCellPoints(fineCellRefPoints);

      BasisCachePtr coarseCellBasisCache = BasisCache::basisCacheForCell(mesh, neighborInfo.first);
      BasisCachePtr coarseSideBasisCache = coarseCellBasisCache->getSideBasisCache(neighborInfo.second);

      coarseCellBasisCache->setRefCellPoints(coarseCellRefPoints);

      bool hasSidePoints = (fineSideRefPoints.size() > 0);
      if (hasSidePoints)
      {
        fineSideBasisCache->setRefCellPoints(fineSideRefPoints);
        coarseSideBasisCache->setRefCellPoints(coarseSideRefPoints);
      }

      // sanity check: do physical points match?

      FieldContainer<double> fineCellPhysicalCubaturePoints = fineCellBasisCache->getPhysicalCubaturePoints();
      FieldContainer<double> coarseCellPhysicalCubaturePoints = coarseCellBasisCache->getPhysicalCubaturePoints();

      double tol = 1e-14;
      double maxDiff = 0;
      if (! fcsAgree(coarseCellPhysicalCubaturePoints, fineCellPhysicalCubaturePoints, tol, maxDiff) )
      {
        cout << "ERROR: MeshTestUtility::neighborBasesAgreeOnSides internal error: fine and coarse cell cubature points do not agree.\n";
        success = false;
        continue;
      }

      if (hasSidePoints)
      {
        FieldContainer<double> fineSidePhysicalCubaturePoints = fineSideBasisCache->getPhysicalCubaturePoints();
        FieldContainer<double> coarseSidePhysicalCubaturePoints = coarseSideBasisCache->getPhysicalCubaturePoints();

        double tol = 1e-14;
        double maxDiff = 0;
        if (! fcsAgree(fineSidePhysicalCubaturePoints, fineCellPhysicalCubaturePoints, tol, maxDiff) )
        {
          cout << "ERROR: MeshTestUtility::neighborBasesAgreeOnSides internal error: fine side and cell cubature points do not agree.\n";
          success = false;
          continue;
        }
        if (! fcsAgree(coarseSidePhysicalCubaturePoints, coarseCellPhysicalCubaturePoints, tol, maxDiff) )
        {
          cout << "ERROR: MeshTestUtility::neighborBasesAgreeOnSides internal error: coarse side and cell cubature points do not agree.\n";
          success = false;
          continue;
        }
        if (! fcsAgree(coarseSidePhysicalCubaturePoints, fineSidePhysicalCubaturePoints, tol, maxDiff) )
        {
          cout << "ERROR: MeshTestUtility::neighborBasesAgreeOnSides internal error: fine and coarse side cubature points do not agree.\n";
          success = false;
          continue;
        }
      }

      ElementTypePtr coarseElementType = mesh->getElementType(neighborInfo.first);
      DofOrderingPtr coarseElemTrialOrder = coarseElementType->trialOrderPtr;

      FieldContainer<double> coarseSolutionCoefficients(coarseElemTrialOrder->totalDofs());
      mesh->globalDofAssignment()->interpretGlobalCoefficients(neighborInfo.first, coarseSolutionCoefficients, globalSolutionCoefficients);

      set<int> varIDs = fineElemTrialOrder->getVarIDs();
      for (set<int>::iterator varIt = varIDs.begin(); varIt != varIDs.end(); varIt++)
      {
        int varID = *varIt;
//        cout << "MeshTestUtility: varID " << varID << ":\n";
        bool isTraceVar = mesh->bilinearForm()->isFluxOrTrace(varID);
        BasisPtr fineBasis, coarseBasis;
        BasisCachePtr fineCache, coarseCache;
        if (isTraceVar)
        {
          if (! hasSidePoints) continue; // then nothing to do for traces
          fineBasis = fineElemTrialOrder->getBasis(varID, sideOrdinal);
          coarseBasis = coarseElemTrialOrder->getBasis(varID, neighborInfo.second);
          fineCache = fineSideBasisCache;
          coarseCache = coarseSideBasisCache;
        }
        else
        {
          fineBasis = fineElemTrialOrder->getBasis(varID);
          coarseBasis = coarseElemTrialOrder->getBasis(varID);
          fineCache = fineCellBasisCache;
          coarseCache = coarseCellBasisCache;

          Camellia::EFunctionSpace fs = fineBasis->functionSpace();
          if ((fs == Camellia::FUNCTION_SPACE_HVOL)
              || (fs == Camellia::FUNCTION_SPACE_VECTOR_HVOL)
              || (fs == Camellia::FUNCTION_SPACE_TENSOR_HVOL))
          {
            // volume L^2 basis: no continuities expected...
            continue;
          }
        }
        FieldContainer<double> localValuesFine = *fineCache->getTransformedValues(fineBasis, OP_VALUE);
        FieldContainer<double> localValuesCoarse = *coarseCache->getTransformedValues(coarseBasis, OP_VALUE);

        bool scalarValued = (localValuesFine.rank() == 3);

        // the following used if vector or tensor-valued:
        Teuchos::Array<int> valueDim;
        unsigned componentsPerValue = 1;
        FieldContainer<double> valueContainer; // just used for enumeration computation...
        if (!scalarValued)
        {
          localValuesFine.dimensions(valueDim);
          // clear first three:
          valueDim.erase(valueDim.begin());
          valueDim.erase(valueDim.begin());
          valueDim.erase(valueDim.begin());
          valueContainer.resize(valueDim);
          componentsPerValue = valueContainer.size();
        }

        //          if (localValuesFine.rank() != 3) {
        //            cout << "WARNING: MeshTestUtility::neighborBasesAgreeOnSides() only supports scalar-valued bases right now.  Skipping check for varID " << varID << endl;
        //            continue;
        //          }

        FieldContainer<double> localPointValuesFine(fineElemTrialOrder->totalDofs());
        FieldContainer<double> localPointValuesCoarse(coarseElemTrialOrder->totalDofs());

        for (int valueComponentOrdinal=0; valueComponentOrdinal<componentsPerValue; valueComponentOrdinal++)
        {
          Teuchos::Array<int> valueMultiIndex(valueContainer.rank());

          if (!scalarValued)
            valueContainer.getMultiIndex(valueMultiIndex, valueComponentOrdinal);

          Teuchos::Array<int> localValuesMultiIndex(localValuesFine.rank());

          for (int r=0; r<valueMultiIndex.size(); r++)
          {
            localValuesMultiIndex[r+3] = valueMultiIndex[r];
          }

          for (int ptOrdinal=0; ptOrdinal<numTestPoints; ptOrdinal++)
          {
            localPointValuesCoarse.initialize(0);
            localPointValuesFine.initialize(0);
            localValuesMultiIndex[2] = ptOrdinal;

            double fineSolutionValue = 0, coarseSolutionValue = 0;

            for (int basisOrdinal=0; basisOrdinal < fineBasis->getCardinality(); basisOrdinal++)
            {
              int fineDofIndex;
              if (isTraceVar)
                fineDofIndex = fineElemTrialOrder->getDofIndex(varID, basisOrdinal, sideOrdinal);
              else
                fineDofIndex = fineElemTrialOrder->getDofIndex(varID, basisOrdinal);
              if (scalarValued)
              {
                localPointValuesFine(fineDofIndex) = localValuesFine(0,basisOrdinal,ptOrdinal);
              }
              else
              {
                localValuesMultiIndex[1] = basisOrdinal;
                localPointValuesFine(fineDofIndex) = localValuesFine.getValue(localValuesMultiIndex);
              }

              fineSolutionValue += fineSolutionCoefficients(fineDofIndex) * localPointValuesFine(fineDofIndex);
            }
            for (int basisOrdinal=0; basisOrdinal < coarseBasis->getCardinality(); basisOrdinal++)
            {
              int coarseDofIndex;
              if (isTraceVar)
                coarseDofIndex = coarseElemTrialOrder->getDofIndex(varID, basisOrdinal, neighborInfo.second);
              else
                coarseDofIndex = coarseElemTrialOrder->getDofIndex(varID, basisOrdinal);
              if (scalarValued)
              {
                localPointValuesCoarse(coarseDofIndex) = localValuesCoarse(0,basisOrdinal,ptOrdinal);
              }
              else
              {
                localValuesMultiIndex[1] = basisOrdinal;
                localPointValuesCoarse(coarseDofIndex) = localValuesCoarse.getValue(localValuesMultiIndex);
              }
              coarseSolutionValue += coarseSolutionCoefficients(coarseDofIndex) * localPointValuesCoarse(coarseDofIndex);
            }

            if (abs(coarseSolutionValue - fineSolutionValue) > 1e-13)
            {
              success = false;
              cout << "coarseSolutionValue (" << coarseSolutionValue << ") and fineSolutionValue (" << fineSolutionValue << ") differ by " << abs(coarseSolutionValue - fineSolutionValue);
              cout << " at point " << ptOrdinal << " for varID " << varID << ".  ";
              cout << "This may be an indication that something is amiss with the global-to-local map.\n";
            }
            else
            {
//              // DEBUGGING:
//              cout << "solution value at point (";
//              for (int d=0; d<spaceDim-1; d++) {
//                cout << fineSidePhysicalCubaturePoints(0,ptOrdinal,d) << ", ";
//              }
//              cout << fineSidePhysicalCubaturePoints(0,ptOrdinal,spaceDim-1) << "): ";
//              cout << coarseSolutionValue << endl;
            }

            FieldContainer<double> globalValuesFromFine, globalValuesFromCoarse;
            FieldContainer<GlobalIndexType> globalDofIndicesFromFine, globalDofIndicesFromCoarse;

            mesh->globalDofAssignment()->interpretLocalData(cellIndex, localPointValuesFine, globalValuesFromFine, globalDofIndicesFromFine);
            mesh->globalDofAssignment()->interpretLocalData(neighborInfo.first, localPointValuesCoarse, globalValuesFromCoarse, globalDofIndicesFromCoarse);

            std::map<GlobalIndexType, double> fineValuesMap;
            std::map<GlobalIndexType, double> coarseValuesMap;

            for (int i=0; i<globalDofIndicesFromCoarse.size(); i++)
            {
              GlobalIndexType globalDofIndex = globalDofIndicesFromCoarse[i];
              coarseValuesMap[globalDofIndex] = globalValuesFromCoarse[i];
            }

            double maxDiff = 0;
            for (int i=0; i<globalDofIndicesFromFine.size(); i++)
            {
              GlobalIndexType globalDofIndex = globalDofIndicesFromFine[i];
              fineValuesMap[globalDofIndex] = globalValuesFromFine[i];

              double diff = abs( fineValuesMap[globalDofIndex] - coarseValuesMap[globalDofIndex]);
              maxDiff = std::max(diff, maxDiff);
              if (diff > tol)
              {
                success = false;
                cout << "interpreted fine and coarse disagree at point (";
                for (int d=0; d<spaceDim; d++)
                {
                  cout << fineCellPhysicalCubaturePoints(0,ptOrdinal,d);
                  if (d==spaceDim-1)
                    cout <<  ").\n";
                  else
                    cout << ", ";
                }
              }
            }
            if (maxDiff > tol)
            {
              cout << "maxDiff: " << maxDiff << endl;
              cout << "globalValuesFromFine:\n" << globalValuesFromFine;
              cout << "globalValuesFromCoarse:\n" << globalValuesFromCoarse;

              cout << "globalDofIndicesFromFine:\n" << globalDofIndicesFromFine;
              cout <<  "globalDofIndicesFromCoarse:\n" << globalDofIndicesFromCoarse;

              continue; // only worth testing further if we passed the above
            }
          }
        }
      }
    }
  }

//  cout << "Completed neighborBasesAgreeOnSides.\n";
  return success;
}
Esempio n. 2
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;
}