bool checkVertexOrdinalsQuad(BasisPtr basis, vector<int> &vertexOrdinals)
{
  // check that the given indices are exactly the vertex basis functions:
  // a) these are (1,0) or (0,1) at the corresponding vertices
  // b) others are (0,0) at the vertices

  int numVertices = 4;

  FieldContainer<double> refCellPoints(numVertices,2); // vertices, in order
  refCellPoints(0,0) = -1;
  refCellPoints(0,1) = -1;
  refCellPoints(1,0) =  1;
  refCellPoints(1,1) = -1;
  refCellPoints(2,0) =  1;
  refCellPoints(2,1) =  1;
  refCellPoints(3,0) = -1;
  refCellPoints(3,1) =  1;

  // assume scalar basis for now -- we'll throw an exception if not...
  FieldContainer<double> values(basis->getCardinality(), numVertices); // F, P
  basis->getValues(values, refCellPoints, OPERATOR_VALUE);

  double tol = 1e-14;
  for (int vertexIndex=0; vertexIndex<numVertices; vertexIndex++)
  {
    int vertexOrdinal = vertexOrdinals[vertexIndex];
    for (int fieldIndex=0; fieldIndex<basis->getCardinality(); fieldIndex++)
    {
      double value = values(fieldIndex,vertexIndex);
      if (fieldIndex==vertexOrdinal)
      {
        // expect non-zero
        if (value < tol)
        {
          return false;
        }
      }
      else
      {
        // expect zero
        if (value > tol)
        {
          return false;
        }
      }
    }
  }
  return true;
}
Beispiel #2
0
void DofOrdering::addEntry(int varID, BasisPtr basis, int basisRank, int sideIndex) {
  // test to see if we already have one matching this.  (If so, that's an error.)
  pair<int,int> key = make_pair(varID, sideIndex);
  if ( indices.find(key) != indices.end() ) {
    TEUCHOS_TEST_FOR_EXCEPTION( true,
                       std::invalid_argument,
                       "Already have an entry in DofOrdering for this varID, sideIndex pair.");
  } else {
    indices[key] = vector<int>(basis->getCardinality());
  }
  
  vector<int>* dofIndices = &(indices[key]);
  
  for (vector<int>::iterator dofEntryIt = dofIndices->begin(); dofEntryIt != dofIndices->end(); dofEntryIt++) {
    *dofEntryIt = _nextIndex;
    _nextIndex++;
  }
  
  varIDs.insert(varID);
  
  numSidesForVarID[varID]++;
  //cout << "numSidesForVarID[" << varID << "]" << numSidesForVarID[varID] << endl;
  pair<int, int> basisKey = make_pair(varID,sideIndex);
//  _nextIndex += basis->getCardinality();
  bases[basisKey] = basis;
  basisRanks[varID] = basisRank;
}
Beispiel #3
0
void DofOrdering::copyLikeCoefficients( FieldContainer<double> &newValues, Teuchos::RCP<DofOrdering> oldDofOrdering,
                          const FieldContainer<double> &oldValues ) {
  // copy the coefficients for the bases that agree between the two DofOrderings
  // requires that "like" bases are actually pointers to the same memory location
  TEUCHOS_TEST_FOR_EXCEPTION( newValues.rank() != 1, std::invalid_argument, "newValues.rank() != 1");
  TEUCHOS_TEST_FOR_EXCEPTION( newValues.size() != totalDofs(), std::invalid_argument, "newValues.size() != totalDofs()");
  TEUCHOS_TEST_FOR_EXCEPTION( oldValues.rank() != 1, std::invalid_argument, "oldValues.rank() != 1");
  TEUCHOS_TEST_FOR_EXCEPTION( oldValues.size() != oldDofOrdering->totalDofs(), std::invalid_argument, "oldValues.size() != oldDofOrdering->totalDofs()");
  
  newValues.initialize(0.0);
  
  for (set<int>::iterator varIDIt = varIDs.begin(); varIDIt != varIDs.end(); varIDIt++) {
    int varID = *varIDIt;
    int numSides = getNumSidesForVarID(varID);
    if ( numSides == oldDofOrdering->getNumSidesForVarID(varID) ) {
      for (int sideIndex=0; sideIndex < numSides; sideIndex++) {
        BasisPtr basis = getBasis(varID,sideIndex);
        if (basis.get() == oldDofOrdering->getBasis(varID,sideIndex).get() ) {
          // bases alike: copy coefficients
          int cardinality = basis->getCardinality();
          for (int dofOrdinal=0; dofOrdinal < cardinality; dofOrdinal++) {
            int dofIndex = getDofIndex(varID,dofOrdinal,sideIndex);
            newValues(dofIndex) = oldValues( oldDofOrdering->getDofIndex(varID,dofOrdinal,sideIndex) );
          }
        }
      }
    }
  }
}
Beispiel #4
0
int DofOrdering::getTotalBasisCardinality() { // total number of *distinct* basis functions
  int totalBasisCardinality = 0;
  set< ::Camellia::Basis<>* > basesCounted;
  for (map< pair<int, int>, BasisPtr >::iterator basisIt = bases.begin(); basisIt != bases.end(); basisIt++) {
    BasisPtr basis = basisIt->second;
    if (basesCounted.find(basis.get())==basesCounted.end()) {
      basesCounted.insert(basis.get());
      totalBasisCardinality += basis->getCardinality();
    }
  }
  return totalBasisCardinality;
}
Beispiel #5
0
void DofOrdering::rebuildIndex() {
  if (dofIdentifications.size() == 0) {
    // nothing to do
    return;
  }
  set<int>::iterator varIDIterator;
  int numIdentificationsProcessed = 0;
  for (varIDIterator = varIDs.begin(); varIDIterator != varIDs.end(); varIDIterator++) {
    int varID = *varIDIterator;
    //cout << "rebuildIndex: varID=" << varID << endl;
    for (int sideIndex=0; sideIndex<numSidesForVarID[varID]; sideIndex++) {
      BasisPtr basis = getBasis(varID,sideIndex);
      int cellTopoSideIndex = (numSidesForVarID[varID]==1) ? -1 : sideIndex;
      if ( _cellTopologyForSide.find(cellTopoSideIndex) == _cellTopologyForSide.end() ) {
        CellTopoPtr cellTopoPtr = basis->domainTopology();
        _cellTopologyForSide[cellTopoSideIndex] = cellTopoPtr;
      }
      for (int dofOrdinal=0; dofOrdinal < basis->getCardinality(); dofOrdinal++) {
        pair<int, pair<int,int> > key = make_pair(varID, make_pair(sideIndex,dofOrdinal));
        pair<int, int> indexKey = make_pair(key.first,key.second.first); // key into indices container
        if ( dofIdentifications.find(key) != dofIdentifications.end() ) {
          int earlierSideIndex  = dofIdentifications[key].first;
          int earlierDofOrdinal = dofIdentifications[key].second;
          pair<int,int> earlierIndexKey = make_pair(varID,earlierSideIndex);
          if (indices[indexKey][dofOrdinal] != indices[earlierIndexKey][earlierDofOrdinal]) {
            indices[indexKey][dofOrdinal] = indices[earlierIndexKey][earlierDofOrdinal];
//            cout << "processed identification for varID " << varID << ": (" << sideIndex << "," << dofOrdinal << ")";
//            cout << " --> " << "(" << earlierSideIndex << "," << earlierDofOrdinal << ")" << endl;
            numIdentificationsProcessed++;
          }
        } else {
          // modify the index according to the number of dofs we've consolidated
//          cout << "Reducing indices for key (varID=" << indexKey.first << ", sideIndex " << indexKey.second << ") for dofOrdinal " << dofOrdinal << " from ";
//          cout << indices[indexKey][dofOrdinal] << " to ";
          indices[indexKey][dofOrdinal] -= numIdentificationsProcessed;
//          cout << indices[indexKey][dofOrdinal] << "\n";
        }
      }
    }
  }
  _nextIndex -= numIdentificationsProcessed;
  //cout << "index rebuilt; _nextIndex = " << _nextIndex << "; numIdentificationsProcessed: " << numIdentificationsProcessed << endl;
  _indexNeedsToBeRebuilt = false;
}
Beispiel #6
0
int DofOrdering::getBasisCardinality(int varID, int sideIndex) {
  BasisPtr basis = getBasis(varID,sideIndex);
  return basis->getCardinality();
//  return getBasis(varID,sideIndex)->getCardinality();
}
bool VectorizedBasisTestSuite::testVectorizedBasis()
{
  bool success = true;

  string myName = "testVectorizedBasis";

  shards::CellTopology quad_4(shards::getCellTopologyData<shards::Quadrilateral<4> >() );

  int polyOrder = 3, numPoints = 5, spaceDim = 2;

  BasisPtr hgradBasis = BasisFactory::basisFactory()->getBasis(polyOrder, quad_4.getKey(), Camellia::FUNCTION_SPACE_HGRAD);

  // first test: make a single-component vector basis.  This should agree in every entry with the basis itself, but its field container will have one higher rank...
  VectorizedBasis<> oneComp(hgradBasis, 1);

  FieldContainer<double> linePoints(numPoints, spaceDim);
  for (int i=0; i<numPoints; i++)
  {
    for (int j=0; j<spaceDim; j++)
    {
      linePoints(i,j) = ((double)(i + j)) / (numPoints + spaceDim);
    }
  }

  FieldContainer<double> compValues(hgradBasis->getCardinality(),numPoints);
  hgradBasis->getValues(compValues, linePoints, Intrepid::OPERATOR_VALUE);

  FieldContainer<double> values(hgradBasis->getCardinality(),linePoints.dimension(0),1); // one component
  oneComp.getValues(values, linePoints, Intrepid::OPERATOR_VALUE);

  for (int i=0; i<compValues.size(); i++)
  {
    double diff = abs(values[i]-compValues[i]);
    if (diff != 0.0)
    {
      success = false;
      cout << myName << ": one-component vector basis doesn't produce same values as component basis." << endl;
      cout << "difference: " << diff << " in enumerated value " << i << endl;
      cout << "values:\n" << values;
      cout << "compValues:\n" << compValues;
      return success;
    }
  }

  vector< BasisPtr > twoComps;
  twoComps.push_back( Teuchos::rcp( new VectorizedBasis<>(hgradBasis, 2) ) );
  twoComps.push_back( BasisFactory::basisFactory()->getBasis( polyOrder,
                      quad_4.getKey(), Camellia::FUNCTION_SPACE_VECTOR_HGRAD) );


  vector< BasisPtr >::iterator twoCompIt;
  for (twoCompIt = twoComps.begin(); twoCompIt != twoComps.end(); twoCompIt++)
  {
    BasisPtr twoComp = *twoCompIt;

    int componentCardinality = hgradBasis->getCardinality();

    if (twoComp->getCardinality() != 2 * hgradBasis->getCardinality() )
    {
      success = false;
      cout << myName << ": two-component vector basis cardinality != one-component cardinality * 2." << endl;
      cout << "twoComp->getCardinality(): " << twoComp->getCardinality() << endl;
      cout << "oneComp->getCardinality(): " << oneComp.getCardinality() << endl;
    }

    values.resize(twoComp->getCardinality(),linePoints.dimension(0),2); // two components
    twoComp->getValues(values, linePoints, Intrepid::OPERATOR_VALUE);
    for (int basisIndex=0; basisIndex<twoComp->getCardinality(); basisIndex++)
    {
      for (int k=0; k<numPoints; k++)
      {
        double xValueExpected = (basisIndex < componentCardinality) ? compValues(basisIndex,k) : 0;
        double xValueActual = values(basisIndex,k,0);
        double yValueExpected = (basisIndex >= componentCardinality) ? compValues(basisIndex - componentCardinality,k) : 0;
        double yValueActual = values(basisIndex,k,1);
        if ( ( abs(xValueActual - xValueExpected) != 0) || ( abs(yValueActual - yValueExpected) != 0) )
        {
          success = false;
          cout << myName << ": expected differs from actual\n";
          cout << "component\n" << compValues;
          cout << "vector values:\n" << values;
          return success;
        }
      }
    }

    // test the mapping from oneComp dofOrdinal to twoComp:
    VectorizedBasis<>* twoCompAsVectorBasis = (VectorizedBasis<>  *) twoComp.get();

    for (int compDofOrdinal=0; compDofOrdinal<oneComp.getCardinality(); compDofOrdinal++)
    {
      int dofOrdinal_0 = twoCompAsVectorBasis->getDofOrdinalFromComponentDofOrdinal(compDofOrdinal, 0);
      int dofOrdinal_1 = twoCompAsVectorBasis->getDofOrdinalFromComponentDofOrdinal(compDofOrdinal, 1);
      // we expect the lists to be stacked (this is implicit in the test above)
      // dofOrdinal_0 we expect to be == compDofOrdinal
      // dofOrdinal_1 we expect to be == compDofOrdinal + oneComp.getCardinality()
      if (dofOrdinal_0 != compDofOrdinal)
      {
        success = false;
        cout << "getDofOrdinalFromComponentDofOrdinal() not returning expected value in first component.\n";
      }
      if (dofOrdinal_1 != compDofOrdinal + oneComp.getCardinality())
      {
        success = false;
        cout << "getDofOrdinalFromComponentDofOrdinal() not returning expected value in second component.\n";
      }
    }

    // finally, test the ordering of gradient values
    // these should be in the order f_i,j
    FieldContainer<double> compGradValues(hgradBasis->getCardinality(),numPoints,spaceDim);
    FieldContainer<double> vectorGradValues(twoComp->getCardinality(),numPoints,spaceDim,spaceDim);

    hgradBasis->getValues(compGradValues, linePoints, OPERATOR_GRAD);
    twoCompAsVectorBasis->getValues(vectorGradValues, linePoints, OPERATOR_GRAD);

    for (int compDofOrdinal=0; compDofOrdinal<oneComp.getCardinality(); compDofOrdinal++)
    {
      for (int comp=0; comp<2; comp++)
      {
        int vectorDofOrdinal = twoCompAsVectorBasis->getDofOrdinalFromComponentDofOrdinal(compDofOrdinal, comp);
        for (int k=0; k<numPoints; k++)
        {
          double dfi_d0_expected = compGradValues(compDofOrdinal,k,0); // i: the comp index
          double dfi_d1_expected = compGradValues(compDofOrdinal,k,1);

          double dfi_d0_actual = vectorGradValues(vectorDofOrdinal,k,comp,0);
          double dfi_d1_actual = vectorGradValues(vectorDofOrdinal,k,comp,1);

          if ( ( abs(dfi_d0_expected - dfi_d0_actual) != 0) || ( abs(dfi_d1_expected - dfi_d1_actual) != 0) )
          {
            success = false;
            cout << myName << ": expected gradient differs from actual\n";
            cout << "component grad values\n" << compGradValues;
            cout << "vector grad values:\n" << vectorGradValues;
            return success;
          }
        }
      }

    }


  }
  return success;
}
Beispiel #8
0
void Projector::projectFunctionOntoBasis(FieldContainer<double> &basisCoefficients, FunctionPtr fxn, 
                                         BasisPtr basis, BasisCachePtr basisCache, IPPtr ip, VarPtr v,
                                         set<int> fieldIndicesToSkip) {
  CellTopoPtr cellTopo = basis->domainTopology();
  DofOrderingPtr dofOrderPtr = Teuchos::rcp(new DofOrdering());
  
  if (! fxn.get()) {
    TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "fxn cannot be null!");
  }
  
  int cardinality = basis->getCardinality();
  int numCells = basisCache->getPhysicalCubaturePoints().dimension(0);
  int numDofs = cardinality - fieldIndicesToSkip.size();
  if (numDofs==0) {
    // we're skipping all the fields, so just initialize basisCoefficients to 0 and return
    basisCoefficients.resize(numCells,cardinality);
    basisCoefficients.initialize(0);
    return;
  }
  
  FieldContainer<double> gramMatrix(numCells,cardinality,cardinality);
  FieldContainer<double> ipVector(numCells,cardinality);

  // fake a DofOrdering
  DofOrderingPtr dofOrdering = Teuchos::rcp( new DofOrdering );
  if (! basisCache->isSideCache()) {
    dofOrdering->addEntry(v->ID(), basis, v->rank());
  } else {
    dofOrdering->addEntry(v->ID(), basis, v->rank(), basisCache->getSideIndex());
  }
  
  ip->computeInnerProductMatrix(gramMatrix, dofOrdering, basisCache);
  ip->computeInnerProductVector(ipVector, v, fxn, dofOrdering, basisCache);
  
