Files
mfem/miniapps/navier/navier_solver.cpp
T
Jean-Sylvain CAMIER 8e251ca987 Navier miniapp compilation with nvcc (#1433)
* navier_solver FORALL scope

* MFEM_FLAGS to MFEM_LINK_FLAGS

* Fext Write => ReadWrite
2020-04-22 08:27:03 -07:00

1079 lines
29 KiB
C++

// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "navier_solver.hpp"
#include "../../general/forall.hpp"
#include <fstream>
#include <iomanip>
using namespace mfem;
using namespace navier;
void CopyDBFIntegrators(ParBilinearForm *src, ParBilinearForm *dst)
{
Array<BilinearFormIntegrator *> *bffis = src->GetDBFI();
for (int i = 0; i < bffis->Size(); ++i)
{
dst->AddDomainIntegrator((*bffis)[i]);
}
}
NavierSolver::NavierSolver(ParMesh *mesh, int order, double kin_vis)
: pmesh(mesh), order(order), kin_vis(kin_vis)
{
vfec = new H1_FECollection(order, pmesh->Dimension());
pfec = new H1_FECollection(order);
vfes = new ParFiniteElementSpace(pmesh, vfec, pmesh->Dimension());
pfes = new ParFiniteElementSpace(pmesh, pfec);
// Check if fully periodic mesh
if (!(pmesh->bdr_attributes.Size() == 0))
{
vel_ess_attr.SetSize(pmesh->bdr_attributes.Max());
vel_ess_attr = 0;
pres_ess_attr.SetSize(pmesh->bdr_attributes.Max());
pres_ess_attr = 0;
}
int vfes_truevsize = vfes->GetTrueVSize();
int pfes_truevsize = pfes->GetTrueVSize();
un.SetSize(vfes_truevsize);
un = 0.0;
unm1.SetSize(vfes_truevsize);
unm1 = 0.0;
unm2.SetSize(vfes_truevsize);
unm2 = 0.0;
fn.SetSize(vfes_truevsize);
Nun.SetSize(vfes_truevsize);
Nun = 0.0;
Nunm1.SetSize(vfes_truevsize);
Nunm1 = 0.0;
Nunm2.SetSize(vfes_truevsize);
Nunm2 = 0.0;
Fext.SetSize(vfes_truevsize);
FText.SetSize(vfes_truevsize);
Lext.SetSize(vfes_truevsize);
resu.SetSize(vfes_truevsize);
tmp1.SetSize(vfes_truevsize);
pn.SetSize(pfes_truevsize);
pn = 0.0;
resp.SetSize(pfes_truevsize);
resp = 0.0;
FText_bdr.SetSize(pfes_truevsize);
g_bdr.SetSize(pfes_truevsize);
un_gf.SetSpace(vfes);
un_gf = 0.0;
Lext_gf.SetSpace(vfes);
curlu_gf.SetSpace(vfes);
curlcurlu_gf.SetSpace(vfes);
FText_gf.SetSpace(vfes);
resu_gf.SetSpace(vfes);
pn_gf.SetSpace(pfes);
pn_gf = 0.0;
resp_gf.SetSpace(pfes);
cur_step = 0;
PrintInfo();
}
void NavierSolver::Setup(double dt)
{
if (verbose && pmesh->GetMyRank() == 0)
{
mfem::out << "Setup" << std::endl;
if (partial_assembly)
{
mfem::out << "Using Partial Assembly" << std::endl;
}
else
{
mfem::out << "Using Full Assembly" << std::endl;
}
}
sw_setup.Start();
pmesh_lor = new ParMesh(pmesh, order, BasisType::GaussLobatto);
pfec_lor = new H1_FECollection(1);
pfes_lor = new ParFiniteElementSpace(pmesh_lor, pfec_lor);
vfes->GetEssentialTrueDofs(vel_ess_attr, vel_ess_tdof);
pfes->GetEssentialTrueDofs(pres_ess_attr, pres_ess_tdof);
Array<int> empty;
// GLL integration rule (Numerical Integration)
IntegrationRules rules_ni(0, Quadrature1D::GaussLobatto);
const IntegrationRule &ir_ni = rules_ni.Get(vfes->GetFE(0)->GetGeomType(),
2 * order - 1);
nlcoeff.constant = -1.0;
N = new ParNonlinearForm(vfes);
N->AddDomainIntegrator(new VectorConvectionNLFIntegrator(nlcoeff));
if (partial_assembly)
{
N->SetAssemblyLevel(AssemblyLevel::PARTIAL);
N->Setup();
}
Mv_form = new ParBilinearForm(vfes);
BilinearFormIntegrator *mv_blfi = new VectorMassIntegrator;
if (numerical_integ)
{
mv_blfi->SetIntRule(&ir_ni);
}
Mv_form->AddDomainIntegrator(mv_blfi);
if (partial_assembly)
{
Mv_form->SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
Mv_form->Assemble();
Mv_form->FormSystemMatrix(empty, Mv);
Sp_form = new ParBilinearForm(pfes);
BilinearFormIntegrator *sp_blfi = new DiffusionIntegrator;
if (numerical_integ)
{
// blfi->SetIntRule(&ir_ni);
}
Sp_form->AddDomainIntegrator(sp_blfi);
if (partial_assembly)
{
Sp_form->SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
Sp_form->Assemble();
Sp_form->FormSystemMatrix(pres_ess_tdof, Sp);
D_form = new ParMixedBilinearForm(vfes, pfes);
D_form->AddDomainIntegrator(new VectorDivergenceIntegrator);
if (partial_assembly)
{
D_form->SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
D_form->Assemble();
D_form->FormRectangularSystemMatrix(empty, empty, D);
G_form = new ParMixedBilinearForm(pfes, vfes);
G_form->AddDomainIntegrator(new GradientIntegrator);
if (partial_assembly)
{
G_form->SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
G_form->Assemble();
G_form->FormRectangularSystemMatrix(empty, empty, G);
H_lincoeff.constant = kin_vis;
H_bdfcoeff.constant = 1.0 / dt;
H_form = new ParBilinearForm(vfes);
H_form->AddDomainIntegrator(new VectorMassIntegrator(H_bdfcoeff));
H_form->AddDomainIntegrator(new VectorDiffusionIntegrator(H_lincoeff));
if (partial_assembly)
{
H_form->SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
H_form->Assemble();
H_form->FormSystemMatrix(vel_ess_tdof, H);
FText_gfcoeff = new VectorGridFunctionCoefficient(&FText_gf);
FText_bdr_form = new ParLinearForm(pfes);
FText_bdr_form->AddBoundaryIntegrator(new BoundaryNormalLFIntegrator(
*FText_gfcoeff),
vel_ess_attr);
g_bdr_form = new ParLinearForm(pfes);
for (auto &vel_dbc : vel_dbcs)
{
g_bdr_form->AddBoundaryIntegrator(new BoundaryNormalLFIntegrator(
*vel_dbc.coeff),
vel_dbc.attr);
}
f_form = new ParLinearForm(vfes);
for (auto &accel_term : accel_terms)
{
auto *vdlfi = new VectorDomainLFIntegrator(*accel_term.coeff);
// @TODO: This order should always be the same as the nonlinear forms one!
// const IntegrationRule &ir = IntRules.Get(vfes->GetFE(0)->GetGeomType(),
// 4 * order);
// vdlfi->SetIntRule(&ir);
f_form->AddDomainIntegrator(vdlfi);
}
if (partial_assembly)
{
Vector diag_pa(vfes->GetTrueVSize());
Mv_form->AssembleDiagonal(diag_pa);
MvInvPC = new OperatorJacobiSmoother(diag_pa, empty);
}
else
{
MvInvPC = new HypreSmoother(*Mv.As<HypreParMatrix>());
dynamic_cast<HypreSmoother *>(MvInvPC)->SetType(HypreSmoother::Jacobi, 1);
}
MvInv = new CGSolver(MPI_COMM_WORLD);
MvInv->iterative_mode = false;
MvInv->SetOperator(*Mv);
MvInv->SetPreconditioner(*MvInvPC);
MvInv->SetPrintLevel(pl_mvsolve);
MvInv->SetRelTol(1e-12);
MvInv->SetMaxIter(200);
if (partial_assembly)
{
Sp_form_lor = new ParBilinearForm(pfes_lor);
Sp_form_lor->UseExternalIntegrators();
CopyDBFIntegrators(Sp_form, Sp_form_lor);
Sp_form_lor->Assemble();
Sp_form_lor->FormSystemMatrix(pres_ess_tdof, Sp_lor);
SpInvPC = new HypreBoomerAMG(*Sp_lor.As<HypreParMatrix>());
SpInvPC->SetPrintLevel(pl_amg);
SpInvPC->Mult(resp, pn);
SpInvOrthoPC = new OrthoSolver();
SpInvOrthoPC->SetOperator(*SpInvPC);
}
else
{
SpInvPC = new HypreBoomerAMG(*Sp.As<HypreParMatrix>());
SpInvPC->SetPrintLevel(0);
SpInvOrthoPC = new OrthoSolver();
SpInvOrthoPC->SetOperator(*SpInvPC);
}
SpInv = new CGSolver(MPI_COMM_WORLD);
SpInv->iterative_mode = true;
SpInv->SetOperator(*Sp);
if (pres_dbcs.empty())
{
SpInv->SetPreconditioner(*SpInvOrthoPC);
}
else
{
SpInv->SetPreconditioner(*SpInvPC);
}
SpInv->SetPrintLevel(pl_spsolve);
SpInv->SetRelTol(rtol_spsolve);
SpInv->SetMaxIter(200);
if (partial_assembly)
{
Vector diag_pa(vfes->GetTrueVSize());
H_form->AssembleDiagonal(diag_pa);
HInvPC = new OperatorJacobiSmoother(diag_pa, vel_ess_tdof);
}
else
{
HInvPC = new HypreSmoother(*H.As<HypreParMatrix>());
dynamic_cast<HypreSmoother *>(HInvPC)->SetType(HypreSmoother::Jacobi, 1);
}
HInv = new CGSolver(MPI_COMM_WORLD);
HInv->iterative_mode = true;
HInv->SetOperator(*H);
HInv->SetPreconditioner(*HInvPC);
HInv->SetPrintLevel(pl_hsolve);
HInv->SetRelTol(rtol_hsolve);
HInv->SetMaxIter(200);
un_gf.GetTrueDofs(un);
sw_setup.Stop();
}
void NavierSolver::Step(double &time, double dt, int cur_step)
{
sw_step.Start();
time += dt;
// Set current time for velocity dirichlet boundary conditions.
for (auto &vel_dbc : vel_dbcs)
{
vel_dbc.coeff->SetTime(time);
}
// Set current time for pressure dirichlet boundary conditons.
for (auto &pres_dbc : pres_dbcs)
{
pres_dbc.coeff->SetTime(time);
}
SetTimeIntegrationCoefficients(cur_step);
if (cur_step <= 2)
{
H_bdfcoeff.constant = bd0 / dt;
H_form->Update();
H_form->Assemble();
H_form->FormSystemMatrix(vel_ess_tdof, H);
if (partial_assembly)
{
HInv->SetOperator(*H);
delete HInvPC;
Vector diag_pa(vfes->GetTrueVSize());
H_form->AssembleDiagonal(diag_pa);
HInvPC = new OperatorJacobiSmoother(diag_pa, vel_ess_tdof);
HInv->SetPreconditioner(*HInvPC);
}
else
{
HInv->SetOperator(*H);
}
}
// Extrapolated f^{n+1}.
for (auto &accel_term : accel_terms)
{
accel_term.coeff->SetTime(time);
}
f_form->Assemble();
f_form->ParallelAssemble(fn);
//
// Nonlinear extrapolated terms.
//
sw_extrap.Start();
N->Mult(un, Nun);
Nun.Add(1.0, fn);
{
const auto d_Nun = Nun.Read();
const auto d_Nunm1 = Nunm1.Read();
const auto d_Nunm2 = Nunm2.Read();
auto d_Fext = Fext.Write();
MFEM_FORALL(i, Fext.Size(),
d_Fext[i] = ab1 * d_Nun[i] +
ab2 * d_Nunm1[i] +
ab3 * d_Nunm2[i];);
}
// Rotate the solutions from previous time steps.
Nunm2 = Nunm1;
Nunm1 = Nun;
// Fext = M^{-1} (F(u^{n}) + f^{n+1})
MvInv->Mult(Fext, tmp1);
iter_mvsolve = MvInv->GetNumIterations();
res_mvsolve = MvInv->GetFinalNorm();
Fext.Set(1.0, tmp1);
// Compute BDF terms.
{
const double bd1idt = -bd1 / dt;
const double bd2idt = -bd2 / dt;
const double bd3idt = -bd3 / dt;
const auto d_un = un.Read();
const auto d_unm1 = unm1.Read();
const auto d_unm2 = unm2.Read();
auto d_Fext = Fext.ReadWrite();
MFEM_FORALL(i, Fext.Size(),
d_Fext[i] += bd1idt * d_un[i] +
bd2idt * d_unm1[i] +
bd3idt * d_unm2[i];);
}
sw_extrap.Stop();
//
// Pressure poisson.
//
sw_curlcurl.Start();
{
const auto d_un = un.Read();
const auto d_unm1 = unm1.Read();
const auto d_unm2 = unm2.Read();
auto d_Lext = Lext.Write();
MFEM_FORALL(i, Lext.Size(),
d_Lext[i] = ab1 * d_un[i] +
ab2 * d_unm1[i] +
ab3 * d_unm2[i];);
}
Lext_gf.SetFromTrueDofs(Lext);
if (pmesh->Dimension() == 2)
{
ComputeCurl2D(Lext_gf, curlu_gf);
ComputeCurl2D(curlu_gf, curlcurlu_gf, true);
}
else
{
ComputeCurl3D(Lext_gf, curlu_gf);
ComputeCurl3D(curlu_gf, curlcurlu_gf);
}
curlcurlu_gf.GetTrueDofs(Lext);
Lext *= kin_vis;
sw_curlcurl.Stop();
// \tilde{F} = F - \nu CurlCurl(u)
FText.Set(-1.0, Lext);
FText.Add(1.0, Fext);
// p_r = \nabla \cdot FText
D->Mult(FText, resp);
resp.Neg();
// Add boundary terms.
FText_gf.SetFromTrueDofs(FText);
FText_bdr_form->Assemble();
FText_bdr_form->ParallelAssemble(FText_bdr);
g_bdr_form->Assemble();
g_bdr_form->ParallelAssemble(g_bdr);
resp.Add(1.0, FText_bdr);
resp.Add(-bd0 / dt, g_bdr);
if (pres_dbcs.empty())
{
Orthogonalize(resp);
}
for (auto &pres_dbc : pres_dbcs)
{
pn_gf.ProjectBdrCoefficient(*pres_dbc.coeff, pres_dbc.attr);
}
pfes->GetRestrictionMatrix()->MultTranspose(resp, resp_gf);
Vector X1, B1;
if (partial_assembly)
{
auto *SpC = Sp.As<ConstrainedOperator>();
EliminateRHS(*Sp_form, *SpC, pres_ess_tdof, pn_gf, resp_gf, X1, B1, 1);
}
else
{
Sp_form->FormLinearSystem(pres_ess_tdof, pn_gf, resp_gf, Sp, X1, B1, 1);
}
sw_spsolve.Start();
SpInv->Mult(B1, X1);
sw_spsolve.Stop();
iter_spsolve = SpInv->GetNumIterations();
res_spsolve = SpInv->GetFinalNorm();
Sp_form->RecoverFEMSolution(X1, resp_gf, pn_gf);
// If the boundary conditions on the pressure are pure Neumann remove the
// nullspace by removing the mean of the pressure solution. This is also
// ensured by the OrthoSolver wrapper for the preconditioner which removes
// the nullspace after every application.
if (pres_dbcs.empty())
{
MeanZero(pn_gf);
}
pn_gf.GetTrueDofs(pn);
//
// Project velocity.
//
G->Mult(pn, resu);
resu.Neg();
Mv->Mult(Fext, tmp1);
resu.Add(1.0, tmp1);
for (auto &vel_dbc : vel_dbcs)
{
un_gf.ProjectBdrCoefficient(*vel_dbc.coeff, vel_dbc.attr);
}
vfes->GetRestrictionMatrix()->MultTranspose(resu, resu_gf);
// Rotate solutions from previous time steps.
unm2 = unm1;
unm1 = un;
Vector X2, B2;
if (partial_assembly)
{
auto *HC = H.As<ConstrainedOperator>();
EliminateRHS(*H_form, *HC, vel_ess_tdof, un_gf, resu_gf, X2, B2, 1);
}
else
{
H_form->FormLinearSystem(vel_ess_tdof, un_gf, resu_gf, H, X2, B2, 1);
}
sw_hsolve.Start();
HInv->Mult(B2, X2);
sw_hsolve.Stop();
iter_hsolve = HInv->GetNumIterations();
res_hsolve = HInv->GetFinalNorm();
H_form->RecoverFEMSolution(X2, resu_gf, un_gf);
un_gf.GetTrueDofs(un);
sw_step.Stop();
if (verbose && pmesh->GetMyRank() == 0)
{
mfem::out << std::setw(7) << "" << std::setw(3) << "It" << std::setw(8)
<< "Resid" << std::setw(12) << "Reltol"
<< "\n";
// If numerical integration is active, there is no solve (thus no
// iterations), on the inverse velocity mass application.
if (!numerical_integ)
{
mfem::out << std::setw(5) << "MVIN " << std::setw(5) << std::fixed
<< iter_mvsolve << " " << std::setw(3)
<< std::setprecision(2) << std::scientific << res_mvsolve
<< " " << 1e-12 << "\n";
}
mfem::out << std::setw(5) << "PRES " << std::setw(5) << std::fixed
<< iter_spsolve << " " << std::setw(3) << std::setprecision(2)
<< std::scientific << res_spsolve << " " << rtol_spsolve
<< "\n";
mfem::out << std::setw(5) << "HELM " << std::setw(5) << std::fixed
<< iter_hsolve << " " << std::setw(3) << std::setprecision(2)
<< std::scientific << res_hsolve << " " << rtol_hsolve
<< "\n";
mfem::out << std::setprecision(8);
mfem::out << std::fixed;
}
}
void NavierSolver::MeanZero(ParGridFunction &v)
{
// Make sure not to recompute the inner product linear form every
// application.
if (mass_lf == nullptr)
{
onecoeff.constant = 1.0;
mass_lf = new ParLinearForm(v.ParFESpace());
mass_lf->AddDomainIntegrator(new DomainLFIntegrator(onecoeff));
mass_lf->Assemble();
ParGridFunction one_gf(v.ParFESpace());
one_gf.ProjectCoefficient(onecoeff);
volume = mass_lf->operator()(one_gf);
}
double integ = mass_lf->operator()(v);
v -= integ / volume;
}
void NavierSolver::EliminateRHS(Operator &A,
ConstrainedOperator &constrainedA,
const Array<int> &ess_tdof_list,
Vector &x,
Vector &b,
Vector &X,
Vector &B,
int copy_interior)
{
const Operator *Po = A.GetOutputProlongation();
const Operator *Pi = A.GetProlongation();
const Operator *Ri = A.GetRestriction();
A.InitTVectors(Po, Ri, Pi, x, b, X, B);
if (!copy_interior)
{
X.SetSubVectorComplement(ess_tdof_list, 0.0);
}
constrainedA.EliminateRHS(X, B);
}
void NavierSolver::Orthogonalize(Vector &v)
{
double loc_sum = v.Sum();
double global_sum = 0.0;
int loc_size = v.Size();
int global_size = 0;
MPI_Allreduce(&loc_sum, &global_sum, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
MPI_Allreduce(&loc_size, &global_size, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
v -= global_sum / static_cast<double>(global_size);
}
void NavierSolver::ComputeCurl3D(ParGridFunction &u, ParGridFunction &cu)
{
FiniteElementSpace *fes = u.FESpace();
// AccumulateAndCountZones.
Array<int> zones_per_vdof;
zones_per_vdof.SetSize(fes->GetVSize());
zones_per_vdof = 0;
cu = 0.0;
// Local interpolation.
int elndofs;
Array<int> vdofs;
Vector vals;
Vector loc_data;
int vdim = fes->GetVDim();
DenseMatrix grad_hat;
DenseMatrix dshape;
DenseMatrix grad;
Vector curl;
for (int e = 0; e < fes->GetNE(); ++e)
{
fes->GetElementVDofs(e, vdofs);
u.GetSubVector(vdofs, loc_data);
vals.SetSize(vdofs.Size());
ElementTransformation *tr = fes->GetElementTransformation(e);
const FiniteElement *el = fes->GetFE(e);
elndofs = el->GetDof();
int dim = el->GetDim();
dshape.SetSize(elndofs, dim);
for (int dof = 0; dof < elndofs; ++dof)
{
// Project.
const IntegrationPoint &ip = el->GetNodes().IntPoint(dof);
tr->SetIntPoint(&ip);
// Eval and GetVectorGradientHat.
el->CalcDShape(tr->GetIntPoint(), dshape);
grad_hat.SetSize(vdim, dim);
DenseMatrix loc_data_mat(loc_data.GetData(), elndofs, vdim);
MultAtB(loc_data_mat, dshape, grad_hat);
const DenseMatrix &Jinv = tr->InverseJacobian();
grad.SetSize(grad_hat.Height(), Jinv.Width());
Mult(grad_hat, Jinv, grad);
curl.SetSize(3);
curl(0) = grad(2, 1) - grad(1, 2);
curl(1) = grad(0, 2) - grad(2, 0);
curl(2) = grad(1, 0) - grad(0, 1);
for (int j = 0; j < curl.Size(); ++j)
{
vals(elndofs * j + dof) = curl(j);
}
}
// Accumulate values in all dofs, count the zones.
for (int j = 0; j < vdofs.Size(); j++)
{
int ldof = vdofs[j];
cu(ldof) += vals[j];
zones_per_vdof[ldof]++;
}
}
// Communication
// Count the zones globally.
GroupCommunicator &gcomm = u.ParFESpace()->GroupComm();
gcomm.Reduce<int>(zones_per_vdof, GroupCommunicator::Sum);
gcomm.Bcast(zones_per_vdof);
// Accumulate for all vdofs.
gcomm.Reduce<double>(cu.GetData(), GroupCommunicator::Sum);
gcomm.Bcast<double>(cu.GetData());
// Compute means.
for (int i = 0; i < cu.Size(); i++)
{
const int nz = zones_per_vdof[i];
if (nz)
{
cu(i) /= nz;
}
}
}
void NavierSolver::ComputeCurl2D(ParGridFunction &u,
ParGridFunction &cu,
bool assume_scalar)
{
FiniteElementSpace *fes = u.FESpace();
// AccumulateAndCountZones.
Array<int> zones_per_vdof;
zones_per_vdof.SetSize(fes->GetVSize());
zones_per_vdof = 0;
cu = 0.0;
// Local interpolation.
int elndofs;
Array<int> vdofs;
Vector vals;
Vector loc_data;
int vdim = fes->GetVDim();
DenseMatrix grad_hat;
DenseMatrix dshape;
DenseMatrix grad;
Vector curl;
for (int e = 0; e < fes->GetNE(); ++e)
{
fes->GetElementVDofs(e, vdofs);
u.GetSubVector(vdofs, loc_data);
vals.SetSize(vdofs.Size());
ElementTransformation *tr = fes->GetElementTransformation(e);
const FiniteElement *el = fes->GetFE(e);
elndofs = el->GetDof();
int dim = el->GetDim();
dshape.SetSize(elndofs, dim);
for (int dof = 0; dof < elndofs; ++dof)
{
// Project.
const IntegrationPoint &ip = el->GetNodes().IntPoint(dof);
tr->SetIntPoint(&ip);
// Eval and GetVectorGradientHat.
el->CalcDShape(tr->GetIntPoint(), dshape);
grad_hat.SetSize(vdim, dim);
DenseMatrix loc_data_mat(loc_data.GetData(), elndofs, vdim);
MultAtB(loc_data_mat, dshape, grad_hat);
const DenseMatrix &Jinv = tr->InverseJacobian();
grad.SetSize(grad_hat.Height(), Jinv.Width());
Mult(grad_hat, Jinv, grad);
if (assume_scalar)
{
curl.SetSize(2);
curl(0) = grad(0, 1);
curl(1) = -grad(0, 0);
}
else
{
curl.SetSize(2);
curl(0) = grad(1, 0) - grad(0, 1);
curl(1) = 0.0;
}
for (int j = 0; j < curl.Size(); ++j)
{
vals(elndofs * j + dof) = curl(j);
}
}
// Accumulate values in all dofs, count the zones.
for (int j = 0; j < vdofs.Size(); j++)
{
int ldof = vdofs[j];
cu(ldof) += vals[j];
zones_per_vdof[ldof]++;
}
}
// Communication.
// Count the zones globally.
GroupCommunicator &gcomm = u.ParFESpace()->GroupComm();
gcomm.Reduce<int>(zones_per_vdof, GroupCommunicator::Sum);
gcomm.Bcast(zones_per_vdof);
// Accumulate for all vdofs.
gcomm.Reduce<double>(cu.GetData(), GroupCommunicator::Sum);
gcomm.Bcast<double>(cu.GetData());
// Compute means.
for (int i = 0; i < cu.Size(); i++)
{
const int nz = zones_per_vdof[i];
if (nz)
{
cu(i) /= nz;
}
}
}
double NavierSolver::ComputeCFL(ParGridFunction &u, double dt)
{
ParMesh *pmesh = u.ParFESpace()->GetParMesh();
FiniteElementSpace *fes = u.FESpace();
int vdim = fes->GetVDim();
Vector ux, uy, uz;
Vector ur, us, ut;
double cflx = 0.0;
double cfly = 0.0;
double cflz = 0.0;
double cflm = 0.0;
double cflmax = 0.0;
for (int e = 0; e < fes->GetNE(); ++e)
{
const FiniteElement *fe = fes->GetFE(e);
const IntegrationRule &ir = IntRules.Get(fe->GetGeomType(),
fe->GetOrder());
ElementTransformation *tr = fes->GetElementTransformation(e);
u.GetValues(e, ir, ux, 1);
ur.SetSize(ux.Size());
u.GetValues(e, ir, uy, 2);
us.SetSize(uy.Size());
if (vdim == 3)
{
u.GetValues(e, ir, uz, 3);
ut.SetSize(uz.Size());
}
double hmin = pmesh->GetElementSize(e, 1) / (double) fes->GetOrder(0);
for (int i = 0; i < ir.GetNPoints(); ++i)
{
const IntegrationPoint &ip = ir.IntPoint(i);
tr->SetIntPoint(&ip);
const DenseMatrix &invJ = tr->InverseJacobian();
const double detJinv = 1.0 / tr->Jacobian().Det();
if (vdim == 2)
{
ur(i) = (ux(i) * invJ(0, 0) + uy(i) * invJ(1, 0)) * detJinv;
us(i) = (ux(i) * invJ(0, 1) + uy(i) * invJ(1, 1)) * detJinv;
}
else if (vdim == 3)
{
ur(i) = (ux(i) * invJ(0, 0) + uy(i) * invJ(1, 0)
+ uz(i) * invJ(2, 0))
* detJinv;
us(i) = (ux(i) * invJ(0, 1) + uy(i) * invJ(1, 1)
+ uz(i) * invJ(2, 1))
* detJinv;
ut(i) = (ux(i) * invJ(0, 2) + uy(i) * invJ(1, 2)
+ uz(i) * invJ(2, 2))
* detJinv;
}
cflx = fabs(dt * ux(i) / hmin);
cfly = fabs(dt * uy(i) / hmin);
if (vdim == 3)
{
cflz = fabs(dt * uz(i) / hmin);
}
cflm = cflx + cfly + cflz;
cflmax = fmax(cflmax, cflm);
}
}
double cflmax_global = 0.0;
MPI_Allreduce(&cflmax,
&cflmax_global,
1,
MPI_DOUBLE,
MPI_MAX,
pmesh->GetComm());
return cflmax_global;
}
void NavierSolver::AddVelDirichletBC(VectorCoefficient *coeff, Array<int> &attr)
{
vel_dbcs.emplace_back(attr, coeff);
if (verbose && pmesh->GetMyRank() == 0)
{
mfem::out << "Adding Velocity Dirichlet BC to attributes ";
for (int i = 0; i < attr.Size(); ++i)
{
if (attr[i] == 1)
{
mfem::out << i << " ";
}
}
mfem::out << std::endl;
}
for (int i = 0; i < attr.Size(); ++i)
{
MFEM_ASSERT((vel_ess_attr[i] && attr[i]) == 0,
"Duplicate boundary definition deteceted.");
if (attr[i] == 1)
{
vel_ess_attr[i] = 1;
}
}
}
void NavierSolver::AddVelDirichletBC(VecFuncT *f, Array<int> &attr)
{
AddVelDirichletBC(new VectorFunctionCoefficient(pmesh->Dimension(), f), attr);
}
void NavierSolver::AddPresDirichletBC(Coefficient *coeff, Array<int> &attr)
{
pres_dbcs.emplace_back(attr, coeff);
if (verbose && pmesh->GetMyRank() == 0)
{
mfem::out << "Adding Pressure Dirichlet BC to attributes ";
for (int i = 0; i < attr.Size(); ++i)
{
if (attr[i] == 1)
{
mfem::out << i << " ";
}
}
mfem::out << std::endl;
}
for (int i = 0; i < attr.Size(); ++i)
{
MFEM_ASSERT((pres_ess_attr[i] && attr[i]) == 0,
"Duplicate boundary definition deteceted.");
if (attr[i] == 1)
{
pres_ess_attr[i] = 1;
}
}
}
void NavierSolver::AddPresDirichletBC(ScalarFuncT *f, Array<int> &attr)
{
AddPresDirichletBC(new FunctionCoefficient(f), attr);
}
void NavierSolver::AddAccelTerm(VectorCoefficient *coeff, Array<int> &attr)
{
accel_terms.emplace_back(attr, coeff);
if (verbose && pmesh->GetMyRank() == 0)
{
mfem::out << "Adding Acceleration term to attributes ";
for (int i = 0; i < attr.Size(); ++i)
{
if (attr[i] == 1)
{
mfem::out << i << " ";
}
}
mfem::out << std::endl;
}
}
void NavierSolver::AddAccelTerm(VecFuncT *f, Array<int> &attr)
{
AddAccelTerm(new VectorFunctionCoefficient(pmesh->Dimension(), f), attr);
}
void NavierSolver::SetTimeIntegrationCoefficients(int step)
{
if (step == 0)
{
bd0 = 1.0;
bd1 = -1.0;
bd2 = 0.0;
bd3 = 0.0;
ab1 = 1.0;
ab2 = 0.0;
ab3 = 0.0;
}
else if (step == 1)
{
bd0 = 3.0 / 2.0;
bd1 = -4.0 / 2.0;
bd2 = 1.0 / 2.0;
bd3 = 0.0;
ab1 = 2.0;
ab2 = -1.0;
ab3 = 0.0;
}
else if (step == 2)
{
bd0 = 11.0 / 6.0;
bd1 = -18.0 / 6.0;
bd2 = 9.0 / 6.0;
bd3 = -2.0 / 6.0;
ab1 = 3.0;
ab2 = -3.0;
ab3 = 1.0;
}
}
void NavierSolver::PrintTimingData()
{
double my_rt[6], rt_max[6];
my_rt[0] = sw_setup.RealTime();
my_rt[1] = sw_step.RealTime();
my_rt[2] = sw_extrap.RealTime();
my_rt[3] = sw_curlcurl.RealTime();
my_rt[4] = sw_spsolve.RealTime();
my_rt[5] = sw_hsolve.RealTime();
MPI_Reduce(my_rt, rt_max, 6, MPI_DOUBLE, MPI_MAX, 0, pmesh->GetComm());
if (pmesh->GetMyRank() == 0)
{
mfem::out << std::setw(10) << "SETUP" << std::setw(10) << "STEP"
<< std::setw(10) << "EXTRAP" << std::setw(10) << "CURLCURL"
<< std::setw(10) << "PSOLVE" << std::setw(10) << "HSOLVE"
<< "\n";
mfem::out << std::setprecision(3) << std::setw(10) << my_rt[0]
<< std::setw(10) << my_rt[1] << std::setw(10) << my_rt[2]
<< std::setw(10) << my_rt[3] << std::setw(10) << my_rt[4]
<< std::setw(10) << my_rt[5] << "\n";
mfem::out << std::setprecision(3) << std::setw(10) << " " << std::setw(10)
<< my_rt[1] / my_rt[1] << std::setw(10) << my_rt[2] / my_rt[1]
<< std::setw(10) << my_rt[3] / my_rt[1] << std::setw(10)
<< my_rt[4] / my_rt[1] << std::setw(10) << my_rt[5] / my_rt[1]
<< "\n";
mfem::out << std::setprecision(8);
}
}
void NavierSolver::PrintInfo()
{
int fes_size0 = vfes->GlobalVSize();
int fes_size1 = pfes->GlobalVSize();
if (pmesh->GetMyRank() == 0)
{
mfem::out << "NAVIER version: " << NAVIER_VERSION << std::endl
<< "MFEM version: " << MFEM_VERSION << std::endl
<< "MFEM GIT: " << MFEM_GIT_STRING << std::endl
<< "Velocity #DOFs: " << fes_size0 << std::endl
<< "Pressure #DOFs: " << fes_size1 << std::endl;
}
}
NavierSolver::~NavierSolver()
{
delete FText_gfcoeff;
delete g_bdr_form;
delete FText_bdr_form;
delete mass_lf;
delete Mv_form;
delete N;
delete Sp_form;
delete D_form;
delete G_form;
delete HInvPC;
delete HInv;
delete H_form;
delete SpInv;
delete MvInvPC;
delete Sp_form_lor;
delete SpInvOrthoPC;
delete SpInvPC;
delete f_form;
delete pfes_lor;
delete pfec_lor;
delete pmesh_lor;
delete MvInv;
delete vfec;
delete pfec;
delete vfes;
delete pfes;
}