bool ParametricCurveTests::testGradientWrapper()
{
  bool success = true;

  // create an artificial function whose gradient is "interesting" and known
  FunctionPtr t1 = Function::xn(1);
  FunctionPtr t2 = Function::yn(1);
  FunctionPtr xt = t1 + t1 * t2;
  FunctionPtr yt = t2 + 2 * t1 * t2;
  FunctionPtr xt_dt1 = 1 + t2;
  FunctionPtr xt_dt2 = t1;
  FunctionPtr yt_dt1 = 2 * t2;
  FunctionPtr yt_dt2 = 1 + 2 * t1;

  FunctionPtr ft = Function::vectorize(xt, yt);
  FunctionPtr ft_dt1 = Function::vectorize(xt_dt1, yt_dt1);
  FunctionPtr ft_dt2 = Function::vectorize(xt_dt2, yt_dt2);

  FunctionPtr ft_gradt = Function::vectorize(ft_dt1, ft_dt2);

  // first test: confirm that on a parametric quad, the wrapped function agrees with the naked one
  int cubatureDegree = 5;
  BasisCachePtr parametricQuadCache = BasisCache::parametricQuadCache(cubatureDegree);
  FunctionPtr fx_gradx = ParametricCurve::parametricGradientWrapper(ft_gradt, true);

  double tol = 1e-14;
  if (! ft_gradt->equals(fx_gradx, parametricQuadCache))
  {
    success = false;
    cout << "on a parametric quad, the wrapped gradient doesn't agree with the naked one";
    reportFunctionValueDifferences(ft_gradt, fx_gradx, parametricQuadCache, tol);
  }
  if (! ft_gradt->equals(ft->grad(), parametricQuadCache))
  {
    success = false;
    cout << "on a parametric quad, manual gradient disagrees with automatic one (error in test construction, likely).";
    reportFunctionValueDifferences(ft_gradt, ft->grad(), parametricQuadCache, tol);
  }

  // on the quad domain defined by (0,0), (1,0), (2,3), (0,1),
  // some algebra shows that for x and y as functions of the parametric
  // coordinates, we have
  // x = t1 +     t1 * t2
  // y = t2 + 2 * t1 * t2
  // which gives the result that our original function f(t1,t2) = (t1 + t1 * t2, t2 + 2 * t1 * t2) =  (x, y)

  FunctionPtr x = Function::xn(1); // understood in physical space
  FunctionPtr y = Function::yn(1);
  FunctionPtr f1_xy = x;
  FunctionPtr f2_xy = y;
  FunctionPtr f_xy = Function::vectorize(f1_xy, f2_xy);

  // set up the quad domain
  FieldContainer<double> physicalCellNodes(1,4,2); // (C,P,D)
  physicalCellNodes(0,0,0) = 0;
  physicalCellNodes(0,0,1) = 0;

  physicalCellNodes(0,1,0) = 1;
  physicalCellNodes(0,1,1) = 0;

  physicalCellNodes(0,2,0) = 2;
  physicalCellNodes(0,2,1) = 3;

  physicalCellNodes(0,3,0) = 0;
  physicalCellNodes(0,3,1) = 1;

  // physical space BasisCache:
  shards::CellTopology quad_4(shards::getCellTopologyData<shards::Quadrilateral<4> >() );
  BasisCachePtr basisCache = Teuchos::rcp( new BasisCache(physicalCellNodes, quad_4, cubatureDegree));

  // as a preliminary test, check that the Jacobian values and inverse values agree with our expectations
  // we expect the Jacobian to be:
  //        [ 1 + t2      t1 ]
  // 1/2 *  [                ]
  //        [ 2 * t2  1 + t2 ]
  // where (t1,t2) are parametric coordinates and the 1/2 comes from the transformation from reference
  // to parametric space
  int numCells = 1;
  int numPoints = basisCache->getRefCellPoints().dimension(0);
  int spaceDim = 2;
  FieldContainer<double> jacobianExpected(numCells,numPoints,spaceDim,spaceDim);
  FieldContainer<double> jacobianInvExpected(numCells,numPoints,spaceDim,spaceDim);
  // also check that the function we've chosen has the expected values
  // by first computing its gradient in parametric space and then dividing by 2 to account
  // for the transformation from reference to parametric space
  FieldContainer<double> fgrad_based_jacobian(numCells,numPoints,spaceDim,spaceDim);
  ft_gradt->values(fgrad_based_jacobian, parametricQuadCache);
  FieldContainer<double> parametricPoints = basisCache->computeParametricPoints();
  for (int ptIndex=0; ptIndex<numPoints; ptIndex++)
  {
    double t1 = parametricPoints(0,ptIndex,0);
    double t2 = parametricPoints(0,ptIndex,1);
    jacobianExpected(0,ptIndex,0,0) = 0.5 * (1 + t2);
    jacobianExpected(0,ptIndex,0,1) = 0.5 * (t1);
    jacobianExpected(0,ptIndex,1,0) = 0.5 * (2 * t2);
    jacobianExpected(0,ptIndex,1,1) = 0.5 * (1 + 2 * t1);
    jacobianInvExpected(0,ptIndex,0,0) = (2.0 / (1 + 2 * t1 + t2) ) * (1 + 2 * t1);
    jacobianInvExpected(0,ptIndex,0,1) = (2.0 / (1 + 2 * t1 + t2) ) * (- t1);
    jacobianInvExpected(0,ptIndex,1,0) = (2.0 / (1 + 2 * t1 + t2) ) * (- 2 * t2);
    jacobianInvExpected(0,ptIndex,1,1) = (2.0 / (1 + 2 * t1 + t2) ) * (1 + t2);

    fgrad_based_jacobian(0,ptIndex,0,0) /= 2.0;
    fgrad_based_jacobian(0,ptIndex,0,1) /= 2.0;
    fgrad_based_jacobian(0,ptIndex,1,0) /= 2.0;
    fgrad_based_jacobian(0,ptIndex,1,1) /= 2.0;
  }
  FieldContainer<double> jacobian = basisCache->getJacobian();
  FieldContainer<double> jacobianInv = basisCache->getJacobianInv();
  double maxDiff = 0;
  if (! fcsAgree(jacobianExpected, jacobian, tol, maxDiff))
  {
    success = false;
    cout << "Jacobian expected does not match actual.\n";
    reportFunctionValueDifferences(parametricPoints, jacobian, jacobianExpected, tol);
  }
  if (! fcsAgree(jacobianInvExpected, jacobianInv, tol, maxDiff))
  {
    success = false;
    cout << "Jacobian inverse expected does not match actual.\n";
    reportFunctionValueDifferences(parametricPoints, jacobianInv, jacobianInvExpected, tol);
  }
  if (! fcsAgree(fgrad_based_jacobian, jacobianExpected, tol, maxDiff))
  {
    success = false;
    cout << "Jacobian from fgrad does not agree with the transformation jacobian (problem with test?).\n";
    reportFunctionValueDifferences(parametricPoints, fgrad_based_jacobian, jacobianExpected, tol);
  }

  // test that the gradient values agree
  if (! fx_gradx->equals(f_xy->grad(), basisCache))
  {
    success = false;
    cout << "wrapped gradient does not agree with analytically transformed function.\n";
    reportFunctionValueDifferences(fx_gradx, f_xy->grad(), basisCache, tol);
  }

  // finally, although this isn't really the right place for this, it is convenient here
  // to test the TFI for the "mesh" we were concerned with above.
  int H1Order = 5;
  BFPtr bf = VGPStokesFormulation(1.0).bf();
  physicalCellNodes.resize(4,2);
  int horizontalElements = 1, verticalElements = 1;
  MeshPtr mesh = MeshFactory::quadMesh(bf, H1Order, physicalCellNodes);

  int cellID = 0;
  vector< ParametricCurvePtr > edges = mesh->parametricEdgesForCell(cellID);
  ParametricSurfacePtr tfi = ParametricSurface::transfiniteInterpolant(edges);

  double v2[2];
  edges[2]->value(0, v2[0], v2[1]);
//  cout << "v2 = (" << v2[0] << ", " << v2[1] << ")\n";

  if ( ! tfi->equals(f_xy, basisCache) )
  {
    success = false;
    cout << "TFI does not agree with analytically constructed transformation function.\n";
    reportFunctionValueDifferences(tfi, f_xy, basisCache, tol);
  }
  if ( ! tfi->grad()->equals(f_xy->grad(), basisCache) )
  {
    success = false;
    cout << "TFI does not agree with analytically constructed transformation function.\n";
    reportFunctionValueDifferences(tfi->grad(), f_xy->grad(), basisCache, tol);
  }

  return success;
}
bool ParametricCurveTests::testTransfiniteInterpolant()
{
  bool success = true;

  // to begin, just let's do a simple quad mesh
  double width=4, height=3;
  double x0 = 0, y0 = 0;
  double x1 = width, y1 = 0;
  double x2 = width, y2 = height;
  double x3 = 0, y3 = height;

  vector< ParametricCurvePtr > edges(4);
  edges[0] = ParametricCurve::line(x0, y0, x1, y1);
  edges[1] = ParametricCurve::line(x1, y1, x2, y2);
  edges[2] = ParametricCurve::line(x2, y2, x3, y3);
  edges[3] = ParametricCurve::line(x3, y3, x0, y0);

  ParametricSurfacePtr transfiniteInterpolant = ParametricSurface::transfiniteInterpolant(edges);

  double x0_actual, y0_actual, x2_actual, y2_actual;
  transfiniteInterpolant->value(0, 0, x0_actual, y0_actual);
  transfiniteInterpolant->value(1, 1, x2_actual, y2_actual);

  double tol=1e-14;
  if ((abs(x0_actual-x0) > tol) || (abs(y0_actual-y0) > tol))
  {
    success = false;
    cout << "transfinite interpolant doesn't interpolate (x0,y0).\n";
  }
  if ((abs(x2_actual-x2) > tol) || (abs(y2_actual-y2) > tol))
  {
    success = false;
    cout << "transfinite interpolant doesn't interpolate (x2,y2).\n";
  }

  // the transfinite interpolant should be just (4t1, 3t2)
  FunctionPtr t1 = Function::xn(1);
  FunctionPtr t2 = Function::yn(1);
  FunctionPtr xPart = 4 * t1;
  FunctionPtr yPart = 3 * t2;
  FunctionPtr expected_tfi = Function::vectorize(xPart, yPart);

  int cubatureDegree = 4;
  BasisCachePtr parametricCache = BasisCache::parametricQuadCache(cubatureDegree);

  // a couple quick sanity checks:
  if (! expected_tfi->equals(expected_tfi, parametricCache))
  {
    success = false;
    cout << "ERROR in Function::equals(): vector Function does not equal itself.\n";
  }
  if (! transfiniteInterpolant->equals(transfiniteInterpolant, parametricCache))
  {
    success = false;
    cout << "Weird error: transfiniteInterpolant does not equal itself.\n";
  }

  // check that the transfiniteInterpolant's value() method matches values()
  {
    int numCells = 1;
    int numPoints = parametricCache->getRefCellPoints().dimension(0);
    int spaceDim = 2;
    FieldContainer<double> values(numCells,numPoints,spaceDim);
    transfiniteInterpolant->values(values, parametricCache);
    int cellIndex = 0;
    for (int ptIndex=0; ptIndex<numPoints; ptIndex++)
    {
      double t1 = parametricCache->getPhysicalCubaturePoints()(cellIndex,ptIndex,0);
      double t2 = parametricCache->getPhysicalCubaturePoints()(cellIndex,ptIndex,1);
      double x, y;
      transfiniteInterpolant->value(t1, t2, x, y);
      double x_expected = values(cellIndex,ptIndex,0);
      double y_expected = values(cellIndex,ptIndex,1);
      if (abs(x-x_expected) > tol)
      {
        cout << "transfinite interpolant value() does not match values()\n";
        success = false;
      }
      if (abs(y-y_expected) > tol)
      {
        cout << "transfinite interpolant value() does not match values()\n";
        success = false;
      }
    }
  }

  if (! expected_tfi->equals(transfiniteInterpolant, parametricCache, tol))
  {
    cout << "transfinite interpolant doesn't match expected.\n";
    success = false;
    int numCells = 1;
    int numPoints = parametricCache->getRefCellPoints().dimension(0);
    int spaceDim = 2;
    FieldContainer<double> values(numCells,numPoints,spaceDim);
    FieldContainer<double> expected_values(numCells,numPoints,spaceDim);
    expected_tfi->values(expected_values, parametricCache);
    transfiniteInterpolant->values(values, parametricCache);
    reportFunctionValueDifferences(parametricCache->getPhysicalCubaturePoints(), expected_values,
                                   values, tol);
  }

  // derivatives
  FunctionPtr xPart_dt1 = Function::constant(4);
  FunctionPtr yPart_dt1 = Function::constant(0);
  FunctionPtr expected_tfi_dt1 = Function::vectorize(xPart_dt1, yPart_dt1);
  if (! expected_tfi_dt1->equals(transfiniteInterpolant->dt1(), parametricCache, tol))
  {
    cout << "d/dt1 of transfinite interpolant doesn't match expected.\n";
    success = false;
    int numCells = 1;
    int numPoints = parametricCache->getRefCellPoints().dimension(0);
    int spaceDim = 2;
    FieldContainer<double> values(numCells,numPoints,spaceDim);
    FieldContainer<double> expected_values(numCells,numPoints,spaceDim);
    expected_tfi_dt1->values(expected_values, parametricCache);
    transfiniteInterpolant->dt1()->values(values, parametricCache);
    reportFunctionValueDifferences(parametricCache->getPhysicalCubaturePoints(), expected_values,
                                   values, tol);
  }

  FunctionPtr xPart_dt2 = Function::constant(0);
  FunctionPtr yPart_dt2 = Function::constant(3);
  FunctionPtr expected_tfi_dt2 = Function::vectorize(xPart_dt2, yPart_dt2);
  if (! expected_tfi_dt2->equals(transfiniteInterpolant->dt2(), parametricCache, tol))
  {
    cout << "d/dt2 of transfinite interpolant doesn't match expected.\n";
    success = false;
    int numCells = 1;
    int numPoints = parametricCache->getRefCellPoints().dimension(0);
    int spaceDim = 2;
    FieldContainer<double> values(numCells,numPoints,spaceDim);
    FieldContainer<double> expected_values(numCells,numPoints,spaceDim);
    expected_tfi_dt2->values(expected_values, parametricCache);
    transfiniteInterpolant->dt2()->values(values, parametricCache);
    reportFunctionValueDifferences(parametricCache->getPhysicalCubaturePoints(), expected_values,
                                   values, tol);
  }

  BasisCachePtr physicalBasisCache = BasisCache::quadBasisCache(width, height, cubatureDegree);

  FunctionPtr one = Function::constant(1);
  FunctionPtr expected_tfi_dx = Function::vectorize(one, Function::zero());
  FunctionPtr expected_tfi_dy = Function::vectorize(Function::zero(), one);

  FunctionPtr expected_tfi_grad = Function::vectorize(expected_tfi_dx, expected_tfi_dy);

  if (! expected_tfi_grad->equals(transfiniteInterpolant->grad(), physicalBasisCache, tol))
  {
    cout << "grad of transfinite interpolant doesn't match expected.\n";
    success = false;
    int numCells = 1;
    int numPoints = physicalBasisCache->getRefCellPoints().dimension(0);
    int spaceDim = 2;
    FieldContainer<double> values(numCells,numPoints,spaceDim,spaceDim);
    FieldContainer<double> expected_values(numCells,numPoints,spaceDim,spaceDim);
    expected_tfi_grad->values(expected_values, physicalBasisCache);
    transfiniteInterpolant->grad()->values(values, physicalBasisCache);
    reportFunctionValueDifferences(physicalBasisCache->getPhysicalCubaturePoints(), expected_values,
                                   values, tol);
  }

  return success;
}
Example #3
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);
          }
        }
      }
    }
  }
}