//  cout << "physical points for projection:\n" << basisCache->getPhysicalCubaturePoints();
//  cout << "gramMatrix:\n" << gramMatrix;
//  cout << "ipVector:\n" << ipVector;
  
  map<int,int> oldToNewIndices;
  if (fieldIndicesToSkip.size() > 0) {
    // the code to do with fieldIndicesToSkip might not be terribly efficient...
    // (but it's not likely to be called too frequently)
    int i_indices_skipped = 0;
    for (int i=0; i<cardinality; i++) {
      int new_index;
      if (fieldIndicesToSkip.find(i) != fieldIndicesToSkip.end()) {
        i_indices_skipped++;
        new_index = -1;
      } else {
        new_index = i - i_indices_skipped;
      }
      oldToNewIndices[i] = new_index;
    }
    
    FieldContainer<double> gramMatrixFiltered(numCells,numDofs,numDofs);
    FieldContainer<double> ipVectorFiltered(numCells,numDofs);
    // now filter out the values that we're to skip
    
    for (int cellIndex=0; cellIndex<numCells; cellIndex++) {
      for (int i=0; i<cardinality; i++) {
        int i_filtered = oldToNewIndices[i];
        if (i_filtered == -1) {
          continue;
        }
        ipVectorFiltered(cellIndex,i_filtered) = ipVector(cellIndex,i);
        
        for (int j=0; j<cardinality; j++) {
          int j_filtered = oldToNewIndices[j];
          if (j_filtered == -1) {
            continue;
          }
          gramMatrixFiltered(cellIndex,i_filtered,j_filtered) = gramMatrix(cellIndex,i,j);
        }
      }
    }
//    cout << "gramMatrixFiltered:\n" << gramMatrixFiltered;
//    cout << "ipVectorFiltered:\n" << ipVectorFiltered;
    gramMatrix = gramMatrixFiltered;
    ipVector = ipVectorFiltered;
  }
  
  for (int cellIndex=0; cellIndex<numCells; cellIndex++){
    
    // TODO: rewrite to take advantage of SerialDenseWrapper...
    Epetra_SerialDenseSolver solver;
    
    Epetra_SerialDenseMatrix A(Copy,
                               &gramMatrix(cellIndex,0,0),
                               gramMatrix.dimension(2), 
                               gramMatrix.dimension(2),  
                               gramMatrix.dimension(1)); // stride -- fc stores in row-major order (a.o.t. SDM)
    
    Epetra_SerialDenseVector b(Copy,
                               &ipVector(cellIndex,0),
                               ipVector.dimension(1));
    
    Epetra_SerialDenseVector x(gramMatrix.dimension(1));
    
    solver.SetMatrix(A);
    int info = solver.SetVectors(x,b);
    if (info!=0){
      cout << "projectFunctionOntoBasis: failed to SetVectors with error " << info << endl;
    }
    
    bool equilibrated = false;
    if (solver.ShouldEquilibrate()){
      solver.EquilibrateMatrix();
      solver.EquilibrateRHS();      
      equilibrated = true;
    }   
    
    info = solver.Solve();
    if (info!=0){
      cout << "projectFunctionOntoBasis: failed to solve with error " << info << endl;
    }
    
    if (equilibrated) {
      int successLocal = solver.UnequilibrateLHS();
      if (successLocal != 0) {
        cout << "projection: unequilibration FAILED with error: " << successLocal << endl;
      }
    }
    
    basisCoefficients.resize(numCells,cardinality);
    for (int i=0;i<cardinality;i++) {
      if (fieldIndicesToSkip.size()==0) {
        basisCoefficients(cellIndex,i) = x(i);
      } else {
        int i_filtered = oldToNewIndices[i];
        if (i_filtered==-1) {
          basisCoefficients(cellIndex,i) = 0.0;
        } else {
          basisCoefficients(cellIndex,i) = x(i_filtered);
        }
      }
    }
    
  }
}
Beispiel #9
0
bool testBasisClassifications(BasisPtr basis) {
  bool success = true;
  
  CellTopoPtr cellTopo = basis->domainTopology();
  
  int numVertices = cellTopo->getVertexCount();
  int numEdges = cellTopo->getEdgeCount();
  
  int degree = basis->getDegree();
  
  // TODO: finish this
  vector<int> vertexOrdinals;
  for (int vertexIndex=0; vertexIndex < numVertices; vertexIndex++) {
    set<int> dofOrdinals = basis->dofOrdinalsForVertex(vertexIndex);
    if (dofOrdinals.size() == 0) TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "No dofOrdinal for vertex...");
    if (dofOrdinals.size() > 1) TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "More than one dofOrdinal per vertex...");
    vertexOrdinals.push_back(*(dofOrdinals.begin()));
  }
