int main(int argc, char *argv[]) { Teuchos::GlobalMPISession mpiSession(&argc, &argv,0); int rank=mpiSession.getRank(); int numProcs=mpiSession.getNProc(); int spaceDim = 2; #ifdef HAVE_MPI choice::MpiArgs args( argc, argv ); #else choice::Args args(argc, argv ); #endif int minPolyOrder = args.Input<int>("--minPolyOrder", "L^2 (field) minimum polynomial order",0); int maxPolyOrder = args.Input<int>("--maxPolyOrder", "L^2 (field) maximum polynomial order",1); int minLogElements = args.Input<int>("--minLogElements", "base 2 log of the minimum number of elements in one mesh direction", 0); int maxLogElements = args.Input<int>("--maxLogElements", "base 2 log of the maximum number of elements in one mesh direction", 4); double Re = args.Input<double>("--Re", "Reynolds number", 40); // bool outputStiffnessMatrix = args.Input<bool>("--writeFinalStiffnessToDisk", "write the final stiffness matrix to disk.", false); bool computeMaxConditionNumber = args.Input<bool>("--computeMaxConditionNumber", "compute the maximum Gram matrix condition number for final mesh.", false); int maxIters = args.Input<int>("--maxIters", "maximum number of Newton-Raphson iterations to take to try to match tolerance", 50); double minL2Increment = args.Input<double>("--NRtol", "Newton-Raphson tolerance, L^2 norm of increment", 1e-12); string normChoice = args.Input<string>("--norm", "norm choice: graph, compliantGraph, stokesGraph, or stokesCompliantGraph", "graph"); bool useCondensedSolve = args.Input<bool>("--useCondensedSolve", "use static condensation", true); double dt = args.Input<double>("--timeStep", "time step (0 for none)", 0); double zmcRho = args.Input<double>("--zmcRho", "zero-mean constraint rho (stabilization parameter)", -1); // string replayFile = args.Input<string>("--replayFile", "file with refinement history to replay", ""); // string saveFile = args.Input<string>("--saveReplay", "file to which to save refinement history", ""); args.Process(); int pToAdd = 2; // for optimal test function approximation bool useLineSearch = false; bool computeRelativeErrors = true; // we'll say false when one of the exact solution components is 0 bool useEnrichedTraces = true; // enriched traces are the right choice, mathematically speaking BasisFactory::basisFactory()->setUseEnrichedTraces(useEnrichedTraces); // parse args: bool useTriangles = false, useGraphNorm = false, useCompliantNorm = false, useStokesCompliantNorm = false, useStokesGraphNorm = false; if (normChoice=="graph") { useGraphNorm = true; } else if (normChoice=="compliantGraph") { useCompliantNorm = true; } else if (normChoice=="stokesGraph") { useStokesGraphNorm = true; } else if (normChoice=="stokesCompliantGraph") { useStokesCompliantNorm = true; } else { if (rank==0) cout << "unknown norm choice. Exiting.\n"; exit(-1); } bool artificialTimeStepping = (dt > 0); if (rank == 0) { cout << "pToAdd = " << pToAdd << endl; cout << "useTriangles = " << (useTriangles ? "true" : "false") << "\n"; cout << "norm = " << normChoice << endl; } // define Kovasznay domain: FieldContainer<double> quadPointsKovasznay(4,2); // domain from Cockburn Kanschat for Stokes: quadPointsKovasznay(0,0) = -0.5; // x1 quadPointsKovasznay(0,1) = 0.0; // y1 quadPointsKovasznay(1,0) = 1.5; quadPointsKovasznay(1,1) = 0.0; quadPointsKovasznay(2,0) = 1.5; quadPointsKovasznay(2,1) = 2.0; quadPointsKovasznay(3,0) = -0.5; quadPointsKovasznay(3,1) = 2.0; // Domain from Evans Hughes for Navier-Stokes: // quadPointsKovasznay(0,0) = 0.0; // x1 // quadPointsKovasznay(0,1) = -0.5; // y1 // quadPointsKovasznay(1,0) = 1.0; // quadPointsKovasznay(1,1) = -0.5; // quadPointsKovasznay(2,0) = 1.0; // quadPointsKovasznay(2,1) = 0.5; // quadPointsKovasznay(3,0) = 0.0; // quadPointsKovasznay(3,1) = 0.5; // double Re = 10.0; // Cockburn Kanschat Stokes // double Re = 40.0; // Evans Hughes Navier-Stokes // double Re = 1000.0; string formulationTypeStr = "vgp"; FunctionPtr u1_exact, u2_exact, p_exact; int numCellsFineMesh = 20; // for computing a zero-mean pressure int H1OrderFineMesh = 5; // VGPNavierStokesProblem(double Re, Intrepid::FieldContainer<double> &quadPoints, int horizontalCells, // int verticalCells, int H1Order, int pToAdd, // TFunctionPtr<double> u1_0, TFunctionPtr<double> u2_0, TFunctionPtr<double> f1, TFunctionPtr<double> f2, // bool enrichVelocity = false, bool enhanceFluxes = false) FunctionPtr zero = Function::zero(); VGPNavierStokesProblem zeroProblem = VGPNavierStokesProblem(Re, quadPointsKovasznay, numCellsFineMesh, numCellsFineMesh, H1OrderFineMesh, pToAdd, zero, zero, zero, useCompliantNorm || useStokesCompliantNorm); VarFactoryPtr varFactory = VGPStokesFormulation::vgpVarFactory(); VarPtr u1_vgp = varFactory->fieldVar(VGP_U1_S); VarPtr u2_vgp = varFactory->fieldVar(VGP_U2_S); VarPtr sigma11_vgp = varFactory->fieldVar(VGP_SIGMA11_S); VarPtr sigma12_vgp = varFactory->fieldVar(VGP_SIGMA12_S); VarPtr sigma21_vgp = varFactory->fieldVar(VGP_SIGMA21_S); VarPtr sigma22_vgp = varFactory->fieldVar(VGP_SIGMA22_S); VarPtr p_vgp = varFactory->fieldVar(VGP_P_S); VarPtr v1_vgp = varFactory->testVar(VGP_V1_S, HGRAD); VarPtr v2_vgp = varFactory->testVar(VGP_V2_S, HGRAD); // if (rank==0) { // cout << "bilinear form with zero background flow:\n"; // zeroProblem.bf()->printTrialTestInteractions(); // } VGPStokesFormulation stokesForm(1/Re); NavierStokesFormulation::setKovasznay(Re, zeroProblem.mesh(), u1_exact, u2_exact, p_exact); // if (rank==0) cout << "Stokes bilinearForm: " << stokesForm.bf()->displayString() << endl; map< string, string > convergenceDataForMATLAB; // key: field file name for (int polyOrder = minPolyOrder; polyOrder <= maxPolyOrder; polyOrder++) { int H1Order = polyOrder + 1; int numCells1D = pow(2.0,minLogElements); if (rank==0) { cout << "L^2 order: " << polyOrder << endl; cout << "Re = " << Re << endl; } int kovasznayCubatureEnrichment = 20; // 20 is better than 10 for accurately measuring error on the coarser meshes. vector< VGPNavierStokesProblem > problems; do { VGPNavierStokesProblem problem = VGPNavierStokesProblem(Re,quadPointsKovasznay, numCells1D,numCells1D, H1Order, pToAdd, u1_exact, u2_exact, p_exact, useCompliantNorm || useStokesCompliantNorm); problem.backgroundFlow()->setCubatureEnrichmentDegree(kovasznayCubatureEnrichment); problem.solutionIncrement()->setCubatureEnrichmentDegree(kovasznayCubatureEnrichment); problem.backgroundFlow()->setZeroMeanConstraintRho(zmcRho); problem.solutionIncrement()->setZeroMeanConstraintRho(zmcRho); FunctionPtr dt_inv; if (artificialTimeStepping) { // // LHS gets u_inc / dt: BFPtr bf = problem.bf(); dt_inv = ParameterFunction::parameterFunction(1.0 / dt); //Teuchos::rcp( new ConstantScalarFunction(1.0 / dt, "\\frac{1}{dt}") ); bf->addTerm(-dt_inv * u1_vgp, v1_vgp); bf->addTerm(-dt_inv * u2_vgp, v2_vgp); problem.setIP( bf->graphNorm() ); // graph norm has changed... } else { dt_inv = Function::zero(); } problems.push_back(problem); if ( useCompliantNorm ) { problem.setIP(problem.vgpNavierStokesFormulation()->scaleCompliantGraphNorm(dt_inv)); } else if (useStokesCompliantNorm) { VGPStokesFormulation stokesForm(1.0); // pretend Re = 1 in the graph norm problem.setIP(stokesForm.scaleCompliantGraphNorm()); } else if (useStokesGraphNorm) { VGPStokesFormulation stokesForm(1.0); // pretend Re = 1 in the graph norm problem.setIP(stokesForm.graphNorm()); } else if (! useGraphNorm ) { // then use the naive: problem.setIP(problem.bf()->naiveNorm(spaceDim)); } if (rank==0) { cout << numCells1D << " x " << numCells1D << ": " << problem.mesh()->numGlobalDofs() << " dofs " << endl; } numCells1D *= 2; } while (pow(2.0,maxLogElements) >= numCells1D); // note that rhs and bilinearForm aren't really going to be right here, since they // involve a background flow which varies over the various problems... HConvergenceStudy study(problems[0].exactSolution(), problems[0].mesh()->bilinearForm(), problems[0].exactSolution()->rhs(), problems[0].backgroundFlow()->bc(), problems[0].bf()->graphNorm(), minLogElements, maxLogElements, H1Order, pToAdd, false, useTriangles, false); study.setReportRelativeErrors(computeRelativeErrors); study.setCubatureDegreeForExact(kovasznayCubatureEnrichment); vector< SolutionPtr > solutions; numCells1D = pow(2.0,minLogElements); for (vector< VGPNavierStokesProblem >::iterator problem = problems.begin(); problem != problems.end(); problem++) { SolutionPtr solnIncrement = problem->solutionIncrement(); FunctionPtr u1_incr = Function::solution(u1_vgp, solnIncrement); FunctionPtr u2_incr = Function::solution(u2_vgp, solnIncrement); FunctionPtr sigma11_incr = Function::solution(sigma11_vgp, solnIncrement); FunctionPtr sigma12_incr = Function::solution(sigma12_vgp, solnIncrement); FunctionPtr sigma21_incr = Function::solution(sigma21_vgp, solnIncrement); FunctionPtr sigma22_incr = Function::solution(sigma22_vgp, solnIncrement); FunctionPtr p_incr = Function::solution(p_vgp, solnIncrement); // LinearTermPtr rhsLT = problem->backgroundFlow()->rhs()->linearTerm(); // if (rank==0) cout << "bilinearForm: " << problems[0].mesh()->bilinearForm()->displayString() << endl; // if (rank==0) cout << "RHS: " << rhsLT->displayString() << endl; FunctionPtr l2_incr = u1_incr * u1_incr + u2_incr * u2_incr + p_incr * p_incr + sigma11_incr * sigma11_incr + sigma12_incr * sigma12_incr + sigma21_incr * sigma21_incr + sigma22_incr * sigma22_incr; double weight = 1.0; do { weight = problem->iterate(useLineSearch, useCondensedSolve); LinearTermPtr rhsLT = problem->backgroundFlow()->rhs()->linearTerm(); RieszRep rieszRep(problem->backgroundFlow()->mesh(), problem->backgroundFlow()->ip(), rhsLT); rieszRep.computeRieszRep(); double costFunction = rieszRep.getNorm(); double incr_norm = sqrt(l2_incr->integrate(problem->mesh())); if (rank==0) { cout << setprecision(6) << scientific; cout << "\x1B[2K"; // Erase the entire current line. cout << "\x1B[0E"; // Move to the beginning of the current line. cout << "Iteration: " << problem->iterationCount() << "; L^2(incr) = " << incr_norm; flush(cout); // cout << setprecision(6) << scientific; // cout << "Took " << weight << "-weighted step for " << numCells1D; // cout << " x " << numCells1D << " mesh: " << problem->iterationCount(); // cout << setprecision(6) << fixed; // cout << " iterations; cost function " << costFunction << endl; } } while ((sqrt(l2_incr->integrate(problem->mesh())) > minL2Increment ) && (problem->iterationCount() < maxIters) && (weight != 0)); if (rank==0) cout << endl; solutions.push_back( problem->backgroundFlow() ); // set the IP to the naive norm for clearer comparison with the best approximation energy error // problem->backgroundFlow()->setIP(problem->bf()->naiveNorm()); // double energyError = problem->backgroundFlow()->energyErrorTotal(); // if (rank==0) { // cout << setprecision(6) << fixed; // cout << numCells1D << " x " << numCells1D << ": " << problem->iterationCount(); // cout << " iterations; actual energy error " << energyError << endl; // } numCells1D *= 2; } study.setSolutions(solutions); for (int i=0; i<=maxLogElements-minLogElements; i++) { SolutionPtr bestApproximation = study.bestApproximations()[i]; VGPNavierStokesFormulation nsFormBest = VGPNavierStokesFormulation(Re, bestApproximation); SpatialFilterPtr entireBoundary = Teuchos::rcp( new SpatialFilterUnfiltered ); // SpatialFilterUnfiltered returns true everywhere Teuchos::RCP<ExactSolution<double>> exact = nsFormBest.exactSolution(u1_exact, u2_exact, p_exact, entireBoundary); // bestApproximation->setIP( nsFormBest.bf()->naiveNorm() ); // bestApproximation->setRHS( exact->rhs() ); // use backgroundFlow's IP so that they're comparable IPPtr ip = problems[i].backgroundFlow()->ip(); LinearTermPtr rhsLT = exact->rhs()->linearTerm(); RieszRep rieszRep(bestApproximation->mesh(), ip, rhsLT); rieszRep.computeRieszRep(); double bestCostFunction = rieszRep.getNorm(); if (rank==0) cout << "best energy error (measured according to the actual solution's test space IP): " << bestCostFunction << endl; } map< int, double > energyNormWeights; if (useCompliantNorm) { energyNormWeights[u1_vgp->ID()] = 1.0; // should be 1/h energyNormWeights[u2_vgp->ID()] = 1.0; // should be 1/h energyNormWeights[sigma11_vgp->ID()] = Re; // 1/mu energyNormWeights[sigma12_vgp->ID()] = Re; // 1/mu energyNormWeights[sigma21_vgp->ID()] = Re; // 1/mu energyNormWeights[sigma22_vgp->ID()] = Re; // 1/mu if (Re < 1) // assuming we're using the experimental small Re thing { energyNormWeights[p_vgp->ID()] = Re; } else { energyNormWeights[p_vgp->ID()] = 1.0; } } else { energyNormWeights[u1_vgp->ID()] = 1.0; energyNormWeights[u2_vgp->ID()] = 1.0; energyNormWeights[sigma11_vgp->ID()] = 1.0; energyNormWeights[sigma12_vgp->ID()] = 1.0; energyNormWeights[sigma21_vgp->ID()] = 1.0; energyNormWeights[sigma22_vgp->ID()] = 1.0; energyNormWeights[p_vgp->ID()] = 1.0; } vector<double> bestEnergy = study.weightedL2Error(energyNormWeights,true); vector<double> solnEnergy = study.weightedL2Error(energyNormWeights,false); map<int, double> velocityWeights; velocityWeights[u1_vgp->ID()] = 1.0; velocityWeights[u2_vgp->ID()] = 1.0; vector<double> bestVelocityError = study.weightedL2Error(velocityWeights,true); vector<double> solnVelocityError = study.weightedL2Error(velocityWeights,false); map<int, double> pressureWeight; pressureWeight[p_vgp->ID()] = 1.0; vector<double> bestPressureError = study.weightedL2Error(pressureWeight,true); vector<double> solnPressureError = study.weightedL2Error(pressureWeight,false); if (rank==0) { cout << setw(25); cout << "Solution Energy Error:" << setw(25) << "Best Energy Error:" << endl; cout << scientific << setprecision(1); for (int i=0; i<bestEnergy.size(); i++) { cout << setw(25) << solnEnergy[i] << setw(25) << bestEnergy[i] << endl; } cout << setw(25); cout << "Solution Velocity Error:" << setw(25) << "Best Velocity Error:" << endl; cout << scientific << setprecision(1); for (int i=0; i<bestEnergy.size(); i++) { cout << setw(25) << solnVelocityError[i] << setw(25) << bestVelocityError[i] << endl; } cout << setw(25); cout << "Solution Pressure Error:" << setw(25) << "Best Pressure Error:" << endl; cout << scientific << setprecision(1); for (int i=0; i<bestEnergy.size(); i++) { cout << setw(25) << solnPressureError[i] << setw(25) << bestPressureError[i] << endl; } vector< string > tableHeaders; vector< vector<double> > dataTable; vector< double > meshWidths; for (int i=minLogElements; i<=maxLogElements; i++) { double width = pow(2.0,i); meshWidths.push_back(width); } tableHeaders.push_back("mesh_width"); dataTable.push_back(meshWidths); tableHeaders.push_back("soln_energy_error"); dataTable.push_back(solnEnergy); tableHeaders.push_back("best_energy_error"); dataTable.push_back(bestEnergy); tableHeaders.push_back("soln_velocity_error"); dataTable.push_back(solnVelocityError); tableHeaders.push_back("best_velocity_error"); dataTable.push_back(bestVelocityError); tableHeaders.push_back("soln_pressure_error"); dataTable.push_back(solnPressureError); tableHeaders.push_back("best_pressure_error"); dataTable.push_back(bestPressureError); ostringstream fileNameStream; fileNameStream << "nsStudy_Re" << Re << "k" << polyOrder << "_results.dat"; DataIO::outputTableToFile(tableHeaders,dataTable,fileNameStream.str()); } if (rank == 0) { cout << study.TeXErrorRateTable(); vector<int> primaryVariables; stokesForm.primaryTrialIDs(primaryVariables); vector<int> fieldIDs,traceIDs; vector<string> fieldFileNames; stokesForm.trialIDs(fieldIDs,traceIDs,fieldFileNames); cout << "******** Best Approximation comparison: ********\n"; cout << study.TeXBestApproximationComparisonTable(primaryVariables); ostringstream filePathPrefix; filePathPrefix << "navierStokes/" << formulationTypeStr << "_p" << polyOrder << "_velpressure"; study.TeXBestApproximationComparisonTable(primaryVariables,filePathPrefix.str()); filePathPrefix.str(""); filePathPrefix << "navierStokes/" << formulationTypeStr << "_p" << polyOrder << "_all"; study.TeXBestApproximationComparisonTable(fieldIDs); for (int i=0; i<fieldIDs.size(); i++) { int fieldID = fieldIDs[i]; int traceID = traceIDs[i]; string fieldName = fieldFileNames[i]; ostringstream filePathPrefix; filePathPrefix << "navierStokes/" << fieldName << "_p" << polyOrder; bool writeMATLABplotData = false; study.writeToFiles(filePathPrefix.str(),fieldID,traceID, writeMATLABplotData); } for (int i=0; i<primaryVariables.size(); i++) { string convData = study.convergenceDataMATLAB(primaryVariables[i], minPolyOrder); cout << convData; convergenceDataForMATLAB[fieldFileNames[i]] += convData; } filePathPrefix.str(""); filePathPrefix << "navierStokes/" << formulationTypeStr << "_p" << polyOrder << "_numDofs"; cout << study.TeXNumGlobalDofsTable(); } if (computeMaxConditionNumber) { for (int i=minLogElements; i<=maxLogElements; i++) { SolutionPtr soln = study.getSolution(i); ostringstream fileNameStream; fileNameStream << "nsStudy_maxConditionIPMatrix_" << i << ".dat"; IPPtr ip = Teuchos::rcp( dynamic_cast< IP* >(soln->ip().get()), false ); bool jacobiScalingTrue = true; double maxConditionNumber = MeshUtilities::computeMaxLocalConditionNumber(ip, soln->mesh(), jacobiScalingTrue, fileNameStream.str()); if (rank==0) { cout << "max Gram matrix condition number estimate for logElements " << i << ": " << maxConditionNumber << endl; cout << "putative worst-conditioned Gram matrix written to: " << fileNameStream.str() << "." << endl; } } } } if (rank==0) { ostringstream filePathPrefix; filePathPrefix << "navierStokes/" << formulationTypeStr << "_"; for (map<string,string>::iterator convIt = convergenceDataForMATLAB.begin(); convIt != convergenceDataForMATLAB.end(); convIt++) { string fileName = convIt->first + ".m"; string data = convIt->second; fileName = filePathPrefix.str() + fileName; ofstream fout(fileName.c_str()); fout << data; fout.close(); } } }
int main(int argc, char *argv[]) { int rank = 0; #ifdef HAVE_MPI // TODO: figure out the right thing to do here... // may want to modify argc and argv before we make the following call: Teuchos::GlobalMPISession mpiSession(&argc, &argv,0); rank=mpiSession.getRank(); #else #endif bool useLineSearch = false; int pToAdd = 2; // for optimal test function approximation int pToAddForStreamFunction = 2; double nonlinearStepSize = 1.0; double dt = 0.5; double nonlinearRelativeEnergyTolerance = 0.015; // used to determine convergence of the nonlinear solution // double nonlinearRelativeEnergyTolerance = 0.15; // used to determine convergence of the nonlinear solution double eps = 1.0/64.0; // width of ramp up to 1.0 for top BC; eps == 0 ==> soln not in H1 // epsilon above is chosen to match our initial 16x16 mesh, to avoid quadrature errors. // double eps = 0.0; // John Evans's problem: not in H^1 bool enforceLocalConservation = false; bool enforceOneIrregularity = true; bool reportPerCellErrors = true; bool useMumps = true; int horizontalCells, verticalCells; int maxIters = 50; // for nonlinear steps vector<double> ReValues; // usage: polyOrder [numRefinements] // parse args: if (argc < 6) { cout << "Usage: NavierStokesCavityFlowContinuationFixedMesh fieldPolyOrder hCells vCells energyErrorGoal Re0 [Re1 ...]\n"; return -1; } int polyOrder = atoi(argv[1]); horizontalCells = atoi(argv[2]); verticalCells = atoi(argv[3]); double energyErrorGoal = atof(argv[4]); for (int i=5; i<argc; i++) { ReValues.push_back(atof(argv[i])); } if (rank == 0) { cout << "L^2 order: " << polyOrder << endl; cout << "initial mesh size: " << horizontalCells << " x " << verticalCells << endl; cout << "energy error goal: " << energyErrorGoal << endl; cout << "Reynolds number values for continuation:\n"; for (int i=0; i<ReValues.size(); i++) { cout << ReValues[i] << ", "; } cout << endl; } 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; // define meshes: int H1Order = polyOrder + 1; bool useTriangles = false; bool meshHasTriangles = useTriangles; double minL2Increment = 1e-8; // get variable definitions: VarFactory varFactory = VGPStokesFormulation::vgpVarFactory(); u1 = varFactory.fieldVar(VGP_U1_S); u2 = varFactory.fieldVar(VGP_U2_S); sigma11 = varFactory.fieldVar(VGP_SIGMA11_S); sigma12 = varFactory.fieldVar(VGP_SIGMA12_S); sigma21 = varFactory.fieldVar(VGP_SIGMA21_S); sigma22 = varFactory.fieldVar(VGP_SIGMA22_S); p = varFactory.fieldVar(VGP_P_S); u1hat = varFactory.traceVar(VGP_U1HAT_S); u2hat = varFactory.traceVar(VGP_U2HAT_S); t1n = varFactory.fluxVar(VGP_T1HAT_S); t2n = varFactory.fluxVar(VGP_T2HAT_S); v1 = varFactory.testVar(VGP_V1_S, HGRAD); v2 = varFactory.testVar(VGP_V2_S, HGRAD); tau1 = varFactory.testVar(VGP_TAU1_S, HDIV); tau2 = varFactory.testVar(VGP_TAU2_S, HDIV); q = varFactory.testVar(VGP_Q_S, HGRAD); FunctionPtr u1_0 = Teuchos::rcp( new U1_0(eps) ); FunctionPtr u2_0 = Teuchos::rcp( new U2_0 ); FunctionPtr zero = Function::zero(); ParameterFunctionPtr Re_param = ParameterFunction::parameterFunction(1); VGPNavierStokesProblem problem = VGPNavierStokesProblem(Re_param,quadPoints, horizontalCells,verticalCells, H1Order, pToAdd, u1_0, u2_0, // BC for u zero, zero); // zero forcing function SolutionPtr solution = problem.backgroundFlow(); SolutionPtr solnIncrement = problem.solutionIncrement(); Teuchos::RCP<Mesh> mesh = problem.mesh(); mesh->registerSolution(solution); mesh->registerSolution(solnIncrement); /////////////////////////////////////////////////////////////////////////// // define bilinear form for stream function: VarFactory streamVarFactory; VarPtr phi_hat = streamVarFactory.traceVar("\\widehat{\\phi}"); VarPtr psin_hat = streamVarFactory.fluxVar("\\widehat{\\psi}_n"); VarPtr psi_1 = streamVarFactory.fieldVar("\\psi_1"); VarPtr psi_2 = streamVarFactory.fieldVar("\\psi_2"); VarPtr phi = streamVarFactory.fieldVar("\\phi"); VarPtr q_s = streamVarFactory.testVar("q_s", HGRAD); VarPtr v_s = streamVarFactory.testVar("v_s", HDIV); BFPtr streamBF = Teuchos::rcp( new BF(streamVarFactory) ); streamBF->addTerm(psi_1, q_s->dx()); streamBF->addTerm(psi_2, q_s->dy()); streamBF->addTerm(-psin_hat, q_s); streamBF->addTerm(psi_1, v_s->x()); streamBF->addTerm(psi_2, v_s->y()); streamBF->addTerm(phi, v_s->div()); streamBF->addTerm(-phi_hat, v_s->dot_normal()); Teuchos::RCP<Mesh> streamMesh, overkillMesh; streamMesh = MeshFactory::buildQuadMesh(quadPoints, horizontalCells, verticalCells, streamBF, H1Order+pToAddForStreamFunction, H1Order+pToAdd+pToAddForStreamFunction, useTriangles); mesh->registerObserver(streamMesh); // will refine streamMesh in the same way as mesh. map<int, double> dofsToL2error; // key: numGlobalDofs, value: total L2error compared with overkill vector< VarPtr > fields; fields.push_back(u1); fields.push_back(u2); fields.push_back(sigma11); fields.push_back(sigma12); fields.push_back(sigma21); fields.push_back(sigma22); fields.push_back(p); if (rank == 0) { cout << "Starting mesh has " << horizontalCells << " x " << verticalCells << " elements and "; cout << mesh->numGlobalDofs() << " total dofs.\n"; cout << "polyOrder = " << polyOrder << endl; cout << "pToAdd = " << pToAdd << endl; cout << "eps for top BC = " << eps << endl; if (useTriangles) { cout << "Using triangles.\n"; } if (enforceLocalConservation) { cout << "Enforcing local conservation.\n"; } else { cout << "NOT enforcing local conservation.\n"; } if (enforceOneIrregularity) { cout << "Enforcing 1-irregularity.\n"; } else { cout << "NOT enforcing 1-irregularity.\n"; } } //////////////////// CREATE BCs /////////////////////// SpatialFilterPtr entireBoundary = Teuchos::rcp( new SpatialFilterUnfiltered ); FunctionPtr u1_prev = Function::solution(u1,solution); FunctionPtr u2_prev = Function::solution(u2,solution); FunctionPtr u1hat_prev = Function::solution(u1hat,solution); FunctionPtr u2hat_prev = Function::solution(u2hat,solution); //////////////////// SOLVE & REFINE /////////////////////// FunctionPtr vorticity = Teuchos::rcp( new PreviousSolutionFunction(solution, - u1->dy() + u2->dx() ) ); // FunctionPtr vorticity = Teuchos::rcp( new PreviousSolutionFunction(solution,sigma12 - sigma21) ); RHSPtr streamRHS = RHS::rhs(); streamRHS->addTerm(vorticity * q_s); ((PreviousSolutionFunction*) vorticity.get())->setOverrideMeshCheck(true); ((PreviousSolutionFunction*) u1_prev.get())->setOverrideMeshCheck(true); ((PreviousSolutionFunction*) u2_prev.get())->setOverrideMeshCheck(true); BCPtr streamBC = BC::bc(); // streamBC->addDirichlet(psin_hat, entireBoundary, u0_cross_n); streamBC->addDirichlet(phi_hat, entireBoundary, zero); // streamBC->addZeroMeanConstraint(phi); IPPtr streamIP = Teuchos::rcp( new IP ); streamIP->addTerm(q_s); streamIP->addTerm(q_s->grad()); streamIP->addTerm(v_s); streamIP->addTerm(v_s->div()); SolutionPtr streamSolution = Teuchos::rcp( new Solution( streamMesh, streamBC, streamRHS, streamIP ) ); if (enforceLocalConservation) { FunctionPtr zero = Function::zero(); solution->lagrangeConstraints()->addConstraint(u1hat->times_normal_x() + u2hat->times_normal_y()==zero); solnIncrement->lagrangeConstraints()->addConstraint(u1hat->times_normal_x() + u2hat->times_normal_y()==zero); } if (true) { FunctionPtr u1_incr = Function::solution(u1, solnIncrement); FunctionPtr u2_incr = Function::solution(u2, solnIncrement); FunctionPtr sigma11_incr = Function::solution(sigma11, solnIncrement); FunctionPtr sigma12_incr = Function::solution(sigma12, solnIncrement); FunctionPtr sigma21_incr = Function::solution(sigma21, solnIncrement); FunctionPtr sigma22_incr = Function::solution(sigma22, solnIncrement); FunctionPtr p_incr = Function::solution(p, solnIncrement); FunctionPtr l2_incr = u1_incr * u1_incr + u2_incr * u2_incr + p_incr * p_incr + sigma11_incr * sigma11_incr + sigma12_incr * sigma12_incr + sigma21_incr * sigma21_incr + sigma22_incr * sigma22_incr; double energyThreshold = 0.20; Teuchos::RCP< RefinementStrategy > refinementStrategy = Teuchos::rcp( new RefinementStrategy( solnIncrement, energyThreshold )); for (int i=0; i<ReValues.size(); i++) { double Re = ReValues[i]; Re_param->setValue(Re); if (rank==0) cout << "Solving with Re = " << Re << ":\n"; double energyErrorTotal; do { double incr_norm; do { problem.iterate(useLineSearch); incr_norm = sqrt(l2_incr->integrate(problem.mesh())); if (rank==0) { cout << "\x1B[2K"; // Erase the entire current line. cout << "\x1B[0E"; // Move to the beginning of the current line. cout << "Iteration: " << problem.iterationCount() << "; L^2(incr) = " << incr_norm; flush(cout); } } while ((incr_norm > minL2Increment ) && (problem.iterationCount() < maxIters)); if (rank==0) cout << endl; problem.setIterationCount(1); // 1 means reuse background flow (which we must, given that we want continuation in Re...) energyErrorTotal = solnIncrement->energyErrorTotal(); //solution->energyErrorTotal(); if (energyErrorTotal > energyErrorGoal) { refinementStrategy->refine(false); } if (rank==0) { cout << "Energy error: " << energyErrorTotal << endl; } } while (energyErrorTotal > energyErrorGoal); } } double energyErrorTotal = solution->energyErrorTotal(); double incrementalEnergyErrorTotal = solnIncrement->energyErrorTotal(); if (rank == 0) { cout << "final mesh has " << mesh->numActiveElements() << " elements and " << mesh->numGlobalDofs() << " dofs.\n"; cout << "energy error: " << energyErrorTotal << endl; cout << " (Incremental solution's energy error is " << incrementalEnergyErrorTotal << ".)\n"; } FunctionPtr u1_sq = u1_prev * u1_prev; FunctionPtr u_dot_u = u1_sq + (u2_prev * u2_prev); FunctionPtr u_mag = Teuchos::rcp( new SqrtFunction( u_dot_u ) ); FunctionPtr u_div = Teuchos::rcp( new PreviousSolutionFunction(solution, u1->dx() + u2->dy() ) ); FunctionPtr massFlux = Teuchos::rcp( new PreviousSolutionFunction(solution, u1hat->times_normal_x() + u2hat->times_normal_y()) ); // check that the zero mean pressure is being correctly imposed: FunctionPtr p_prev = Teuchos::rcp( new PreviousSolutionFunction(solution,p) ); double p_avg = p_prev->integrate(mesh); if (rank==0) cout << "Integral of pressure: " << p_avg << endl; // integrate massFlux over each element (a test): // fake a new bilinear form so we can integrate against 1 VarPtr testOne = varFactory.testVar("1",CONSTANT_SCALAR); BFPtr fakeBF = Teuchos::rcp( new BF(varFactory) ); LinearTermPtr massFluxTerm = massFlux * testOne; CellTopoPtrLegacy quadTopoPtr = Teuchos::rcp(new shards::CellTopology(shards::getCellTopologyData<shards::Quadrilateral<4> >() )); DofOrderingFactory dofOrderingFactory(fakeBF); int fakeTestOrder = H1Order; DofOrderingPtr testOrdering = dofOrderingFactory.testOrdering(fakeTestOrder, *quadTopoPtr); int testOneIndex = testOrdering->getDofIndex(testOne->ID(),0); vector< ElementTypePtr > elemTypes = mesh->elementTypes(); // global element types map<int, double> massFluxIntegral; // cellID -> integral double maxMassFluxIntegral = 0.0; double totalMassFlux = 0.0; double totalAbsMassFlux = 0.0; double maxCellMeasure = 0; double minCellMeasure = 1; for (vector< ElementTypePtr >::iterator elemTypeIt = elemTypes.begin(); elemTypeIt != elemTypes.end(); elemTypeIt++) { ElementTypePtr elemType = *elemTypeIt; vector< ElementPtr > elems = mesh->elementsOfTypeGlobal(elemType); vector<GlobalIndexType> cellIDs; for (int i=0; i<elems.size(); i++) { cellIDs.push_back(elems[i]->cellID()); } FieldContainer<double> physicalCellNodes = mesh->physicalCellNodesGlobal(elemType); BasisCachePtr basisCache = Teuchos::rcp( new BasisCache(elemType,mesh,polyOrder) ); // enrich by trial space order basisCache->setPhysicalCellNodes(physicalCellNodes,cellIDs,true); // true: create side caches FieldContainer<double> cellMeasures = basisCache->getCellMeasures(); FieldContainer<double> fakeRHSIntegrals(elems.size(),testOrdering->totalDofs()); massFluxTerm->integrate(fakeRHSIntegrals,testOrdering,basisCache,true); // true: force side evaluation // cout << "fakeRHSIntegrals:\n" << fakeRHSIntegrals; for (int i=0; i<elems.size(); i++) { int cellID = cellIDs[i]; // pick out the ones for testOne: massFluxIntegral[cellID] = fakeRHSIntegrals(i,testOneIndex); } // find the largest: for (int i=0; i<elems.size(); i++) { int cellID = cellIDs[i]; maxMassFluxIntegral = max(abs(massFluxIntegral[cellID]), maxMassFluxIntegral); } for (int i=0; i<elems.size(); i++) { int cellID = cellIDs[i]; maxCellMeasure = max(maxCellMeasure,cellMeasures(i)); minCellMeasure = min(minCellMeasure,cellMeasures(i)); maxMassFluxIntegral = max(abs(massFluxIntegral[cellID]), maxMassFluxIntegral); totalMassFlux += massFluxIntegral[cellID]; totalAbsMassFlux += abs( massFluxIntegral[cellID] ); } } if (rank==0) { cout << "largest mass flux: " << maxMassFluxIntegral << endl; cout << "total mass flux: " << totalMassFlux << endl; cout << "sum of mass flux absolute value: " << totalAbsMassFlux << endl; cout << "largest h: " << sqrt(maxCellMeasure) << endl; cout << "smallest h: " << sqrt(minCellMeasure) << endl; cout << "ratio of largest / smallest h: " << sqrt(maxCellMeasure) / sqrt(minCellMeasure) << endl; } if (rank == 0) { cout << "phi ID: " << phi->ID() << endl; cout << "psi1 ID: " << psi_1->ID() << endl; cout << "psi2 ID: " << psi_2->ID() << endl; cout << "streamMesh has " << streamMesh->numActiveElements() << " elements.\n"; cout << "solving for approximate stream function...\n"; } streamSolution->solve(useMumps); energyErrorTotal = streamSolution->energyErrorTotal(); if (rank == 0) { cout << "...solved.\n"; cout << "Stream mesh has energy error: " << energyErrorTotal << endl; } if (rank==0) { solution->writeToVTK("nsCavitySoln.vtk"); if (! meshHasTriangles ) { massFlux->writeBoundaryValuesToMATLABFile(solution->mesh(), "massFlux.dat"); u_mag->writeValuesToMATLABFile(solution->mesh(), "u_mag.m"); u_div->writeValuesToMATLABFile(solution->mesh(), "u_div.m"); solution->writeFieldsToFile(u1->ID(), "u1.m"); solution->writeFluxesToFile(u1hat->ID(), "u1_hat.dat"); solution->writeFieldsToFile(u2->ID(), "u2.m"); solution->writeFluxesToFile(u2hat->ID(), "u2_hat.dat"); solution->writeFieldsToFile(p->ID(), "p.m"); streamSolution->writeFieldsToFile(phi->ID(), "phi.m"); streamSolution->writeFluxesToFile(phi_hat->ID(), "phi_hat.dat"); streamSolution->writeFieldsToFile(psi_1->ID(), "psi1.m"); streamSolution->writeFieldsToFile(psi_2->ID(), "psi2.m"); vorticity->writeValuesToMATLABFile(streamMesh, "vorticity.m"); FunctionPtr ten = Teuchos::rcp( new ConstantScalarFunction(10) ); ten->writeBoundaryValuesToMATLABFile(solution->mesh(), "skeleton.dat"); cout << "wrote files: u_mag.m, u_div.m, u1.m, u1_hat.dat, u2.m, u2_hat.dat, p.m, phi.m, vorticity.m.\n"; } else { solution->writeToFile(u1->ID(), "u1.dat"); solution->writeToFile(u2->ID(), "u2.dat"); solution->writeToFile(u2->ID(), "p.dat"); cout << "wrote files: u1.dat, u2.dat, p.dat\n"; } FieldContainer<double> points = pointGrid(0, 1, 0, 1, 100); FieldContainer<double> pointData = solutionData(points, streamSolution, phi); GnuPlotUtil::writeXYPoints("phi_patch_navierStokes_cavity.dat", pointData); set<double> patchContourLevels = diagonalContourLevels(pointData,1); vector<string> patchDataPath; patchDataPath.push_back("phi_patch_navierStokes_cavity.dat"); GnuPlotUtil::writeContourPlotScript(patchContourLevels, patchDataPath, "lidCavityNavierStokes.p"); GnuPlotUtil::writeExactMeshSkeleton("lid_navierStokes_continuation_adaptive", mesh, 2); writePatchValues(0, 1, 0, 1, streamSolution, phi, "phi_patch.m"); writePatchValues(0, .1, 0, .1, streamSolution, phi, "phi_patch_detail.m"); writePatchValues(0, .01, 0, .01, streamSolution, phi, "phi_patch_minute_detail.m"); writePatchValues(0, .001, 0, .001, streamSolution, phi, "phi_patch_minute_minute_detail.m"); } return 0; }
bool HConvergenceStudyTests::testBestApproximationErrorComputation() { bool success = true; bool enrichVelocity = false; // true would be for the "compliant" norm, which isn't working well yet int minLogElements = 0, maxLogElements = minLogElements; int numCells1D = pow(2.0,minLogElements); int H1Order = 1; int pToAdd = 2; double tol = 1e-16; double Re = 40.0; VarFactory varFactory = VGPStokesFormulation::vgpVarFactory(); VarPtr u1_vgp = varFactory.fieldVar(VGP_U1_S); VarPtr u2_vgp = varFactory.fieldVar(VGP_U2_S); VarPtr sigma11_vgp = varFactory.fieldVar(VGP_SIGMA11_S); VarPtr sigma12_vgp = varFactory.fieldVar(VGP_SIGMA12_S); VarPtr sigma21_vgp = varFactory.fieldVar(VGP_SIGMA21_S); VarPtr sigma22_vgp = varFactory.fieldVar(VGP_SIGMA22_S); VarPtr p_vgp = varFactory.fieldVar(VGP_P_S); VGPStokesFormulation stokesForm(1/Re); int numCellsFineMesh = 20; // for computing a zero-mean pressure int H1OrderFineMesh = 5; // define Kovasznay domain: FieldContainer<double> quadPointsKovasznay(4,2); // Domain from Evans Hughes for Navier-Stokes: quadPointsKovasznay(0,0) = 0.0; // x1 quadPointsKovasznay(0,1) = -0.5; // y1 quadPointsKovasznay(1,0) = 1.0; quadPointsKovasznay(1,1) = -0.5; quadPointsKovasznay(2,0) = 1.0; quadPointsKovasznay(2,1) = 0.5; quadPointsKovasznay(3,0) = 0.0; quadPointsKovasznay(3,1) = 0.5; FunctionPtr zero = Function::zero(); bool dontEnhanceFluxes = false; VGPNavierStokesProblem zeroProblem = VGPNavierStokesProblem(Re, quadPointsKovasznay, numCellsFineMesh, numCellsFineMesh, H1OrderFineMesh, pToAdd, zero, zero, zero, enrichVelocity, dontEnhanceFluxes); FunctionPtr u1_exact, u2_exact, p_exact; NavierStokesFormulation::setKovasznay(Re, zeroProblem.mesh(), u1_exact, u2_exact, p_exact); VGPNavierStokesProblem problem = VGPNavierStokesProblem(Re,quadPointsKovasznay, numCells1D,numCells1D, H1Order, pToAdd, u1_exact, u2_exact, p_exact, enrichVelocity, dontEnhanceFluxes); HConvergenceStudy study(problem.exactSolution(), problem.mesh()->bilinearForm(), problem.exactSolution()->rhs(), problem.backgroundFlow()->bc(), problem.bf()->graphNorm(), minLogElements, maxLogElements, H1Order, pToAdd, false, false, false); study.setReportRelativeErrors(false); // we want absolute errors Teuchos::RCP<Mesh> mesh = problem.mesh(); int cubatureDegreeEnrichment = 10; int L2Order = H1Order - 1; int meshCubatureDegree = L2Order + H1Order + pToAdd; study.setCubatureDegreeForExact(cubatureDegreeEnrichment + meshCubatureDegree); FunctionPtr f = u1_exact; int trialID = u1_vgp->ID(); { double fIntegral = f->integrate(mesh,cubatureDegreeEnrichment); // cout << "testBestApproximationErrorComputation: integral of f on whole mesh = " << fIntegral << endl; double l2ErrorOfAverage = (Function::constant(fIntegral) - f)->l2norm(mesh,cubatureDegreeEnrichment); // cout << "testBestApproximationErrorComputation: l2 error of fIntegral: " << l2ErrorOfAverage << endl; ElementTypePtr elemType = mesh->elementTypes()[0]; vector<GlobalIndexType> cellIDs = mesh->cellIDsOfTypeGlobal(elemType); bool testVsTest = false; BasisCachePtr basisCache = Teuchos::rcp( new BasisCache(elemType, mesh, testVsTest, cubatureDegreeEnrichment) ); basisCache->setPhysicalCellNodes(mesh->physicalCellNodesGlobal(elemType), cellIDs, false); // false: no side cache FieldContainer<double> projectionValues(cellIDs.size()); f->integrate(projectionValues, basisCache); FieldContainer<double> cellMeasures = basisCache->getCellMeasures(); for (int i=0; i<projectionValues.size(); i++) { projectionValues(i) /= cellMeasures(i); } // since we're not worried about the actual solution values at all, just use a single zero solution: vector< SolutionPtr > solutions; solutions.push_back( problem.backgroundFlow() ); study.setSolutions(solutions); // this will call computeError() double approximationError = study.bestApproximationErrors()[trialID][0]; // 0: solution/mesh index // for a single-cell mesh, approximation error should be the same as the L^2 error of the average double diff = abs(approximationError - l2ErrorOfAverage); if (diff > tol) { cout << "testBestApproximationErrorComputation: diff " << diff << " exceeds tol " << tol << endl; success = false; } else { // cout << "testBestApproximationErrorComputation: diff " << diff << " is below tol " << tol << endl; } } return success; }