bool MeshTestUtility::checkConstraintConsistency(MeshPtr meshMinimumRule) { MeshTopologyViewPtr meshTopo = meshMinimumRule->getTopology(); GDAMinimumRule* minRule = dynamic_cast<GDAMinimumRule*>(meshMinimumRule->globalDofAssignment().get()); bool consistent = true; for (IndexType cellID : meshMinimumRule->cellIDsInPartition()) { CellConstraints constraints = minRule->getCellConstraints(cellID); CellPtr cell = meshTopo->getCell(cellID); CellTopoPtr cellTopo = cell->topology(); for (int d=0; d<cellTopo->getDimension(); d++) { int scCount = cellTopo->getSubcellCount(d); for (int scord=0; scord<scCount; scord++) { IndexType entityIndex = cell->entityIndex(d, scord); AnnotatedEntity constrainingEntity = constraints.subcellConstraints[d][scord]; bool isConsistent = constraintIsConsistent(meshTopo, constrainingEntity, d, entityIndex, true); if (!isConsistent) { cout << "Failed consistency test for standard constraints on cell " << cellID << ", " << CamelliaCellTools::entityTypeString(d) << " " << scord << endl; consistent = false; break; } // now, check space-only constraints (for space-time meshes), if these are defined for this subcell if (constraints.spatialSliceConstraints != Teuchos::null) { AnnotatedEntity constrainingEntityForSpatialSlice = constraints.spatialSliceConstraints->subcellConstraints[d][scord]; if (constrainingEntityForSpatialSlice.cellID != -1) { isConsistent = constraintIsConsistent(meshTopo, constrainingEntityForSpatialSlice, d, entityIndex, true); } if (!isConsistent) { cout << "Failed consistency test for spatial slice on cell " << cellID << ", " << CamelliaCellTools::entityTypeString(d) << " " << scord << endl; consistent = false; break; } } } if (!consistent) break; } if (!consistent) break; } return consistent; }
VectorBasisPtr basisForTransformation(ElementTypePtr cellType) { // int polyOrder = std::max(cellType->trialOrderPtr->maxBasisDegree(), cellType->testOrderPtr->maxBasisDegree()); int polyOrder = cellType->trialOrderPtr->maxBasisDegree(); CellTopoPtr cellTopo = cellType->cellTopoPtr; if (cellTopo->getTensorialDegree() > 0) { // for now, we assume that this means space-time. (At some point, we may support tensor-product spatial cell topologies for // things like fast quadrature support, and this would need revisiting then.) // we also assume that the curvilinearity is purely spatial (and in fact, just 2D for now). cellTopo = CellTopology::cellTopology(cellTopo->getShardsTopology(), cellTopo->getTensorialDegree() - 1); } BasisPtr basis = BasisFactory::basisFactory()->getBasis(polyOrder, cellTopo, Camellia::FUNCTION_SPACE_VECTOR_HGRAD); VectorBasisPtr vectorBasis = Teuchos::rcp( (VectorizedBasis<> *)basis.get(),false); // dynamic cast would be better return vectorBasis; }
int main(int argc, char *argv[]) { #ifdef HAVE_MPI Teuchos::GlobalMPISession mpiSession(&argc, &argv,0); int rank=mpiSession.getRank(); int numProcs=mpiSession.getNProc(); #else int rank = 0; int numProcs = 1; #endif int polyOrder = 3; int pToAdd = 2; // for tests // define our manufactured solution or problem bilinear form: double epsilon = 1e-2; bool useTriangles = false; FieldContainer<double> quadPoints(4,2); quadPoints(0,0) = 0.0; // x1 quadPoints(0,1) = 0.0; // y1 quadPoints(1,0) = 1.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 = polyOrder + 1; int horizontalCells = 1, verticalCells = 1; double energyThreshold = 0.2; // for mesh refinements double nonlinearStepSize = 0.5; double nonlinearRelativeEnergyTolerance = 0.015; // used to determine convergence of the nonlinear solution //////////////////////////////////////////////////////////////////// // SET UP PROBLEM //////////////////////////////////////////////////////////////////// Teuchos::RCP<BurgersBilinearForm> oldBurgersBF = Teuchos::rcp(new BurgersBilinearForm(epsilon)); // new-style bilinear form definition VarFactory varFactory; VarPtr uhat = varFactory.traceVar("\\widehat{u}"); VarPtr beta_n_u_minus_sigma_hat = varFactory.fluxVar("\\widehat{\\beta_n u - \\sigma_n}"); VarPtr u = varFactory.fieldVar("u"); VarPtr sigma1 = varFactory.fieldVar("\\sigma_1"); VarPtr sigma2 = varFactory.fieldVar("\\sigma_2"); VarPtr tau = varFactory.testVar("\\tau",HDIV); VarPtr v = varFactory.testVar("v",HGRAD); BFPtr bf = Teuchos::rcp( new BF(varFactory) ); // create a pointer to a new mesh: Teuchos::RCP<Mesh> mesh = Mesh::buildQuadMesh(quadPoints, horizontalCells, verticalCells, bf, H1Order, H1Order+pToAdd, useTriangles); mesh->setPartitionPolicy(Teuchos::rcp(new ZoltanMeshPartitionPolicy("HSFC"))); Teuchos::RCP<Solution> backgroundFlow = Teuchos::rcp(new Solution(mesh, Teuchos::rcp((BC*)NULL) , Teuchos::rcp((RHS*)NULL), Teuchos::rcp((DPGInnerProduct*)NULL))); // create null solution oldBurgersBF->setBackgroundFlow(backgroundFlow); // tau parts: // 1/eps (sigma, tau)_K + (u, div tau)_K - (u_hat, tau_n)_dK bf->addTerm(sigma1 / epsilon, tau->x()); bf->addTerm(sigma2 / epsilon, tau->y()); bf->addTerm(u, tau->div()); bf->addTerm( - uhat, tau->dot_normal() ); vector<double> e1(2); // (1,0) e1[0] = 1; vector<double> e2(2); // (0,1) e2[1] = 1; FunctionPtr u_prev = Teuchos::rcp( new PreviousSolutionFunction(backgroundFlow, u) ); FunctionPtr beta = e1 * u_prev + Teuchos::rcp( new ConstantVectorFunction( e2 ) ); // v: // (sigma, grad v)_K - (sigma_hat_n, v)_dK - (u, beta dot grad v) + (u_hat * n dot beta, v)_dK bf->addTerm( sigma1, v->dx() ); bf->addTerm( sigma2, v->dy() ); bf->addTerm( -u, beta * v->grad()); bf->addTerm( beta_n_u_minus_sigma_hat, v); // ==================== SET INITIAL GUESS ========================== mesh->registerSolution(backgroundFlow); map<int, Teuchos::RCP<AbstractFunction> > functionMap; functionMap[BurgersBilinearForm::U] = Teuchos::rcp(new InitialGuess()); functionMap[BurgersBilinearForm::SIGMA_1] = Teuchos::rcp(new ZeroFunction()); functionMap[BurgersBilinearForm::SIGMA_2] = Teuchos::rcp(new ZeroFunction()); backgroundFlow->projectOntoMesh(functionMap); // ==================== END SET INITIAL GUESS ========================== // compare stiffness matrices for first linear step: int trialOrder = 1; pToAdd = 0; int testOrder = trialOrder + pToAdd; CellTopoPtr quadTopoPtr = Teuchos::rcp(new shards::CellTopology(shards::getCellTopologyData<shards::Quadrilateral<4> >() )); DofOrderingFactory dofOrderingFactory(bf); DofOrderingPtr testOrdering = dofOrderingFactory.testOrdering(testOrder, *quadTopoPtr); DofOrderingPtr trialOrdering = dofOrderingFactory.trialOrdering(trialOrder, *quadTopoPtr); int numCells = 1; // just use testOrdering for both trial and test spaces (we only use to define BasisCache) ElementTypePtr elemType = Teuchos::rcp( new ElementType(trialOrdering, testOrdering, quadTopoPtr) ); BasisCachePtr basisCache = Teuchos::rcp( new BasisCache(elemType) ); quadPoints.resize(1,quadPoints.dimension(0),quadPoints.dimension(1)); basisCache->setPhysicalCellNodes(quadPoints,vector<int>(1),true); // true: do create side cache FieldContainer<double> cellSideParities(numCells,quadTopoPtr->getSideCount()); cellSideParities.initialize(1.0); // not worried here about neighbors actually having opposite parity -- just want the two BF implementations to agree... FieldContainer<double> expectedValues(numCells, testOrdering->totalDofs(), trialOrdering->totalDofs() ); FieldContainer<double> actualValues(numCells, testOrdering->totalDofs(), trialOrdering->totalDofs() ); oldBurgersBF->stiffnessMatrix(expectedValues, elemType, cellSideParities, basisCache); bf->stiffnessMatrix(actualValues, elemType, cellSideParities, basisCache); // compare beta's as well FieldContainer<double> expectedBeta = oldBurgersBF->getBeta(basisCache); Teuchos::Array<int> dim; expectedBeta.dimensions(dim); FieldContainer<double> actualBeta(dim); beta->values(actualBeta,basisCache); double tol = 1e-14; double maxDiff; if (rank == 0) { if ( ! TestSuite::fcsAgree(expectedBeta,actualBeta,tol,maxDiff) ) { cout << "Test failed: old Burgers beta differs from new; maxDiff " << maxDiff << ".\n"; cout << "Old beta: \n" << expectedBeta; cout << "New beta: \n" << actualBeta; } else { cout << "Old and new Burgers beta agree!!\n"; } if ( ! TestSuite::fcsAgree(expectedValues,actualValues,tol,maxDiff) ) { cout << "Test failed: old Burgers stiffness differs from new; maxDiff " << maxDiff << ".\n"; cout << "Old: \n" << expectedValues; cout << "New: \n" << actualValues; cout << "TrialDofOrdering: \n" << *trialOrdering; cout << "TestDofOrdering:\n" << *testOrdering; } else { cout << "Old and new Burgers stiffness agree!!\n"; } } // define our inner product: // Teuchos::RCP<BurgersInnerProduct> ip = Teuchos::rcp( new BurgersInnerProduct( bf, mesh ) ); // function to scale the squared guy by epsilon/h FunctionPtr epsilonOverHScaling = Teuchos::rcp( new EpsilonScaling(epsilon) ); IPPtr ip = Teuchos::rcp( new IP ); ip->addTerm(tau); ip->addTerm(tau->div()); ip->addTerm( epsilonOverHScaling * v ); ip->addTerm( sqrt(sqrt(epsilon)) * v->grad() ); ip->addTerm( beta * v->grad() ); // use old IP instead, for now... Teuchos::RCP<BurgersInnerProduct> oldIP = Teuchos::rcp( new BurgersInnerProduct( oldBurgersBF, mesh ) ); expectedValues.resize(numCells, testOrdering->totalDofs(), testOrdering->totalDofs() ); actualValues.resize (numCells, testOrdering->totalDofs(), testOrdering->totalDofs() ); BasisCachePtr ipBasisCache = Teuchos::rcp( new BasisCache(elemType, true) ); // true: test vs. test ipBasisCache->setPhysicalCellNodes(quadPoints,vector<int>(1),false); // false: don't create side cache oldIP->computeInnerProductMatrix(expectedValues,testOrdering,ipBasisCache); ip->computeInnerProductMatrix(actualValues,testOrdering,ipBasisCache); tol = 1e-14; maxDiff = 0.0; if (rank==0) { if ( ! TestSuite::fcsAgree(expectedValues,actualValues,tol,maxDiff) ) { cout << "Test failed: old inner product differs from new IP; maxDiff " << maxDiff << ".\n"; cout << "Old: \n" << expectedValues; cout << "New IP: \n" << actualValues; cout << "testOrdering: \n" << *testOrdering; } else { cout << "Old inner product and new IP agree!!\n"; } } Teuchos::RCP<RHSEasy> rhs = Teuchos::rcp( new RHSEasy ); // the RHS as implemented by BurgersProblem divides the first component of beta by 2.0 // so we follow that. I've not done the math; just imitating the code... Teuchos::RCP<RHSEasy> otherRHS = Teuchos::rcp( new RHSEasy ); vector<double> e1_div2 = e1; e1_div2[0] /= 2.0; FunctionPtr rhsBeta = (e1_div2 * beta * e1 + Teuchos::rcp( new ConstantVectorFunction( e2 ) )) * u_prev; otherRHS->addTerm( rhsBeta * v->grad() - u_prev * tau->div() ); Teuchos::RCP<BurgersProblem> problem = Teuchos::rcp( new BurgersProblem(oldBurgersBF) ); expectedValues.resize(numCells, testOrdering->totalDofs() ); actualValues.resize (numCells, testOrdering->totalDofs() ); problem->integrateAgainstStandardBasis(expectedValues,testOrdering,basisCache); otherRHS->integrateAgainstStandardBasis(actualValues,testOrdering,basisCache); tol = 1e-14; maxDiff = 0.0; if (rank==0) { if ( ! TestSuite::fcsAgree(expectedValues,actualValues,tol,maxDiff) ) { cout << "Test failed: old RHS differs from new (\"otherRHS\"); maxDiff " << maxDiff << ".\n"; cout << "Old: \n" << expectedValues; cout << "New: \n" << actualValues; cout << "testOrdering: \n" << *testOrdering; } else { cout << "Old and new RHS (\"otherRHS\") agree!!\n"; } } FunctionPtr u_prev_squared_div2 = 0.5 * u_prev * u_prev; rhs->addTerm( (e1 * u_prev_squared_div2 + e2 * u_prev) * v->grad() - u_prev * tau->div()); if (! functionsAgree(e2 * u_prev, Teuchos::rcp( new ConstantVectorFunction( e2 ) ) * u_prev, basisCache) ) { cout << "two like functions differ...\n"; } FunctionPtr e1_f = Teuchos::rcp( new ConstantVectorFunction( e1 ) ); FunctionPtr e2_f = Teuchos::rcp( new ConstantVectorFunction( e2 ) ); FunctionPtr one = Teuchos::rcp( new ConstantScalarFunction( 1.0 ) ); if (! functionsAgree( Teuchos::rcp( new ProductFunction(e1_f, (e1_f + e2_f)) ), // e1_f * (e1_f + e2_f) one, basisCache) ) { cout << "two like functions differ...\n"; } if (! functionsAgree(u_prev_squared_div2, (e1_div2 * beta) * u_prev, basisCache) ) { cout << "two like functions differ...\n"; } if (! functionsAgree(e1 * u_prev_squared_div2, (e1_div2 * beta * e1) * u_prev, basisCache) ) { cout << "two like functions differ...\n"; } if (! functionsAgree(e1 * u_prev_squared_div2 + e2 * u_prev, (e1_div2 * beta * e1 + Teuchos::rcp( new ConstantVectorFunction( e2 ) )) * u_prev, basisCache) ) { cout << "two like functions differ...\n"; } problem->integrateAgainstStandardBasis(expectedValues,testOrdering,basisCache); rhs->integrateAgainstStandardBasis(actualValues,testOrdering,basisCache); tol = 1e-14; maxDiff = 0.0; if (rank==0) { if ( ! TestSuite::fcsAgree(expectedValues,actualValues,tol,maxDiff) ) { cout << "Test failed: old RHS differs from new (\"rhs\"); maxDiff " << maxDiff << ".\n"; cout << "Old: \n" << expectedValues; cout << "New: \n" << actualValues; cout << "testOrdering: \n" << *testOrdering; } else { cout << "Old and new RHS (\"rhs\") agree!!\n"; } } SpatialFilterPtr outflowBoundary = Teuchos::rcp( new TopBoundary ); SpatialFilterPtr inflowBoundary = Teuchos::rcp( new NegatedSpatialFilter(outflowBoundary) ); Teuchos::RCP<PenaltyConstraints> pc = Teuchos::rcp(new PenaltyConstraints); LinearTermPtr sigma_hat = beta * uhat->times_normal() - beta_n_u_minus_sigma_hat; FunctionPtr zero = Teuchos::rcp( new ConstantScalarFunction(0.0) ); pc->addConstraint(sigma_hat==zero,outflowBoundary); FunctionPtr u0 = Teuchos::rcp( new U0 ); FunctionPtr n = Teuchos::rcp( new UnitNormalFunction ); Teuchos::RCP<BCEasy> inflowBC = Teuchos::rcp( new BCEasy ); FunctionPtr u0_squared_div_2 = 0.5 * u0 * u0; inflowBC->addDirichlet(beta_n_u_minus_sigma_hat,inflowBoundary, ( e1 * u0_squared_div_2 + e2 * u0) * n ); // create a solution object Teuchos::RCP<Solution> solution = Teuchos::rcp(new Solution(mesh, inflowBC, rhs, ip)); mesh->registerSolution(solution); solution->setFilter(pc); // old penalty filter: Teuchos::RCP<LocalStiffnessMatrixFilter> penaltyBC = Teuchos::rcp(new PenaltyMethodFilter(problem)); // solution->setFilter(penaltyBC); // compare old and new filters elemType = mesh->getElement(0)->elementType(); trialOrdering = elemType->trialOrderPtr; testOrdering = elemType->testOrderPtr; // stiffness expectedValues.resize(numCells, trialOrdering->totalDofs(), trialOrdering->totalDofs() ); actualValues.resize (numCells, trialOrdering->totalDofs(), trialOrdering->totalDofs() ); expectedValues.initialize(0.0); actualValues.initialize(0.0); // load FieldContainer<double> expectedLoad(numCells, trialOrdering->totalDofs() ); FieldContainer<double> actualLoad(numCells, trialOrdering->totalDofs() ); penaltyBC->filter(expectedValues,expectedLoad,basisCache,mesh,problem); pc->filter(actualValues,actualLoad,basisCache,mesh,problem); maxDiff = 0.0; if (rank==0) { if ( ! TestSuite::fcsAgree(expectedValues,actualValues,tol,maxDiff) ) { cout << "Test failed: old penalty stiffness differs from new; maxDiff " << maxDiff << ".\n"; cout << "Old: \n" << expectedValues; cout << "New: \n" << actualValues; cout << "trialOrdering: \n" << *trialOrdering; } else { cout << "Old and new penalty stiffness agree!!\n"; } } if (rank==0) { if ( ! TestSuite::fcsAgree(expectedLoad,actualLoad,tol,maxDiff) ) { cout << "Test failed: old penalty load differs from new; maxDiff " << maxDiff << ".\n"; cout << "Old: \n" << expectedValues; cout << "New: \n" << actualValues; cout << "trialOrdering: \n" << *trialOrdering; } else { cout << "Old and new penalty load agree!!\n"; } } // define refinement strategy: Teuchos::RCP<RefinementStrategy> refinementStrategy = Teuchos::rcp(new RefinementStrategy(solution,energyThreshold)); // =================== END INITIALIZATION CODE ========================== // refine the spectral mesh, for comparability with the original Burgers' driver mesh->hRefine(vector<int>(1),RefinementPattern::regularRefinementPatternQuad()); int numRefs = 5; Teuchos::RCP<NonlinearStepSize> stepSize = Teuchos::rcp(new NonlinearStepSize(nonlinearStepSize)); Teuchos::RCP<NonlinearSolveStrategy> solveStrategy = Teuchos::rcp( new NonlinearSolveStrategy(backgroundFlow, solution, stepSize, nonlinearRelativeEnergyTolerance) ); for (int refIndex=0; refIndex<numRefs; refIndex++) { solveStrategy->solve(rank==0); refinementStrategy->refine(rank==0); // print to console on rank 0 } // one more nonlinear solve on refined mesh int numNRSteps = 5; for (int i=0; i<numNRSteps; i++) { solution->solve(false); backgroundFlow->addSolution(solution,1.0); } if (rank==0) { backgroundFlow->writeFieldsToFile(BurgersBilinearForm::U, "u_ref.m"); backgroundFlow->writeFieldsToFile(BurgersBilinearForm::SIGMA_1, "sigmax.m"); backgroundFlow->writeFieldsToFile(BurgersBilinearForm::SIGMA_2, "sigmay.m"); solution->writeFluxesToFile(BurgersBilinearForm::U_HAT, "du_hat_ref.dat"); } return 0; }
void Projector::projectFunctionOntoBasisInterpolating(FieldContainer<double> &basisCoefficients, FunctionPtr fxn, BasisPtr basis, BasisCachePtr domainBasisCache) { basisCoefficients.initialize(0); CellTopoPtr domainTopo = basis->domainTopology(); unsigned domainDim = domainTopo->getDimension(); IPPtr ip; bool traceVar = domainBasisCache->isSideCache(); pair<IPPtr, VarPtr> ipVarPair = IP::standardInnerProductForFunctionSpace(basis->functionSpace(), traceVar, domainDim); ip = ipVarPair.first; VarPtr v = ipVarPair.second; IPPtr ip_l2 = Teuchos::rcp( new IP ); ip_l2->addTerm(v); // for now, make all projections use L^2... (having some issues with gradients and cell Jacobians--I think we need the restriction of the cell Jacobian to the subcell, e.g., and it's not clear how to do that...) ip = ip_l2; FieldContainer<double> referenceDomainNodes(domainTopo->getVertexCount(),domainDim); CamelliaCellTools::refCellNodesForTopology(referenceDomainNodes, domainTopo); int basisCardinality = basis->getCardinality(); set<int> allDofs; for (int i=0; i<basisCardinality; i++) { allDofs.insert(i); } for (int d=0; d<=domainDim; d++) { FunctionPtr projectionThusFar = NewBasisSumFunction::basisSumFunction(basis, basisCoefficients); FunctionPtr fxnToApproximate = fxn - projectionThusFar; int subcellCount = domainTopo->getSubcellCount(d); for (int subcord=0; subcord<subcellCount; subcord++) { set<int> subcellDofOrdinals = basis->dofOrdinalsForSubcell(d, subcord); if (subcellDofOrdinals.size() > 0) { FieldContainer<double> refCellPoints; FieldContainer<double> cubatureWeightsSubcell; // allows us to integrate over the fine subcell even when domain is higher-dimensioned if (d == 0) { refCellPoints.resize(1,domainDim); for (int d1=0; d1<domainDim; d1++) { refCellPoints(0,d1) = referenceDomainNodes(subcord,d1); } cubatureWeightsSubcell.resize(1); cubatureWeightsSubcell(0) = 1.0; } else { CellTopoPtr subcellTopo = domainTopo->getSubcell(d, subcord); // Teuchos::RCP<Cubature<double> > subcellCubature = cubFactory.create(subcellTopo, domainBasisCache->cubatureDegree()); BasisCachePtr subcellCache = Teuchos::rcp( new BasisCache(subcellTopo, domainBasisCache->cubatureDegree(), false) ); int numPoints = subcellCache->getRefCellPoints().dimension(0); refCellPoints.resize(numPoints,domainDim); cubatureWeightsSubcell = subcellCache->getCubatureWeights(); if (d == domainDim) { refCellPoints = subcellCache->getRefCellPoints(); } else { CamelliaCellTools::mapToReferenceSubcell(refCellPoints, subcellCache->getRefCellPoints(), d, subcord, domainTopo); } } domainBasisCache->setRefCellPoints(refCellPoints, cubatureWeightsSubcell); IPPtr ipForProjection = (d==0) ? ip_l2 : ip; // just use values at vertices (ignore derivatives) set<int> dofsToSkip = allDofs; for (set<int>::iterator dofOrdinalIt=subcellDofOrdinals.begin(); dofOrdinalIt != subcellDofOrdinals.end(); dofOrdinalIt++) { dofsToSkip.erase(*dofOrdinalIt); } FieldContainer<double> newBasisCoefficients; projectFunctionOntoBasis(newBasisCoefficients, fxnToApproximate, basis, domainBasisCache, ipForProjection, v, dofsToSkip); for (int cellOrdinal=0; cellOrdinal<newBasisCoefficients.dimension(0); cellOrdinal++) { for (set<int>::iterator dofOrdinalIt=subcellDofOrdinals.begin(); dofOrdinalIt != subcellDofOrdinals.end(); dofOrdinalIt++) { basisCoefficients(cellOrdinal,*dofOrdinalIt) = newBasisCoefficients(cellOrdinal,*dofOrdinalIt); } } } } } }
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); } } }
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; }
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++; } }
void Boundary::singletonBCsToImpose(std::map<GlobalIndexType,Scalar> &dofIndexToValue, TBC<Scalar> &bc, DofInterpreter* dofInterpreter) { // first, let's check for any singletons (one-point BCs) map<IndexType, set < pair<int, unsigned> > > singletonsForCell; const set<GlobalIndexType>* rankLocalCells = &_mesh->cellIDsInPartition(); vector< int > trialIDs = _mesh->bilinearForm()->trialIDs(); for (int trialID : trialIDs) { if (bc.singlePointBC(trialID)) { auto knownActiveCells = &_mesh->getTopology()->getLocallyKnownActiveCellIndices(); vector<double> spatialVertex = bc.pointForSpatialPointBC(trialID); vector<IndexType> matchingVertices = _mesh->getTopology()->getVertexIndicesMatching(spatialVertex); unsigned vertexDim = 0; for (IndexType vertexIndex : matchingVertices) { set< pair<IndexType, unsigned> > cellsForVertex = _mesh->getTopology()->getCellsContainingEntity(vertexDim, vertexIndex); for (pair<IndexType, unsigned> cellForVertex : cellsForVertex) { // active cell IndexType matchingCellID = cellForVertex.first; if (rankLocalCells->find(matchingCellID) != rankLocalCells->end()) // we own this cell, so we're responsible for imposing the singleton BC { CellPtr cell = _mesh->getTopology()->getCell(matchingCellID); unsigned vertexOrdinal = cell->findSubcellOrdinal(vertexDim, vertexIndex); TEUCHOS_TEST_FOR_EXCEPTION(vertexOrdinal == -1, std::invalid_argument, "Internal error: vertexOrdinal not found for cell to which it supposedly belongs"); singletonsForCell[matchingCellID].insert(make_pair(trialID, vertexOrdinal)); } } } } } for (auto cellEntry : singletonsForCell) { GlobalIndexType cellID = cellEntry.first; auto singletons = cellEntry.second; ElementTypePtr elemType = _mesh->getElementType(cellID); map<int, vector<unsigned> > vertexOrdinalsForTrialID; for (pair<int, unsigned> trialVertexPair : singletons) { vertexOrdinalsForTrialID[trialVertexPair.first].push_back(trialVertexPair.second); } for (auto trialVertexOrdinals : vertexOrdinalsForTrialID) { int trialID = trialVertexOrdinals.first; vector<unsigned> vertexOrdinalsInCell = trialVertexOrdinals.second; CellTopoPtr cellTopo = elemType->cellTopoPtr; CellTopoPtr spatialCellTopo; bool spaceTime; int vertexOrdinalInSpatialCell; if (vertexOrdinalsInCell.size() == 2) { // we'd better be doing space-time in this case, and the vertices should be the same spatially spaceTime = (cellTopo->getTensorialDegree() > 0); TEUCHOS_TEST_FOR_EXCEPTION(!spaceTime, std::invalid_argument, "multiple vertices for spatial point only supported for space-time"); spatialCellTopo = cellTopo->getTensorialComponent(); vertexOrdinalInSpatialCell = -1; for (unsigned spatialVertexOrdinal = 0; spatialVertexOrdinal < spatialCellTopo->getNodeCount(); spatialVertexOrdinal++) { vector<unsigned> tensorComponentNodes = {spatialVertexOrdinal,0}; unsigned spaceTimeVertexOrdinal_t0 = cellTopo->getNodeFromTensorialComponentNodes(tensorComponentNodes); if ((spaceTimeVertexOrdinal_t0 == vertexOrdinalsInCell[0]) || (spaceTimeVertexOrdinal_t0 == vertexOrdinalsInCell[1])) { // then this should be our match. Confirm this: tensorComponentNodes = {spatialVertexOrdinal,1}; unsigned spaceTimeVertexOrdinal_t1 = cellTopo->getNodeFromTensorialComponentNodes(tensorComponentNodes); bool t1VertexMatches = (spaceTimeVertexOrdinal_t1 == vertexOrdinalsInCell[0]) || (spaceTimeVertexOrdinal_t1 == vertexOrdinalsInCell[1]); TEUCHOS_TEST_FOR_EXCEPTION(!t1VertexMatches, std::invalid_argument, "Internal error: space-time vertices do not belong to the same spatial vertex"); vertexOrdinalInSpatialCell = spatialVertexOrdinal; break; } } TEUCHOS_TEST_FOR_EXCEPTION(vertexOrdinalInSpatialCell == -1, std::invalid_argument, "Internal error: spatial vertex ordinal not found"); } else if (vertexOrdinalsInCell.size() == 1) { spaceTime = false; spatialCellTopo = cellTopo; vertexOrdinalInSpatialCell = vertexOrdinalsInCell[0]; } else { TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "vertexOrdinalsInCell must have 1 or 2 vertices"); } set<GlobalIndexType> globalIndicesForVariable; DofOrderingPtr trialOrderingPtr = elemType->trialOrderPtr; int numSpatialSides = spatialCellTopo->getSideCount(); vector<unsigned> spatialSidesForVertex; vector<unsigned> sideVertexOrdinals; // same index in this container as spatialSidesForVertex: gets the node ordinal of the vertex in that side int sideDim = spatialCellTopo->getDimension() - 1; if (!_mesh->bilinearForm()->isFluxOrTrace(trialID)) { spatialSidesForVertex.push_back(VOLUME_INTERIOR_SIDE_ORDINAL); sideVertexOrdinals.push_back(vertexOrdinalInSpatialCell); } else { for (int spatialSideOrdinal=0; spatialSideOrdinal < numSpatialSides; spatialSideOrdinal++) { CellTopoPtr sideTopo = spatialCellTopo->getSide(spatialSideOrdinal); for (unsigned sideVertexOrdinal = 0; sideVertexOrdinal < sideTopo->getNodeCount(); sideVertexOrdinal++) { unsigned spatialVertexOrdinal = spatialCellTopo->getNodeMap(sideDim, spatialSideOrdinal, sideVertexOrdinal); if (spatialVertexOrdinal == vertexOrdinalInSpatialCell) { spatialSidesForVertex.push_back(spatialSideOrdinal); sideVertexOrdinals.push_back(sideVertexOrdinal); break; // this is the only match we should find on this side } } } if (spatialSidesForVertex.size() == 0) { cout << "ERROR: no spatial side for vertex found during singleton BC imposition.\n"; TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "no spatial side for vertex found during singleton BC imposition"); } } for (int i=0; i<spatialSidesForVertex.size(); i++) { unsigned spatialSideOrdinal = spatialSidesForVertex[i]; unsigned vertexOrdinalInSide = sideVertexOrdinals[i]; unsigned sideForImposition; BasisPtr spatialBasis, temporalBasis, spaceTimeBasis, basisForImposition; if (!spaceTime) { spatialBasis = trialOrderingPtr->getBasis(trialID,spatialSideOrdinal); sideForImposition = spatialSideOrdinal; } else { unsigned spaceTimeSideOrdinal; if (!_mesh->bilinearForm()->isFluxOrTrace(trialID)) { spaceTimeSideOrdinal = VOLUME_INTERIOR_SIDE_ORDINAL; } else { spaceTimeSideOrdinal = cellTopo->getSpatialSideOrdinal(spatialSideOrdinal); } spaceTimeBasis = trialOrderingPtr->getBasis(trialID,spaceTimeSideOrdinal); sideForImposition = spaceTimeSideOrdinal; TensorBasis<>* tensorBasis = dynamic_cast<TensorBasis<>*>(spaceTimeBasis.get()); TEUCHOS_TEST_FOR_EXCEPTION(!tensorBasis, std::invalid_argument, "space-time basis must be a subclass of TensorBasis"); if (tensorBasis) { spatialBasis = tensorBasis->getSpatialBasis(); temporalBasis = tensorBasis->getTemporalBasis(); } } bool constantSpatialBasis = false; // upgrade bases to continuous ones of the same cardinality, if they are discontinuous. if (spatialBasis->getDegree() == 0) { constantSpatialBasis = true; } else if ((spatialBasis->functionSpace() == Camellia::FUNCTION_SPACE_HVOL) || (spatialBasis->functionSpace() == Camellia::FUNCTION_SPACE_HVOL_DISC)) { spatialBasis = BasisFactory::basisFactory()->getBasis(spatialBasis->getDegree(), spatialBasis->domainTopology(), Camellia::FUNCTION_SPACE_HGRAD); } else if (Camellia::functionSpaceIsDiscontinuous(spatialBasis->functionSpace())) { Camellia::EFunctionSpace fsContinuous = Camellia::continuousSpaceForDiscontinuous((spatialBasis->functionSpace())); spatialBasis = BasisFactory::basisFactory()->getBasis(spatialBasis->getDegree(), spatialBasis->domainTopology(), fsContinuous, Camellia::FUNCTION_SPACE_HGRAD); } if (temporalBasis.get()) { if ((temporalBasis->functionSpace() == Camellia::FUNCTION_SPACE_HVOL) || (temporalBasis->functionSpace() == Camellia::FUNCTION_SPACE_HVOL_DISC)) { temporalBasis = BasisFactory::basisFactory()->getBasis(temporalBasis->getDegree(), temporalBasis->domainTopology(), Camellia::FUNCTION_SPACE_HGRAD); } else if (Camellia::functionSpaceIsDiscontinuous(temporalBasis->functionSpace())) { Camellia::EFunctionSpace fsContinuous = Camellia::continuousSpaceForDiscontinuous((temporalBasis->functionSpace())); temporalBasis = BasisFactory::basisFactory()->getBasis(temporalBasis->getDegree(), temporalBasis->domainTopology(), fsContinuous, Camellia::FUNCTION_SPACE_HGRAD); } } if (spaceTime) { if (constantSpatialBasis) { // then use the original basis for imposition basisForImposition = spaceTimeBasis; } else { vector<int> H1Orders = {spatialBasis->getDegree(),temporalBasis->getDegree()}; spaceTimeBasis = BasisFactory::basisFactory()->getBasis(H1Orders, spaceTimeBasis->domainTopology(), spatialBasis->functionSpace(), temporalBasis->functionSpace()); basisForImposition = spaceTimeBasis; } } else { basisForImposition = spatialBasis; } vector<int> spatialDofOrdinalsForVertex = constantSpatialBasis ? vector<int>{0} : spatialBasis->dofOrdinalsForVertex(vertexOrdinalInSide); if (spatialDofOrdinalsForVertex.size() != 1) { cout << "ERROR: spatialDofOrdinalsForVertex.size() != 1 during singleton BC imposition.\n"; TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "spatialDofOrdinalsForVertex.size() != 1 during singleton BC imposition"); } int spatialDofOrdinalForVertex = spatialDofOrdinalsForVertex[0]; vector<int> basisDofOrdinals; if (!spaceTime) { basisDofOrdinals.push_back(spatialDofOrdinalForVertex); } else { int temporalBasisCardinality = temporalBasis->getCardinality(); TensorBasis<>* tensorBasis = dynamic_cast<TensorBasis<>*>(spaceTimeBasis.get()); for (int temporalBasisOrdinal=0; temporalBasisOrdinal<temporalBasisCardinality; temporalBasisOrdinal++) { basisDofOrdinals.push_back(tensorBasis->getDofOrdinalFromComponentDofOrdinals({spatialDofOrdinalForVertex, temporalBasisOrdinal})); } } for (int dofOrdinal : basisDofOrdinals) { FieldContainer<double> basisCoefficients(basisForImposition->getCardinality()); basisCoefficients[dofOrdinal] = 1.0; FieldContainer<double> globalCoefficients; FieldContainer<GlobalIndexType> globalDofIndices; dofInterpreter->interpretLocalBasisCoefficients(cellID, trialID, sideForImposition, basisCoefficients, globalCoefficients, globalDofIndices); double tol = 1e-14; int nonzeroEntryOrdinal = -1; for (int fieldOrdinal=0; fieldOrdinal < globalCoefficients.size(); fieldOrdinal++) { if (abs(globalCoefficients[fieldOrdinal]) > tol) { if (nonzeroEntryOrdinal != -1) { // previous nonzero entry found; this is a problem--it means we have multiple global coefficients that depend on this vertex // (could happen if user specified a hanging node) cout << "Error: vertex for single-point imposition has multiple global degrees of freedom.\n"; TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Error: vertex for single-point imposition has multiple global degrees of freedom."); } // nonzero entry: store the fact, and impose the constraint nonzeroEntryOrdinal = fieldOrdinal; bool isRankLocal = dofInterpreter->isLocallyOwnedGlobalDofIndex(globalDofIndices[fieldOrdinal]); if (isRankLocal) { dofIndexToValue[globalDofIndices[fieldOrdinal]] = bc.valueForSinglePointBC(trialID) * globalCoefficients[fieldOrdinal]; } else { cout << "ERROR: global dof index for single-point BC is not locally owned.\n"; TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "ERROR: global dof index for single-point BC is not locally owned"); } } } } } } } }
bool MeshTestUtility::determineRefTestPointsForNeighbors(MeshTopologyViewPtr meshTopo, CellPtr fineCell, unsigned int sideOrdinal, FieldContainer<double> &fineSideRefPoints, FieldContainer<double> &fineCellRefPoints, FieldContainer<double> &coarseSideRefPoints, FieldContainer<double> &coarseCellRefPoints) { unsigned spaceDim = meshTopo->getDimension(); unsigned sideDim = spaceDim - 1; if (spaceDim == 1) { fineSideRefPoints.resize(0,0); coarseSideRefPoints.resize(0,0); fineCellRefPoints.resize(1,1); coarseCellRefPoints.resize(1,1); FieldContainer<double> lineRefNodes(2,1); CellTopoPtr line = CellTopology::line(); CamelliaCellTools::refCellNodesForTopology(lineRefNodes, line); fineCellRefPoints[0] = lineRefNodes[sideOrdinal]; unsigned neighborSideOrdinal = fineCell->getNeighborInfo(sideOrdinal, meshTopo).second; if (neighborSideOrdinal != -1) { coarseCellRefPoints[0] = lineRefNodes[neighborSideOrdinal]; return true; } else { return false; } } pair<GlobalIndexType, unsigned> neighborInfo = fineCell->getNeighborInfo(sideOrdinal, meshTopo); if (neighborInfo.first == -1) { // boundary return false; } CellPtr neighborCell = meshTopo->getCell(neighborInfo.first); if (neighborCell->isParent(meshTopo)) { return false; // fineCell isn't the finer of the two... } CellTopoPtr fineSideTopo = fineCell->topology()->getSubcell(sideDim, sideOrdinal); CubatureFactory cubFactory; int cubDegree = 4; // fairly arbitrary choice: enough to get a decent number of test points... Teuchos::RCP<Cubature<double> > fineSideTopoCub = cubFactory.create(fineSideTopo, cubDegree); int numCubPoints = fineSideTopoCub->getNumPoints(); FieldContainer<double> cubPoints(numCubPoints, sideDim); FieldContainer<double> cubWeights(numCubPoints); // we neglect these... fineSideTopoCub->getCubature(cubPoints, cubWeights); FieldContainer<double> sideRefCellNodes(fineSideTopo->getNodeCount(),sideDim); CamelliaCellTools::refCellNodesForTopology(sideRefCellNodes, fineSideTopo); int numTestPoints = numCubPoints + fineSideTopo->getNodeCount(); FieldContainer<double> testPoints(numTestPoints, sideDim); for (int ptOrdinal=0; ptOrdinal<testPoints.dimension(0); ptOrdinal++) { if (ptOrdinal<fineSideTopo->getNodeCount()) { for (int d=0; d<sideDim; d++) { testPoints(ptOrdinal,d) = sideRefCellNodes(ptOrdinal,d); } } else { for (int d=0; d<sideDim; d++) { testPoints(ptOrdinal,d) = cubPoints(ptOrdinal-fineSideTopo->getNodeCount(),d); } } } fineSideRefPoints = testPoints; fineCellRefPoints.resize(numTestPoints, spaceDim); CamelliaCellTools::mapToReferenceSubcell(fineCellRefPoints, testPoints, sideDim, sideOrdinal, fineCell->topology()); CellTopoPtr coarseSideTopo = neighborCell->topology()->getSubcell(sideDim, neighborInfo.second); unsigned fineSideAncestorPermutation = fineCell->ancestralPermutationForSubcell(sideDim, sideOrdinal, meshTopo); unsigned coarseSidePermutation = neighborCell->subcellPermutation(sideDim, neighborInfo.second); unsigned coarseSideAncestorPermutationInverse = CamelliaCellTools::permutationInverse(coarseSideTopo, coarseSidePermutation); unsigned composedPermutation = CamelliaCellTools::permutationComposition(coarseSideTopo, coarseSideAncestorPermutationInverse, fineSideAncestorPermutation); // goes from coarse ordering to fine. RefinementBranch fineRefBranch = fineCell->refinementBranchForSide(sideOrdinal, meshTopo); FieldContainer<double> fineSideNodes(fineSideTopo->getNodeCount(), sideDim); // relative to the ancestral cell whose neighbor is compatible if (fineRefBranch.size() == 0) { CamelliaCellTools::refCellNodesForTopology(fineSideNodes, coarseSideTopo, composedPermutation); } else { FieldContainer<double> ancestralSideNodes(coarseSideTopo->getNodeCount(), sideDim); CamelliaCellTools::refCellNodesForTopology(ancestralSideNodes, coarseSideTopo, composedPermutation); RefinementBranch fineSideRefBranch = RefinementPattern::sideRefinementBranch(fineRefBranch, sideOrdinal); fineSideNodes = RefinementPattern::descendantNodes(fineSideRefBranch, ancestralSideNodes); } BasisCachePtr sideTopoCache = Teuchos::rcp( new BasisCache(fineSideTopo, 1, false) ); sideTopoCache->setRefCellPoints(testPoints); // add cell dimension fineSideNodes.resize(1,fineSideNodes.dimension(0), fineSideNodes.dimension(1)); sideTopoCache->setPhysicalCellNodes(fineSideNodes, vector<GlobalIndexType>(), false); coarseSideRefPoints = sideTopoCache->getPhysicalCubaturePoints(); // strip off the cell dimension: coarseSideRefPoints.resize(coarseSideRefPoints.dimension(1),coarseSideRefPoints.dimension(2)); coarseCellRefPoints.resize(numTestPoints,spaceDim); CamelliaCellTools::mapToReferenceSubcell(coarseCellRefPoints, coarseSideRefPoints, sideDim, neighborInfo.second, neighborCell->topology()); return true; // containers filled.... }
bool MeshTransformationFunction::mapRefCellPointsUsingExactGeometry(FieldContainer<double> &cellPoints, const FieldContainer<double> &refCellPoints, GlobalIndexType cellID) { // returns true if the MeshTransformationFunction handles this cell, false otherwise // if true, then populates cellPoints if (_cellTransforms.find(cellID) == _cellTransforms.end()) { return false; } // cout << "refCellPoints in mapRefCellPointsUsingExactGeometry():\n" << refCellPoints; // // cout << "cellPoints prior to mapRefCellPointsUsingExactGeometry():\n" << cellPoints; int numPoints = refCellPoints.dimension(0); int spaceDim = refCellPoints.dimension(1); CellTopoPtr cellTopo = _mesh->getElementType(cellID)->cellTopoPtr; bool spaceTime = false; CellTopoPtr spaceTimeTopo; FieldContainer<double> refCellPointsSpaceTime, refCellPointsSpace; double time0, time1; if (cellTopo->getTensorialDegree() > 0) { spaceDim = spaceDim - 1; spaceTime = true; spaceTimeTopo = cellTopo; cellTopo = CellTopology::cellTopology(cellTopo->getShardsTopology(), cellTopo->getTensorialDegree() - 1); refCellPointsSpaceTime = refCellPoints; // copy out the spatial points: refCellPointsSpace.resize(numPoints, spaceDim); for (int ptOrdinal=0; ptOrdinal<numPoints; ptOrdinal++) { for (int d=0; d<spaceDim; d++) { refCellPointsSpace(ptOrdinal,d) = refCellPointsSpaceTime(ptOrdinal,d); } } // determine the temporal bounds: unsigned sideOrdinal_t0 = spaceTimeTopo->getTemporalSideOrdinal(0); unsigned sideOrdinal_t1 = spaceTimeTopo->getTemporalSideOrdinal(1); unsigned sampleVertexOrdinal_t0 = spaceTimeTopo->getNodeMap(spaceDim, sideOrdinal_t0, 0); unsigned sampleVertexOrdinal_t1 = spaceTimeTopo->getNodeMap(spaceDim, sideOrdinal_t1, 0); CellPtr cell = _mesh->getTopology()->getCell(cellID); IndexType sampleVertexIndex_t0 = cell->vertices()[sampleVertexOrdinal_t0]; IndexType sampleVertexIndex_t1 = cell->vertices()[sampleVertexOrdinal_t1]; time0 = _mesh->getTopology()->getVertex(sampleVertexIndex_t0)[spaceDim]; time1 = _mesh->getTopology()->getVertex(sampleVertexIndex_t1)[spaceDim]; } else { refCellPointsSpace = refCellPoints; // would be possible to avoid this copy by e.g. using a pointer in the call to mapToPhysicalFrame() below } if (cellTopo->getKey().first == shards::Quadrilateral<4>::key) { TEUCHOS_TEST_FOR_EXCEPTION(spaceDim != 2, std::invalid_argument, "points must be in 2D for the quad!"); FieldContainer<double> parametricPoints(numPoints,spaceDim); // map to (t1,t2) space int whichCell = 0; CamelliaCellTools::mapToPhysicalFrame(parametricPoints,refCellPointsSpace, ParametricSurface::parametricQuadNodes(), cellTopo,whichCell); // cout << "parametricPoints in mapRefCellPointsUsingExactGeometry():\n" << parametricPoints; // for space-time, this is a bit fragile. Would be better to do something manually in terms of the first temporal side... // (one could do this in Mesh or MeshTopology, and that would be OK) vector< ParametricCurvePtr > edgeFunctions = _mesh->parametricEdgesForCell(cellID); ParametricSurfacePtr interpolant = ParametricSurface::transfiniteInterpolant(edgeFunctions); for (int ptIndex=0; ptIndex<numPoints; ptIndex++) { double t1 = parametricPoints(ptIndex,0); double t2 = parametricPoints(ptIndex,1); double x,y; // transfinite interpolation: interpolant->value(t1, t2, x, y); cellPoints(ptIndex,0) = x; cellPoints(ptIndex,1) = y; if (spaceTime) { // per our assumptions on mesh transformations, we do a linear transform in time dimension double t_reference = refCellPoints(ptIndex,2); // goes from -1 to 1 double t_physical = time0 + (t_reference + 1.0) * (time1-time0) / 2.0; cellPoints(ptIndex,2) = t_physical; } } } else { // TODO: work out what to do for triangles (or perhaps even a general polygon) TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unhandled cell type"); } // cout << "cellPoints after to mapRefCellPointsUsingExactGeometry():\n" << cellPoints; return true; }
void ParametricSurface::basisWeightsForProjectedInterpolant(FieldContainer<double> &basisCoefficients, VectorBasisPtr basis, MeshPtr mesh, int cellID) { vector< ParametricCurvePtr > curves = mesh->parametricEdgesForCell(cellID); Teuchos::RCP<TransfiniteInterpolatingSurface> exactSurface = Teuchos::rcp( new TransfiniteInterpolatingSurface(curves) ); exactSurface->setNeglectVertices(false); FieldContainer<double> edgeInterpolationCoefficients(basis->getCardinality()); basisWeightsForEdgeInterpolant(edgeInterpolationCoefficients, basis, mesh, cellID); set<int> edgeFieldIndices = BasisFactory::basisFactory()->sideFieldIndices(basis,true); // true: include vertex dofs TFunctionPtr<double> edgeInterpolant = Teuchos::rcp( new BasisSumFunction(basis, edgeInterpolationCoefficients) ); IPPtr L2 = Teuchos::rcp( new IP ); // we assume that basis is a vector HGRAD basis VarFactoryPtr vf = VarFactory::varFactory(); VarPtr v = vf->testVar("v", VECTOR_HGRAD); L2->addTerm(v); IPPtr H1 = Teuchos::rcp( new IP ); // H1->addTerm(v); // experiment: seminorm is a norm when the edge dofs are excluded--and this is what LD does H1->addTerm(v->grad()); int maxTestDegree = mesh->getElementType(cellID)->testOrderPtr->maxBasisDegree(); TEUCHOS_TEST_FOR_EXCEPTION(maxTestDegree < 1, std::invalid_argument, "Constant test spaces unsupported."); int cubatureDegree = std::max(maxTestDegree*2,15); // chosen to match that used in edge projection. FieldContainer<double> physicalCellNodes; CellTopoPtr cellTopo = mesh->getElementType(cellID)->cellTopoPtr; if (cellTopo->getDimension() == 2) { physicalCellNodes = mesh->physicalCellNodesForCell(cellID); } if ((cellTopo->getDimension() == 3) && (cellTopo->getTensorialDegree() > 0)) { // then we interpret this as space-time, and we just treat the first temporal side: unsigned temporalSideOrdinal = cellTopo->getTemporalSideOrdinal(0); FieldContainer<double> spaceTimePhysicalNodes = mesh->physicalCellNodesForCell(cellID); int sideDim = cellTopo->getDimension() - 1; int nodeCount = cellTopo->getNodeCount(sideDim, temporalSideOrdinal); physicalCellNodes.resize(1,nodeCount,sideDim); for (int node=0; node<nodeCount; node++) { int spaceTimeNode = cellTopo->getNodeMap(sideDim, temporalSideOrdinal, node); for (int d=0; d<sideDim; d++) { physicalCellNodes(0,node,d) = spaceTimePhysicalNodes(0,spaceTimeNode,d); } } // replace space-time cell topology with the purely spatial one: cellTopo = cellTopo->getSide(temporalSideOrdinal); } BasisCachePtr basisCache = BasisCache::basisCacheForCellTopology(cellTopo, cubatureDegree, physicalCellNodes); // project, skipping edgeNodeFieldIndices: Projector<double>::projectFunctionOntoBasis(basisCoefficients, TFunctionPtr<double>(exactSurface)-edgeInterpolant, basis, basisCache, H1, v, edgeFieldIndices); basisCoefficients.resize(basis->getCardinality()); // get rid of dummy numCells dimension // add the two sets of basis coefficients together for (int i=0; i<edgeInterpolationCoefficients.size(); i++) { basisCoefficients[i] += edgeInterpolationCoefficients[i]; } }
bool MeshTopologyTests::testEntityConstraints() { bool success = true; // make two simple meshes MeshTopologyPtr mesh2D = makeRectMesh(0.0, 0.0, 2.0, 1.0, 2, 1); MeshTopologyPtr mesh3D = makeHexMesh(0.0, 0.0, 0.0, 2.0, 4.0, 3.0, 2, 2, 1); unsigned vertexDim = 0; unsigned edgeDim = 1; unsigned faceDim = 2; // first, check that unconstrained edges and faces are unconstrained set< unsigned > boundaryEdges; set< unsigned > internalEdges; for (unsigned cellIndex=0; cellIndex<mesh2D->cellCount(); cellIndex++) { CellPtr cell = mesh2D->getCell(cellIndex); unsigned sideCount = cell->getSideCount(); for (unsigned sideOrdinal=0; sideOrdinal<sideCount; sideOrdinal++) { unsigned edgeIndex = cell->entityIndex(edgeDim, sideOrdinal); unsigned numCells = mesh2D->getActiveCellCount(edgeDim,edgeIndex); if (numCells == 1) // boundary edge { boundaryEdges.insert(edgeIndex); } else if (numCells == 2) { internalEdges.insert(edgeIndex); } else { success = false; cout << "testEntityConstraints: In initial 2D mesh, edge " << edgeIndex << " has active cell count of " << numCells << ".\n"; } } } if (internalEdges.size() != 1) { success = false; cout << "testEntityConstraints: In initial 2D mesh, there are " << internalEdges.size() << " internal edges (expected 1).\n"; } for (set<unsigned>::iterator edgeIt=internalEdges.begin(); edgeIt != internalEdges.end(); edgeIt++) { unsigned edgeIndex = *edgeIt; unsigned constrainingEntityIndex = mesh2D->getConstrainingEntity(edgeDim,edgeIndex).first; if (constrainingEntityIndex != edgeIndex) { success = false; cout << "testEntityConstraints: In initial 2D mesh, internal edge is constrained by a different edge.\n"; } } set<unsigned> boundaryFaces; set<unsigned> internalFaces; map<unsigned, vector<unsigned> > faceToEdges; for (unsigned cellIndex=0; cellIndex<mesh3D->cellCount(); cellIndex++) { CellPtr cell = mesh3D->getCell(cellIndex); unsigned sideCount = cell->getSideCount(); for (unsigned sideOrdinal=0; sideOrdinal<sideCount; sideOrdinal++) { unsigned faceIndex = cell->entityIndex(faceDim, sideOrdinal); unsigned numCells = mesh3D->getActiveCellCount(faceDim,faceIndex); if (numCells == 1) // boundary face { boundaryFaces.insert(faceIndex); } else if (numCells == 2) { internalFaces.insert(faceIndex); } else { success = false; cout << "testEntityConstraints: In initial 3D mesh, face " << faceIndex << " has active cell count of " << numCells << ".\n"; } if (faceToEdges.find(faceIndex) == faceToEdges.end()) { CellTopoPtr faceTopo = cell->topology()->getSubcell(faceDim, sideOrdinal); unsigned numEdges = faceTopo->getSubcellCount(edgeDim); vector<unsigned> edgeIndices(numEdges); for (unsigned edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++) { edgeIndices[edgeOrdinal] = mesh3D->getFaceEdgeIndex(faceIndex, edgeOrdinal); } } } } if (internalFaces.size() != 4) { success = false; cout << "testEntityConstraints: In initial 3D mesh, there are " << internalFaces.size() << " internal faces (expected 4).\n"; } for (set<unsigned>::iterator faceIt=internalFaces.begin(); faceIt != internalFaces.end(); faceIt++) { unsigned faceIndex = *faceIt; unsigned constrainingEntityIndex = mesh3D->getConstrainingEntity(faceDim,faceIndex).first; if (constrainingEntityIndex != faceIndex) { success = false; cout << "testEntityConstraints: In initial 3D mesh, internal face is constrained by a different face.\n"; } } // now, make a single refinement in each mesh: unsigned cellToRefine2D = 0, cellToRefine3D = 3; mesh2D->refineCell(cellToRefine2D, RefinementPattern::regularRefinementPatternQuad(), mesh2D->cellCount()); mesh3D->refineCell(cellToRefine3D, RefinementPattern::regularRefinementPatternHexahedron(), mesh3D->cellCount()); // printMeshInfo(mesh2D); // figure out which faces/edges were refined and add the corresponding map<unsigned,pair<IndexType,unsigned> > expectedEdgeConstraints2D; set<unsigned> refinedEdges; for (set<unsigned>::iterator edgeIt=boundaryEdges.begin(); edgeIt != boundaryEdges.end(); edgeIt++) { set<unsigned> children = mesh2D->getChildEntitiesSet(edgeDim, *edgeIt); if (children.size() > 0) { refinedEdges.insert(*edgeIt); boundaryEdges.insert(children.begin(), children.end()); } } for (set<unsigned>::iterator edgeIt=internalEdges.begin(); edgeIt != internalEdges.end(); edgeIt++) { set<unsigned> children = mesh2D->getChildEntitiesSet(edgeDim, *edgeIt); if (children.size() > 0) { refinedEdges.insert(*edgeIt); internalEdges.insert(children.begin(), children.end()); for (set<unsigned>::iterator childIt = children.begin(); childIt != children.end(); childIt++) { unsigned childIndex = *childIt; expectedEdgeConstraints2D[childIndex] = make_pair(*edgeIt, edgeDim); } } } // 1 quad refined: expect 4 refined edges if (refinedEdges.size() != 4) { success = false; cout << "After initial refinement, 2D mesh has " << refinedEdges.size() << " refined edges (expected 4).\n"; } checkConstraints(mesh2D, edgeDim, expectedEdgeConstraints2D); set<unsigned> refinedFaces; map<unsigned,pair<IndexType,unsigned> > expectedFaceConstraints3D; map<unsigned,pair<IndexType,unsigned> > expectedEdgeConstraints3D; for (set<unsigned>::iterator faceIt=boundaryFaces.begin(); faceIt != boundaryFaces.end(); faceIt++) { set<unsigned> children = mesh3D->getChildEntitiesSet(faceDim, *faceIt); if (children.size() > 0) { refinedFaces.insert(*faceIt); boundaryFaces.insert(children.begin(), children.end()); } } for (set<unsigned>::iterator faceIt=internalFaces.begin(); faceIt != internalFaces.end(); faceIt++) { vector<unsigned> children = mesh3D->getChildEntities(faceDim, *faceIt); if (children.size() > 0) { refinedFaces.insert(*faceIt); internalFaces.insert(children.begin(), children.end()); for (unsigned childOrdinal = 0; childOrdinal < children.size(); childOrdinal++) { unsigned childIndex = children[childOrdinal]; expectedFaceConstraints3D[childIndex] = make_pair(*faceIt, faceDim); unsigned numEdges = 4; unsigned internalEdgeCount = 0; // for each child of a quad, we expect to have 2 internal edges for (unsigned edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++) { unsigned edgeIndex = mesh3D->getFaceEdgeIndex(childIndex, edgeOrdinal); unsigned activeCellCount = mesh3D->getActiveCellCount(edgeDim, edgeIndex); if (activeCellCount==2) { internalEdgeCount++; expectedEdgeConstraints3D[edgeIndex] = make_pair(*faceIt, faceDim); } else if (activeCellCount==1) // hanging edge { if (! mesh3D->entityHasParent(edgeDim, edgeIndex)) { cout << "Hanging edge with edgeIndex " << edgeIndex << " (in face " << childIndex << ") does not have a parent edge.\n"; cout << "Edge vertices:\n"; mesh3D->printEntityVertices(edgeDim, edgeIndex); cout << "Face vertices:\n"; mesh3D->printEntityVertices(faceDim, childIndex); success = false; } else { unsigned edgeParentIndex = mesh3D->getEntityParent(edgeDim, edgeIndex); expectedEdgeConstraints3D[edgeIndex] = make_pair(edgeParentIndex, edgeDim); } } else { cout << "Unexpected number of active cells: " << activeCellCount << endl; } } if (internalEdgeCount != 2) { cout << "Expected internalEdgeCount to be 2; was " << internalEdgeCount << endl; success = false; } } } } // 1 hex refined: expect 6 refined faces if (refinedFaces.size() != 6) { success = false; cout << "After initial refinement, 3D mesh has " << refinedFaces.size() << " refined faces (expected 6).\n"; } if (! checkConstraints(mesh3D, faceDim, expectedFaceConstraints3D, "refined 3D mesh") ) { cout << "Failed face constraint check for refined 3D mesh." << endl; success = false; } if (! checkConstraints(mesh3D, edgeDim, expectedEdgeConstraints3D, "refined 3D mesh") ) { cout << "Failed edge constraint check for refined 3D mesh." << endl; success = false; } // now, we refine one of the children of the refined cells in each mesh, to produce a 2-level constraint set<unsigned> edgeChildren2D; set<unsigned> cellsForEdgeChildren2D; for (map<unsigned,pair<IndexType,unsigned> >::iterator edgeConstraint=expectedEdgeConstraints2D.begin(); edgeConstraint != expectedEdgeConstraints2D.end(); edgeConstraint++) { edgeChildren2D.insert(edgeConstraint->first); unsigned cellIndex = mesh2D->getActiveCellIndices(edgeDim, edgeConstraint->first).begin()->first; cellsForEdgeChildren2D.insert(cellIndex); // cout << "cellsForEdgeChildren2D: " << cellIndex << endl; } // one of these has (1,0) as one of its vertices. Let's figure out which one: unsigned vertexIndex; if (! mesh2D->getVertexIndex(makeVertex(1, 0), vertexIndex) ) { cout << "Error: vertex not found.\n"; success = false; } vector< pair<unsigned,unsigned> > cellsForVertex = mesh2D->getActiveCellIndices(vertexDim, vertexIndex); if (cellsForVertex.size() != 2) { cout << "cellsForVertex should have 2 entries; has " << cellsForVertex.size() << endl; success = false; } unsigned childCellForVertex, childCellConstrainedEdge; set<unsigned> childNewlyConstrainingEdges; // the two interior edges that we break for (vector< pair<unsigned,unsigned> >::iterator cellIt=cellsForVertex.begin(); cellIt != cellsForVertex.end(); cellIt++) { // cout << "cellsForVertex: " << cellIt->first << endl; if ( cellsForEdgeChildren2D.find( cellIt->first ) != cellsForEdgeChildren2D.end() ) { // found match childCellForVertex = cellIt->first; // now, figure out which of the "edgeChildren2D" is shared by this cell: CellPtr cell = mesh2D->getCell(childCellForVertex); unsigned numEdges = cell->getSideCount(); for (unsigned edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++) { unsigned edgeIndex = cell->entityIndex(edgeDim, edgeOrdinal); if (edgeChildren2D.find(edgeIndex) != edgeChildren2D.end()) { childCellConstrainedEdge = edgeIndex; } else if ( mesh2D->getActiveCellCount(edgeDim, edgeIndex) == 2 ) { childNewlyConstrainingEdges.insert(edgeIndex); } } } } if (childNewlyConstrainingEdges.size() != 2) { cout << "Expected 2 newly constraining edges after 2nd refinement of 2D mesh, but found " << childNewlyConstrainingEdges.size() << endl; success = false; } // refine the cell that matches (1,0): mesh2D->refineCell(childCellForVertex, RefinementPattern::regularRefinementPatternQuad(), mesh2D->cellCount()); // now, fix the expected edge constraints, then check them... set<unsigned> childEdges = mesh2D->getChildEntitiesSet(edgeDim, childCellConstrainedEdge); if (childEdges.size() != 2) { cout << "Expected 2 child edges, but found " << childEdges.size() << ".\n"; success = false; } for (set<unsigned>::iterator edgeIt = childEdges.begin(); edgeIt != childEdges.end(); edgeIt++) { expectedEdgeConstraints2D[*edgeIt] = expectedEdgeConstraints2D[childCellConstrainedEdge]; } expectedEdgeConstraints2D.erase(childCellConstrainedEdge); for (set<unsigned>::iterator edgeIt = childNewlyConstrainingEdges.begin(); edgeIt != childNewlyConstrainingEdges.end(); edgeIt++) { set<unsigned> newChildEdges = mesh2D->getChildEntitiesSet(edgeDim, *edgeIt); for (set<unsigned>::iterator newEdgeIt = newChildEdges.begin(); newEdgeIt != newChildEdges.end(); newEdgeIt++) { expectedEdgeConstraints2D[*newEdgeIt] = make_pair(*edgeIt,edgeDim); } } if (! checkConstraints(mesh2D, edgeDim, expectedEdgeConstraints2D, "twice-refined 2D mesh") ) { cout << "Failed constraint check for twice-refined 2D mesh." << endl; success = false; } // now, do a second level of refinement for 3D mesh // one of these has (1,2,0) as one of its vertices. Let's figure out which one: if (! mesh3D->getVertexIndex(makeVertex(1, 2, 0), vertexIndex) ) { cout << "Error: vertex not found.\n"; success = false; } cellsForVertex = mesh3D->getActiveCellIndices(vertexDim, vertexIndex); if (cellsForVertex.size() != 4) { cout << "cellsForVertex should have 4 entries; has " << cellsForVertex.size() << endl; success = false; } vector<unsigned> justCellsForVertex; for (vector< pair<unsigned,unsigned> >::iterator entryIt = cellsForVertex.begin(); entryIt != cellsForVertex.end(); entryIt++) { justCellsForVertex.push_back(entryIt->first); } vector<unsigned> childCellIndices = mesh3D->getCell(cellToRefine3D)->getChildIndices(mesh3D); std::sort(childCellIndices.begin(), childCellIndices.end()); vector<unsigned> matches(childCellIndices.size() + cellsForVertex.size()); vector<unsigned>::iterator matchEnd = std::set_intersection(justCellsForVertex.begin(), justCellsForVertex.end(), childCellIndices.begin(), childCellIndices.end(), matches.begin()); matches.resize(matchEnd-matches.begin()); if (matches.size() != 1) { cout << "matches should have exactly one entry, but has " << matches.size(); success = false; } unsigned childCellIndex = matches[0]; CellPtr childCell = mesh3D->getCell(childCellIndex); set<unsigned> childInteriorUnconstrainedFaces; set<unsigned> childInteriorConstrainedFaces; unsigned faceCount = childCell->getSideCount(); for (unsigned faceOrdinal=0; faceOrdinal<faceCount; faceOrdinal++) { unsigned faceIndex = childCell->entityIndex(faceDim, faceOrdinal); if (mesh3D->getActiveCellCount(faceDim, faceIndex) == 1) { // that's an interior constrained face, or a boundary face if (expectedFaceConstraints3D.find(faceIndex) != expectedFaceConstraints3D.end()) { // constrained face childInteriorConstrainedFaces.insert(faceIndex); } } else if (mesh3D->getActiveCellCount(faceDim, faceIndex) == 2) { // an interior unconstrained face childInteriorUnconstrainedFaces.insert(faceIndex); } else { cout << "Error: unexpected active cell count. Expected 1 or 2, but was " << mesh3D->getActiveCellCount(faceDim, faceIndex) << endl; success = false; } } // Camellia::print("childInteriorUnconstrainedFaces", childInteriorUnconstrainedFaces); // Camellia::print("childInteriorConstrainedFaces", childInteriorConstrainedFaces); mesh3D->refineCell(childCellIndex, RefinementPattern::regularRefinementPatternHexahedron(), mesh3D->cellCount()); // update expected face and edge constraints // set<unsigned> edgeConstraintsToDrop; for (set<unsigned>::iterator faceIt=childInteriorConstrainedFaces.begin(); faceIt != childInteriorConstrainedFaces.end(); faceIt++) { unsigned faceIndex = *faceIt; set<unsigned> newChildFaces = mesh3D->getChildEntitiesSet(faceDim, faceIndex); for (set<unsigned>::iterator newChildIt=newChildFaces.begin(); newChildIt != newChildFaces.end(); newChildIt++) { unsigned newChildIndex = *newChildIt; expectedFaceConstraints3D[newChildIndex] = expectedFaceConstraints3D[faceIndex]; // cout << "Expecting two-level face constraint: face " << newChildIndex << " constrained by face " << expectedFaceConstraints3D[newChildIndex].first << endl; } unsigned numEdges = mesh3D->getSubEntityCount(faceDim, faceIndex, edgeDim); set<IndexType> childEdgesOnParentBoundary; for (unsigned edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++) { unsigned edgeIndex = mesh3D->getSubEntityIndex(faceDim, faceIndex, edgeDim, edgeOrdinal); set<unsigned> newChildEdges = mesh3D->getChildEntitiesSet(edgeDim, edgeIndex); for (set<unsigned>::iterator newChildIt=newChildEdges.begin(); newChildIt != newChildEdges.end(); newChildIt++) { unsigned newChildIndex = *newChildIt; expectedEdgeConstraints3D[newChildIndex] = expectedEdgeConstraints3D[edgeIndex]; // cout << "Expecting two-level edge constraint: edge " << newChildIndex << " constrained by "; // cout << typeString(expectedEdgeConstraints3D[newChildIndex].second) << " " << expectedEdgeConstraints3D[newChildIndex].first << endl; childEdgesOnParentBoundary.insert(newChildIndex); // edgeConstraintsToDrop.insert(edgeIndex); } } for (set<unsigned>::iterator newChildIt=newChildFaces.begin(); newChildIt != newChildFaces.end(); newChildIt++) { unsigned newChildFaceIndex = *newChildIt; int numEdges = mesh3D->getSubEntityCount(faceDim, newChildFaceIndex, edgeDim); for (unsigned edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++) { unsigned newChildEdgeIndex = mesh3D->getSubEntityIndex(faceDim, newChildFaceIndex, edgeDim, edgeOrdinal); if (childEdgesOnParentBoundary.find(newChildEdgeIndex) == childEdgesOnParentBoundary.end()) { expectedEdgeConstraints3D[newChildEdgeIndex] = expectedFaceConstraints3D[faceIndex]; } } } expectedFaceConstraints3D.erase(faceIndex); } // for (set<unsigned>::iterator edgeToDropIt=edgeConstraintsToDrop.begin(); edgeToDropIt != edgeConstraintsToDrop.end(); edgeToDropIt++) { // expectedEdgeConstraints3D.erase(*edgeToDropIt); // } for (set<unsigned>::iterator faceIt=childInteriorUnconstrainedFaces.begin(); faceIt != childInteriorUnconstrainedFaces.end(); faceIt++) { unsigned faceIndex = *faceIt; set<unsigned> newChildFaces = mesh3D->getChildEntitiesSet(faceDim, faceIndex); for (set<unsigned>::iterator newChildIt=newChildFaces.begin(); newChildIt != newChildFaces.end(); newChildIt++) { unsigned newChildIndex = *newChildIt; expectedFaceConstraints3D[newChildIndex] = make_pair(faceIndex, faceDim); } expectedFaceConstraints3D.erase(faceIndex); unsigned numEdges = mesh3D->getSubEntityCount(faceDim, faceIndex, edgeDim); set<IndexType> childEdgesOnParentBoundary; for (unsigned edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++) { unsigned edgeIndex = mesh3D->getSubEntityIndex(faceDim, faceIndex, edgeDim, edgeOrdinal); set<unsigned> newChildEdges = mesh3D->getChildEntitiesSet(edgeDim, edgeIndex); for (set<unsigned>::iterator newChildIt=newChildEdges.begin(); newChildIt != newChildEdges.end(); newChildIt++) { unsigned newChildIndex = *newChildIt; if (expectedEdgeConstraints3D.find(newChildIndex) == expectedEdgeConstraints3D.end()) // only impose edge constraint if there is not one already present { expectedEdgeConstraints3D[newChildIndex] = make_pair(edgeIndex,edgeDim); } childEdgesOnParentBoundary.insert(newChildIndex); } } for (set<unsigned>::iterator newChildIt=newChildFaces.begin(); newChildIt != newChildFaces.end(); newChildIt++) { unsigned newChildFaceIndex = *newChildIt; int numEdges = mesh3D->getSubEntityCount(faceDim, newChildFaceIndex, edgeDim); for (unsigned edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++) { unsigned newChildEdgeIndex = mesh3D->getSubEntityIndex(faceDim, newChildFaceIndex, edgeDim, edgeOrdinal); if (childEdgesOnParentBoundary.find(newChildEdgeIndex) == childEdgesOnParentBoundary.end()) { if (expectedEdgeConstraints3D.find(newChildEdgeIndex) == expectedEdgeConstraints3D.end()) // only impose edge constraint if there is not one already present { expectedEdgeConstraints3D[newChildEdgeIndex] = make_pair(faceIndex, faceDim); } } } } } if (! checkConstraints(mesh3D, edgeDim, expectedEdgeConstraints3D, "twice-refined 3D mesh") ) { cout << "Failed edge constraint check for twice-refined 3D mesh." << endl; success = false; } if (! checkConstraints(mesh3D, faceDim, expectedFaceConstraints3D, "twice-refined 3D mesh") ) { cout << "Failed face constraint check for twice-refined 3D mesh." << endl; success = false; } return success; }