//  
//  if (! checkVertexOrdinalsQuad(basis, vertexOrdinals) ) {
//    success = false;
//    cout << "vertex dof ordinals don't match expected\n";
//    cout << "ordinals: ";
//    for (int vertexIndex=0; vertexIndex < numVertices; vertexIndex++) {
//      cout << vertexOrdinals[vertexIndex] << " ";
//    }
//    cout << endl;
//  }
  
  // get the points in reference space for each vertex
  FieldContainer<double> points;
  if (numVertices == 2) { // line
    points.resize(2,1);
    points(0,0) = -1;
    points(1,0) = 1;
  } else if (numVertices == 3) { // triangle
    TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "triangles not yet supported");
  } else if (numVertices == 4) { // quad
    points.resize(4,2);
    points(0,0) = -1;
    points(0,1) = -1;
    points(1,0) =  1;
    points(1,1) = -1;
    points(2,0) =  1;
    points(2,1) =  1;
    points(3,0) = -1;
    points(3,1) =  1;
  } else {
    TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "unsupported topology");
  }
  
  FieldContainer<double> vertexValues;
  if (basis->rangeRank() > 0) {
    TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "rank > 0 bases not yet supported");
  } else {
    vertexValues.resize(basis->getCardinality(),numVertices);
  }
  
  basis->getValues(vertexValues, points, Intrepid::OPERATOR_VALUE);
  
  // test that the points are correctly classified
  for (int fieldIndex=0; fieldIndex<basis->getCardinality(); fieldIndex++) {
    for (int ptIndex=0; ptIndex<numVertices; ptIndex++) {
      int dofOrdinalForPoint = vertexOrdinals[ptIndex];
      bool expectZero = (dofOrdinalForPoint != fieldIndex);
      if (expectZero) {
        if (vertexValues(fieldIndex,ptIndex) != 0) {
          success = false;
          cout << "Expected 0 for fieldIndex " << fieldIndex << " and ptIndex " << ptIndex;
          cout << ", but got " << vertexValues(fieldIndex,ptIndex) << endl;
        }
      } else {
        if (vertexValues(fieldIndex,ptIndex) == 0) {
          cout << "Expected nonzero for fieldIndex " << fieldIndex << " and ptIndex " << ptIndex << endl;
          success = false;
        }
      }
    }
  }
  
  if (!success) {
    cout << "Failed testBasisClassifications; vertexValues:\n" << vertexValues;
  }
  
  return success;
}
Beispiel #10
0
void Boundary::bcsToImpose( map<  GlobalIndexType, Scalar > &globalDofIndicesAndValues, TBC<Scalar> &bc,
                            GlobalIndexType cellID, DofInterpreter* dofInterpreter)
{
  // this is where we actually compute the BCs; the other bcsToImpose variants call this one.
  CellPtr cell = _mesh->getTopology()->getCell(cellID);

  // define a couple of important inner products:
  TIPPtr<Scalar> ipL2 = Teuchos::rcp( new TIP<Scalar> );
  TIPPtr<Scalar> ipH1 = Teuchos::rcp( new TIP<Scalar> );
  VarFactoryPtr varFactory = VarFactory::varFactory();
  VarPtr trace = varFactory->traceVar("trace");
  VarPtr flux = varFactory->traceVar("flux");
  ipL2->addTerm(flux);
  ipH1->addTerm(trace);
  ipH1->addTerm(trace->grad());
  ElementTypePtr elemType = _mesh->getElementType(cellID);
  DofOrderingPtr trialOrderingPtr = elemType->trialOrderPtr;
  vector< int > trialIDs = _mesh->bilinearForm()->trialIDs();

  vector<unsigned> boundarySides = cell->boundarySides();
  if (boundarySides.size() > 0)
  {
    BasisCachePtr basisCache = BasisCache::basisCacheForCell(_mesh, cellID);
    for (vector< int >::iterator trialIt = trialIDs.begin(); trialIt != trialIDs.end(); trialIt++)
    {
      int trialID = *(trialIt);
      if ( bc.bcsImposed(trialID) )
      {
//        // DEBUGGING: keep track of which sides we impose BCs on:
//        set<unsigned> bcImposedSides;
//
        // Determine global dof indices and values, in one pass per side
        for (int i=0; i<boundarySides.size(); i++)
        {
          unsigned sideOrdinal = boundarySides[i];
          // TODO: here, we need to treat the volume basis case.
          /*
           To do this:
           1. (Determine which dofs in the basis have support on the side.)
           2. (Probably should resize dirichletValues to match number of dofs with support on the side.)
           3. (Within coefficientsForBC, and the projection method it calls, when it's a side cache, check whether the basis being projected has a higher dimension.  If so, do the same determination regarding the support of basis on the side as #1.)
           4. DofInterpreter::interpretLocalBasisCoefficients() needs to handle the case that trialID has volume support, and in this case interpret the provided data appropriately.
           */
          
          BasisPtr basis;
          int numDofsSide;
          if (trialOrderingPtr->getSidesForVarID(trialID).size() == 1)
          {
            // volume basis
            basis = trialOrderingPtr->getBasis(trialID);
            // get the dof ordinals for the side (interpreted as a "continuous" basis)
            numDofsSide = basis->dofOrdinalsForSide(sideOrdinal).size();
          }
          else if (! trialOrderingPtr->hasBasisEntry(trialID, sideOrdinal))
          {
            continue;
          }
          else
          {
            basis = trialOrderingPtr->getBasis(trialID,sideOrdinal);
            numDofsSide = basis->getCardinality();
          }
          
          GlobalIndexType numCells = 1;
          if (numCells > 0)
          {
            FieldContainer<double> dirichletValues(numCells,numDofsSide);
            // project bc function onto side basis:
            BCPtr bcPtr = Teuchos::rcp(&bc, false);
            Teuchos::RCP<BCFunction<double>> bcFunction = BCFunction<double>::bcFunction(bcPtr, trialID);
            bcPtr->coefficientsForBC(dirichletValues, bcFunction, basis, basisCache->getSideBasisCache(sideOrdinal));
            dirichletValues.resize(numDofsSide);
            if (bcFunction->imposeOnCell(0))
            {
              FieldContainer<double> globalData;
              FieldContainer<GlobalIndexType> globalDofIndices;

              dofInterpreter->interpretLocalBasisCoefficients(cellID, trialID, sideOrdinal, dirichletValues, globalData, globalDofIndices);
              for (int globalDofOrdinal=0; globalDofOrdinal<globalDofIndices.size(); globalDofOrdinal++)
              {
                GlobalIndexType globalDofIndex = globalDofIndices(globalDofOrdinal);
                Scalar value = globalData(globalDofOrdinal);
                
                // sanity check: if this has been previously set, do the two values roughly agree?
                if (globalDofIndicesAndValues.find(globalDofIndex) != globalDofIndicesAndValues.end())
                {
                  double tol = 1e-10;
                  Scalar prevValue = globalDofIndicesAndValues[globalDofIndex];
                  double absDiff = abs(prevValue - value);
                  if (absDiff > tol)
                  {
                    double relativeDiff = absDiff / max(abs(prevValue),abs(value));
                    int rank = _mesh->Comm()->MyPID();
                    if (relativeDiff > tol)
                    {
                      cout << "WARNING: in Boundary::bcsToImpose(), inconsistent values for BC: " << prevValue << " and ";
                      cout << value << " prescribed for global dof index " << globalDofIndex;
                      cout << " on rank " << rank << endl;
                    }
                  }
                }
                globalDofIndicesAndValues[globalDofIndex] = value;
              }
            }
          }
        }
      }
    }
  }
}
bool MeshTestUtility::checkMeshDofConnectivities(Teuchos::RCP<Mesh> mesh)
{
  int numCells = mesh->activeElements().size();
  bool success = true;
  int numGlobalDofs = mesh->numGlobalDofs();
  vector<int> globalDofIndexHitCount(numGlobalDofs,0);
  for (int cellIndex=0; cellIndex<numCells; cellIndex++)
  {
    Teuchos::RCP<Element> elem = mesh->activeElements()[cellIndex];
    GlobalIndexType cellID = elem->cellID();
    DofOrdering trialOrder = *(elem->elementType()->trialOrderPtr.get());
    vector< int > trialIDs = mesh->bilinearForm()->trialIDs();
    for (vector< int >::iterator trialIt = trialIDs.begin(); trialIt != trialIDs.end(); trialIt++)
    {
      int trialID = *(trialIt);
      const vector<int>* sidesForVar = &trialOrder.getSidesForVarID(trialID);
      for (vector<int>::const_iterator sideIt = sidesForVar->begin(); sideIt != sidesForVar->end(); sideIt++)
      {
        int sideIndex = *sideIt;
        int numBasisDofs = trialOrder.getBasisCardinality(trialID, sideIndex);
        for (int dofOrdinal=0; dofOrdinal<numBasisDofs; dofOrdinal++)
        {
          // a very basic check on the mesh dof ordering: the globalDofIndices for all localDofs should not be negative!
          int localDofIndex = trialOrder.getDofIndex(trialID, dofOrdinal, sideIndex);
          int globalDofIndex = mesh->globalDofIndex(cellID,localDofIndex);
          if (globalDofIndex < 0)
          {
            cout << "mesh->globalDofIndex(" << cellID << "," << localDofIndex << ") = " << globalDofIndex << " < 0.  Error!";
            success = false;
          }
          else if (globalDofIndex >= mesh->numGlobalDofs())
          {
            cout << "mesh->globalDofIndex(" << cellID << "," << localDofIndex << ") = " << globalDofIndex << " >= mymesh->numGlobalDofs().  Error!";
            success = false;
          }
          else
          {
            globalDofIndexHitCount[globalDofIndex]++;
          }
        }

        // now a more subtle check: given the mesh layout (that all vertices are specified CCW),
        // the dofs for boundary variables (fluxes & traces) should be reversed between element and its neighbor
        if (mesh->bilinearForm()->isFluxOrTrace(trialID))
        {
          CellPtr cell = mesh->getTopology()->getCell(cellID);
          if (! mesh->getTopology()->getCell(cellID)->isBoundary(sideIndex))   // not boundary...
          {
            //            if (neighbor->cellID() != -1) { // not boundary...
            int ancestralSideIndexInNeighbor;
            Teuchos::RCP<Element> neighbor = mesh->ancestralNeighborForSide(elem, sideIndex, ancestralSideIndexInNeighbor);

            Teuchos::RCP<DofOrdering> neighborTrialOrder = neighbor->elementType()->trialOrderPtr;
            int neighborNumBasisDofs = neighborTrialOrder->getBasisCardinality(trialID,ancestralSideIndexInNeighbor);
            if (neighborNumBasisDofs != numBasisDofs)
            {
              if ( mesh->usePatchBasis() )
              {
                cout << "FAILURE: usePatchBasis==true, but neighborNumBasisDofs != numBasisDofs.\n";
                success = false;
                continue;
              }
              if ( neighbor->isParent() )
              {
                // Here, we need to deal with the possibility that neighbor is a parent, broken along the shared side
                //  -- if so, we have a MultiBasis, and we need to match with each of neighbor's descendants along that side...
                vector< pair<int,int> > descendantsForSide = neighbor->getDescendantsForSide(ancestralSideIndexInNeighbor);
                vector< pair<int,int> >:: iterator entryIt;
                int descendantIndex = -1;
                for (entryIt = descendantsForSide.begin(); entryIt != descendantsForSide.end(); entryIt++)
                {
                  descendantIndex++;
                  int neighborSubSideIndexInMe = GDAMaximumRule2D::neighborChildPermutation(descendantIndex, descendantsForSide.size());
                  int neighborCellID = (*entryIt).first;
                  int mySideIndexInNeighbor = (*entryIt).second;
                  neighbor = mesh->getElement(neighborCellID);
                  int neighborNumDofs = neighbor->elementType()->trialOrderPtr->getBasisCardinality(trialID,mySideIndexInNeighbor);

                  for (int dofOrdinal=0; dofOrdinal<neighborNumDofs; dofOrdinal++)
                  {
                    int myLocalDofIndex;
                    if ((descendantsForSide.size() > 1) && !mesh->usePatchBasis())
                    {
                      myLocalDofIndex = elem->elementType()->trialOrderPtr->getDofIndex(trialID,dofOrdinal,sideIndex,neighborSubSideIndexInMe);
                    }
                    else
                    {
                      myLocalDofIndex = elem->elementType()->trialOrderPtr->getDofIndex(trialID,dofOrdinal,sideIndex);
                    }
                    int globalDofIndex = mesh->globalDofIndex(cellID,myLocalDofIndex);

                    // neighbor's dofs are in reverse order from mine along each side
                    int permutedDofOrdinal = GDAMaximumRule2D::neighborDofPermutation(dofOrdinal,neighborNumDofs);

                    int neighborLocalDofIndex = neighbor->elementType()->trialOrderPtr->getDofIndex(trialID,permutedDofOrdinal,mySideIndexInNeighbor);
                    int neighborsGlobalDofIndex = mesh->globalDofIndex(neighbor->cellID(),neighborLocalDofIndex);
                    if (neighborsGlobalDofIndex != globalDofIndex)
                    {

                      cout << "FAILURE: checkDofConnectivities--(cellID, localDofIndex) : (" << cellID << ", " << myLocalDofIndex << ") != (";
                      cout << neighborCellID << ", " << neighborLocalDofIndex << ") -- ";
                      cout << globalDofIndex << " != " << neighborsGlobalDofIndex << "\n";
                      success = false;
                    }
                  }
                }
              }
              else if (neighbor->getNeighborCellID(ancestralSideIndexInNeighbor) != cellID)
              {
                // elem is small, neighbor big
                // first, find my leaf index in neighbor:
                int ancestorCellID = neighbor->getNeighborCellID(ancestralSideIndexInNeighbor);
                Teuchos::RCP<Element> ancestor = mesh->getElement(ancestorCellID);
                int ancestorSideIndex = neighbor->getSideIndexInNeighbor(ancestralSideIndexInNeighbor);
                vector< pair<int,int> > descendantsForSide = ancestor->getDescendantsForSide(ancestorSideIndex);
                int descendantIndex = 0;
                int leafIndexInNeighbor = -1;
                for (vector< pair<int,int> >::iterator entryIt = descendantsForSide.begin();
                     entryIt != descendantsForSide.end();  entryIt++, descendantIndex++)
                {
                  if (entryIt->first == cellID)
                  {
                    leafIndexInNeighbor = GDAMaximumRule2D::neighborChildPermutation(descendantIndex, descendantsForSide.size());
                    break;
                  }
                }
                if (leafIndexInNeighbor == -1)
                {
                  TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Could not determine leafIndexInNeigbor.");
                }
                // check whether the basis is the right size:
                MultiBasis<>* neighborMultiBasis = (MultiBasis<>*) neighbor->elementType()->trialOrderPtr->getBasis(trialID,ancestralSideIndexInNeighbor).get();
                BasisPtr neighborLeafBasis = neighborMultiBasis->getLeafBasis(leafIndexInNeighbor);
                if (numBasisDofs != neighborLeafBasis->getCardinality())
                {
                  success = false;
                  cout << "FAILURE: cellID " << cellID << "'s basis for trialID " << trialID;
                  cout << " along sideIndex " << sideIndex << " has cardinality " << numBasisDofs;
                  cout << ", but neighbor leaf basis along that side (cellID " << neighbor->cellID();
                  cout << ", sideIndex " << ancestralSideIndexInNeighbor;
                  cout << ", leaf node " << leafIndexInNeighbor;
                  cout << ") has cardinality " << neighborLeafBasis->getCardinality() << endl;
                }
                else
                {
                  // cardinalities match, check that global dofs line up
                  for (int dofOrdinal = 0; dofOrdinal < numBasisDofs; dofOrdinal++)
                  {
                    int permutedDofOrdinal = GDAMaximumRule2D::neighborDofPermutation(dofOrdinal, numBasisDofs);
                    int neighborDofOrdinal = neighborMultiBasis->relativeToAbsoluteDofOrdinal(permutedDofOrdinal,
                                             leafIndexInNeighbor);
                    int neighborLocalDofIndex = neighbor->elementType()->trialOrderPtr->getDofIndex(trialID, neighborDofOrdinal,ancestralSideIndexInNeighbor);
                    int neighborGlobalDofIndex = mesh->globalDofIndex(neighbor->cellID(), neighborLocalDofIndex);
                    int myLocalDofIndex = elem->elementType()->trialOrderPtr->getDofIndex(trialID, dofOrdinal, sideIndex);
                    int myGlobalDofIndex = mesh->globalDofIndex(cellID, myLocalDofIndex);
                    if (neighborGlobalDofIndex != myGlobalDofIndex)
                    {
                      success = false;
                      cout << "FAILURE: checkDofConnectivities--(cellID, localDofIndex) : (" << cellID << ", ";
                      cout << myLocalDofIndex << ") != (";
                      cout << neighbor->cellID() << ", " << neighborLocalDofIndex << ") -- ";
                      cout << myGlobalDofIndex << " != " << neighborGlobalDofIndex << "\n";
                    }
                  }
                }
              }
              else
              {
                cout << "FAILURE: cellID " << cellID << "'s basis for trialID " << trialID;
                cout << " along sideIndex " << sideIndex << " has cardinality " << numBasisDofs;
                cout << ", but neighbor along that side (cellID " << neighbor->cellID();
                cout << ", sideIndex " << ancestralSideIndexInNeighbor << ") has cardinality " << neighborNumBasisDofs << endl;
                success = false;
              }
            }
            else     // (neighborNumBasisDofs == numBasisDofs)
            {
              if (! neighbor->isParent() )
              {
                for (int dofOrdinal=0; dofOrdinal<numBasisDofs; dofOrdinal++)
                {
                  int permutedDofOrdinal = GDAMaximumRule2D::neighborDofPermutation(dofOrdinal,numBasisDofs);
                  int neighborsLocalDofIndex = neighborTrialOrder->getDofIndex(trialID, permutedDofOrdinal, ancestralSideIndexInNeighbor);
                  GlobalIndexType neighborsGlobalDofIndex = mesh->globalDofIndex(neighbor->cellID(),neighborsLocalDofIndex);
                  int localDofIndex = trialOrder.getDofIndex(trialID, dofOrdinal, sideIndex);
                  GlobalIndexType globalDofIndex = mesh->globalDofIndex(cellID,localDofIndex);
                  if (neighborsGlobalDofIndex != globalDofIndex)
                  {
                    cout << "FAILURE: cellID " << cellID << "'s neighbor " << sideIndex << "'s globalDofIndex " << neighborsGlobalDofIndex << " doesn't match element globalDofIndex " << globalDofIndex << ". (trialID, element dofOrdinal)=(" << trialID << "," << dofOrdinal << ")" << endl;
                    success = false;
                  }
                }
              }
              else     // neighbor->isParent()
              {
                // for PatchBasis:
                for (int dofOrdinal=0; dofOrdinal<numBasisDofs; dofOrdinal++)
                {
                  int localDofIndex = trialOrder.getDofIndex(trialID, dofOrdinal, sideIndex);
                  GlobalIndexType globalDofIndex = mesh->globalDofIndex(cellID,localDofIndex);
                  vector< pair<int,int> > descendantsForSide = neighbor->getDescendantsForSide(ancestralSideIndexInNeighbor);
                  vector< pair<int,int> >:: iterator entryIt;
                  for (entryIt = descendantsForSide.begin(); entryIt != descendantsForSide.end(); entryIt++)
                  {
                    GlobalIndexType neighborCellID = (*entryIt).first;
                    int mySideIndexInNeighbor = (*entryIt).second;
                    neighbor = mesh->getElement(neighborCellID);
                    neighborTrialOrder = neighbor->elementType()->trialOrderPtr;
                    int permutedDofOrdinal = GDAMaximumRule2D::neighborDofPermutation(dofOrdinal,numBasisDofs);
                    int neighborsLocalDofIndex = neighborTrialOrder->getDofIndex(trialID, permutedDofOrdinal, mySideIndexInNeighbor);
                    GlobalIndexType neighborsGlobalDofIndex = mesh->globalDofIndex(neighbor->cellID(),neighborsLocalDofIndex);
                    if (neighborsGlobalDofIndex != globalDofIndex)
                    {
                      cout << "FAILURE: cellID " << cellID << "'s neighbor on side " << sideIndex;
                      cout << " (cellID " << neighborCellID << ")'s globalDofIndex " << neighborsGlobalDofIndex;
                      cout << " doesn't match element globalDofIndex " << globalDofIndex;
                      cout << ". (trialID, element dofOrdinal): (" << trialID << "," << dofOrdinal << ")" << endl;
                      cout << "         (cellID,localDofIndex): (" << cellID << "," << localDofIndex << ") ≠ (";
                      cout << neighborCellID << "," << neighborsLocalDofIndex << ")\n";
                      success = false;
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  for (int i=0; i<numGlobalDofs; i++)
  {
    if ( globalDofIndexHitCount[i] == 0 )
    {
      success = false;
      cout << "FAILURE: meshDofConnectivity: globalDofIndex " << i << " is unreachable.\n";
    }
  }
  return success;
}
bool MeshTestUtility::checkLocalGlobalConsistency(MeshPtr mesh, double tol)
{
  bool success = true;

  set<GlobalIndexType> cellIDs = mesh->getActiveCellIDs();

  GlobalDofAssignmentPtr gda = mesh->globalDofAssignment();

  // TODO: make this only check the locally-owned cells (right now does the whole mesh on every rank)

  int numGlobalDofs = gda->globalDofCount();
  FieldContainer<double> globalCoefficients(numGlobalDofs);
  for (int i=0; i<numGlobalDofs; i++)
  {
    globalCoefficients(i) = 2*i + 1; // arbitrary cofficients
  }
  FieldContainer<double> globalCoefficientsExpected = globalCoefficients;
  FieldContainer<double> globalCoefficientsActual(numGlobalDofs);
  FieldContainer<double> localCoefficients;

  Epetra_SerialComm Comm;
  Epetra_BlockMap map(numGlobalDofs, 1, 0, Comm);
  Epetra_Vector globalCoefficientsVector(map);
  for (int i=0; i<numGlobalDofs; i++)
  {
    globalCoefficientsVector[i] = globalCoefficients(i);
  }

  cellIDs = mesh->getActiveCellIDs();
  for (set<GlobalIndexType>::iterator cellIDIt = cellIDs.begin(); cellIDIt != cellIDs.end(); cellIDIt++)
  {
    GlobalIndexType cellID = *cellIDIt;
    gda->interpretGlobalCoefficients(cellID, localCoefficients, globalCoefficientsVector);
    FieldContainer<GlobalIndexType> globalDofIndices;
    FieldContainer<double> globalCoefficientsForCell;

    DofOrderingPtr trialOrder = mesh->getElementType(cellID)->trialOrderPtr;
    set<int> varIDs = trialOrder->getVarIDs();

    for (set<int>::iterator varIt=varIDs.begin(); varIt != varIDs.end(); varIt++)
    {
      int varID = *varIt;
      const vector<int>* sidesForVar = &trialOrder->getSidesForVarID(varID);
      for (vector<int>::const_iterator sideIt = sidesForVar->begin(); sideIt != sidesForVar->end(); sideIt++)
      {
        int sideOrdinal = *sideIt;

        BasisPtr basis;
        if (sidesForVar->size() == 1)
        {
          basis = trialOrder->getBasis(varID);
        }
        else
        {
          basis = trialOrder->getBasis(varID,sideOrdinal);
        }

        FieldContainer<double> basisCoefficients(basis->getCardinality());

        for (int dofOrdinal=0; dofOrdinal<basis->getCardinality(); dofOrdinal++)
        {
          int localDofIndex;
          if (sidesForVar->size() == 1)
          {
            localDofIndex = trialOrder->getDofIndex(varID, dofOrdinal);
          }
          else
          {
            localDofIndex = trialOrder->getDofIndex(varID, dofOrdinal, sideOrdinal);
          }

          basisCoefficients(dofOrdinal) = localCoefficients(localDofIndex);
        }

        gda->interpretLocalBasisCoefficients(cellID, varID, sideOrdinal,
                                             basisCoefficients, globalCoefficientsForCell, globalDofIndices);

        //        if ( (cellID==2) && (sideOrdinal==1) && (varID==0) ) {
        //          cout << "DEBUGGING: (cellID==2) && (sideOrdinal==1).\n";
        //          cout << "globalDofIndices:\n" << globalDofIndices;
        //          cout << "globalCoefficientsForCell:\n" << globalCoefficientsForCell;
        //          cout << "basisCoefficients:\n" << basisCoefficients;
        //        }

        for (int dofOrdinal=0; dofOrdinal < globalDofIndices.size(); dofOrdinal++)
        {
          GlobalIndexType dofIndex = globalDofIndices(dofOrdinal);
          globalCoefficientsActual(dofIndex) = globalCoefficientsForCell(dofOrdinal);

          double diff = abs(globalCoefficientsForCell(dofOrdinal) - globalCoefficientsExpected(dofIndex)) / globalCoefficientsExpected(dofIndex);
          if (diff > tol)
          {
            cout << "In mapping for cell " << cellID << " and var " << varID << " on side " << sideOrdinal << ", ";
            cout << "expected coefficient for global dof index " << dofIndex << " to be " << globalCoefficientsExpected(dofIndex);
            cout << ", but was " << globalCoefficientsForCell(dofOrdinal);
            cout << " (relative diff = " << diff << "; tol = " << tol << ")\n";
            success = false;
          }
        }
      }
    }
  }

  //  double maxDiff;
  //  if (TestSuite::fcsAgree(globalCoefficientsActual, globalCoefficientsExpected, tol, maxDiff)) {
  //    //    cout << "global data actual and expected AGREE; max difference is " << maxDiff << endl;
  //    //    cout << "globalCoefficientsActual:\n" << globalCoefficientsActual;
  //  } else {
  //    cout << "global data actual and expected DISAGREE; max difference is " << maxDiff << endl;
  //    success = false;
  //    cout << "Expected:\n" << globalCoefficientsExpected;
  //    cout << "Actual:\n" << globalCoefficientsActual;
  //  }

  return success;
}
FCPtr BasisEvaluation::getValues(BasisPtr basis, Camellia::EOperator op,
                                 const FieldContainer<double> &refPoints)
{
  int numPoints = refPoints.dimension(0);
  int spaceDim = refPoints.dimension(1);  // points dimensions are (numPoints, spaceDim)
  int basisCardinality = basis->getCardinality();
  int componentOfInterest = -1;
  Camellia::EFunctionSpace fs = basis->functionSpace();
  Intrepid::EOperator relatedOp = relatedOperator(op, fs, spaceDim, componentOfInterest);

  int opCardinality = spaceDim; // for now, we assume basis values are in the same spaceDim as points (e.g. vector 1D has just 1 component)
  
  bool relatedOpIsDkOperator = (relatedOp >= OPERATOR_D1) && (relatedOp <= OPERATOR_D10);
  if (relatedOpIsDkOperator)
  {
    opCardinality = Intrepid::getDkCardinality(relatedOp, spaceDim);
  }
  
  if ((Camellia::EOperator)relatedOp != op)
  {
    // we can assume relatedResults has dimensions (numPoints,basisCardinality,spaceDimOut)
    FCPtr relatedResults = Teuchos::rcp(new FieldContainer<double>(basisCardinality,numPoints,opCardinality));
    basis->getValues(*(relatedResults.get()), refPoints, relatedOp);
    FCPtr result = getComponentOfInterest(relatedResults,op,fs,componentOfInterest);
    if ( result.get() == 0 )
    {
      result = relatedResults;
    }
    return result;
  }
  // if we get here, we should have a standard Intrepid operator, in which case we should
  // be able to: size a FieldContainer appropriately, and then call basis->getValues

  // But let's do just check that we have a standard Intrepid operator
  if ( (op >= Camellia::OP_X) || (op <  Camellia::OP_VALUE) )
  {
    TEUCHOS_TEST_FOR_EXCEPTION(true,std::invalid_argument,"Unknown operator.");
  }

  // result dimensions should be either (numPoints,basisCardinality) or (numPoints,basisCardinality,spaceDimOut);
  Teuchos::Array<int> dimensions;
  dimensions.push_back(basisCardinality);
  dimensions.push_back(numPoints);
  int basisRank = basis->rangeRank();
  if (((basisRank == 0) && (op ==  Camellia::OP_VALUE)))
  {
    // scalar; leave as is
  }
  else if ((basisRank == 0) && relatedOpIsDkOperator)
  {
    dimensions.push_back(opCardinality);
  }
  else if ( ( ( basisRank == 1) && (op ==  Camellia::OP_VALUE) ) )
  {
    dimensions.push_back(opCardinality);
  }
  else if (
    ( ( basisRank == 0) && (op == Camellia::OP_GRAD) )
    ||
    ( ( basisRank == 0) && (op == Camellia::OP_CURL) ) )
  {
    dimensions.push_back(spaceDim);
  }
  else if ( (basis->rangeRank() == 1) && (op == Camellia::OP_GRAD) )
  {
    // grad of vector: a tensor
    dimensions.push_back(spaceDim);
    dimensions.push_back(opCardinality);
  }
  FCPtr result = Teuchos::rcp(new FieldContainer<double>(dimensions));
  basis->getValues(*(result.get()), refPoints, (Intrepid::EOperator)op);
  return result;
}
FCPtr BasisEvaluation::getTransformedValuesWithBasisValues(BasisPtr basis, Camellia::EOperator op,
                                                           constFCPtr referenceValues, int numCells,
                                                           BasisCache* basisCache)
{
  typedef FunctionSpaceTools fst;
  //  int numCells = cellJacobian.dimension(0);
  
  int spaceDim = basisCache->getSpaceDim(); // changed 3-21-16
  
  int componentOfInterest;
  Camellia::EFunctionSpace fs = basis->functionSpace();
  Intrepid::EOperator relatedOp = relatedOperator(op,fs,spaceDim, componentOfInterest);
  Teuchos::Array<int> dimensions;
  referenceValues->dimensions(dimensions);
  dimensions.insert(dimensions.begin(), numCells);
  Teuchos::RCP<FieldContainer<double> > transformedValues = Teuchos::rcp(new FieldContainer<double>(dimensions));
  bool vectorizedBasis = functionSpaceIsVectorized(fs);
  if (vectorizedBasis && (op ==  Camellia::OP_VALUE))
  {
    TEUCHOS_TEST_FOR_EXCEPTION( vectorizedBasis && (op ==  Camellia::OP_VALUE),
                               std::invalid_argument, "Vector HGRAD/HVOL with OP_VALUE not supported by getTransformedValuesWithBasisValues.  Please use getTransformedVectorValuesWithComponentBasisValues instead.");
  }
  switch(relatedOp)
  {
    case(Intrepid::OPERATOR_VALUE):
      switch(fs)
    {
      case Camellia::FUNCTION_SPACE_REAL_SCALAR:
        //          cout << "Reference values for FUNCTION_SPACE_REAL_SCALAR: " << *referenceValues;
      case Camellia::FUNCTION_SPACE_HGRAD:
      case Camellia::FUNCTION_SPACE_HGRAD_DISC:
        fst::HGRADtransformVALUE<double>(*transformedValues,*referenceValues);
        break;
      case Camellia::FUNCTION_SPACE_HCURL:
      case Camellia::FUNCTION_SPACE_HCURL_DISC:
        fst::HCURLtransformVALUE<double>(*transformedValues,basisCache->getJacobianInv(),*referenceValues);
        break;
      case Camellia::FUNCTION_SPACE_HDIV:
      case Camellia::FUNCTION_SPACE_HDIV_DISC:
      case Camellia::FUNCTION_SPACE_HDIV_FREE:
        fst::HDIVtransformVALUE<double>(*transformedValues,basisCache->getJacobian(),basisCache->getJacobianDet(),*referenceValues);
        break;
      case Camellia::FUNCTION_SPACE_HVOL:
      case Camellia::FUNCTION_SPACE_HVOL_SPACE_HGRAD_TIME:
      case Camellia::FUNCTION_SPACE_HGRAD_SPACE_HVOL_TIME:
        //        {
        //          static bool haveWarned = false;
        //          if (!haveWarned) {
        //            cout << "WARNING: for the moment, switching to the standard HVOLtransformVALUE method.\n";
        //            haveWarned = true;
        //          }
        //        }
        //          fst::HVOLtransformVALUE<double>(*transformedValues, cellJacobianDet, *referenceValues);
        // for the moment, use the fact that we know the HVOL basis is always an HGRAD basis:
        // (I think using the below amounts to solving for the HVOL variables scaled by Jacobian)
        fst::HGRADtransformVALUE<double>(*transformedValues,*referenceValues);
        break;
      default:
        TEUCHOS_TEST_FOR_EXCEPTION(true,std::invalid_argument, "unhandled transformation");
        break;
    }
      break;
    case(Intrepid::OPERATOR_GRAD):
    case(Intrepid::OPERATOR_D1):
      switch(fs)
    {
      case Camellia::FUNCTION_SPACE_HVOL:
      case Camellia::FUNCTION_SPACE_HGRAD:
      case Camellia::FUNCTION_SPACE_HGRAD_DISC:
        fst::HGRADtransformGRAD<double>(*transformedValues,basisCache->getJacobianInv(),*referenceValues);
        break;
      case Camellia::FUNCTION_SPACE_VECTOR_HVOL:
      case Camellia::FUNCTION_SPACE_VECTOR_HGRAD:
      case Camellia::FUNCTION_SPACE_VECTOR_HGRAD_DISC:
        // referenceValues has dimensions (F,P,D1,D2).  D1 is our component dimension, and D2 is the one that came from the gradient.
        // HGRADtransformGRAD expects (F,P,D) for input, and (C,F,P,D) for output.
        // If we split referenceValues into (F,P,D1=0,D2) and (F,P,D1=1,D2), then we can transform each of those, and then interleave the results…
      {
        // block off so we can create new stuff inside the switch case
        Teuchos::Array<int> dimensions;
        referenceValues->dimensions(dimensions);
        int numFields = dimensions[0];
        int numPoints = dimensions[1];
        int D1 = dimensions[dimensions.size()-2];
        int D2 = dimensions[dimensions.size()-1];
        dimensions[dimensions.size()-2] = D2; // put D2 in the D1 spot
        dimensions.pop_back(); // get rid of original D2
        FieldContainer<double> refValuesSlice(dimensions);
        dimensions.insert(dimensions.begin(),numCells);
        FieldContainer<double> transformedValuesSlice(dimensions);
        
        //          int numEntriesPerSlice = refValuesSlice.size();
        //          int numEntriesPerTransformedSlice = transformedValuesSlice.size();
        
        for (int compIndex1=0; compIndex1<D1; compIndex1++)
        {
          // could speed the following along by doing the enumeration arithmetic in place...
          for (int fieldIndex=0; fieldIndex<numFields; fieldIndex++)
          {
            for (int ptIndex=0; ptIndex<numPoints; ptIndex++)
            {
              for (int compIndex2=0; compIndex2<D2; compIndex2++)
              {
                refValuesSlice(fieldIndex,ptIndex,compIndex2) = (*referenceValues)(fieldIndex,ptIndex,compIndex1,compIndex2);
              }
            }
          }
          
          //            for (int i=0; i<numEntriesPerSlice; i++) {
          //              refValuesSlice[i] = (*referenceValues)[i*D2 + compIndex];
          //            }
          fst::HGRADtransformGRAD<double>(transformedValuesSlice,basisCache->getJacobianInv(),refValuesSlice);
          // could speed the following along by doing the enumeration arithmetic in place...
          for (int cellIndex=0; cellIndex<numCells; cellIndex++)
          {
            for (int fieldIndex=0; fieldIndex<numFields; fieldIndex++)
            {
              for (int ptIndex=0; ptIndex<numPoints; ptIndex++)
              {
                for (int compIndex2=0; compIndex2<D2; compIndex2++)
                {
                  (*transformedValues)(cellIndex,fieldIndex,ptIndex,compIndex1,compIndex2) = transformedValuesSlice(cellIndex,fieldIndex,ptIndex,compIndex2);
                }
              }
            }
          }
          //            for (int i=0; i<numEntriesPerTransformedSlice; i++) {
          //              (*transformedValues)[i*D2 + compIndex] = transformedValuesSlice[i];
          //            }
        }
      }
        
        break;
      default:
        TEUCHOS_TEST_FOR_EXCEPTION(true,std::invalid_argument, "unhandled transformation");
        break;
    }
      break;
    case(Intrepid::OPERATOR_CURL):
      switch(fs)
    {
      case Camellia::FUNCTION_SPACE_HCURL:
      case Camellia::FUNCTION_SPACE_HCURL_DISC:
        if (spaceDim == 2)
        {
          // TODO: confirm that this is correct
          //            static bool warningIssued = false;
          //            if ( ! warningIssued ) {
          //              cout << "WARNING: for HCURL in 2D, transforming with HVOLtransformVALUE. Need to confirm this is correct.\n";
          //              warningIssued = true;
          //            }
          fst::HVOLtransformVALUE<double>(*transformedValues,basisCache->getJacobianDet(), *referenceValues);
        }
        else
        {
          fst::HCURLtransformCURL<double>(*transformedValues,basisCache->getJacobian(),basisCache->getJacobianDet(),*referenceValues);
        }
        break;
      case Camellia::FUNCTION_SPACE_HGRAD:
      case Camellia::FUNCTION_SPACE_HGRAD_DISC:
        // in 2D, HGRADtransformCURL == HDIVtransformVALUE (because curl(H1) --> H(div))
        fst::HDIVtransformVALUE<double>(*transformedValues,basisCache->getJacobian(),basisCache->getJacobianDet(),*referenceValues);
        break;
      case Camellia::FUNCTION_SPACE_VECTOR_HVOL:
      case Camellia::FUNCTION_SPACE_VECTOR_HGRAD:
      case Camellia::FUNCTION_SPACE_VECTOR_HGRAD_DISC: // shouldn't take the transform so late
      default:
        TEUCHOS_TEST_FOR_EXCEPTION(true,std::invalid_argument, "unhandled transformation");
        break;
    }
      break;
    case(Intrepid::OPERATOR_DIV):
      switch(fs)
    {
      case Camellia::FUNCTION_SPACE_HDIV:
      case Camellia::FUNCTION_SPACE_HDIV_DISC:
      case Camellia::FUNCTION_SPACE_HDIV_FREE:
        fst::HDIVtransformDIV<double>(*transformedValues,basisCache->getJacobianDet(),*referenceValues);
        break;
      case Camellia::FUNCTION_SPACE_VECTOR_HVOL:
      case Camellia::FUNCTION_SPACE_VECTOR_HGRAD:
      case Camellia::FUNCTION_SPACE_VECTOR_HGRAD_DISC: // shouldn't take the transform so late
      default:
        TEUCHOS_TEST_FOR_EXCEPTION(true,std::invalid_argument, "unhandled transformation");
        break;
    }
      break;
    case(Intrepid::OPERATOR_D2):
      switch(fs)
    {
      case Camellia::FUNCTION_SPACE_HVOL:
      case Camellia::FUNCTION_SPACE_HGRAD:
      case Camellia::FUNCTION_SPACE_HGRAD_DISC:
      {
        // first term in the sum is:
        //      J^{-1} * OPD2 * J^{-T}
        // where J^{-1} is the inverse Jacabian, and OPD2 is the matrix of reference-space values formed from OP_D2
        
        const FieldContainer<double> *J_inv = &basisCache->getJacobianInv();
        transformedValues->initialize(0.0);
        int basisCardinality = basis->getCardinality();
        
        Teuchos::Array<int> multiplicities(spaceDim);
        Teuchos::Array<int> multiplicitiesTransformed(spaceDim);
        int dkCardinality = Intrepid::getDkCardinality(Intrepid::OPERATOR_D2, spaceDim);
        int numPoints = referenceValues->dimension(1);
        
        for (int cellOrdinal=0; cellOrdinal<numCells; cellOrdinal++)
        {
          for (int fieldOrdinal=0; fieldOrdinal<basisCardinality; fieldOrdinal++)
          {
            for (int pointOrdinal=0; pointOrdinal<numPoints; pointOrdinal++)
            {
              for (int dkOrdinal=0; dkOrdinal<dkCardinality; dkOrdinal++) // ref values dkOrdinal
              {
                double refValue = (*referenceValues)(fieldOrdinal,pointOrdinal,dkOrdinal);
                Intrepid::getDkMultiplicities(multiplicities, dkOrdinal, Intrepid::OPERATOR_D2, spaceDim);
                int k,l; // ref space coordinate directions for refValue (columns in J_inv)
                getD2_ij_FromMultiplicities(k,l,multiplicities);
                
                for (int dkOrdinalTransformed=0; dkOrdinalTransformed<dkCardinality; dkOrdinalTransformed++) // physical values dkOrdinal
                {
                  Intrepid::getDkMultiplicities(multiplicitiesTransformed, dkOrdinalTransformed, Intrepid::OPERATOR_D2, spaceDim);
                  int i,j; // physical space coordinate directions for refValue (rows in J_inv)
                  getD2_ij_FromMultiplicities(i,j,multiplicitiesTransformed);
                  double J_inv_ik = (*J_inv)(cellOrdinal,pointOrdinal,i,k);
                  double J_inv_jl = (*J_inv)(cellOrdinal,pointOrdinal,j,l);
                  (*transformedValues)(cellOrdinal,fieldOrdinal,pointOrdinal,dkOrdinalTransformed) += refValue * J_inv_ik * J_inv_jl;
                }
              }
            }
          }
        }
        
        // do we need the second term in the sum?  (So far, we don't support this)
        if (!basisCache->neglectHessian())
        {
          // then we need to do include the gradient of the basis times the (inverse) of the Hessian
          // this seems complicated, and we don't need it for computing the Laplacian in physical space
          // (at least not unless we have curvilinear geometry) so we don't support it for now...
          TEUCHOS_TEST_FOR_EXCEPTION(!basisCache->neglectHessian(), std::invalid_argument, "Support for the Hessian of the reference-to-physical mapping not yet implemented.");
        }
      }
        break;
      default:
        TEUCHOS_TEST_FOR_EXCEPTION(true,std::invalid_argument, "unhandled transformation");
        break;
    }
      break;

    default:
      TEUCHOS_TEST_FOR_EXCEPTION(true,std::invalid_argument, "unhandled transformation");
      break;
  }
  
  FCPtr result = getComponentOfInterest(transformedValues,op,fs,componentOfInterest);
  if ( result.get() == 0 )
  {
    result = transformedValues;
  }
  return result;
}
void ParametricSurface::basisWeightsForEdgeInterpolant(FieldContainer<double> &edgeInterpolationCoefficients,
    VectorBasisPtr basis,
    MeshPtr mesh, int cellID)
{
  vector< ParametricCurvePtr > curves = mesh->parametricEdgesForCell(cellID);
  Teuchos::RCP<TransfiniteInterpolatingSurface> exactSurface = Teuchos::rcp( new TransfiniteInterpolatingSurface(curves) );
  exactSurface->setNeglectVertices(false);

  int basisDegree = basis->getDegree();
  shards::CellTopology line_2(shards::getCellTopologyData<shards::Line<2> >() );
  BasisPtr basis1D = BasisFactory::basisFactory()->getBasis(basisDegree, line_2.getKey(),
                     Camellia::FUNCTION_SPACE_HGRAD);

  BasisPtr compBasis = basis->getComponentBasis();
  int numComponents = basis->getNumComponents();
  if (numComponents != 2)
  {
    TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Only 2D surfaces supported right now");
  }

  edgeInterpolationCoefficients.resize(basis->getCardinality());

  set<int> edgeNodeFieldIndices = BasisFactory::basisFactory()->sideFieldIndices(basis,true); // true: include vertex dofs

  FieldContainer<double> dofCoords(compBasis->getCardinality(),2);
  IntrepidBasisWrapper< double, Intrepid::FieldContainer<double> >* intrepidBasisWrapper = dynamic_cast< IntrepidBasisWrapper< double, Intrepid::FieldContainer<double> >* >(compBasis.get());
  if (!intrepidBasisWrapper)
  {
    TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "compBasis does not appear to be an instance of IntrepidBasisWrapper");
  }
  Basis_HGRAD_QUAD_Cn_FEM<double, Intrepid::FieldContainer<double> >* intrepidBasis = dynamic_cast< Basis_HGRAD_QUAD_Cn_FEM<double, Intrepid::FieldContainer<double> >* >(intrepidBasisWrapper->intrepidBasis().get());
  if (!intrepidBasis)
  {
    TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "IntrepidBasisWrapper does not appear to wrap Basis_HGRAD_QUAD_Cn_FEM.");
  }
  intrepidBasis->getDofCoords(dofCoords);

  int edgeDim = 1;
  int vertexDim = 0;

  // set vertex dofs:
  for (int vertexIndex=0; vertexIndex<curves.size(); vertexIndex++)
  {
    double x = exactSurface->vertices()[vertexIndex].first;
    double y = exactSurface->vertices()[vertexIndex].second;
    int compDofOrdinal = compBasis->getDofOrdinal(vertexDim, vertexIndex, 0);
    int basisDofOrdinal_x = basis->getDofOrdinalFromComponentDofOrdinal(compDofOrdinal, 0);
    int basisDofOrdinal_y = basis->getDofOrdinalFromComponentDofOrdinal(compDofOrdinal, 1);
    edgeInterpolationCoefficients[basisDofOrdinal_x] = x;
    edgeInterpolationCoefficients[basisDofOrdinal_y] = y;
  }

  for (int edgeIndex=0; edgeIndex<curves.size(); edgeIndex++)
  {
    bool edgeDofsFlipped = edgeIndex >= 2; // because Intrepid's ordering of dofs on the quad is not CCW but tensor-product, we need to flip for the opposite edges
    // (what makes things worse is that the vertex/edge numbering *is* CCW)
    if (curves.size() != 4)
    {
      cout << "WARNING: have not worked out the rule for flipping or not flipping edge dofs for anything but quads.\n";
    }
    double edgeLength = curves[edgeIndex]->linearLength();

    //    cout << "edgeIndex " << edgeIndex << endl;
    for (int comp=0; comp<numComponents; comp++)
    {
      FieldContainer<double> basisCoefficients_comp;
      bool useH1ForEdgeInterpolant = true; // an experiment
      curves[edgeIndex]->projectionBasedInterpolant(basisCoefficients_comp, basis1D, comp, edgeLength, useH1ForEdgeInterpolant);
      //      cout << "for edge " << edgeIndex << " and comp " << comp << ", projection-based interpolant dofs:\n";
      //      cout << basisCoefficients_comp;
      ////      cout << "basis dof coords:\n" << dofCoords;
      //      int basisDofOrdinal = basis->getDofOrdinalFromComponentDofOrdinal(v0_dofOrdinal_comp, comp);
      //      edgeInterpolationCoefficients[basisDofOrdinal] = basisCoefficients_comp[v0_dofOrdinal_1D];

      if (compBasis->getDegree() >= 2)   // then there are some "middle" nodes on the edge
      {
        // get the first dofOrdinal for the edge, so we can check the number of edge basis functions
        int firstEdgeDofOrdinal = compBasis->getDofOrdinal(edgeDim, edgeIndex, 0);

        //        cout << "first edge dofOrdinal: " << firstEdgeDofOrdinal << endl;

        int numEdgeDofs = compBasis->getDofTag(firstEdgeDofOrdinal)[3];
        if (numEdgeDofs != basis1D->getCardinality() - 2)
        {
          TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "numEdgeDofs does not match 1D basis cardinality");
        }
        for (int edgeDofOrdinal=0; edgeDofOrdinal<numEdgeDofs; edgeDofOrdinal++)
        {
          // determine the index into basisCoefficients_comp:
          int edgeDofOrdinalIn1DBasis = edgeDofsFlipped ? numEdgeDofs - 1 - edgeDofOrdinal : edgeDofOrdinal;
          int dofOrdinal1D = basis1D->getDofOrdinal(edgeDim, 0, edgeDofOrdinalIn1DBasis);
          // determine the ordinal of the edge dof in the component basis:
          int compDofOrdinal = compBasis->getDofOrdinal(edgeDim, edgeIndex, edgeDofOrdinal);
          // now, determine its ordinal in the vector basis
          int basisDofOrdinal = basis->getDofOrdinalFromComponentDofOrdinal(compDofOrdinal, comp);

          //          cout << "edge dof ordinal " << edgeDofOrdinal << " has basis weight " << basisCoefficients_comp[dofOrdinal1D] << " for component " << comp << endl;
          //          cout << "node on cell is at (" << dofCoords(compDofOrdinal,0) << ", " << dofCoords(compDofOrdinal,1) << ")\n";
          //          cout << "mapping to basisDofOrdinal " << basisDofOrdinal << endl;

          edgeInterpolationCoefficients[basisDofOrdinal] = basisCoefficients_comp[dofOrdinal1D];
        }
      }
    }
  }
  edgeInterpolationCoefficients.resize(edgeInterpolationCoefficients.size());

  // print out a report of what the edge interpolation is doing:
  /*cout << "projection-based interpolation of edges maps the following points:\n";
  for (int compDofOrdinal=0; compDofOrdinal<compBasis->getCardinality(); compDofOrdinal++) {
    double x_ref = dofCoords(compDofOrdinal,0);
    double y_ref = dofCoords(compDofOrdinal,1);
    int basisDofOrdinal_x = basis->getDofOrdinalFromComponentDofOrdinal(compDofOrdinal, 0);
    int basisDofOrdinal_y = basis->getDofOrdinalFromComponentDofOrdinal(compDofOrdinal, 1);
    if (edgeNodeFieldIndices.find(basisDofOrdinal_x) != edgeNodeFieldIndices.end()) {
      double x_phys = edgeInterpolationCoefficients[basisDofOrdinal_x];
      double y_phys = edgeInterpolationCoefficients[basisDofOrdinal_y];
      cout << "(" << x_ref << ", " << y_ref << ") --> (" << x_phys << ", " << y_phys << ")\n";
    }
  }*/
}
Beispiel #16
0
void Boundary::bcsToImpose(FieldContainer<GlobalIndexType> &globalIndices,
                           FieldContainer<Scalar> &globalValues, TBC<Scalar> &bc,
                           DofInterpreter* dofInterpreter)
{
  set< GlobalIndexType > rankLocalCells = _mesh->cellIDsInPartition();
  map< GlobalIndexType, double> bcGlobalIndicesAndValues;

  for (GlobalIndexType cellID : rankLocalCells)
  {
    bcsToImpose(bcGlobalIndicesAndValues, bc, cellID, dofInterpreter);
  }
  singletonBCsToImpose(bcGlobalIndicesAndValues, bc, dofInterpreter);
  
  // ****** New, tag-based BC imposition follows ******
  map< GlobalIndexType, double> bcTagGlobalIndicesAndValues;
  
  map< int, vector<pair<VarPtr, TFunctionPtr<Scalar>>>> tagBCs = bc.getDirichletTagBCs(); // keys are tags
  
  MeshTopology* meshTopo = dynamic_cast<MeshTopology*>(_mesh->getTopology().get());
  
  TEUCHOS_TEST_FOR_EXCEPTION(!meshTopo, std::invalid_argument, "pure MeshTopologyViews are not yet supported by new tag-based BC imposition");
  
  for (auto tagBC : tagBCs)
  {
    int tagID = tagBC.first;
    
    vector<EntitySetPtr> entitySets = meshTopo->getEntitySetsForTagID(DIRICHLET_SET_TAG_NAME, tagID);
    for (EntitySetPtr entitySet : entitySets)
    {
      // get rank-local cells that match the entity set:
      set<IndexType> matchingCellIDs = entitySet->cellIDsThatMatch(_mesh->getTopology(), rankLocalCells);
      for (IndexType cellID : matchingCellIDs)
      {
        ElementTypePtr elemType = _mesh->getElementType(cellID);
        BasisCachePtr basisCache = BasisCache::basisCacheForCell(_mesh, cellID);
        
        for (auto varFunctionPair : tagBC.second)
        {
          VarPtr var = varFunctionPair.first;
          FunctionPtr f = varFunctionPair.second;
          
          vector<int> sideOrdinals = elemType->trialOrderPtr->getSidesForVarID(var->ID());
          
          for (int sideOrdinal : sideOrdinals)
          {
            BasisPtr basis = elemType->trialOrderPtr->getBasis(var->ID(), sideOrdinal);
            bool isVolume = basis->domainTopology()->getDimension() == _mesh->getDimension();
            for (int d=0; d<_mesh->getDimension(); d++)
            {
              vector<unsigned> matchingSubcells;
              if (isVolume)
                matchingSubcells = entitySet->subcellOrdinals(_mesh->getTopology(), cellID, d);
              else
              {
                CellTopoPtr cellTopo = elemType->cellTopoPtr;
                int sideDim = cellTopo->getDimension() - 1;
                vector<unsigned> matchingSubcellsOnSide = entitySet->subcellOrdinalsOnSide(_mesh->getTopology(), cellID, sideOrdinal, d);
                for (unsigned sideSubcellOrdinal : matchingSubcellsOnSide)
                {
                  unsigned cellSubcellOrdinal = CamelliaCellTools::subcellOrdinalMap(cellTopo, sideDim, sideOrdinal, d, sideSubcellOrdinal);
                  matchingSubcells.push_back(cellSubcellOrdinal);
                }
              }
              
              if (matchingSubcells.size() == 0) continue; // nothing to impose
              
              /*
               What follows - projecting the function onto the basis on the whole domain - is more expensive than necessary,
               in the general case: we can do the projection on just the matching subcells, and if we had a way of taking the
               restriction of a basis to a subcell of the domain, then we could avoid computing the whole basis as well.
               
               But for now, this should work, and it's simple to implement.
               */
              BasisCachePtr basisCacheForImposition = isVolume ? basisCache : basisCache->getSideBasisCache(sideOrdinal);
              int numCells = 1;
              FieldContainer<double> basisCoefficients(numCells,basis->getCardinality());
              Projector<double>::projectFunctionOntoBasisInterpolating(basisCoefficients, f, basis, basisCacheForImposition);
              basisCoefficients.resize(basis->getCardinality());
              
              set<GlobalIndexType> matchingGlobalIndices;
              for (unsigned matchingSubcell : matchingSubcells)
              {
                set<GlobalIndexType> subcellGlobalIndices = dofInterpreter->globalDofIndicesForVarOnSubcell(var->ID(),cellID,d,matchingSubcell);
                matchingGlobalIndices.insert(subcellGlobalIndices.begin(),subcellGlobalIndices.end());
              }
              
              FieldContainer<double> globalData;
              FieldContainer<GlobalIndexType> globalDofIndices;
//              dofInterpreter->interpretLocalBasisCoefficients(cellID, var->ID(), sideOrdinal, basisCoefficientsToImpose, globalData, globalDofIndices);
              dofInterpreter->interpretLocalBasisCoefficients(cellID, var->ID(), sideOrdinal, basisCoefficients, globalData, globalDofIndices);
              for (int globalDofOrdinal=0; globalDofOrdinal<globalDofIndices.size(); globalDofOrdinal++)
              {
                GlobalIndexType globalDofIndex = globalDofIndices(globalDofOrdinal);
                if (matchingGlobalIndices.find(globalDofIndex) != matchingGlobalIndices.end())
                  bcTagGlobalIndicesAndValues[globalDofIndex] = globalData(globalDofOrdinal);
              }
            }
          }
        }
      }
    }
  }
  
  // merge tag-based and legacy BC maps
  double tol = 1e-15;
  for (auto tagEntry : bcTagGlobalIndicesAndValues)
  {
    if (bcGlobalIndicesAndValues.find(tagEntry.first) != bcGlobalIndicesAndValues.end())
    {
      // then check that they match, within tolerance
      double diff = abs(bcGlobalIndicesAndValues[tagEntry.first] - tagEntry.second);
      TEUCHOS_TEST_FOR_EXCEPTION(diff > tol, std::invalid_argument, "Incompatible BC entries encountered");
    }
    else
    {
      bcGlobalIndicesAndValues[tagEntry.first] = tagEntry.second;
    }
  }
  
  globalIndices.resize(bcGlobalIndicesAndValues.size());
  globalValues.resize(bcGlobalIndicesAndValues.size());
  globalIndices.initialize(0);
  globalValues.initialize(0.0);
  int entryOrdinal = 0;
  for (auto bcEntry : bcGlobalIndicesAndValues)
  {
    globalIndices[entryOrdinal] = bcEntry.first;
    globalValues[entryOrdinal] = bcEntry.second;
    entryOrdinal++;
  }
}
Beispiel #17
0
bool FunctionTests::testBasisSumFunction()
{
  bool success = true;
  // on a single-element mesh, the BasisSumFunction should be identical to
  // the Solution with those coefficients

  // define a new mesh: more interesting if we're not on the ref cell
  int spaceDim = 2;
  FieldContainer<double> quadPoints(4,2);

  quadPoints(0,0) = 0.0; // x1
  quadPoints(0,1) = 0.0; // y1
  quadPoints(1,0) = 2.0;
  quadPoints(1,1) = 0.0;
  quadPoints(2,0) = 1.0;
  quadPoints(2,1) = 1.0;
  quadPoints(3,0) = 0.0;
  quadPoints(3,1) = 1.0;

  int H1Order = 1, pToAdd = 0;
  int horizontalCells = 1, verticalCells = 1;

  // create a pointer to a new mesh:
  MeshPtr spectralConfusionMesh = MeshFactory::buildQuadMesh(quadPoints, horizontalCells, verticalCells,
                                  _confusionBF, H1Order, H1Order+pToAdd);

  BCPtr bc = BC::bc();
  SolutionPtr soln = Teuchos::rcp( new Solution(spectralConfusionMesh, bc) );

  soln->initializeLHSVector();

  int cellID = 0;
  double tol = 1e-16; // overly restrictive, just for now.

  DofOrderingPtr trialSpace = spectralConfusionMesh->getElementType(cellID)->trialOrderPtr;
  set<int> trialIDs = trialSpace->getVarIDs();

  BasisCachePtr volumeCache = BasisCache::basisCacheForCell(spectralConfusionMesh, cellID);

  for (set<int>::iterator trialIt=trialIDs.begin(); trialIt != trialIDs.end(); trialIt++)
  {
    int trialID = *trialIt;
    const vector<int>* sidesForVar = &trialSpace->getSidesForVarID(trialID);
    bool boundaryValued = sidesForVar->size() != 1;
    // note that for volume trialIDs, sideIndex = 0, and numSides = 1…
    for (vector<int>::const_iterator sideIt = sidesForVar->begin(); sideIt != sidesForVar->end(); sideIt++)
    {
      int sideIndex = *sideIt;

      BasisCachePtr sideCache = volumeCache->getSideBasisCache(sideIndex);
      BasisPtr basis = trialSpace->getBasis(trialID, sideIndex);
      int basisCardinality = basis->getCardinality();
      for (int basisOrdinal = 0; basisOrdinal<basisCardinality; basisOrdinal++)
      {
        FieldContainer<double> basisCoefficients(basisCardinality);
        basisCoefficients(basisOrdinal) = 1.0;
        soln->setSolnCoeffsForCellID(basisCoefficients, cellID, trialID, sideIndex);

        VarPtr v = Var::varForTrialID(trialID, spectralConfusionMesh->bilinearForm());
        FunctionPtr solnFxn = Function::solution(v, soln, false);
        FunctionPtr basisSumFxn = Teuchos::rcp( new BasisSumFunction(basis, basisCoefficients, Teuchos::rcp((BasisCache*)NULL), OP_VALUE, boundaryValued) );
        if (!boundaryValued)
        {
          double l2diff = (solnFxn - basisSumFxn)->l2norm(spectralConfusionMesh);
//          cout << "l2diff = " << l2diff << endl;
          if (l2diff > tol)
          {
            success = false;
            cout << "testBasisSumFunction: l2diff of " << l2diff << " exceeds tol of " << tol << endl;
            cout << "l2norm of basisSumFxn: " << basisSumFxn->l2norm(spectralConfusionMesh) << endl;
            cout << "l2norm of solnFxn: " << solnFxn->l2norm(spectralConfusionMesh) << endl;
          }
          l2diff = (solnFxn->dx() - basisSumFxn->dx())->l2norm(spectralConfusionMesh);
          //          cout << "l2diff = " << l2diff << endl;
          if (l2diff > tol)
          {
            success = false;
            cout << "testBasisSumFunction: l2diff of dx() " << l2diff << " exceeds tol of " << tol << endl;
            cout << "l2norm of basisSumFxn->dx(): " << basisSumFxn->dx()->l2norm(spectralConfusionMesh) << endl;
            cout << "l2norm of solnFxn->dx(): " << solnFxn->dx()->l2norm(spectralConfusionMesh) << endl;
          }

          // test that the restriction to a side works
          int numSides = volumeCache->cellTopology()->getSideCount();
          for (int i=0; i<numSides; i++)
          {
            BasisCachePtr mySideCache = volumeCache->getSideBasisCache(i);
            if (! solnFxn->equals(basisSumFxn, mySideCache, tol))
            {
              success = false;
              cout << "testBasisSumFunction: on side 0, l2diff of " << l2diff << " exceeds tol of " << tol << endl;
              reportFunctionValueDifferences(solnFxn, basisSumFxn, mySideCache, tol);
            }
            if (! solnFxn->grad(spaceDim)->equals(basisSumFxn->grad(spaceDim), mySideCache, tol))
            {
              success = false;
              cout << "testBasisSumFunction: on side 0, l2diff of dx() " << l2diff << " exceeds tol of " << tol << endl;
              reportFunctionValueDifferences(solnFxn->grad(spaceDim), basisSumFxn->grad(spaceDim), mySideCache, tol);
            }
          }
        }
        else
        {
          FieldContainer<double> cellIntegral(1);
          // compute l2 diff of integral along the one side where we can legitimately assert equality:
          FunctionPtr diffFxn = solnFxn - basisSumFxn;
          (diffFxn*diffFxn)->integrate(cellIntegral, sideCache);
          double l2diff = sqrt(cellIntegral(0));
          if (l2diff > tol)
          {
            success = false;
            cout << "testBasisSumFunction: on side " << sideIndex << ", l2diff of " << l2diff << " exceeds tol of " << tol << endl;

            int numCubPoints = sideCache->getPhysicalCubaturePoints().dimension(1);
            FieldContainer<double> solnFxnValues(1,numCubPoints);
            FieldContainer<double> basisFxnValues(1,numCubPoints);
            solnFxn->values(solnFxnValues, sideCache);
            basisSumFxn->values(basisFxnValues, sideCache);
            cout << "solnFxnValues:\n" << solnFxnValues;
            cout << "basisFxnValues:\n" << basisFxnValues;
          }
          else
          {
//            cout << "testBasisSumFunction: on side " << sideIndex << ", l2diff of " << l2diff << " is within tol of " << tol << endl;
          }
        }
      }
    }
  }

  return success;
}
Beispiel #18
0
void Projector::projectFunctionOntoBasis(FieldContainer<double> &basisCoefficients, Teuchos::RCP<AbstractFunction> fxn, BasisPtr basis,
                                         const FieldContainer<double> &physicalCellNodes) {

  CellTopoPtr cellTopo = basis->domainTopology();
  DofOrderingPtr dofOrderPtr = Teuchos::rcp(new DofOrdering());

  int basisRank = BasisFactory::basisFactory()->getBasisRank(basis);
  int ID = 0; // only one entry for this fake dofOrderPtr
  dofOrderPtr->addEntry(ID,basis,basisRank);
  int maxTrialDegree = dofOrderPtr->maxBasisDegree();

  // do not build side caches - no projections for sides supported at the moment
  if (cellTopo->getTensorialDegree() != 0) {
    cout << "Projector::projectFunctionOntoBasis() does not yet support tensorial degree > 0.\n";
    TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Projector::projectFunctionOntoBasis() does not yet support tensorial degree > 0.");
  }
  shards::CellTopology shardsTopo = cellTopo->getShardsTopology();
  BasisCache basisCache(physicalCellNodes, shardsTopo, *(dofOrderPtr), maxTrialDegree, false);
  // assume only L2 projections
  IntrepidExtendedTypes::EOperatorExtended op =  IntrepidExtendedTypes::OP_VALUE;

  // have information, build inner product matrix
  int numDofs = basis->getCardinality();
  FieldContainer<double> cubPoints = basisCache.getPhysicalCubaturePoints();    

  FieldContainer<double> basisValues = *(basisCache.getTransformedValues(basis, op));
  FieldContainer<double> testBasisValues = *(basisCache.getTransformedWeightedValues(basis, op));

  int numCells = physicalCellNodes.dimension(0);
  int numPts = cubPoints.dimension(1);
  FieldContainer<double> functionValues;
  fxn->getValues(functionValues, cubPoints);  

  FieldContainer<double> gramMatrix(numCells,numDofs,numDofs);
  FieldContainer<double> ipVector(numCells,numDofs);
  FunctionSpaceTools::integrate<double>(gramMatrix,basisValues,testBasisValues,COMP_BLAS);
  FunctionSpaceTools::integrate<double>(ipVector,functionValues,testBasisValues,COMP_BLAS); 
  
  basisCoefficients.resize(numCells,numDofs);
  for (int cellIndex=0; cellIndex<numCells; cellIndex++){

    Epetra_SerialDenseSolver solver;

    Epetra_SerialDenseMatrix A(Copy,
			       &gramMatrix(cellIndex,0,0),
			       gramMatrix.dimension(2), 
			       gramMatrix.dimension(2),  
			       gramMatrix.dimension(1)); // stride -- fc stores in row-major order (a.o.t. SDM)
    
    Epetra_SerialDenseVector b(Copy,
			       &ipVector(cellIndex,0),
			       ipVector.dimension(1));
    
    Epetra_SerialDenseVector x(gramMatrix.dimension(1));
    
    /*
    cout << "matrix A = " << endl;
    for (int i=0;i<gramMatrix.dimension(2);i++){
      for (int j=0;j<gramMatrix.dimension(1);j++){
	cout << A(i,j) << " ";
      }
      cout << endl;
    }
    cout << endl;

    cout << "vector B = " << endl;
    for (int i=0;i<functionValues.dimension(1);i++){
      cout << b(i) << endl;
    }
    */

    solver.SetMatrix(A);
    int info = solver.SetVectors(x,b);
    if (info!=0){
      cout << "projectFunctionOntoBasis: failed to SetVectors with error " << info << endl;
    }

    bool equilibrated = false;
    if (solver.ShouldEquilibrate()){
      solver.EquilibrateMatrix();
      solver.EquilibrateRHS();      
      equilibrated = true;
    }   

    info = solver.Solve();
    if (info!=0){
      cout << "projectFunctionOntoBasis: failed to solve with error " << info << endl;
    }

    if (equilibrated) {
      int successLocal = solver.UnequilibrateLHS();
      if (successLocal != 0) {
        cout << "projection: unequilibration FAILED with error: " << successLocal << endl;
      }
    }

    for (int i=0;i<numDofs;i++){
      basisCoefficients(cellIndex,i) = x(i);
    }   
    
  } 
}
Beispiel #19
0
bool FunctionTests::testValuesDottedWithTensor()
{
  bool success = true;

  vector< FunctionPtr > vectorFxns;

  double xValue = 3, yValue = 4;
  FunctionPtr simpleVector = Function::vectorize(Function::constant(xValue), Function::constant(yValue));
  vectorFxns.push_back(simpleVector);
  FunctionPtr x = Function::xn(1);
  FunctionPtr y = Function::yn(1);
  vectorFxns.push_back( Function::vectorize(x*x, x*y) );

  VGPStokesFormulation vgpStokes = VGPStokesFormulation(1.0);
  BFPtr bf = vgpStokes.bf();

  int h1Order = 1;
  MeshPtr mesh = MeshFactory::quadMesh(bf, h1Order);

  int cellID=0; // the only cell
  BasisCachePtr basisCache = BasisCache::basisCacheForCell(mesh, cellID);

  for (int i=0; i<vectorFxns.size(); i++)
  {
    FunctionPtr vectorFxn_i = vectorFxns[i];
    for (int j=0; j<vectorFxns.size(); j++)
    {
      FunctionPtr vectorFxn_j = vectorFxns[j];
      FunctionPtr dotProduct = vectorFxn_i * vectorFxn_j;
      FunctionPtr expectedDotProduct = vectorFxn_i->x() * vectorFxn_j->x() + vectorFxn_i->y() * vectorFxn_j->y();
      if (! expectedDotProduct->equals(dotProduct, basisCache))
      {
        cout << "testValuesDottedWithTensor() failed: expected dot product does not match dotProduct.\n";
        success = false;
        double tol = 1e-14;
        reportFunctionValueDifferences(dotProduct, expectedDotProduct, basisCache, tol);
      }
    }
  }

  // now, let's try the same thing, but for a LinearTerm dot product
  VarFactoryPtr vf = VarFactory::varFactory();
  VarPtr v = vf->testVar("v", HGRAD);

  DofOrderingPtr dofOrdering = Teuchos::rcp( new DofOrdering(CellTopology::quad()) );
  shards::CellTopology quad_4(shards::getCellTopologyData<shards::Quadrilateral<4> >() );
  BasisPtr basis = BasisFactory::basisFactory()->getBasis(h1Order, quad_4.getKey(), Camellia::FUNCTION_SPACE_HGRAD);
  dofOrdering->addEntry(v->ID(), basis, v->rank());

  int numCells = 1;
  int numFields = basis->getCardinality();

  for (int i=0; i<vectorFxns.size(); i++)
  {
    FunctionPtr f_i = vectorFxns[i];
    LinearTermPtr lt_i = f_i * v;
    LinearTermPtr lt_i_x = f_i->x() * v;
    LinearTermPtr lt_i_y = f_i->y() * v;
    for (int j=0; j<vectorFxns.size(); j++)
    {
      FunctionPtr f_j = vectorFxns[j];
      LinearTermPtr lt_j = f_j * v;
      LinearTermPtr lt_j_x = f_j->x() * v;
      LinearTermPtr lt_j_y = f_j->y() * v;
      FieldContainer<double> values(numCells,numFields,numFields);
      lt_i->integrate(values, dofOrdering, lt_j, dofOrdering, basisCache);
      FieldContainer<double> values_expected(numCells,numFields,numFields);
      lt_i_x->integrate(values_expected,dofOrdering,lt_j_x,dofOrdering,basisCache);
      lt_i_y->integrate(values_expected,dofOrdering,lt_j_y,dofOrdering,basisCache);
      double tol = 1e-14;
      double maxDiff = 0;
      if (!fcsAgree(values, values_expected, tol, maxDiff))
      {
        cout << "FunctionTests::testValuesDottedWithTensor: ";
        cout << "dot product and sum of the products of scalar components differ by maxDiff " << maxDiff;
        cout << " in LinearTerm::integrate().\n";
        success = false;
      }
    }
  }

//  // finally, let's try the same sort of thing, but now with a vector-valued basis
//  BasisPtr vectorBasisTemp = BasisFactory::basisFactory()->getBasis(h1Order, quad_4.getKey(), Camellia::FUNCTION_SPACE_VECTOR_HGRAD);
//  VectorBasisPtr vectorBasis = Teuchos::rcp( (VectorizedBasis<double, FieldContainer<double> > *)vectorBasisTemp.get(),false);
//
//  BasisPtr compBasis = vectorBasis->getComponentBasis();
//
//  // create a new v, and a new dofOrdering
//  VarPtr v_vector = vf->testVar("v_vector", VECTOR_HGRAD);
//  dofOrdering = Teuchos::rcp( new DofOrdering );
//  dofOrdering->addEntry(v_vector->ID(), vectorBasis, v_vector->rank());
//
//  DofOrderingPtr dofOrderingComp = Teuchos::rcp( new DofOrdering );
//  dofOrderingComp->addEntry(v->ID(), compBasis, v->rank());
//

  return success;
}
Beispiel #20
0
void Projector::projectFunctionOntoBasisInterpolating(FieldContainer<double> &basisCoefficients, FunctionPtr fxn,
                                                      BasisPtr basis, BasisCachePtr domainBasisCache) {
  basisCoefficients.initialize(0);
  CellTopoPtr domainTopo = basis->domainTopology();
  unsigned domainDim = domainTopo->getDimension();
  
  IPPtr ip;
  
  bool traceVar = domainBasisCache->isSideCache();
  
  pair<IPPtr, VarPtr> ipVarPair = IP::standardInnerProductForFunctionSpace(basis->functionSpace(), traceVar, domainDim);
  ip = ipVarPair.first;
  VarPtr v = ipVarPair.second;
  
  IPPtr ip_l2 = Teuchos::rcp( new IP );
  ip_l2->addTerm(v);
  
  // for now, make all projections use L^2... (having some issues with gradients and cell Jacobians--I think we need the restriction of the cell Jacobian to the subcell, e.g., and it's not clear how to do that...)
  ip = ip_l2;
  
  FieldContainer<double> referenceDomainNodes(domainTopo->getVertexCount(),domainDim);
  CamelliaCellTools::refCellNodesForTopology(referenceDomainNodes, domainTopo);
  
  int basisCardinality = basis->getCardinality();
  
  set<int> allDofs;
  for (int i=0; i<basisCardinality; i++) {
    allDofs.insert(i);
  }
  
  for (int d=0; d<=domainDim; d++) {
    FunctionPtr projectionThusFar = NewBasisSumFunction::basisSumFunction(basis, basisCoefficients);
    FunctionPtr fxnToApproximate = fxn - projectionThusFar;
    int subcellCount = domainTopo->getSubcellCount(d);
    for (int subcord=0; subcord<subcellCount; subcord++) {
      set<int> subcellDofOrdinals = basis->dofOrdinalsForSubcell(d, subcord);
      if (subcellDofOrdinals.size() > 0) {
        FieldContainer<double> refCellPoints;
        FieldContainer<double> cubatureWeightsSubcell; // allows us to integrate over the fine subcell even when domain is higher-dimensioned
        if (d == 0) {
          refCellPoints.resize(1,domainDim);
          for (int d1=0; d1<domainDim; d1++) {
            refCellPoints(0,d1) = referenceDomainNodes(subcord,d1);
          }
          cubatureWeightsSubcell.resize(1);
          cubatureWeightsSubcell(0) = 1.0;
        } else {
          CellTopoPtr subcellTopo = domainTopo->getSubcell(d, subcord);
//          Teuchos::RCP<Cubature<double> > subcellCubature = cubFactory.create(subcellTopo, domainBasisCache->cubatureDegree());
          BasisCachePtr subcellCache = Teuchos::rcp( new BasisCache(subcellTopo, domainBasisCache->cubatureDegree(), false) );
          int numPoints = subcellCache->getRefCellPoints().dimension(0);
          refCellPoints.resize(numPoints,domainDim);
          cubatureWeightsSubcell = subcellCache->getCubatureWeights();
          
          if (d == domainDim) {
            refCellPoints = subcellCache->getRefCellPoints();
          } else {
            CamelliaCellTools::mapToReferenceSubcell(refCellPoints, subcellCache->getRefCellPoints(), d,
                                                     subcord, domainTopo);
          }
        }
        domainBasisCache->setRefCellPoints(refCellPoints, cubatureWeightsSubcell);
        IPPtr ipForProjection = (d==0) ? ip_l2 : ip; // just use values at vertices (ignore derivatives)
        set<int> dofsToSkip = allDofs;
        for (set<int>::iterator dofOrdinalIt=subcellDofOrdinals.begin(); dofOrdinalIt != subcellDofOrdinals.end(); dofOrdinalIt++) {
          dofsToSkip.erase(*dofOrdinalIt);
        }
        FieldContainer<double> newBasisCoefficients;
        projectFunctionOntoBasis(newBasisCoefficients, fxnToApproximate, basis, domainBasisCache, ipForProjection, v, dofsToSkip);
        for (int cellOrdinal=0; cellOrdinal<newBasisCoefficients.dimension(0); cellOrdinal++) {
          for (set<int>::iterator dofOrdinalIt=subcellDofOrdinals.begin(); dofOrdinalIt != subcellDofOrdinals.end(); dofOrdinalIt++) {
            basisCoefficients(cellOrdinal,*dofOrdinalIt) = newBasisCoefficients(cellOrdinal,*dofOrdinalIt);
          }
        }
      }
    }
  }
}
Beispiel #21
0
    virtual void localStiffnessMatrixAndRHS(FieldContainer<double> &localStiffness, FieldContainer<double> &rhsVector,
                                            IPPtr ip, BasisCachePtr ipBasisCache,
                                            RHSPtr rhs, BasisCachePtr basisCache) {
        double testMatrixAssemblyTime = 0, testMatrixInversionTime = 0, localStiffnessDeterminationFromTestsTime = 0;

#ifdef HAVE_MPI
        Epetra_MpiComm Comm(MPI_COMM_WORLD);
        //cout << "rank: " << rank << " of " << numProcs << endl;
#else
        Epetra_SerialComm Comm;
#endif

        Epetra_Time timer(Comm);

        // localStiffness should have dim. (numCells, numTrialFields, numTrialFields)
        MeshPtr mesh = basisCache->mesh();
        if (mesh.get() == NULL) {
            cout << "localStiffnessMatrix requires BasisCache to have mesh set.\n";
            TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "localStiffnessMatrix requires BasisCache to have mesh set.");
        }
        const vector<GlobalIndexType>* cellIDs = &basisCache->cellIDs();
        int numCells = cellIDs->size();
        if (numCells != localStiffness.dimension(0)) {
            cout << "localStiffnessMatrix requires basisCache->cellIDs() to have the same # of cells as the first dimension of localStiffness\n";
            TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "localStiffnessMatrix requires basisCache->cellIDs() to have the same # of cells as the first dimension of localStiffness");
        }

        ElementTypePtr elemType = mesh->getElementType((*cellIDs)[0]); // we assume all cells provided are of the same type
        DofOrderingPtr trialOrder = elemType->trialOrderPtr;
        DofOrderingPtr fieldOrder = mesh->getDofOrderingFactory().getFieldOrdering(trialOrder);
        DofOrderingPtr traceOrder = mesh->getDofOrderingFactory().getTraceOrdering(trialOrder);

        map<int, int> stiffnessIndexForTraceIndex;
        map<int, int> stiffnessIndexForFieldIndex;
        set<int> varIDs = trialOrder->getVarIDs();
        for (set<int>::iterator varIt = varIDs.begin(); varIt != varIDs.end(); varIt++) {
            int varID = *varIt;
            const vector<int>* sidesForVar = &trialOrder->getSidesForVarID(varID);
            bool isTrace = (sidesForVar->size() > 1);
            for (vector<int>::const_iterator sideIt = sidesForVar->begin(); sideIt != sidesForVar->end(); sideIt++) {
                int sideOrdinal = *sideIt;
                vector<int> dofIndices = trialOrder->getDofIndices(varID,sideOrdinal);
                if (isTrace) {
                    vector<int> traceDofIndices = traceOrder->getDofIndices(varID,sideOrdinal);
                    for (int i=0; i<traceDofIndices.size(); i++) {
                        stiffnessIndexForTraceIndex[traceDofIndices[i]] = dofIndices[i];
                    }
                } else {
                    vector<int> fieldDofIndices = fieldOrder->getDofIndices(varID);
                    for (int i=0; i<fieldDofIndices.size(); i++) {
                        stiffnessIndexForFieldIndex[fieldDofIndices[i]] = dofIndices[i];
                    }
                }
            }
        }

        int numTrialDofs = trialOrder->totalDofs();
        if ((numTrialDofs != localStiffness.dimension(1)) || (numTrialDofs != localStiffness.dimension(2))) {
            cout << "localStiffness should have dimensions (C,numTrialFields,numTrialFields).\n";
            TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "localStiffness should have dimensions (C,numTrialFields,numTrialFields).");
        }

        map<int,int> traceTestMap, fieldTestMap;
        int numEquations = _virtualTerms.getFieldTestVars().size();
        for (int eqn=0; eqn<numEquations; eqn++) {
            VarPtr testVar = _virtualTerms.getFieldTestVars()[eqn];
            VarPtr traceVar = _virtualTerms.getAssociatedTrace(testVar);
            VarPtr fieldVar = _virtualTerms.getAssociatedField(testVar);
            traceTestMap[traceVar->ID()] = testVar->ID();
            fieldTestMap[fieldVar->ID()] = testVar->ID();
        }

        int maxDegreeField = fieldOrder->maxBasisDegree();
        int testDegreeInterior = maxDegreeField + _virtualTerms.getTestEnrichment();
        int testDegreeTrace = testDegreeInterior + 2;

        cout << "ERROR in Virtual: getRelabeledDofOrdering() is commented out in DofOrderingFactory.  Need to rewrite for the new caching scheme.\n";
        DofOrderingPtr testOrderInterior; // = mesh->getDofOrderingFactory().getRelabeledDofOrdering(fieldOrder, fieldTestMap);
        testOrderInterior = mesh->getDofOrderingFactory().setBasisDegree(testOrderInterior, testDegreeInterior, false);
        DofOrderingPtr testOrderTrace = mesh->getDofOrderingFactory().setBasisDegree(testOrderInterior, testDegreeTrace, true); // this has a bunch of extra dofs (interior guys)

        map<int, int> remappedTraceIndices; // go from the index that includes the interior dofs to one that doesn't
        set<int> testIDs = testOrderTrace->getVarIDs();
        int testTraceIndex = 0;
        for (set<int>::iterator testIDIt = testIDs.begin(); testIDIt != testIDs.end(); testIDIt++) {
            int testID = *testIDIt;
            BasisPtr basis = testOrderTrace->getBasis(testID);
            vector<int> interiorDofs = basis->dofOrdinalsForInterior();
            for (int basisOrdinal=0; basisOrdinal<basis->getCardinality(); basisOrdinal++) {
                if (std::find(interiorDofs.begin(),interiorDofs.end(),basisOrdinal) == interiorDofs.end()) {
                    int dofIndex = testOrderTrace->getDofIndex(testID, basisOrdinal);
                    remappedTraceIndices[dofIndex] = testTraceIndex;
                    testTraceIndex++;
                }
            }
        }

//    DofOrderingPtr testOrderTrace = mesh->getDofOrderingFactory().getRelabeledDofOrdering(traceOrder, traceTestMap);
//    testOrderTrace = mesh->getDofOrderingFactory().setBasisDegree(testOrderTrace, testDegreeTrace);

        int numTestInteriorDofs = testOrderInterior->totalDofs();
        int numTestTraceDofsIncludingInterior = testOrderTrace->totalDofs();
        int numTestTraceDofs = testTraceIndex;
        int numTestDofs = numTestTraceDofs + numTestInteriorDofs;

        timer.ResetStartTime();

        bool printTimings = true;

        if (printTimings) {
            cout << "numCells: " << numCells << endl;
            cout << "numTestDofs: " << numTestDofs << endl;
        }

        FieldContainer<double> rhsVectorTest(numCells,testOrderInterior->totalDofs()); // rhsVector is zero for the "trace" test dofs
        {
            // project the load f onto the space of interior test dofs.
            LinearTermPtr f = rhs->linearTerm();
            set<int> testIDs = f->varIDs();
            for (int eqn=0; eqn<numEquations; eqn++) {
                VarPtr v = _virtualTerms.getFieldTestVars()[eqn];

                if (testIDs.find(v->ID()) != testIDs.end()) {
                    BasisPtr testInteriorBasis = testOrderInterior->getBasis(v->ID());
                    FieldContainer<double> fValues(numCells,testInteriorBasis->getCardinality());
//          DofOrderingPtr oneVarOrderingTest = Teuchos::rcp(new DofOrdering(testInteriorBasis->domainTopology()));
                    DofOrderingPtr oneVarOrderingTest = Teuchos::rcp(new DofOrdering);
                    oneVarOrderingTest->addEntry(v->ID(), testInteriorBasis, testInteriorBasis->rangeRank());

                    LinearTermPtr f_v = Teuchos::rcp( new LinearTerm );
                    typedef pair< FunctionPtr, VarPtr > LinearSummand;
                    vector<LinearSummand> summands = f->summands();
                    for (int i=0; i<summands.size(); i++) {
                        FunctionPtr f = summands[i].first;
                        if (v->ID() == summands[i].second->ID()) {
                            f_v->addTerm(f * v);
                            f_v->integrate(fValues, oneVarOrderingTest, basisCache);
                        }
                    }

                    LinearTermPtr v_lt = 1.0 * v;
                    FieldContainer<double> l2(numCells,testInteriorBasis->getCardinality(),testInteriorBasis->getCardinality());
                    v_lt->integrate(l2,oneVarOrderingTest,v_lt,oneVarOrderingTest,basisCache,basisCache->isSideCache());

                    Teuchos::Array<int> testTestDim(2), testOneDim(2);
                    testTestDim[0] = testInteriorBasis->getCardinality();
                    testTestDim[1] = testInteriorBasis->getCardinality();
                    testOneDim[0] = testInteriorBasis->getCardinality();
                    testOneDim[1] = 1;
                    FieldContainer<double> projection(testOneDim);
                    for (int cellOrdinal=0; cellOrdinal<numCells; cellOrdinal++) {
                        FieldContainer<double> l2cell(testTestDim,&l2(cellOrdinal,0,0));
                        FieldContainer<double> f_cell(testOneDim,&fValues(cellOrdinal,0));

                        SerialDenseWrapper::solveSystemUsingQR(projection, l2cell, f_cell);

                        // rows in projection correspond to Ae_i, columns to the e_j.  I.e. projection coefficients for e_i are found in the ith row
                        for (int basisOrdinal_j=0; basisOrdinal_j<projection.dimension(0); basisOrdinal_j++) {
                            int testIndex = testOrderInterior->getDofIndex(v->ID(), basisOrdinal_j);
                            rhsVectorTest(cellOrdinal,testIndex) = projection(basisOrdinal_j,0);
                        }
                    }
                }
            }
        }

        // project strong operator applied to field terms, and use this to populate the top left portion of stiffness matrix:
        {
            FieldContainer<double> trialFieldTestInterior(numCells, fieldOrder->totalDofs(), testOrderInterior->totalDofs());
            for (int eqn=0; eqn<numEquations; eqn++) {
                LinearTermPtr Au = _virtualTerms.getFieldOperators()[eqn];
                VarPtr v = _virtualTerms.getFieldTestVars()[eqn];
                set<int> fieldIDs = Au->varIDs();
                for (set<int>::iterator fieldIt = fieldIDs.begin(); fieldIt != fieldIDs.end(); fieldIt++) {
                    int fieldID = *fieldIt;
//          int testID = fieldTestMap[fieldID];
//
//          LinearTermPtr testInteriorVar = 1.0 * this->varFactory().test(testID);

                    BasisPtr vBasis = testOrderInterior->getBasis(v->ID());
                    BasisPtr fieldTrialBasis = fieldOrder->getBasis(fieldID);
//          DofOrderingPtr oneVarOrderingTest = Teuchos::rcp(new DofOrdering(vBasis->domainTopology()));
                    DofOrderingPtr oneVarOrderingTest = Teuchos::rcp(new DofOrdering());
                    oneVarOrderingTest->addEntry(v->ID(), vBasis, vBasis->rangeRank());
                    FieldContainer<double> Au_values(numCells,vBasis->getCardinality(),fieldTrialBasis->getCardinality());
                    FieldContainer<double> l2(numCells,vBasis->getCardinality(),vBasis->getCardinality());

                    DofOrderingPtr oneVarOrderingTrial = Teuchos::rcp(new DofOrdering());
//          DofOrderingPtr oneVarOrderingTrial = Teuchos::rcp(new DofOrdering(fieldTrialBasis->domainTopology()));
                    oneVarOrderingTrial->addEntry(fieldID, fieldTrialBasis, fieldTrialBasis->rangeRank());

                    LinearTermPtr Au_restricted_to_field = Teuchos::rcp( new LinearTerm );
                    typedef pair< FunctionPtr, VarPtr > LinearSummand;
                    vector<LinearSummand> summands = Au->summands();
                    for (int i=0; i<summands.size(); i++) {
                        FunctionPtr f = summands[i].first;
                        VarPtr v = summands[i].second;
                        if (v->ID() == fieldID) {
                            Au_restricted_to_field->addTerm(f * v);
                        }
                    }

                    LinearTermPtr v_lt = 1.0 * v;

                    Au_restricted_to_field->integrate(Au_values,oneVarOrderingTrial,v_lt,oneVarOrderingTest,basisCache,basisCache->isSideCache());
                    v_lt->integrate(l2,oneVarOrderingTest,v_lt,oneVarOrderingTest,basisCache,basisCache->isSideCache());
                    double maxValue = 0;
                    for (int i=0; i<l2.size(); i++) {
                        maxValue = max(abs(l2[i]),maxValue);
                    }
                    cout << "maxValue in l2 is " << maxValue << endl;
                    Teuchos::Array<int> testTestDim(2), trialTestDim(2);
                    testTestDim[0] = vBasis->getCardinality();
                    testTestDim[1] = vBasis->getCardinality();
                    trialTestDim[0] = vBasis->getCardinality();
                    trialTestDim[1] = fieldTrialBasis->getCardinality();
                    FieldContainer<double> projection(trialTestDim);
                    for (int cellOrdinal=0; cellOrdinal<numCells; cellOrdinal++) {
                        FieldContainer<double> l2cell(testTestDim,&l2(cellOrdinal,0,0));
                        FieldContainer<double> AuCell(trialTestDim,&Au_values(cellOrdinal,0,0));
                        // TODO: confirm that I'm doing the right projection here.  I could be missing a key point, but it seems to me that we must
                        //       project onto an *orthonormal* basis here, to achieve the required identity structure of the (field,field) part of the
                        //       Gram matrix.  OTOH, it looks to me like the computation here achieves exactly that, even though I didn't initially
                        //       have that in mind...
//            SerialDenseWrapper::solveSystemUsingQR(projection, l2cell, AuCell);
                        SerialDenseWrapper::solveSystemMultipleRHS(projection, l2cell, AuCell);

                        // rows in projection correspond to Ae_i, columns to the e_j.  I.e. projection coefficients for e_i are found in the ith row
                        for (int basisOrdinal_i=0; basisOrdinal_i<projection.dimension(0); basisOrdinal_i++) {
                            int testIndex = testOrderInterior->getDofIndex(v->ID(), basisOrdinal_i);
                            for (int basisOrdinal_j=0; basisOrdinal_j<projection.dimension(1); basisOrdinal_j++) {
                                int trialIndex = fieldOrder->getDofIndex(fieldID, basisOrdinal_j); // in the *trial* space
                                trialFieldTestInterior(cellOrdinal,trialIndex,testIndex) = projection(basisOrdinal_i,basisOrdinal_j);
                            }
                        }
                    }
                }
            }
            Teuchos::Array<int> trialTestDim(2);
            trialTestDim[0] = fieldOrder->totalDofs();
            trialTestDim[1] = testOrderInterior->totalDofs();
            for (int cellOrdinal=0; cellOrdinal<numCells; cellOrdinal++) {
                FieldContainer<double> trialFieldTrialField(fieldOrder->totalDofs(), fieldOrder->totalDofs());
                FieldContainer<double> trialTestCell(trialTestDim, &trialFieldTestInterior(cellOrdinal,0,0));
                SerialDenseWrapper::multiply(trialFieldTrialField, trialTestCell, trialTestCell, 'N', 'T'); // transpose the second one
                // now, accumulate into localStiffness
                for (int i=0; i<trialFieldTrialField.dimension(0); i++) {
                    int stiff_i = stiffnessIndexForFieldIndex[i];
                    for (int j=0; j<trialFieldTrialField.dimension(1); j++) {
                        int stiff_j = stiffnessIndexForFieldIndex[j];
                        localStiffness(cellOrdinal,stiff_i,stiff_j) = trialFieldTrialField(i,j);
                    }
                }
            }
            // multiply RHS integrated against the interior test space by the trialFieldTestInterior
            for (int cellOrdinal=0; cellOrdinal<numCells; cellOrdinal++) {
                Teuchos::Array<int> trialTestDim(2), oneTestDim(2);
                trialTestDim[0] = fieldOrder->totalDofs();
                trialTestDim[1] = testOrderInterior->totalDofs();
                oneTestDim[0] = 1;
                oneTestDim[1] = testOrderInterior->totalDofs();
                FieldContainer<double> trialTestCell(trialTestDim, &trialFieldTestInterior(cellOrdinal,0,0));
                FieldContainer<double> rhsTestCell(oneTestDim, &rhsVectorTest(cellOrdinal,0));
                FieldContainer<double> rhsTrialCell(1, fieldOrder->totalDofs());

                SerialDenseWrapper::multiply(rhsTrialCell, rhsTestCell, trialTestCell, 'N', 'T');

                for (int fieldIndex=0; fieldIndex<fieldOrder->totalDofs(); fieldIndex++) {
                    int stiffIndex = stiffnessIndexForFieldIndex[fieldIndex];
                    rhsVector(cellOrdinal,stiffIndex) = rhsTrialCell(0, fieldIndex);
                }
            }
        }

        FieldContainer<double> ipMatrixTraceIncludingInterior(numCells,numTestTraceDofsIncludingInterior,numTestTraceDofsIncludingInterior);
        int numTestTerms = _virtualTerms.getTestNormOperators().size();
        for (int i=0; i<numTestTerms; i++) {
            LinearTermPtr testTerm = _virtualTerms.getTestNormOperators()[i];
            LinearTermPtr boundaryTerm = _virtualTerms.getTestNormBoundaryOperators()[i];
            testTerm->integrate(ipMatrixTraceIncludingInterior,testOrderTrace,boundaryTerm,testOrderTrace,ipBasisCache,ipBasisCache->isSideCache());
        }
        FieldContainer<double> ipMatrixTrace(numCells,numTestTraceDofs,numTestTraceDofs);
        for (int cellOrdinal=0; cellOrdinal<numCells; cellOrdinal++) {
            for (int i_dofIndex=0; i_dofIndex<numTestTraceDofsIncludingInterior; i_dofIndex++) {
                if (remappedTraceIndices.find(i_dofIndex) == remappedTraceIndices.end()) {
                    continue;
                }
                int i_remapped = remappedTraceIndices[i_dofIndex];
                for (int j_dofIndex=0; j_dofIndex<numTestTraceDofsIncludingInterior; j_dofIndex++) {
                    if (remappedTraceIndices.find(j_dofIndex) == remappedTraceIndices.end()) {
                        continue;
                    }
                    int j_remapped = remappedTraceIndices[j_dofIndex];
                    ipMatrixTrace(cellOrdinal,i_remapped,j_remapped) = ipMatrixTraceIncludingInterior(cellOrdinal,i_dofIndex,j_dofIndex);
                }
            }
        }

        testMatrixAssemblyTime += timer.ElapsedTime();
        //      cout << "ipMatrix:\n" << ipMatrix;

        timer.ResetStartTime();

        cout << "NOTE: we do not yet enforce continuity on the trace test space.\n"; // I *think* this is fine, but I'm not dead certain -- we do of course in the end enforce continuity in GDAMinimumRule

        // now, determine the trace part of the bilinear form matrix
        FieldContainer<double> bfMatrixTraceTraceIncludingTestInterior(numCells,testOrderTrace->totalDofs(),traceOrder->totalDofs());
        FieldContainer<double> bfMatrixFieldTraceIncludingTestInterior(numCells,testOrderTrace->totalDofs(),fieldOrder->totalDofs());
        for (int eqn=0; eqn<numEquations; eqn++) {
            VarPtr traceVar = _virtualTerms.getTraceVars()[eqn];
            LinearTermPtr termTraced = traceVar->termTraced();
            LinearTermPtr strongOperator = _virtualTerms.getFieldOperators()[eqn];
            VarPtr testVar = _virtualTerms.getFieldTestVars()[eqn];

            // want to determine \hat{C}(\hat{e}_i, \phi_j) for \phi_j with support on the boundary
            // the \phi_j's with support on the boundary are the ones associated with the trace

            LinearTermPtr trialTerm = 1.0 * traceVar;
            LinearTermPtr testTerm;

            if (traceVar->varType() == TRACE) {
                testTerm = Function::normal() * testVar;
            } else {
                testTerm = 1.0 * testVar;
            }

//      trialTerm->integrate(bfMatrixTrace,traceOrder,testTerm,testOrderTrace,basisCache,basisCache->isSideCache());
            trialTerm->integrate(bfMatrixTraceTraceIncludingTestInterior,traceOrder,testTerm,testOrderTrace,basisCache,basisCache->isSideCache());
            termTraced->integrate(bfMatrixFieldTraceIncludingTestInterior,fieldOrder,-testTerm,testOrderTrace,basisCache,basisCache->isSideCache());
        }

        FieldContainer<double> bfMatrixFieldTrace(numCells,numTestTraceDofs,bfMatrixFieldTraceIncludingTestInterior.dimension(2));
        FieldContainer<double> bfMatrixTraceTrace(numCells,numTestTraceDofs,bfMatrixTraceTraceIncludingTestInterior.dimension(2));
        for (int cellOrdinal=0; cellOrdinal<numCells; cellOrdinal++) {
            for (int i_dofIndex=0; i_dofIndex<numTestTraceDofsIncludingInterior; i_dofIndex++) {
                if (remappedTraceIndices.find(i_dofIndex) == remappedTraceIndices.end()) {
                    continue;
                }
                int i_remapped = remappedTraceIndices[i_dofIndex];
                for (int j_dofIndex=0; j_dofIndex<bfMatrixFieldTrace.dimension(2); j_dofIndex++) {
                    bfMatrixFieldTrace(cellOrdinal,i_remapped,j_dofIndex) = bfMatrixFieldTraceIncludingTestInterior(cellOrdinal,i_dofIndex,j_dofIndex);
                }
                for (int j_dofIndex=0; j_dofIndex<bfMatrixTraceTrace.dimension(2); j_dofIndex++) {
                    bfMatrixTraceTrace(cellOrdinal,i_remapped,j_dofIndex) = bfMatrixTraceTraceIncludingTestInterior(cellOrdinal,i_dofIndex,j_dofIndex);
                }
            }
        }

        Teuchos::Array<int> ipMatrixDim(2), bfMatrixTraceTraceDim(2), bfMatrixFieldTraceDim(2);
        Teuchos::Array<int> traceTraceStiffDim(2), fieldTraceStiffDim(2), fieldFieldStiffDim(2);
        ipMatrixDim[0] = ipMatrixTrace.dimension(1);
        ipMatrixDim[1] = ipMatrixTrace.dimension(2);

        bfMatrixTraceTraceDim[0] = bfMatrixTraceTrace.dimension(1);
        bfMatrixTraceTraceDim[1] = bfMatrixTraceTrace.dimension(2);

        bfMatrixFieldTraceDim[0] = bfMatrixFieldTrace.dimension(1);
        bfMatrixFieldTraceDim[1] = bfMatrixFieldTrace.dimension(2);

        traceTraceStiffDim[0] = traceOrder->totalDofs();
        traceTraceStiffDim[1] = traceTraceStiffDim[0];

        fieldTraceStiffDim[0] = fieldOrder->totalDofs();
        fieldTraceStiffDim[1] = traceOrder->totalDofs(); // rectangular

        fieldFieldStiffDim[0] = fieldOrder->totalDofs();
        fieldFieldStiffDim[1] = fieldOrder->totalDofs();

        FieldContainer<double> traceTraceStiffCell(traceTraceStiffDim);
        FieldContainer<double> fieldTraceStiffCell(fieldTraceStiffDim);
        FieldContainer<double> fieldFieldStiffCell(fieldFieldStiffDim);
        for (int cellOrdinal=0; cellOrdinal<numCells; cellOrdinal++) {
            FieldContainer<double> ipMatrixCell(ipMatrixDim,&ipMatrixTrace(cellOrdinal,0,0));

            FieldContainer<double> optTestCoeffsTraceTrace(numTestTraceDofs,traceOrder->totalDofs());
            FieldContainer<double> bfMatrixTraceTraceCell(bfMatrixTraceTraceDim,&bfMatrixTraceTrace(cellOrdinal,0,0));
            int result = SerialDenseWrapper::solveSystemUsingQR(optTestCoeffsTraceTrace, ipMatrixCell, bfMatrixTraceTraceCell);
            SerialDenseWrapper::multiply(traceTraceStiffCell, bfMatrixTraceTraceCell, optTestCoeffsTraceTrace, 'T', 'N');

            // copy into the appropriate spot in localStiffness:
            for (int i=0; i<traceTraceStiffDim[0]; i++) {
                int i_stiff = stiffnessIndexForTraceIndex[i];
                for (int j=0; j<traceTraceStiffDim[1]; j++) {
                    int j_stiff = stiffnessIndexForTraceIndex[j];
                    localStiffness(cellOrdinal,i_stiff,j_stiff) = traceTraceStiffCell(i,j);
                }
            }

            // because of the way the matrix blocks line up, we actually don't have to do a second inversion of ipMatrixCell for this part
            FieldContainer<double> bfMatrixFieldTraceCell(bfMatrixFieldTraceDim,&bfMatrixFieldTrace(cellOrdinal,0,0));
            SerialDenseWrapper::multiply(fieldTraceStiffCell, bfMatrixFieldTraceCell, optTestCoeffsTraceTrace, 'T', 'N');

            // copy into the appropriate spots in localStiffness (taking advantage of symmetry):
            for (int i=0; i<fieldTraceStiffDim[0]; i++) {
                int i_stiff = stiffnessIndexForFieldIndex[i];
                for (int j=0; j<fieldTraceStiffDim[1]; j++) {
                    int j_stiff = stiffnessIndexForTraceIndex[j];
                    localStiffness(cellOrdinal,i_stiff,j_stiff) = fieldTraceStiffCell(i,j);
                    localStiffness(cellOrdinal,j_stiff,i_stiff) = fieldTraceStiffCell(i,j);
                }
            }

            // because of the way the matrix blocks line up, we do have some trace contributions in the (field, field) portion of the matrix
            // these get added to the field contributions (hence the +=)
            FieldContainer<double> optTestCoeffsFieldTrace(numTestTraceDofs,fieldOrder->totalDofs());
            result = SerialDenseWrapper::solveSystemUsingQR(optTestCoeffsFieldTrace, ipMatrixCell, bfMatrixFieldTraceCell);
            SerialDenseWrapper::multiply(fieldFieldStiffCell, bfMatrixFieldTraceCell, optTestCoeffsFieldTrace, 'T', 'N');
            for (int i=0; i<fieldFieldStiffDim[0]; i++) {
                int i_stiff = stiffnessIndexForFieldIndex[i];
                for (int j=0; j<fieldFieldStiffDim[1]; j++) {
                    int j_stiff = stiffnessIndexForFieldIndex[j];
                    localStiffness(cellOrdinal,i_stiff,j_stiff) += fieldFieldStiffCell(i,j);
                }
            }
        }

        testMatrixInversionTime += timer.ElapsedTime();
        //      cout << "optTestCoeffs:\n" << optTestCoeffs;

        if (printTimings) {
            cout << "testMatrixAssemblyTime: " << testMatrixAssemblyTime << " seconds.\n";
            cout << "testMatrixInversionTime: " << testMatrixInversionTime << " seconds.\n";
            cout << "localStiffnessDeterminationFromTestsTime: " << localStiffnessDeterminationFromTestsTime << " seconds.\n";
        }
    }