bool basisSumEqualsFunction(FieldContainer<double> &basisCoefficients, BasisPtr basis, FunctionPtr f) { // tests on [0,1] FunctionPtr sumFunction = Teuchos::rcp( new BasisSumFunction(basis, basisCoefficients) ); int cubatureDegree = basis->getDegree() * 2; BasisCachePtr basisCache = BasisCache::basisCache1D(0, 1, cubatureDegree); double tol = 1e-13; return sumFunction->equals(f, basisCache, tol); }
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; }
bool VectorizedBasisTestSuite::testVectorizedBasisTags() { bool success = true; shards::CellTopology quad_4(shards::getCellTopologyData<shards::Quadrilateral<4> >() ); int numVertices = quad_4.getVertexCount(); int numComponents = quad_4.getDimension(); int vertexDim = 0; for (int polyOrder = 1; polyOrder<10; polyOrder++) { BasisPtr hgradBasis = BasisFactory::basisFactory()->getConformingBasis(polyOrder, quad_4.getKey(), Camellia::FUNCTION_SPACE_HGRAD); BasisPtr vectorHGradBasis = BasisFactory::basisFactory()->getConformingBasis( polyOrder, quad_4.getKey(), Camellia::FUNCTION_SPACE_VECTOR_HGRAD); vector<int> vertexNodeFieldIndices; for (int vertexIndex=0; vertexIndex<numVertices; vertexIndex++) { for (int comp=0; comp<numComponents; comp++) { int vertexNodeFieldIndex = vectorHGradBasis->getDofOrdinal(vertexDim, vertexIndex, comp); vertexNodeFieldIndices.push_back(vertexNodeFieldIndex); // cout << "vertexNodeFieldIndex for vertex index " << vertexIndex << ", comp " << comp; // cout << " = " << vertexNodeFieldIndex << endl; } } if (!checkVertexNodalIndicesQuad(vectorHGradBasis, vertexNodeFieldIndices) ) { success = false; cout << "testVectorizedBasisTags: Vertex tags for vectorized HGRAD basis"; cout << " of order " << polyOrder << " are incorrect.\n"; } } return success; }
FCPtr BasisEvaluation::getTransformedValues(BasisPtr basis, Camellia::EOperator op, const FieldContainer<double> &referencePoints, int numCells, BasisCache* basisCache) { Camellia::EFunctionSpace fs = basis->functionSpace(); int spaceDim = referencePoints.dimension(1); int componentOfInterest; Intrepid::EOperator relatedOp; relatedOp = relatedOperator(op, fs, spaceDim, componentOfInterest); FCPtr referenceValues = getValues(basis,(Camellia::EOperator) relatedOp, referencePoints); return getTransformedValuesWithBasisValues(basis,op,referenceValues,numCells,basisCache); }
bool basisSumInterpolatesCurveEndPoints(FieldContainer<double> &basisCoefficients_x, FieldContainer<double> &basisCoefficients_y, BasisPtr basis, ParametricCurvePtr curve) { double curve_x0, curve_y0, curve_x1, curve_y1; curve->value(0, curve_x0, curve_y0); curve->value(1, curve_x1, curve_y1); BasisCachePtr basisCache = BasisCache::basisCache1D(0, 1, basis->getDegree()*2); double sum_x0 = basisSumAtParametricPoint(basisCoefficients_x, basis, 0, basisCache); double sum_x1 = basisSumAtParametricPoint(basisCoefficients_x, basis, 1, basisCache); double sum_y0 = basisSumAtParametricPoint(basisCoefficients_y, basis, 0, basisCache); double sum_y1 = basisSumAtParametricPoint(basisCoefficients_y, basis, 1, basisCache); double tol = 1e-13; double x0_err = abs(sum_x0-curve_x0); double y0_err = abs(sum_y0-curve_y0); double x1_err = abs(sum_x1-curve_x1); double y1_err = abs(sum_y1-curve_y1); double sum_err = x0_err + y0_err + x1_err + y1_err; return sum_err < tol; }
bool LobattoBasisTests::testSimpleStiffnessMatrix() { bool success = true; int rank = Teuchos::GlobalMPISession::getRank(); VarFactory varFactory; VarPtr u = varFactory.fieldVar("u"); VarPtr un = varFactory.fluxVar("un_hat"); VarPtr v = varFactory.testVar("v", HGRAD); BFPtr bf = Teuchos::rcp( new BF(varFactory) ); vector<double> beta; beta.push_back(1); beta.push_back(1); bf->addTerm(beta * u, v->grad()); bf->addTerm(un, v); DofOrderingPtr trialSpace = Teuchos::rcp( new DofOrdering ); DofOrderingPtr testSpace = Teuchos::rcp( new DofOrdering ); const int numSides = 4; const int spaceDim = 2; int fieldOrder = 3; int testOrder = fieldOrder+2; BasisPtr fieldBasis = Camellia::intrepidQuadHGRAD(fieldOrder); BasisPtr fluxBasis = Camellia::intrepidLineHGRAD(fieldOrder); trialSpace->addEntry(u->ID(), fieldBasis, fieldBasis->rangeRank()); for (int i=0; i<numSides; i++) { trialSpace->addEntry(un->ID(), fluxBasis, fluxBasis->rangeRank(), i); } BasisPtr testBasis = Camellia::lobattoQuadHGRAD(testOrder+1,false); // +1 because it lives in HGRAD testSpace->addEntry(v->ID(), testBasis, testBasis->rangeRank()); int numTrialDofs = trialSpace->totalDofs(); int numTestDofs = testSpace->totalDofs(); int numCells = 1; FieldContainer<double> cellNodes(numCells,numSides,spaceDim); cellNodes(0,0,0) = 0; cellNodes(0,0,1) = 0; cellNodes(0,1,0) = 1; cellNodes(0,1,1) = 0; cellNodes(0,2,0) = 1; cellNodes(0,2,1) = 1; cellNodes(0,3,0) = 0; cellNodes(0,3,1) = 1; FieldContainer<double> stiffness(numCells,numTestDofs,numTrialDofs); FieldContainer<double> cellSideParities(numCells,numSides); cellSideParities.initialize(1.0); Teuchos::RCP<shards::CellTopology> quad_4 = Teuchos::rcp( new shards::CellTopology(shards::getCellTopologyData<shards::Quadrilateral<4> >() ) ); Teuchos::RCP<ElementType> elemType = Teuchos::rcp( new ElementType(trialSpace, testSpace, quad_4)); BasisCachePtr basisCache = Teuchos::rcp( new BasisCache(elemType) ); vector<GlobalIndexType> cellIDs; cellIDs.push_back(0); basisCache->setPhysicalCellNodes(cellNodes, cellIDs, true); bf->stiffnessMatrix(stiffness, elemType, cellSideParities, basisCache); // TODO: finish this test // cout << stiffness; if (rank==0) cout << "Warning: testSimpleStiffnessMatrix() unfinished.\n"; return success; }
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); } } }
void TRieszRep<Scalar>::computeRepresentationValues(FieldContainer<Scalar> &values, int testID, Camellia::EOperator op, BasisCachePtr basisCache) { if (_repsNotComputed) { cout << "Computing riesz rep dofs" << endl; computeRieszRep(); } int spaceDim = _mesh->getTopology()->getDimension(); int numCells = values.dimension(0); int numPoints = values.dimension(1); vector<GlobalIndexType> cellIDs = basisCache->cellIDs(); // all elems coming in should be of same type ElementTypePtr elemTypePtr = _mesh->getElementType(cellIDs[0]); DofOrderingPtr testOrderingPtr = elemTypePtr->testOrderPtr; int numTestDofsForVarID = testOrderingPtr->getBasisCardinality(testID, VOLUME_INTERIOR_SIDE_ORDINAL); BasisPtr testBasis = testOrderingPtr->getBasis(testID); bool testBasisIsVolumeBasis = (spaceDim == testBasis->domainTopology()->getDimension()); bool useCubPointsSideRefCell = testBasisIsVolumeBasis && basisCache->isSideCache(); Teuchos::RCP< const FieldContainer<double> > transformedBasisValues = basisCache->getTransformedValues(testBasis,op,useCubPointsSideRefCell); int rank = values.rank() - 2; // if values are shaped as (C,P), scalar... if (rank > 1) { cout << "ranks greater than 1 not presently supported...\n"; TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "ranks greater than 1 not presently supported..."); } // Camellia::print("cellIDs",cellIDs); values.initialize(0.0); for (int cellIndex = 0; cellIndex<numCells; cellIndex++) { int cellID = cellIDs[cellIndex]; for (int j = 0; j<numTestDofsForVarID; j++) { int dofIndex = testOrderingPtr->getDofIndex(testID, j); for (int i = 0; i<numPoints; i++) { if (rank==0) { double basisValue = (*transformedBasisValues)(cellIndex,j,i); values(cellIndex,i) += basisValue*_rieszRepDofsGlobal[cellID](dofIndex); } else { for (int d = 0; d<spaceDim; d++) { double basisValue = (*transformedBasisValues)(cellIndex,j,i,d); values(cellIndex,i,d) += basisValue*_rieszRepDofsGlobal[cellID](dofIndex); } } } } } // TestSuite::serializeOutput("rep values", values); }
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; }
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; }
int DofOrdering::getBasisCardinality(int varID, int sideIndex) { BasisPtr basis = getBasis(varID,sideIndex); return basis->getCardinality(); // return getBasis(varID,sideIndex)->getCardinality(); }
FCPtr BasisEvaluation::getTransformedValuesWithBasisValues(BasisPtr basis, Camellia::EOperator op, int spaceDim, constFCPtr referenceValues, int numCells, const FieldContainer<double> &cellJacobian, const FieldContainer<double> &cellJacobianInv, const FieldContainer<double> &cellJacobianDet) { typedef FunctionSpaceTools fst; // int numCells = cellJacobian.dimension(0); // int spaceDim = basis->domainTopology()->getDimension(); // changed 2/18/15 // // 6-16-14 NVR: getting the spaceDim from cellJacobian's dimensioning is the way we've historically done it. // // I think it might be better to do this using basis->domainTopology() generally, but for now we only make the // // switch in case the domain topology is a Node. // if (basis->domainTopology()->getDimension() == 0) { // spaceDim = 0; // } else { // spaceDim = cellJacobian.dimension(2); // } 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,cellJacobianInv,*referenceValues); break; case Camellia::FUNCTION_SPACE_HDIV: case Camellia::FUNCTION_SPACE_HDIV_DISC: case Camellia::FUNCTION_SPACE_HDIV_FREE: fst::HDIVtransformVALUE<double>(*transformedValues,cellJacobian,cellJacobianDet,*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): switch(fs) { case Camellia::FUNCTION_SPACE_HVOL: case Camellia::FUNCTION_SPACE_HGRAD: case Camellia::FUNCTION_SPACE_HGRAD_DISC: fst::HGRADtransformGRAD<double>(*transformedValues,cellJacobianInv,*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,cellJacobianInv,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, cellJacobianDet, *referenceValues); } else { fst::HCURLtransformCURL<double>(*transformedValues,cellJacobian,cellJacobianDet,*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,cellJacobian,cellJacobianDet,*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,cellJacobianDet,*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; 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; }
bool ParametricCurveTests::testCircularArc() { bool success = true; // the arc details are copied from CurvilinearMeshTests -- motivation is to diagnose test failure there with a more granular test here double radius = 1.0; double meshWidth = sqrt(2); ParametricCurvePtr circle = ParametricCurve::circle(radius, meshWidth / 2.0, meshWidth / 2.0); ParametricCurvePtr circularArc = ParametricCurve::subCurve(circle, 5.0/8.0, 7.0/8.0); BasisCachePtr basisCache = BasisCache::parametric1DCache(15); // overintegrate to be safe FunctionPtr cos_part = Teuchos::rcp( new Cos_ax(PI/2, 1.25*PI)); FunctionPtr sin_part = Teuchos::rcp( new Sin_ax(PI/2, 1.25*PI)); FunctionPtr x_t = meshWidth / 2 + cos_part; FunctionPtr y_t = meshWidth / 2 + sin_part; FunctionPtr dx_dt = (- PI / 2) * sin_part; FunctionPtr dy_dt = (PI / 2) * cos_part; double arcIntegral_x = circularArc->x()->integrate(basisCache); double x_t_integral = x_t->integrate(basisCache); // check that we have the right idea for all those functions: if (! x_t->equals(circularArc->x(),basisCache)) { double x1_expected = Function::evaluate(x_t,1); double x1_actual; circularArc->xPart()->value(1,x1_actual); cout << "expected x(0) = " << x1_expected; cout << "; actual = " << x1_actual << endl; cout << "x part of circularArc doesn't match expected.\n"; success = false; } if (! y_t->equals(circularArc->y(),basisCache)) { double y1_actual; circularArc->yPart()->value(1,y1_actual); cout << "expected y(1) = " << Function::evaluate(y_t,1); cout << "; actual = " << y1_actual << endl; cout << "y part of circularArc doesn't match expected.\n"; success = false; } if (! dx_dt->equals(circularArc->dt_parametric()->x(),basisCache)) { cout << "dx/dt of circularArc doesn't match expected.\n"; success = false; } if (! dy_dt->equals(circularArc->dt_parametric()->y(),basisCache)) { cout << "dy/dt of circularArc doesn't match expected.\n"; success = false; } // test exact curve at t=0.5 double tol=1e-14; double t = 0.5; double x_expected = meshWidth / 2; double y_expected = meshWidth / 2 - radius; double x,y,xErr,yErr; // check value circularArc->value(t, x, y); xErr = abs(x-x_expected); yErr = abs(y-y_expected); if (xErr > tol) { cout << "exact arc x at t=0.5 is incorrect.\n"; success = false; } if (yErr > tol) { cout << "exact arc y at t=0.5 is incorrect.\n"; success = false; } // check derivatives // figuring out what the x derivative should be is a bit of work, I think, // but the y value is at a minimum, so its derivative should be zero y_expected = 0; circularArc->dt_parametric()->value(t, x, y); yErr = abs(y-y_expected); if (yErr > tol) { cout << "exact arc dy/dt at t=0.5 is nonzero.\n"; success = false; } shards::CellTopology line_2(shards::getCellTopologyData<shards::Line<2> >() ); BasisPtr quadraticBasis = BasisFactory::basisFactory()->getBasis(2, line_2.getKey(), Camellia::FUNCTION_SPACE_HGRAD); // figure out what the weights for the quadratic "middle node" basis function should be: double expected_H1_weight_x, expected_H1_weight_y; double expected_L2_weight_x, expected_L2_weight_y; FunctionPtr middleBasis; { FunctionPtr t = Function::xn(1); middleBasis = 4 * t * (1-t); } double middleBasisL2_squared = (middleBasis*middleBasis)->integrate(basisCache); double middleBasisH1_squared = ( middleBasis->dx() * middleBasis->dx() )->integrate(basisCache) + middleBasisL2_squared; ParametricCurvePtr circularArcBubble = ParametricCurve::bubble(circularArc); FunctionPtr bubble_x = circularArcBubble->x(); FunctionPtr bubble_y = circularArcBubble->y(); double x_against_middle_L2 = (bubble_x * middleBasis)->integrate(basisCache); double x_against_middle_H1 = (bubble_x->dx() * middleBasis->dx())->integrate(basisCache) + x_against_middle_L2; double y_against_middle_L2 = (bubble_y * middleBasis)->integrate(basisCache); double y_against_middle_H1 = (bubble_y->dx() * middleBasis->dx())->integrate(basisCache) + y_against_middle_L2; expected_L2_weight_x = x_against_middle_L2 / middleBasisL2_squared; expected_H1_weight_x = x_against_middle_H1 / middleBasisH1_squared; expected_L2_weight_y = y_against_middle_L2 / middleBasisL2_squared; expected_H1_weight_y = y_against_middle_H1 / middleBasisH1_squared; int middleBasisOrdinal = quadraticBasis->getDofOrdinal(1,0,0); FieldContainer<double> basisCoefficients_x, basisCoefficients_y; bool useH1 = false; // just trying to diagnose whether the issue is in derivatives or values (most likely derivatives) double lengthScale = 1.0; circularArcBubble->projectionBasedInterpolant(basisCoefficients_x, quadraticBasis, 0, lengthScale, useH1); circularArcBubble->projectionBasedInterpolant(basisCoefficients_y, quadraticBasis, 1, lengthScale, useH1); double weightError_x = abs(expected_L2_weight_x-basisCoefficients_x[middleBasisOrdinal]); double weightError_y = abs(expected_L2_weight_y-basisCoefficients_y[middleBasisOrdinal]); if (weightError_x > tol) { success = false; cout << "testCircularArc(): L2 projection doesn't match expected basis weight in x.\n"; cout << "expected " << expected_L2_weight_x << ", was " << basisCoefficients_x[middleBasisOrdinal] << endl; } if (weightError_y > tol) { success = false; cout << "testCircularArc(): L2 projection doesn't match expected basis weight in y.\n"; cout << "expected " << expected_L2_weight_y << ", was " << basisCoefficients_y[middleBasisOrdinal] << endl; } useH1 = true; circularArcBubble->projectionBasedInterpolant(basisCoefficients_x, quadraticBasis, 0, lengthScale, useH1); circularArcBubble->projectionBasedInterpolant(basisCoefficients_y, quadraticBasis, 1, lengthScale, useH1); weightError_x = abs(expected_H1_weight_x-basisCoefficients_x[middleBasisOrdinal]); weightError_y = abs(expected_H1_weight_y-basisCoefficients_y[middleBasisOrdinal]); if (weightError_x > tol) { success = false; cout << "testCircularArc(): H1 projection doesn't match expected basis weight in x.\n"; cout << "expected " << expected_H1_weight_x << ", was " << basisCoefficients_x[middleBasisOrdinal] << endl; } if (weightError_y > tol) { success = false; cout << "testCircularArc(): H1 projection doesn't match expected basis weight in y.\n"; cout << "expected " << expected_H1_weight_y << ", was " << basisCoefficients_y[middleBasisOrdinal] << endl; } /* FunctionPtr projection_x = BasisSumFunction::basisSumFunction(quadraticBasis, basisCoefficients_x); FunctionPtr projection_y = BasisSumFunction::basisSumFunction(quadraticBasis, basisCoefficients_y); FieldContainer<double> parametricPoint(1,1); parametricPoint[0] = t; FieldContainer<double> refPoint = basisCache->getRefCellPointsForPhysicalPoints(parametricPoint); basisCache->setRefCellPoints(refPoint); FieldContainer<double> value(1,1); projection_x->values(value, basisCache); x = value[0]; projection_y->values(value, basisCache); y = value[0]; // same expectations at the beginning, except of course now we don't expect to nail it. // but we do expect to be closer than the linear interpolation of the vertices x_expected = meshWidth / 2; y_expected = meshWidth / 2 - radius; double linearErr_x = 0; // linear interpolant nails the x value double linearErr_y = abs(y_expected); xErr = abs(x-x_expected); yErr = abs(y-y_expected); if (xErr > linearErr_x + tol) { cout << "quadratic projection-based interpolant has greater error in x than linear interpolant.\n"; success = false; } if (yErr > linearErr_y + tol) { cout << "quadratic projection-based interpolant has greater error in y than linear interpolant.\n"; success = false; }*/ return success; }
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++; } }
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; }
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; }
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); } } } } }
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"; } }*/ }
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); } } } } } }
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; }
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; }
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; }
bool ParametricCurveTests::testProjectionBasedInterpolation() { bool success = true; // to start with, project a line onto a linear basis shards::CellTopology line_2(shards::getCellTopologyData<shards::Line<2> >() ); /////////////////// TEST LINEAR CURVES RECOVERED ////////////////////// BasisPtr linearBasis = BasisFactory::basisFactory()->getBasis(1, line_2.getKey(), Camellia::FUNCTION_SPACE_HGRAD); double x0=3, y0=-3, x1=5, y1=4; // double dist = sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); double dist = 1; // the length of the parametric space BasisCachePtr basisCache = BasisCache::basisCache1D(0, dist, linearBasis->getDegree()*2); ParametricCurvePtr myLine = ParametricCurve::line(x0, y0, x1, y1); bool useH1 = true; double lengthScale = 1.0; FieldContainer<double> basisCoefficients_x, basisCoefficients_y; myLine->projectionBasedInterpolant(basisCoefficients_x, linearBasis, 0, lengthScale, useH1); myLine->projectionBasedInterpolant(basisCoefficients_y, linearBasis, 1, lengthScale, useH1); if ( ! basisSumInterpolatesCurveEndPoints(basisCoefficients_x,basisCoefficients_y, linearBasis, myLine)) { cout << "testProjectionBasedInterpolation() failed: projection-based interpolant doesn't interpolate line endpoints.\n"; cout << "basisCoefficients_x:\n" << basisCoefficients_x; cout << "basisCoefficients_y:\n" << basisCoefficients_y; success = false; } // in fact, we should recover the line in x and y: if ( !basisSumEqualsFunction(basisCoefficients_x, linearBasis, myLine->x()) ) { cout << "testProjectionBasedInterpolation() failed: projection-based interpolant doesn't recover the line in the x component.\n"; success = false; } if ( !basisSumEqualsFunction(basisCoefficients_y, linearBasis, myLine->y()) ) { cout << "testProjectionBasedInterpolation() failed: projection-based interpolant doesn't recover the line in the y component.\n"; success = false; } /////////////////// TEST CUBIC CURVES RECOVERED ////////////////////// FunctionPtr t = Function::xn(1); // define x and y as functions of t: FunctionPtr x_t = t*t*t-2*t; FunctionPtr y_t = t*t*t+8*t*t; ParametricCurvePtr myCurve = ParametricCurve::curve(x_t,y_t); BasisPtr cubicBasis = BasisFactory::basisFactory()->getBasis(3, line_2.getKey(), Camellia::FUNCTION_SPACE_HGRAD); myCurve->projectionBasedInterpolant(basisCoefficients_x, cubicBasis, 0, lengthScale, useH1); myCurve->projectionBasedInterpolant(basisCoefficients_y, cubicBasis, 1, lengthScale, useH1); // we should again recover the curve exactly: if ( !basisSumEqualsFunction(basisCoefficients_x, cubicBasis, myCurve->x()) ) { cout << "testProjectionBasedInterpolation() failed: projection-based interpolant doesn't recover the cubic curve in the x component.\n"; success = false; } if ( !basisSumEqualsFunction(basisCoefficients_y, cubicBasis, myCurve->y()) ) { cout << "testProjectionBasedInterpolation() failed: projection-based interpolant doesn't recover the cubic curve in the y component.\n"; success = false; } /////////////////// TEST UNRECOVERABLE CURVE INTERPOLATED ////////////////////// // finally, project the cubic curve onto a quadratic basis, and check that it interpolates the endpoints BasisPtr quadraticBasis = BasisFactory::basisFactory()->getBasis(2, line_2.getKey(), Camellia::FUNCTION_SPACE_HGRAD); myCurve->projectionBasedInterpolant(basisCoefficients_x, quadraticBasis, 0, lengthScale, useH1); myCurve->projectionBasedInterpolant(basisCoefficients_y, quadraticBasis, 1, lengthScale, useH1); if ( ! basisSumInterpolatesCurveEndPoints(basisCoefficients_x,basisCoefficients_y, quadraticBasis, myCurve)) { cout << "testProjectionBasedInterpolation() failed: quadratic projection-based interpolant doesn't interpolate cubic curve endpoints.\n"; cout << "basisCoefficients_x:\n" << basisCoefficients_x; cout << "basisCoefficients_y:\n" << basisCoefficients_y; success = false; } return success; }
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"; } }