From 0e195e2f871d0242599299330f2376b600d9d34b Mon Sep 17 00:00:00 2001 From: Tomov Date: Mon, 21 Sep 2020 22:21:47 -0700 Subject: [PATCH 001/198] wip surface alignment. --- fem/tmop.cpp | 101 ++++++++++++++++++++++----- fem/tmop.hpp | 21 +++++- miniapps/meshing/mesh-optimizer.hpp | 27 +++++++ miniapps/meshing/pmesh-optimizer.cpp | 58 ++++++++++++++- 4 files changed, 184 insertions(+), 23 deletions(-) diff --git a/fem/tmop.cpp b/fem/tmop.cpp index 7f46748af4..078b11ac37 100644 --- a/fem/tmop.cpp +++ b/fem/tmop.cpp @@ -1886,6 +1886,7 @@ TMOP_Integrator::~TMOP_Integrator() { delete lim_func; delete zeta; + delete sigma; for (int i = 0; i < ElemDer.Size(); i++) { delete ElemDer[i]; @@ -1952,6 +1953,24 @@ void TMOP_Integrator::EnableAdaptiveLimiting(const ParGridFunction &z0, } #endif +#ifdef MFEM_USE_MPI +void TMOP_Integrator::EnableSurfaceFitting(const ParGridFunction &s0, + const ParGridFunction &smarker, + Coefficient &coeff, + AdaptivityEvaluator &ae) +{ + sigma = new GridFunction(s0); + sigma_marker = &smarker; + coeff_sigma = &coeff; + sigma_eval = &ae; + + sigma_eval->SetParMetaInfo(*s0.ParFESpace()->GetParMesh(), + *s0.ParFESpace()->FEColl(), 1); + sigma_eval->SetInitialField + (*sigma->FESpace()->GetMesh()->GetNodes(), *sigma); +} +#endif + double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, ElementTransformation &T, const Vector &elfun) @@ -1959,8 +1978,11 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, const int dof = el.GetDof(), dim = el.GetDim(); double energy; - // No adaptive limiting terms if this is a FD computation. + // No adaptive limiting / surface fitting terms if the function is called + // as part of a FD derivative computation (because we include the exact + // derivatives of these terms in FD computations). const bool adaptive_limiting = (zeta && fd_call_flag == false); + const bool surface_fitting = (sigma && fd_call_flag == false); DSh.SetSize(dof, dim); Jrt.SetSize(dim); @@ -1999,7 +2021,7 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, // Define ref->physical transformation, when a Coefficient is specified. IsoparametricTransformation *Tpr = NULL; - if (coeff1 || coeff0 || adaptive_limiting) + if (coeff1 || coeff0 || adaptive_limiting || surface_fitting) { Tpr = new IsoparametricTransformation; Tpr->SetFE(&el); @@ -2022,6 +2044,12 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, zeta->GetValues(T.ElementNo, ir, zeta_q); zeta_0->GetValues(T.ElementNo, ir, zeta0_q); } + Vector sigma_q, sigma_marker_q; + if (surface_fitting) + { + sigma->GetValues(T.ElementNo, ir, sigma_q); + sigma_marker->GetValues(T.ElementNo, ir, sigma_marker_q); + } for (int i = 0; i < ir.GetNPoints(); i++) { @@ -2053,6 +2081,15 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, val += coeff_zeta->Eval(*Tpr, ip) * lim_normal * diff * diff; } + if (surface_fitting) + { + // TODO this is incorrect. + // The below is correct, if I know all values. + // The others should not matter -- how to do it??? + const double diff = sigma_marker_q(i) * (sigma_q(i) - 0.0); + val += coeff_sigma->Eval(*Tpr, ip) * lim_normal * diff * diff; + } + energy += weight * val; } delete Tpr; @@ -2139,7 +2176,7 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el, // Define ref->physical transformation, when a Coefficient is specified. IsoparametricTransformation *Tpr = NULL; - if (coeff1 || coeff0 || zeta || exact_action) + if (coeff1 || coeff0 || zeta || sigma || exact_action) { Tpr = new IsoparametricTransformation; Tpr->SetFE(&el); @@ -2221,7 +2258,17 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el, } } - if (zeta) { AssembleElemVecAdaptLim(el, weights, *Tpr, ir, PMatO); } + if (zeta) + { + AssembleElemVecAdaptLim(*zeta, *zeta_0, *coeff_zeta, + el, weights, *Tpr, ir, PMatO); + } + if (sigma) + { + AssembleElemVecAdaptLim(*sigma, *sigma_marker, *coeff_sigma, + el, weights, *Tpr, ir, PMatO); + } + delete Tpr; } @@ -2327,12 +2374,24 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el, } } - if (zeta) { AssembleElemGradAdaptLim(el, weights, *Tpr, ir, elmat); } + if (zeta) + { + AssembleElemGradAdaptLim(*zeta, *zeta_0, *coeff_zeta, + el, weights, *Tpr, ir, elmat); + } + if (sigma) + { + AssembleElemGradAdaptLim(*sigma, *sigma_marker, *coeff_sigma, + el, weights, *Tpr, ir, elmat); + } delete Tpr; } -void TMOP_Integrator::AssembleElemVecAdaptLim(const FiniteElement &el, +void TMOP_Integrator::AssembleElemVecAdaptLim(const GridFunction &g, + const GridFunction &g0, + Coefficient &coeff, + const FiniteElement &el, const Vector &weights, IsoparametricTransformation &Tpr, const IntegrationRule &ir, @@ -2344,10 +2403,10 @@ void TMOP_Integrator::AssembleElemVecAdaptLim(const FiniteElement &el, Vector shape(dof), zeta_e, zeta_q, zeta0_q; Array dofs; - zeta->FESpace()->GetElementDofs(Tpr.ElementNo, dofs); - zeta->GetSubVector(dofs, zeta_e); - zeta->GetValues(Tpr.ElementNo, ir, zeta_q); - zeta_0->GetValues(Tpr.ElementNo, ir, zeta0_q); + g.FESpace()->GetElementDofs(Tpr.ElementNo, dofs); + g.GetSubVector(dofs, zeta_e); + g.GetValues(Tpr.ElementNo, ir, zeta_q); + g0.GetValues(Tpr.ElementNo, ir, zeta0_q); // Project the gradient of zeta in the same space. // The FE coefficients of the gradient go in zeta_grad_e. @@ -2366,12 +2425,15 @@ void TMOP_Integrator::AssembleElemVecAdaptLim(const FiniteElement &el, el.CalcShape(ip, shape); zeta_grad_e.MultTranspose(shape, zeta_grad_q); zeta_grad_q *= 2.0 * (zeta_q(q) - zeta0_q(q)); - zeta_grad_q *= weights(q) * lim_normal * coeff_zeta->Eval(Tpr, ip); + zeta_grad_q *= weights(q) * lim_normal * coeff.Eval(Tpr, ip); AddMultVWt(shape, zeta_grad_q, mat); } } -void TMOP_Integrator::AssembleElemGradAdaptLim(const FiniteElement &el, +void TMOP_Integrator::AssembleElemGradAdaptLim(const GridFunction &g, + const GridFunction &g0, + Coefficient &coeff, + const FiniteElement &el, const Vector &weights, IsoparametricTransformation &Tpr, const IntegrationRule &ir, @@ -2383,10 +2445,10 @@ void TMOP_Integrator::AssembleElemGradAdaptLim(const FiniteElement &el, Vector shape(dof), zeta_e, zeta_q, zeta0_q; Array dofs; - zeta->FESpace()->GetElementDofs(Tpr.ElementNo, dofs); - zeta->GetSubVector(dofs, zeta_e); - zeta->GetValues(Tpr.ElementNo, ir, zeta_q); - zeta_0->GetValues(Tpr.ElementNo, ir, zeta0_q); + g.FESpace()->GetElementDofs(Tpr.ElementNo, dofs); + g.GetSubVector(dofs, zeta_e); + g.GetValues(Tpr.ElementNo, ir, zeta_q); + g0.GetValues(Tpr.ElementNo, ir, zeta0_q); // Project the gradient of zeta in the same space. // The FE coefficients of the gradient go in zeta_grad_e. @@ -2416,7 +2478,7 @@ void TMOP_Integrator::AssembleElemGradAdaptLim(const FiniteElement &el, Vector gg_ptr(zeta_grad_grad_q.GetData(), dim*dim); zeta_grad_grad_e.MultTranspose(shape, gg_ptr); - const double w = weights(q) * lim_normal * coeff_zeta->Eval(Tpr, ip); + const double w = weights(q) * lim_normal * coeff.Eval(Tpr, ip); for (int i = 0; i < dof * dim; i++) { const int idof = i % dof, idim = i / dof; @@ -2517,7 +2579,7 @@ void TMOP_Integrator::AssembleElementVectorFD(const FiniteElement &el, } PMatO.UseExternalData(elvect.GetData(), dof, dim); - AssembleElemVecAdaptLim(el, weights, Tpr, ir, PMatO); + AssembleElemVecAdaptLim(*zeta, *zeta_0, *coeff_zeta, el, weights, Tpr, ir, PMatO); } } @@ -2612,7 +2674,7 @@ void TMOP_Integrator::AssembleElementGradFD(const FiniteElement &el, weights(q) = ir.IntPoint(q).weight * Jtr(q).Det(); } - AssembleElemGradAdaptLim(el, weights, Tpr, ir, elmat); + AssembleElemGradAdaptLim(*zeta, *zeta_0, *coeff_zeta, el, weights, Tpr, ir, elmat); } } @@ -2724,6 +2786,7 @@ void TMOP_Integrator::UpdateAfterMeshChange(const Vector &new_x) { // Update zeta if adaptive limiting is enabled. if (zeta) { adapt_eval->ComputeAtNewPosition(new_x, *zeta); } + if (sigma) { sigma_eval->ComputeAtNewPosition(new_x, *sigma); } } void TMOP_Integrator::ComputeFDh(const Vector &x, const FiniteElementSpace &fes) diff --git a/fem/tmop.hpp b/fem/tmop.hpp index c370c2aad7..01e4d53222 100644 --- a/fem/tmop.hpp +++ b/fem/tmop.hpp @@ -917,6 +917,12 @@ protected: Coefficient *coeff_zeta; // Not owned. AdaptivityEvaluator *adapt_eval; // Not owned. + // Surface fitting. + GridFunction *sigma; // Owned. Updated by sigma_eval. + const GridFunction *sigma_marker; // Not owned. + Coefficient *coeff_sigma; // Not owned. + AdaptivityEvaluator *sigma_eval; // Not owned. + DiscreteAdaptTC *discr_tc; // Parameters for FD-based Gradient & Hessian calculation. @@ -966,10 +972,14 @@ protected: ElementTransformation &T, const Vector &elfun, DenseMatrix &elmat); - void AssembleElemVecAdaptLim(const FiniteElement &el, const Vector &weights, + void AssembleElemVecAdaptLim(const GridFunction &g, const GridFunction &g0, + Coefficient &coeff, + const FiniteElement &el, const Vector &weights, IsoparametricTransformation &Tpr, const IntegrationRule &ir, DenseMatrix &m); - void AssembleElemGradAdaptLim(const FiniteElement &el, const Vector &weights, + void AssembleElemGradAdaptLim(const GridFunction &g, const GridFunction &g0, + Coefficient &coeff, + const FiniteElement &el, const Vector &weights, IsoparametricTransformation &Tpr, const IntegrationRule &ir, DenseMatrix &m); @@ -1021,6 +1031,7 @@ public: nodes0(NULL), coeff0(NULL), lim_dist(NULL), lim_func(NULL), lim_normal(1.0), zeta_0(NULL), zeta(NULL), coeff_zeta(NULL), adapt_eval(NULL), + sigma(NULL), sigma_marker(NULL), coeff_sigma(NULL), sigma_eval(NULL), discr_tc(dynamic_cast(tc)), fdflag(false), dxscale(1.0e3), fd_call_flag(false), exact_action(false) { } @@ -1081,6 +1092,12 @@ public: AdaptivityEvaluator &ae); #endif +#ifdef MFEM_USE_MPI + void EnableSurfaceFitting(const ParGridFunction &s0, + const ParGridFunction &smarker, Coefficient &coeff, + AdaptivityEvaluator &ae); +#endif + /// Update the original/reference nodes used for limiting. void SetLimitingNodes(const GridFunction &n0) { nodes0 = &n0; } diff --git a/miniapps/meshing/mesh-optimizer.hpp b/miniapps/meshing/mesh-optimizer.hpp index e51ad0fec3..30c81a8f51 100644 --- a/miniapps/meshing/mesh-optimizer.hpp +++ b/miniapps/meshing/mesh-optimizer.hpp @@ -222,6 +222,33 @@ double adapt_lim_fun(const Vector &x) return val; } +// Used for exact surface alignment +double surface_level_set(const Vector &x) +{ + const double sine = 0.25 * std::sin(4 * M_PI * x(0)); + return (x(1) >= sine + 0.5) ? 1.0 : -1.0; +} + +int material_id(int el_id, const GridFunction &g) +{ + const FiniteElementSpace *fes = g.FESpace(); + const FiniteElement *fe = fes->GetFE(el_id); + Vector g_vals; + const IntegrationRule &ir = + IntRules.Get(fe->GetGeomType(), fes->GetOrder(el_id) + 2); + + double integral = 0.0; + g.GetValues(el_id, ir, g_vals); + ElementTransformation *Tr = fes->GetMesh()->GetElementTransformation(el_id); + for (int q = 0; q < ir.GetNPoints(); q++) + { + const IntegrationPoint &ip = ir.IntPoint(q); + Tr->SetIntPoint(&ip); + integral += ip.weight * g_vals(q) * Tr->Weight(); + } + return (integral > 0.0) ? 1.0 : 0.0; +} + void DiffuseField(GridFunction &field, int smooth_steps) { //Setup the Laplacian operator diff --git a/miniapps/meshing/pmesh-optimizer.cpp b/miniapps/meshing/pmesh-optimizer.cpp index d8cb4d3e96..9e1c175db6 100644 --- a/miniapps/meshing/pmesh-optimizer.cpp +++ b/miniapps/meshing/pmesh-optimizer.cpp @@ -110,6 +110,7 @@ int main (int argc, char *argv[]) int target_id = 1; double lim_const = 0.0; double adapt_lim_const = 0.0; + double surface_const = 0.0; int quad_type = 1; int quad_order = 8; int solver_type = 0; @@ -169,6 +170,8 @@ int main (int argc, char *argv[]) args.AddOption(&lim_const, "-lc", "--limit-const", "Limiting constant."); args.AddOption(&adapt_lim_const, "-alc", "--adapt-limit-const", "Adaptive limiting coefficient constant."); + args.AddOption(&surface_const, "-sc", "--surface-const", + "Surface preservation constant."); args.AddOption(&quad_type, "-qt", "--quad-type", "Quadrature rule type:\n\t" "1: Gauss-Lobatto\n\t" @@ -371,11 +374,11 @@ int main (int argc, char *argv[]) TargetConstructor::TargetType target_t; TargetConstructor *target_c = NULL; HessianCoefficient *adapt_coeff = NULL; - H1_FECollection ind_fec(mesh_poly_deg, dim); + H1_FECollection ind_fec(mesh_poly_deg, dim, BasisType::Positive); ParFiniteElementSpace ind_fes(pmesh, &ind_fec); ParFiniteElementSpace ind_fesv(pmesh, &ind_fec, dim); ParGridFunction size(&ind_fes), aspr(&ind_fes), disc(&ind_fes), ori(&ind_fes); - ParGridFunction aspr3d(&ind_fesv), size3d(&ind_fesv); + ParGridFunction aspr3d(&ind_fesv); switch (target_id) { @@ -657,6 +660,57 @@ int main (int argc, char *argv[]) } } + // Surface alignment. + L2_FECollection mat_coll(0, dim); + ParFiniteElementSpace mat_fes(pmesh, &mat_coll); + ParGridFunction mat(&mat_fes); + ParGridFunction ls_0(&ind_fes), marker(&ind_fes); + ConstantCoefficient coef_ls(surface_const); + AdaptivityEvaluator *adapt_surface = NULL; + if (surface_const > 0.0) + { + FunctionCoefficient ls_coeff(surface_level_set); + ls_0.ProjectCoefficient(ls_coeff); + + for (int i = 0; i < pmesh->GetNE(); i++) + { + mat(i) = material_id(i, ls_0); + } + + GridFunctionCoefficient coeff_mat(&mat); + marker.ProjectDiscCoefficient(coeff_mat, GridFunction::ARITHMETIC); + for (int j = 0; j < marker.Size(); j++) + { + if (marker(j) > 0.1 && marker(j) < 0.9) { marker(j) = 1.0; } + else { marker(j) = 0.0; } + } + + if (adapt_eval == 0) { adapt_surface = new AdvectorCG; } + else if (adapt_eval == 1) + { +#ifdef MFEM_USE_GSLIB + adapt_surface = new InterpolatorFP; +#else + MFEM_ABORT("MFEM is not built with GSLIB support!"); +#endif + } + else { MFEM_ABORT("Bad interpolation option."); } + + he_nlf_integ->EnableSurfaceFitting(ls_0, marker, coef_ls, *adapt_surface); + if (visualization) + { + socketstream vis1, vis2, vis3; + common::VisualizeField(vis1, "localhost", 19916, ls_0, "Level Set 0", + 300, 600, 300, 300); + common::VisualizeField(vis2, "localhost", 19916, mat, "Materials", + 600, 600, 300, 300); + common::VisualizeField(vis3, "localhost", 19916, marker, "Dofs to Move", + 900, 600, 300, 300); + } + } + + MFEM_ABORT("test"); + // 13. Setup the final NonlinearForm (which defines the integral of interest, // its first and second derivatives). Here we can use a combination of // metrics, i.e., optimize the sum of two integrals, where both are From 6e88331f8767639d974da57c4d4555ed4f0caaa2 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Wed, 28 Oct 2020 22:51:43 -0700 Subject: [PATCH 002/198] Objective function, derivatives, normalization. --- fem/tmop.cpp | 104 +++++++++++++++++---------- fem/tmop.hpp | 20 +++--- miniapps/meshing/pmesh-optimizer.cpp | 1 + 3 files changed, 78 insertions(+), 47 deletions(-) diff --git a/fem/tmop.cpp b/fem/tmop.cpp index 078b11ac37..408279b383 100644 --- a/fem/tmop.cpp +++ b/fem/tmop.cpp @@ -1954,7 +1954,7 @@ void TMOP_Integrator::EnableAdaptiveLimiting(const ParGridFunction &z0, #endif #ifdef MFEM_USE_MPI -void TMOP_Integrator::EnableSurfaceFitting(const ParGridFunction &s0, +void TMOP_Integrator::EnableSurfaceFitting(ParGridFunction &s0, const ParGridFunction &smarker, Coefficient &coeff, AdaptivityEvaluator &ae) @@ -1968,6 +1968,12 @@ void TMOP_Integrator::EnableSurfaceFitting(const ParGridFunction &s0, *s0.ParFESpace()->FEColl(), 1); sigma_eval->SetInitialField (*sigma->FESpace()->GetMesh()->GetNodes(), *sigma); + + for (int i = 0; i < sigma->Size(); i++) + { + (*sigma)(i) = (*sigma_marker)(i) * (*sigma)(i); + } + s0 = *sigma; } #endif @@ -2044,11 +2050,10 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, zeta->GetValues(T.ElementNo, ir, zeta_q); zeta_0->GetValues(T.ElementNo, ir, zeta0_q); } - Vector sigma_q, sigma_marker_q; + Vector sigma_q; if (surface_fitting) { sigma->GetValues(T.ElementNo, ir, sigma_q); - sigma_marker->GetValues(T.ElementNo, ir, sigma_marker_q); } for (int i = 0; i < ir.GetNPoints(); i++) @@ -2083,11 +2088,8 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, if (surface_fitting) { - // TODO this is incorrect. - // The below is correct, if I know all values. - // The others should not matter -- how to do it??? - const double diff = sigma_marker_q(i) * (sigma_q(i) - 0.0); - val += coeff_sigma->Eval(*Tpr, ip) * lim_normal * diff * diff; + val += coeff_sigma->Eval(*Tpr, ip) * sigma_normal * + sigma_q(i) * sigma_q(i); } energy += weight * val; @@ -2260,13 +2262,13 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el, if (zeta) { - AssembleElemVecAdaptLim(*zeta, *zeta_0, *coeff_zeta, - el, weights, *Tpr, ir, PMatO); + AssembleElemVecAdaptLim(*zeta, zeta_0, *coeff_zeta, + el, weights, lim_normal, *Tpr, ir, PMatO); } if (sigma) { - AssembleElemVecAdaptLim(*sigma, *sigma_marker, *coeff_sigma, - el, weights, *Tpr, ir, PMatO); + AssembleElemVecAdaptLim(*sigma, nullptr, *coeff_sigma, + el, weights, sigma_normal, *Tpr, ir, PMatO); } @@ -2376,37 +2378,42 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el, if (zeta) { - AssembleElemGradAdaptLim(*zeta, *zeta_0, *coeff_zeta, - el, weights, *Tpr, ir, elmat); + AssembleElemGradAdaptLim(*zeta, zeta_0, *coeff_zeta, + el, weights, lim_normal, *Tpr, ir, elmat); } if (sigma) { - AssembleElemGradAdaptLim(*sigma, *sigma_marker, *coeff_sigma, - el, weights, *Tpr, ir, elmat); + AssembleElemGradAdaptLim(*sigma, nullptr, *coeff_sigma, + el, weights, sigma_normal, *Tpr, ir, elmat); } delete Tpr; } void TMOP_Integrator::AssembleElemVecAdaptLim(const GridFunction &g, - const GridFunction &g0, + const GridFunction *g0, Coefficient &coeff, const FiniteElement &el, const Vector &weights, + double normalization, IsoparametricTransformation &Tpr, const IntegrationRule &ir, DenseMatrix &mat) { if (zeta == NULL) { return; } - const int dof = el.GetDof(), dim = el.GetDim(); - Vector shape(dof), zeta_e, zeta_q, zeta0_q; + const int dof = el.GetDof(), dim = el.GetDim(), nqp = weights.Size(); + Vector shape(dof), zeta_e, zeta_q, zeta0_q(nqp); Array dofs; g.FESpace()->GetElementDofs(Tpr.ElementNo, dofs); g.GetSubVector(dofs, zeta_e); g.GetValues(Tpr.ElementNo, ir, zeta_q); - g0.GetValues(Tpr.ElementNo, ir, zeta0_q); + if (g0) + { + g0->GetValues(Tpr.ElementNo, ir, zeta0_q); + } + else { zeta0_q = 0.0; } // Project the gradient of zeta in the same space. // The FE coefficients of the gradient go in zeta_grad_e. @@ -2418,37 +2425,41 @@ void TMOP_Integrator::AssembleElemVecAdaptLim(const GridFunction &g, Vector zeta_grad_q(dim); - const int nqp = weights.Size(); for (int q = 0; q < nqp; q++) { const IntegrationPoint &ip = ir.IntPoint(q); el.CalcShape(ip, shape); zeta_grad_e.MultTranspose(shape, zeta_grad_q); zeta_grad_q *= 2.0 * (zeta_q(q) - zeta0_q(q)); - zeta_grad_q *= weights(q) * lim_normal * coeff.Eval(Tpr, ip); + zeta_grad_q *= weights(q) * normalization * coeff.Eval(Tpr, ip); AddMultVWt(shape, zeta_grad_q, mat); } } void TMOP_Integrator::AssembleElemGradAdaptLim(const GridFunction &g, - const GridFunction &g0, + const GridFunction *g0, Coefficient &coeff, const FiniteElement &el, const Vector &weights, + double normalization, IsoparametricTransformation &Tpr, const IntegrationRule &ir, DenseMatrix &mat) { if (zeta == NULL) { return; } - const int dof = el.GetDof(), dim = el.GetDim(); - Vector shape(dof), zeta_e, zeta_q, zeta0_q; + const int dof = el.GetDof(), dim = el.GetDim(), nqp = weights.Size(); + Vector shape(dof), zeta_e, zeta_q, zeta0_q(nqp); Array dofs; g.FESpace()->GetElementDofs(Tpr.ElementNo, dofs); g.GetSubVector(dofs, zeta_e); g.GetValues(Tpr.ElementNo, ir, zeta_q); - g0.GetValues(Tpr.ElementNo, ir, zeta0_q); + if (g0) + { + g0->GetValues(Tpr.ElementNo, ir, zeta0_q); + } + else { zeta0_q = 0.0; } // Project the gradient of zeta in the same space. // The FE coefficients of the gradient go in zeta_grad_e. @@ -2468,7 +2479,6 @@ void TMOP_Integrator::AssembleElemGradAdaptLim(const GridFunction &g, Vector zeta_grad_q(dim); DenseMatrix zeta_grad_grad_q(dim, dim); - const int nqp = weights.Size(); for (int q = 0; q < nqp; q++) { const IntegrationPoint &ip = ir.IntPoint(q); @@ -2478,7 +2488,7 @@ void TMOP_Integrator::AssembleElemGradAdaptLim(const GridFunction &g, Vector gg_ptr(zeta_grad_grad_q.GetData(), dim*dim); zeta_grad_grad_e.MultTranspose(shape, gg_ptr); - const double w = weights(q) * lim_normal * coeff.Eval(Tpr, ip); + const double w = weights(q) * normalization * coeff.Eval(Tpr, ip); for (int i = 0; i < dof * dim; i++) { const int idof = i % dof, idim = i / dof; @@ -2579,7 +2589,8 @@ void TMOP_Integrator::AssembleElementVectorFD(const FiniteElement &el, } PMatO.UseExternalData(elvect.GetData(), dof, dim); - AssembleElemVecAdaptLim(*zeta, *zeta_0, *coeff_zeta, el, weights, Tpr, ir, PMatO); + AssembleElemVecAdaptLim(*zeta, zeta_0, *coeff_zeta, el, + weights, lim_normal, Tpr, ir, PMatO); } } @@ -2674,35 +2685,39 @@ void TMOP_Integrator::AssembleElementGradFD(const FiniteElement &el, weights(q) = ir.IntPoint(q).weight * Jtr(q).Det(); } - AssembleElemGradAdaptLim(*zeta, *zeta_0, *coeff_zeta, el, weights, Tpr, ir, elmat); + AssembleElemGradAdaptLim(*zeta, zeta_0, *coeff_zeta, el, + weights, lim_normal, Tpr, ir, elmat); } } void TMOP_Integrator::EnableNormalization(const GridFunction &x) { - ComputeNormalizationEnergies(x, metric_normal, lim_normal); + ComputeNormalizationEnergies(x, metric_normal, lim_normal, sigma_normal); metric_normal = 1.0 / metric_normal; lim_normal = 1.0 / lim_normal; + if (sigma) { sigma_normal = 1.0 / sigma_normal; } } #ifdef MFEM_USE_MPI void TMOP_Integrator::ParEnableNormalization(const ParGridFunction &x) { - double loc[2]; - ComputeNormalizationEnergies(x, loc[0], loc[1]); - double rdc[2]; - MPI_Allreduce(loc, rdc, 2, MPI_DOUBLE, MPI_SUM, x.ParFESpace()->GetComm()); + double loc[3]; + ComputeNormalizationEnergies(x, loc[0], loc[1], loc[2]); + double rdc[3]; + MPI_Allreduce(loc, rdc, 3, MPI_DOUBLE, MPI_SUM, x.ParFESpace()->GetComm()); metric_normal = 1.0 / rdc[0]; lim_normal = 1.0 / rdc[1]; + if (sigma) { sigma_normal = 1.0 / rdc[2]; } } #endif void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x, double &metric_energy, - double &lim_energy) + double &lim_energy, + double &sigma_energy) { Array vdofs; - Vector x_vals; + Vector x_vals, sigma_q; const FiniteElementSpace* const fes = x.FESpace(); const int dim = fes->GetMesh()->Dimension(); @@ -2712,6 +2727,7 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x, metric_energy = 0.0; lim_energy = 0.0; + sigma_energy = 0.0; for (int i = 0; i < fes->GetNE(); i++) { const FiniteElement *fe = fes->GetFE(i); @@ -2727,6 +2743,8 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x, targetC->ComputeElementTargets(i, *fe, ir, x_vals, Jtr); + if (sigma) { sigma->GetValues(i, ir, sigma_q); } + for (int q = 0; q < nqp; q++) { const IntegrationPoint &ip = ir.IntPoint(q); @@ -2740,6 +2758,8 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x, metric_energy += weight * metric->EvalW(Jpt); lim_energy += weight; + + if (sigma) { sigma_energy += weight * sigma_q(i) * sigma_q(i); } } } if (targetC->ContainsVolumeInfo() == false) @@ -2786,7 +2806,15 @@ void TMOP_Integrator::UpdateAfterMeshChange(const Vector &new_x) { // Update zeta if adaptive limiting is enabled. if (zeta) { adapt_eval->ComputeAtNewPosition(new_x, *zeta); } - if (sigma) { sigma_eval->ComputeAtNewPosition(new_x, *sigma); } + // Update sigma if surface fitting is enabled. + if (sigma) + { + sigma_eval->ComputeAtNewPosition(new_x, *sigma); + for (int i = 0; i < sigma->Size(); i++) + { + (*sigma)(i) = (*sigma_marker)(i) * (*sigma)(i); + } + } } void TMOP_Integrator::ComputeFDh(const Vector &x, const FiniteElementSpace &fes) diff --git a/fem/tmop.hpp b/fem/tmop.hpp index 01e4d53222..4d1a04d951 100644 --- a/fem/tmop.hpp +++ b/fem/tmop.hpp @@ -919,9 +919,10 @@ protected: // Surface fitting. GridFunction *sigma; // Owned. Updated by sigma_eval. - const GridFunction *sigma_marker; // Not owned. + const GridFunction *sigma_marker; // Not owned. Coefficient *coeff_sigma; // Not owned. AdaptivityEvaluator *sigma_eval; // Not owned. + double sigma_normal; DiscreteAdaptTC *discr_tc; @@ -952,7 +953,8 @@ protected: DenseMatrix DSh, DS, Jrt, Jpr, Jpt, P, PMatI, PMatO; void ComputeNormalizationEnergies(const GridFunction &x, - double &metric_energy, double &lim_energy); + double &metric_energy, double &lim_energy, + double &sigma_energy); void AssembleElementVectorExact(const FiniteElement &el, @@ -972,14 +974,14 @@ protected: ElementTransformation &T, const Vector &elfun, DenseMatrix &elmat); - void AssembleElemVecAdaptLim(const GridFunction &g, const GridFunction &g0, - Coefficient &coeff, - const FiniteElement &el, const Vector &weights, + void AssembleElemVecAdaptLim(const GridFunction &g, const GridFunction *g0, + Coefficient &coeff, const FiniteElement &el, + const Vector &weights, double normalization, IsoparametricTransformation &Tpr, const IntegrationRule &ir, DenseMatrix &m); - void AssembleElemGradAdaptLim(const GridFunction &g, const GridFunction &g0, - Coefficient &coeff, - const FiniteElement &el, const Vector &weights, + void AssembleElemGradAdaptLim(const GridFunction &g, const GridFunction *g0, + Coefficient &coeff, const FiniteElement &el, + const Vector &weights, double normalization, IsoparametricTransformation &Tpr, const IntegrationRule &ir, DenseMatrix &m); @@ -1093,7 +1095,7 @@ public: #endif #ifdef MFEM_USE_MPI - void EnableSurfaceFitting(const ParGridFunction &s0, + void EnableSurfaceFitting(ParGridFunction &s0, const ParGridFunction &smarker, Coefficient &coeff, AdaptivityEvaluator &ae); #endif diff --git a/miniapps/meshing/pmesh-optimizer.cpp b/miniapps/meshing/pmesh-optimizer.cpp index 9e1c175db6..2498c9588a 100644 --- a/miniapps/meshing/pmesh-optimizer.cpp +++ b/miniapps/meshing/pmesh-optimizer.cpp @@ -671,6 +671,7 @@ int main (int argc, char *argv[]) { FunctionCoefficient ls_coeff(surface_level_set); ls_0.ProjectCoefficient(ls_coeff); + DiffuseField(ls_0, 2); for (int i = 0; i < pmesh->GetNE(); i++) { From be3fb993f13b8182bbe72003415a682460d73697 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Mon, 2 Nov 2020 10:06:18 -0800 Subject: [PATCH 003/198] Non-variational form of the fitting term. --- fem/tmop.cpp | 205 ++++++++++++++++++++++----- fem/tmop.hpp | 20 ++- miniapps/meshing/mesh-optimizer.hpp | 14 +- miniapps/meshing/pmesh-optimizer.cpp | 48 +++++-- 4 files changed, 233 insertions(+), 54 deletions(-) diff --git a/fem/tmop.cpp b/fem/tmop.cpp index 408279b383..66291ee5a2 100644 --- a/fem/tmop.cpp +++ b/fem/tmop.cpp @@ -1954,8 +1954,8 @@ void TMOP_Integrator::EnableAdaptiveLimiting(const ParGridFunction &z0, #endif #ifdef MFEM_USE_MPI -void TMOP_Integrator::EnableSurfaceFitting(ParGridFunction &s0, - const ParGridFunction &smarker, +void TMOP_Integrator::EnableSurfaceFitting(const ParGridFunction &s0, + const Array &smarker, Coefficient &coeff, AdaptivityEvaluator &ae) { @@ -1968,12 +1968,6 @@ void TMOP_Integrator::EnableSurfaceFitting(ParGridFunction &s0, *s0.ParFESpace()->FEColl(), 1); sigma_eval->SetInitialField (*sigma->FESpace()->GetMesh()->GetNodes(), *sigma); - - for (int i = 0; i < sigma->Size(); i++) - { - (*sigma)(i) = (*sigma_marker)(i) * (*sigma)(i); - } - s0 = *sigma; } #endif @@ -2050,11 +2044,6 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, zeta->GetValues(T.ElementNo, ir, zeta_q); zeta_0->GetValues(T.ElementNo, ir, zeta0_q); } - Vector sigma_q; - if (surface_fitting) - { - sigma->GetValues(T.ElementNo, ir, sigma_q); - } for (int i = 0; i < ir.GetNPoints(); i++) { @@ -2086,14 +2075,30 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, val += coeff_zeta->Eval(*Tpr, ip) * lim_normal * diff * diff; } - if (surface_fitting) - { - val += coeff_sigma->Eval(*Tpr, ip) * sigma_normal * - sigma_q(i) * sigma_q(i); - } - energy += weight * val; } + + // Non-variation contribution from the surface alignment term. + if (surface_fitting) + { + const IntegrationRule &ir_s = + sigma->FESpace()->GetFE(Tpr->ElementNo)->GetNodes(); + Array dofs; + Vector sigma_e; + sigma->FESpace()->GetElementDofs(Tpr->ElementNo, dofs); + sigma->GetSubVector(dofs, sigma_e); + for (int s = 0; s < dofs.Size(); s++) + { + if ((*sigma_marker)[dofs[s]] == true) + { + const IntegrationPoint &ip_s = ir_s.IntPoint(s); + Tpr->SetIntPoint(&ip_s); + energy += coeff_sigma->Eval(*Tpr, ip_s) * sigma_normal * + sigma_e(s) * sigma_e(s); + } + } + } + delete Tpr; return energy; @@ -2267,11 +2272,9 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el, } if (sigma) { - AssembleElemVecAdaptLim(*sigma, nullptr, *coeff_sigma, - el, weights, sigma_normal, *Tpr, ir, PMatO); + AssembleElemVecSurfAlign(*sigma, *sigma_marker, *coeff_sigma, el, + *Tpr, sigma_normal, PMatO); } - - delete Tpr; } @@ -2322,7 +2325,7 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el, // Define ref->physical transformation, when a Coefficient is specified. IsoparametricTransformation *Tpr = NULL; - if (coeff1 || coeff0 || zeta) + if (coeff1 || coeff0 || zeta || sigma) { Tpr = new IsoparametricTransformation; Tpr->SetFE(&el); @@ -2383,8 +2386,8 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el, } if (sigma) { - AssembleElemGradAdaptLim(*sigma, nullptr, *coeff_sigma, - el, weights, sigma_normal, *Tpr, ir, elmat); + AssembleElemGradSurfAlign(*sigma, *sigma_marker, *coeff_sigma, el, + *Tpr, sigma_normal, elmat); } delete Tpr; @@ -2400,8 +2403,6 @@ void TMOP_Integrator::AssembleElemVecAdaptLim(const GridFunction &g, const IntegrationRule &ir, DenseMatrix &mat) { - if (zeta == NULL) { return; } - const int dof = el.GetDof(), dim = el.GetDim(), nqp = weights.Size(); Vector shape(dof), zeta_e, zeta_q, zeta0_q(nqp); @@ -2446,8 +2447,6 @@ void TMOP_Integrator::AssembleElemGradAdaptLim(const GridFunction &g, const IntegrationRule &ir, DenseMatrix &mat) { - if (zeta == NULL) { return; } - const int dof = el.GetDof(), dim = el.GetDim(), nqp = weights.Size(); Vector shape(dof), zeta_e, zeta_q, zeta0_q(nqp); @@ -2507,6 +2506,130 @@ void TMOP_Integrator::AssembleElemGradAdaptLim(const GridFunction &g, } } +void TMOP_Integrator::AssembleElemVecSurfAlign(const GridFunction &sigma, + const Array &sigma_marker, + Coefficient &coeff, + const FiniteElement &el_x, + IsoparametricTransformation &Tpr, + double normalization, + DenseMatrix &mat) +{ + const FiniteElement &el_s = *sigma.FESpace()->GetFE(Tpr.ElementNo); + + const int dof_x = el_x.GetDof(), dim = el_x.GetDim(), + dof_s = el_s.GetDof(); + + Vector sigma_e; + Array dofs; + sigma.FESpace()->GetElementDofs(Tpr.ElementNo, dofs); + sigma.GetSubVector(dofs, sigma_e); + + // Project the gradient of sigma in the same space. + // The FE coefficients of the gradient go in zeta_grad_e. + DenseMatrix sigma_grad_e(dof_s, dim); + DenseMatrix grad_phys; // This will be (dof x dim, dof). + el_s.ProjectGrad(el_s, Tpr, grad_phys); + Vector grad_ptr(sigma_grad_e.GetData(), dof_s * dim); + grad_phys.Mult(sigma_e, grad_ptr); + + const IntegrationRule &ir = el_s.GetNodes(); + Vector shape_x(dof_x), shape_s(dof_s); + + Vector sigma_grad_s(dim); + + for (int s = 0; s < dof_s; s++) + { + if (sigma_marker[dofs[s]] == false) { continue; } + + const IntegrationPoint &ip = ir.IntPoint(s); + Tpr.SetIntPoint(&ip); + el_x.CalcShape(ip, shape_x); + el_s.CalcShape(ip, shape_s); + + // Note that this gradient is already in physical space. + sigma_grad_e.MultTranspose(shape_s, sigma_grad_s); + + sigma_grad_s *= 2.0 * sigma_e(s); + sigma_grad_s *= normalization * coeff.Eval(Tpr, ip); + + AddMultVWt(shape_x, sigma_grad_s, mat); + } +} + +void TMOP_Integrator::AssembleElemGradSurfAlign(const GridFunction &sigma, + const Array &sigma_marker, + Coefficient &coeff, + const FiniteElement &el_x, + IsoparametricTransformation &Tpr, + double normalization, + DenseMatrix &mat) +{ + const FiniteElement &el_s = *sigma.FESpace()->GetFE(Tpr.ElementNo); + + const int dof_x = el_x.GetDof(), dim = el_x.GetDim(), + dof_s = el_s.GetDof(); + + Vector sigma_e; + + Array dofs; + sigma.FESpace()->GetElementDofs(Tpr.ElementNo, dofs); + sigma.GetSubVector(dofs, sigma_e); + + // Project the gradient of sigma in the same space. + // The FE coefficients of the gradient go in sigma_grad_e. + DenseMatrix sigma_grad_e(dof_s, dim); + DenseMatrix grad_phys; // This will be (dof x dim, dof). + el_s.ProjectGrad(el_s, Tpr, grad_phys); + Vector grad_ptr(sigma_grad_e.GetData(), dof_s * dim); + grad_phys.Mult(sigma_e, grad_ptr); + + // Project the gradient of each gradient of sigma in the same space. + // The FE coefficients of the second derivatives go in sigma_grad_grad_e. + DenseMatrix sigma_grad_grad_e(dof_s * dim, dim); + Mult(grad_phys, sigma_grad_e, sigma_grad_grad_e); + // Reshape to be more convenient later (no change in the data). + sigma_grad_grad_e.SetSize(dof_s, dim * dim); + + const IntegrationRule &ir = el_s.GetNodes(); + Vector shape_x(dof_x), shape_s(dof_s); + + Vector sigma_grad_s(dim); + DenseMatrix sigma_grad_grad_s(dim, dim); + + for (int s = 0; s < dof_s; s++) + { + if (sigma_marker[dofs[s]] == false) { continue; } + + const IntegrationPoint &ip = ir.IntPoint(s); + Tpr.SetIntPoint(&ip); + el_x.CalcShape(ip, shape_x); + el_s.CalcShape(ip, shape_s); + + // These are the sums over k at the dof s (looking at the notes). + sigma_grad_e.MultTranspose(shape_s, sigma_grad_s); + Vector gg_ptr(sigma_grad_grad_s.GetData(), dim * dim); + sigma_grad_grad_e.MultTranspose(shape_s, gg_ptr); + + // Loops over the local matrix. + const double w = normalization * coeff.Eval(Tpr, ip); + for (int i = 0; i < dof_x * dim; i++) + { + const int idof = i % dof_x, idim = i / dof_x; + for (int j = 0; j <= i; j++) + { + const int jdof = j % dof_x, jdim = j / dof_x; + const double entry = + w * ( 2.0 * sigma_grad_s(idim) * shape_x(idof) * + /* */ sigma_grad_s(jdim) * shape_x(jdof) + + 2.0 * sigma_e(s) * sigma_grad_grad_s(idim, jdim) * + /* */ shape_x(idof) * shape_x(jdof)); + mat(i, j) += entry; + if (i != j) { mat(j, i) += entry; } + } + } + } +} + double TMOP_Integrator::GetFDDerivative(const FiniteElement &el, ElementTransformation &T, Vector &elfun, const int dofidx, @@ -2743,8 +2866,6 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x, targetC->ComputeElementTargets(i, *fe, ir, x_vals, Jtr); - if (sigma) { sigma->GetValues(i, ir, sigma_q); } - for (int q = 0; q < nqp; q++) { const IntegrationPoint &ip = ir.IntPoint(q); @@ -2758,8 +2879,22 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x, metric_energy += weight * metric->EvalW(Jpt); lim_energy += weight; + } - if (sigma) { sigma_energy += weight * sigma_q(i) * sigma_q(i); } + // Non-variation contribution from the surface alignment term. + if (sigma) + { + Array dofs; + Vector sigma_e; + sigma->FESpace()->GetElementDofs(i, dofs); + sigma->GetSubVector(dofs, sigma_e); + for (int s = 0; s < dofs.Size(); s++) + { + if ((*sigma_marker)[dofs[s]] == true) + { + sigma_energy += sigma_e(s) * sigma_e(s); + } + } } } if (targetC->ContainsVolumeInfo() == false) @@ -2810,10 +2945,6 @@ void TMOP_Integrator::UpdateAfterMeshChange(const Vector &new_x) if (sigma) { sigma_eval->ComputeAtNewPosition(new_x, *sigma); - for (int i = 0; i < sigma->Size(); i++) - { - (*sigma)(i) = (*sigma_marker)(i) * (*sigma)(i); - } } } diff --git a/fem/tmop.hpp b/fem/tmop.hpp index 4d1a04d951..aaa7f78ea5 100644 --- a/fem/tmop.hpp +++ b/fem/tmop.hpp @@ -919,7 +919,7 @@ protected: // Surface fitting. GridFunction *sigma; // Owned. Updated by sigma_eval. - const GridFunction *sigma_marker; // Not owned. + const Array *sigma_marker; // Not owned. Coefficient *coeff_sigma; // Not owned. AdaptivityEvaluator *sigma_eval; // Not owned. double sigma_normal; @@ -985,6 +985,17 @@ protected: IsoparametricTransformation &Tpr, const IntegrationRule &ir, DenseMatrix &m); + void AssembleElemVecSurfAlign(const GridFunction &sigma, + const Array &sigma_marker, + Coefficient &coeff, const FiniteElement &el_x, + IsoparametricTransformation &Tpr, + double normalization, DenseMatrix &mat); + void AssembleElemGradSurfAlign(const GridFunction &sigma, + const Array &sigma_marker, + Coefficient &coeff, const FiniteElement &el_x, + IsoparametricTransformation &Tpr, + double normalization, DenseMatrix &mat); + double GetFDDerivative(const FiniteElement &el, ElementTransformation &T, Vector &elfun, const int nodenum,const int idir, @@ -1033,7 +1044,8 @@ public: nodes0(NULL), coeff0(NULL), lim_dist(NULL), lim_func(NULL), lim_normal(1.0), zeta_0(NULL), zeta(NULL), coeff_zeta(NULL), adapt_eval(NULL), - sigma(NULL), sigma_marker(NULL), coeff_sigma(NULL), sigma_eval(NULL), + sigma(NULL), sigma_marker(NULL), coeff_sigma(NULL), + sigma_eval(NULL), sigma_normal(1.0), discr_tc(dynamic_cast(tc)), fdflag(false), dxscale(1.0e3), fd_call_flag(false), exact_action(false) { } @@ -1095,8 +1107,8 @@ public: #endif #ifdef MFEM_USE_MPI - void EnableSurfaceFitting(ParGridFunction &s0, - const ParGridFunction &smarker, Coefficient &coeff, + void EnableSurfaceFitting(const ParGridFunction &s0, + const Array &smarker, Coefficient &coeff, AdaptivityEvaluator &ae); #endif diff --git a/miniapps/meshing/mesh-optimizer.hpp b/miniapps/meshing/mesh-optimizer.hpp index 30c81a8f51..77b8ba256f 100644 --- a/miniapps/meshing/mesh-optimizer.hpp +++ b/miniapps/meshing/mesh-optimizer.hpp @@ -225,8 +225,18 @@ double adapt_lim_fun(const Vector &x) // Used for exact surface alignment double surface_level_set(const Vector &x) { - const double sine = 0.25 * std::sin(4 * M_PI * x(0)); - return (x(1) >= sine + 0.5) ? 1.0 : -1.0; + const int type = 0; + if (type == 0) + { + const double sine = 0.25 * std::sin(4 * M_PI * x(0)); + return (x(1) >= sine + 0.5) ? 1.0 : -1.0; + } + else + { + const double xc = x(0) - 0.5, yc = x(1) - 0.5; + const double r = sqrt(xc*xc + yc*yc); + return (r > 0.2) ? 1.0 : -1.0; + } } int material_id(int el_id, const GridFunction &g) diff --git a/miniapps/meshing/pmesh-optimizer.cpp b/miniapps/meshing/pmesh-optimizer.cpp index 2498c9588a..6ca8fbe442 100644 --- a/miniapps/meshing/pmesh-optimizer.cpp +++ b/miniapps/meshing/pmesh-optimizer.cpp @@ -374,7 +374,7 @@ int main (int argc, char *argv[]) TargetConstructor::TargetType target_t; TargetConstructor *target_c = NULL; HessianCoefficient *adapt_coeff = NULL; - H1_FECollection ind_fec(mesh_poly_deg, dim, BasisType::Positive); + H1_FECollection ind_fec(mesh_poly_deg, dim); ParFiniteElementSpace ind_fes(pmesh, &ind_fec); ParFiniteElementSpace ind_fesv(pmesh, &ind_fec, dim); ParGridFunction size(&ind_fes), aspr(&ind_fes), disc(&ind_fes), ori(&ind_fes); @@ -620,8 +620,6 @@ int main (int argc, char *argv[]) << irules->Get(Geometry::PRISM, quad_order).GetNPoints() << endl; } - if (normalization) { he_nlf_integ->ParEnableNormalization(x0); } - // Limit the node movement. // The limiting distances can be given by a general function of space. ParGridFunction dist(pfespace); @@ -664,14 +662,16 @@ int main (int argc, char *argv[]) L2_FECollection mat_coll(0, dim); ParFiniteElementSpace mat_fes(pmesh, &mat_coll); ParGridFunction mat(&mat_fes); - ParGridFunction ls_0(&ind_fes), marker(&ind_fes); + ParGridFunction marker_gf(&ind_fes); + ParGridFunction ls_0(&ind_fes); + Array marker(ls_0.Size()); ConstantCoefficient coef_ls(surface_const); AdaptivityEvaluator *adapt_surface = NULL; if (surface_const > 0.0) { FunctionCoefficient ls_coeff(surface_level_set); ls_0.ProjectCoefficient(ls_coeff); - DiffuseField(ls_0, 2); + DiffuseField(ls_0, 10); for (int i = 0; i < pmesh->GetNE(); i++) { @@ -679,11 +679,19 @@ int main (int argc, char *argv[]) } GridFunctionCoefficient coeff_mat(&mat); - marker.ProjectDiscCoefficient(coeff_mat, GridFunction::ARITHMETIC); + marker_gf.ProjectDiscCoefficient(coeff_mat, GridFunction::ARITHMETIC); for (int j = 0; j < marker.Size(); j++) { - if (marker(j) > 0.1 && marker(j) < 0.9) { marker(j) = 1.0; } - else { marker(j) = 0.0; } + if (marker_gf(j) > 0.1 && marker_gf(j) < 0.9) + { + marker[j] = true; + marker_gf(j) = 1.0; + } + else + { + marker[j] = false; + marker_gf(j) = 0.0; + } } if (adapt_eval == 0) { adapt_surface = new AdvectorCG; } @@ -705,12 +713,19 @@ int main (int argc, char *argv[]) 300, 600, 300, 300); common::VisualizeField(vis2, "localhost", 19916, mat, "Materials", 600, 600, 300, 300); - common::VisualizeField(vis3, "localhost", 19916, marker, "Dofs to Move", + common::VisualizeField(vis3, "localhost", 19916, marker_gf, "Dofs to Move", 900, 600, 300, 300); + + socketstream vis4; + common::VisualizeField(vis4, "localhost", 19916, ls_0, "Level Set 0", + 300, 600, 300, 300); + } } - MFEM_ABORT("test"); + // Has to be after the enabling of the limiting / alignment, as it computes + // normalization factors for these terms as well. + if (normalization) { he_nlf_integ->ParEnableNormalization(x0); } // 13. Setup the final NonlinearForm (which defines the integral of interest, // its first and second derivatives). Here we can use a combination of @@ -907,16 +922,27 @@ int main (int argc, char *argv[]) pmesh->PrintAsOne(mesh_ofs); } + if (visualization) + { + socketstream vis2, vis3; + common::VisualizeField(vis2, "localhost", 19916, mat, "Materials", + 600, 900, 300, 300); + common::VisualizeField(vis3, "localhost", 19916, marker_gf, "Surface dof", + 900, 900, 300, 300); + } + // 17. Compute the amount of energy decrease. const double fin_energy = a.GetParGridFunctionEnergy(x); double metric_part = fin_energy; - if (lim_const > 0.0 || adapt_lim_const > 0.0) + if (lim_const > 0.0 || adapt_lim_const > 0.0 || surface_const > 0.0) { lim_coeff.constant = 0.0; coef_zeta.constant = 0.0; + coef_ls.constant = 0.0; metric_part = a.GetParGridFunctionEnergy(x); lim_coeff.constant = lim_const; coef_zeta.constant = adapt_lim_const; + coef_ls.constant = surface_const; } if (myid == 0) { From 2d39d300d190e9da6eece0658a629475d09e2059 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Fri, 18 Dec 2020 18:34:36 -0800 Subject: [PATCH 004/198] Updated the fitting method. --- fem/tmop.cpp | 300 +++++++++++++++------------- fem/tmop.hpp | 24 +-- miniapps/meshing/mesh-optimizer.hpp | 2 +- 3 files changed, 178 insertions(+), 148 deletions(-) diff --git a/fem/tmop.cpp b/fem/tmop.cpp index 66291ee5a2..303c6cdb1c 100644 --- a/fem/tmop.cpp +++ b/fem/tmop.cpp @@ -1887,6 +1887,7 @@ TMOP_Integrator::~TMOP_Integrator() delete lim_func; delete zeta; delete sigma; + delete sigma_bar; for (int i = 0; i < ElemDer.Size(); i++) { delete ElemDer[i]; @@ -1964,6 +1965,13 @@ void TMOP_Integrator::EnableSurfaceFitting(const ParGridFunction &s0, coeff_sigma = &coeff; sigma_eval = &ae; + // Compute the restricted sigma. + sigma_bar = new GridFunction(*sigma); + for (int i = 0; i < sigma_marker->Size(); i++) + { + if ((*sigma_marker)[i] == false) { (*sigma_bar)(i) = 0.0; } + } + sigma_eval->SetParMetaInfo(*s0.ParFESpace()->GetParMesh(), *s0.ParFESpace()->FEColl(), 1); sigma_eval->SetInitialField @@ -1976,13 +1984,14 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, const Vector &elfun) { const int dof = el.GetDof(), dim = el.GetDim(); + const int el_id = T.ElementNo; double energy; // No adaptive limiting / surface fitting terms if the function is called // as part of a FD derivative computation (because we include the exact // derivatives of these terms in FD computations). const bool adaptive_limiting = (zeta && fd_call_flag == false); - const bool surface_fitting = (sigma && fd_call_flag == false); + const bool surface_fit = (sigma && fd_call_flag == false); DSh.SetSize(dof, dim); Jrt.SetSize(dim); @@ -1994,7 +2003,7 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, energy = 0.0; DenseTensor Jtr(dim, dim, ir.GetNPoints()); - targetC->ComputeElementTargets(T.ElementNo, el, ir, elfun, Jtr); + targetC->ComputeElementTargets(el_id, el, ir, elfun, Jtr); // Limited case. Vector shape, p, p0, d_vals; @@ -2007,11 +2016,11 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, pos0.SetSize(dof, dim); Vector pos0V(pos0.Data(), dof * dim); Array pos_dofs; - nodes0->FESpace()->GetElementVDofs(T.ElementNo, pos_dofs); + nodes0->FESpace()->GetElementVDofs(el_id, pos_dofs); nodes0->GetSubVector(pos_dofs, pos0V); if (lim_dist) { - lim_dist->GetValues(T.ElementNo, ir, d_vals); + lim_dist->GetValues(el_id, ir, d_vals); } else { @@ -2021,11 +2030,11 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, // Define ref->physical transformation, when a Coefficient is specified. IsoparametricTransformation *Tpr = NULL; - if (coeff1 || coeff0 || adaptive_limiting || surface_fitting) + if (coeff1 || coeff0 || adaptive_limiting || surface_fit) { Tpr = new IsoparametricTransformation; Tpr->SetFE(&el); - Tpr->ElementNo = T.ElementNo; + Tpr->ElementNo = el_id; Tpr->ElementType = ElementTransformation::ELEMENT; Tpr->Attribute = T.Attribute; Tpr->GetPointMat().Transpose(PMatI); // PointMat = PMatI^T @@ -2041,8 +2050,14 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, Vector zeta_q, zeta0_q; if (adaptive_limiting) { - zeta->GetValues(T.ElementNo, ir, zeta_q); - zeta_0->GetValues(T.ElementNo, ir, zeta0_q); + zeta->GetValues(el_id, ir, zeta_q); + zeta_0->GetValues(el_id, ir, zeta0_q); + } + + Vector sigma_bar_q; + if (surface_fit) + { + sigma_bar->GetValues(el_id, ir, sigma_bar_q); } for (int i = 0; i < ir.GetNPoints(); i++) @@ -2069,34 +2084,21 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, lim_func->Eval(p, p0, d_vals(i)) * coeff0->Eval(*Tpr, ip); } + // Contribution from the adaptive limiting term. if (adaptive_limiting) { const double diff = zeta_q(i) - zeta0_q(i); val += coeff_zeta->Eval(*Tpr, ip) * lim_normal * diff * diff; } - energy += weight * val; - } - - // Non-variation contribution from the surface alignment term. - if (surface_fitting) - { - const IntegrationRule &ir_s = - sigma->FESpace()->GetFE(Tpr->ElementNo)->GetNodes(); - Array dofs; - Vector sigma_e; - sigma->FESpace()->GetElementDofs(Tpr->ElementNo, dofs); - sigma->GetSubVector(dofs, sigma_e); - for (int s = 0; s < dofs.Size(); s++) + // Contribution from the surface fitting term. + if (surface_fit) { - if ((*sigma_marker)[dofs[s]] == true) - { - const IntegrationPoint &ip_s = ir_s.IntPoint(s); - Tpr->SetIntPoint(&ip_s); - energy += coeff_sigma->Eval(*Tpr, ip_s) * sigma_normal * - sigma_e(s) * sigma_e(s); - } + val += coeff_sigma->Eval(*Tpr, ip) * sigma_normal * + sigma_bar_q(i) * sigma_bar_q(i); } + + energy += weight * val; } delete Tpr; @@ -2270,11 +2272,8 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el, AssembleElemVecAdaptLim(*zeta, zeta_0, *coeff_zeta, el, weights, lim_normal, *Tpr, ir, PMatO); } - if (sigma) - { - AssembleElemVecSurfAlign(*sigma, *sigma_marker, *coeff_sigma, el, - *Tpr, sigma_normal, PMatO); - } + if (sigma) { AssembleElemVecSurfFit(el, *Tpr, ir, weights, PMatO); } + delete Tpr; } @@ -2384,11 +2383,7 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el, AssembleElemGradAdaptLim(*zeta, zeta_0, *coeff_zeta, el, weights, lim_normal, *Tpr, ir, elmat); } - if (sigma) - { - AssembleElemGradSurfAlign(*sigma, *sigma_marker, *coeff_sigma, el, - *Tpr, sigma_normal, elmat); - } + if (sigma) { AssembleElemGradSurfFit(el, *Tpr, ir, weights, elmat); } delete Tpr; } @@ -2506,74 +2501,25 @@ void TMOP_Integrator::AssembleElemGradAdaptLim(const GridFunction &g, } } -void TMOP_Integrator::AssembleElemVecSurfAlign(const GridFunction &sigma, - const Array &sigma_marker, - Coefficient &coeff, - const FiniteElement &el_x, - IsoparametricTransformation &Tpr, - double normalization, - DenseMatrix &mat) +void TMOP_Integrator::AssembleElemVecSurfFit(const FiniteElement &el_x, + IsoparametricTransformation &Tpr, + const IntegrationRule &ir_quad, + const Vector &weights, + DenseMatrix &mat) { - const FiniteElement &el_s = *sigma.FESpace()->GetFE(Tpr.ElementNo); + const int el_id = Tpr.ElementNo; + const FiniteElement &el_s = *sigma->FESpace()->GetFE(el_id); const int dof_x = el_x.GetDof(), dim = el_x.GetDim(), - dof_s = el_s.GetDof(); + dof_s = el_s.GetDof(), nqp = ir_quad.GetNPoints(); - Vector sigma_e; + Vector sigma_e, sigma_bar_e; + Vector sigma_bar_q; Array dofs; - sigma.FESpace()->GetElementDofs(Tpr.ElementNo, dofs); - sigma.GetSubVector(dofs, sigma_e); - - // Project the gradient of sigma in the same space. - // The FE coefficients of the gradient go in zeta_grad_e. - DenseMatrix sigma_grad_e(dof_s, dim); - DenseMatrix grad_phys; // This will be (dof x dim, dof). - el_s.ProjectGrad(el_s, Tpr, grad_phys); - Vector grad_ptr(sigma_grad_e.GetData(), dof_s * dim); - grad_phys.Mult(sigma_e, grad_ptr); - - const IntegrationRule &ir = el_s.GetNodes(); - Vector shape_x(dof_x), shape_s(dof_s); - - Vector sigma_grad_s(dim); - - for (int s = 0; s < dof_s; s++) - { - if (sigma_marker[dofs[s]] == false) { continue; } - - const IntegrationPoint &ip = ir.IntPoint(s); - Tpr.SetIntPoint(&ip); - el_x.CalcShape(ip, shape_x); - el_s.CalcShape(ip, shape_s); - - // Note that this gradient is already in physical space. - sigma_grad_e.MultTranspose(shape_s, sigma_grad_s); - - sigma_grad_s *= 2.0 * sigma_e(s); - sigma_grad_s *= normalization * coeff.Eval(Tpr, ip); - - AddMultVWt(shape_x, sigma_grad_s, mat); - } -} - -void TMOP_Integrator::AssembleElemGradSurfAlign(const GridFunction &sigma, - const Array &sigma_marker, - Coefficient &coeff, - const FiniteElement &el_x, - IsoparametricTransformation &Tpr, - double normalization, - DenseMatrix &mat) -{ - const FiniteElement &el_s = *sigma.FESpace()->GetFE(Tpr.ElementNo); - - const int dof_x = el_x.GetDof(), dim = el_x.GetDim(), - dof_s = el_s.GetDof(); - - Vector sigma_e; - - Array dofs; - sigma.FESpace()->GetElementDofs(Tpr.ElementNo, dofs); - sigma.GetSubVector(dofs, sigma_e); + sigma->FESpace()->GetElementDofs(el_id, dofs); + sigma->GetSubVector(dofs, sigma_e); + sigma_bar->GetSubVector(dofs, sigma_bar_e); + sigma_bar->GetValues(el_id, ir_quad, sigma_bar_q); // Project the gradient of sigma in the same space. // The FE coefficients of the gradient go in sigma_grad_e. @@ -2583,46 +2529,132 @@ void TMOP_Integrator::AssembleElemGradSurfAlign(const GridFunction &sigma, Vector grad_ptr(sigma_grad_e.GetData(), dof_s * dim); grad_phys.Mult(sigma_e, grad_ptr); + // Gradient of sigma_bar. + DenseMatrix sigma_bar_grad_e(dof_s, dim); + Vector ptr(sigma_bar_grad_e.GetData(), dof_s * dim); + grad_phys.Mult(sigma_bar_e, ptr); + + Vector shape_x(dof_x), shape_s(dof_s), grad_q(dim); + + for (int q = 0; q < nqp; q++) + { + const IntegrationPoint &ip = ir_quad.IntPoint(q); + Tpr.SetIntPoint(&ip); + el_s.CalcShape(ip, shape_s); + + // Grad of sigma_bar at the current quad point. + sigma_bar_grad_e.MultTranspose(shape_s, grad_q); + + for (int s = 0; s < dof_s; s++) + { + if ((*sigma_marker)[dofs[s]] == false) { continue; } + + for (int d = 0; d < dim; d++) + { + grad_q(d) += sigma_grad_e(s, d) * shape_s(s); + } + } + + grad_q *= 2.0 * sigma_normal * coeff_sigma->Eval(Tpr, ip) * + weights(q) * sigma_bar_q(q); + + el_x.CalcShape(ip, shape_x); + AddMultVWt(shape_x, grad_q, mat); + } +} + +void TMOP_Integrator::AssembleElemGradSurfFit(const FiniteElement &el_x, + IsoparametricTransformation &Tpr, + const IntegrationRule &ir_quad, + const Vector &weights, + DenseMatrix &mat) +{ + const int el_id = Tpr.ElementNo, nqp = ir_quad.GetNPoints(); + const FiniteElement &el_s = *sigma->FESpace()->GetFE(Tpr.ElementNo); + + const int dof_x = el_x.GetDof(), dim = el_x.GetDim(), + dof_s = el_s.GetDof(); + + Vector sigma_e, sigma_bar_e; + Vector sigma_bar_q; + + Array dofs; + sigma->FESpace()->GetElementDofs(el_id, dofs); + sigma->GetSubVector(dofs, sigma_e); + sigma_bar->GetSubVector(dofs, sigma_bar_e); + sigma_bar->GetValues(el_id, ir_quad, sigma_bar_q); + + // Project the gradient of sigma in the same space. + // The FE coefficients of the gradient go in sigma_grad_e. + DenseMatrix sigma_grad_e(dof_s, dim); + DenseMatrix grad_phys; // This will be (dof x dim, dof). + el_s.ProjectGrad(el_s, Tpr, grad_phys); + Vector grad_ptr(sigma_grad_e.GetData(), dof_s * dim); + grad_phys.Mult(sigma_e, grad_ptr); + + // Gradient of sigma_bar. + DenseMatrix sigma_bar_grad_e(dof_s, dim); + Vector ptr(sigma_bar_grad_e.GetData(), dof_s * dim); + grad_phys.Mult(sigma_bar_e, ptr); + // Project the gradient of each gradient of sigma in the same space. // The FE coefficients of the second derivatives go in sigma_grad_grad_e. DenseMatrix sigma_grad_grad_e(dof_s * dim, dim); Mult(grad_phys, sigma_grad_e, sigma_grad_grad_e); + + // Project the gradient of each gradient of sigma in the same space. + // The FE coefficients of the second derivatives go in sigma_grad_grad_e. + DenseMatrix sigma_bar_grad_grad_e(dof_s * dim, dim); + Mult(grad_phys, sigma_bar_grad_e, sigma_bar_grad_grad_e); // Reshape to be more convenient later (no change in the data). - sigma_grad_grad_e.SetSize(dof_s, dim * dim); + sigma_bar_grad_grad_e.SetSize(dof_s, dim * dim); - const IntegrationRule &ir = el_s.GetNodes(); - Vector shape_x(dof_x), shape_s(dof_s); + DenseMatrix sigma_bar_grad_grad_q(dim, dim); - Vector sigma_grad_s(dim); - DenseMatrix sigma_grad_grad_s(dim, dim); + Vector shape_x(dof_x), shape_s(dof_s), sigma_bar_grad_q(dim); + DenseMatrix dshape_s(dof_s, dim); - for (int s = 0; s < dof_s; s++) + for (int q = 0; q < nqp; q++) { - if (sigma_marker[dofs[s]] == false) { continue; } - - const IntegrationPoint &ip = ir.IntPoint(s); + const IntegrationPoint &ip = ir_quad.IntPoint(q); Tpr.SetIntPoint(&ip); - el_x.CalcShape(ip, shape_x); el_s.CalcShape(ip, shape_s); + el_x.CalcShape(ip, shape_x); + el_s.CalcPhysDShape(Tpr, dshape_s); - // These are the sums over k at the dof s (looking at the notes). - sigma_grad_e.MultTranspose(shape_s, sigma_grad_s); - Vector gg_ptr(sigma_grad_grad_s.GetData(), dim * dim); - sigma_grad_grad_e.MultTranspose(shape_s, gg_ptr); + // Grad of sigma_bar at the current quad point. + sigma_bar_grad_e.MultTranspose(shape_s, sigma_bar_grad_q); + + // Grad-grad of sigma_bar at the current quad point. + Vector gg_ptr(sigma_bar_grad_grad_q.GetData(), dim * dim); + sigma_bar_grad_grad_e.MultTranspose(shape_s, gg_ptr); // Loops over the local matrix. - const double w = normalization * coeff.Eval(Tpr, ip); + const double w = 2.0 * sigma_normal * + coeff_sigma->Eval(Tpr, ip) * weights(q); for (int i = 0; i < dof_x * dim; i++) { const int idof = i % dof_x, idim = i / dof_x; for (int j = 0; j <= i; j++) { const int jdof = j % dof_x, jdim = j / dof_x; - const double entry = - w * ( 2.0 * sigma_grad_s(idim) * shape_x(idof) * - /* */ sigma_grad_s(jdim) * shape_x(jdof) + - 2.0 * sigma_e(s) * sigma_grad_grad_s(idim, jdim) * - /* */ shape_x(idof) * shape_x(jdof)); + + double Di = sigma_bar_grad_q(idim), + Dj = sigma_bar_grad_q(jdim), + DD = sigma_bar_grad_grad_q(idim, jdim); + for (int s = 0; s < dof_s; s++) + { + if ((*sigma_marker)[dofs[s]] == false) { continue; } + + Di += sigma_grad_e(s, idim) * shape_s(s); + Dj += sigma_grad_e(s, jdim) * shape_s(s); + DD += sigma_grad_e(s, idim) * dshape_s(s, jdim) + + sigma_grad_grad_e(s*dim + idim, jdim) * shape_s(s) + + sigma_grad_e(s, jdim) * dshape_s(s, idim); + } + const double entry = w *(Di * Dj + sigma_bar_q(q) * DD) * + shape_x(idof) * shape_x(jdof); + mat(i, j) += entry; if (i != j) { mat(j, i) += entry; } } @@ -2840,7 +2872,7 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x, double &sigma_energy) { Array vdofs; - Vector x_vals, sigma_q; + Vector x_vals, sigma_bar_q; const FiniteElementSpace* const fes = x.FESpace(); const int dim = fes->GetMesh()->Dimension(); @@ -2866,6 +2898,8 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x, targetC->ComputeElementTargets(i, *fe, ir, x_vals, Jtr); + if (sigma) { sigma_bar->GetValues(i, ir, sigma_bar_q); } + for (int q = 0; q < nqp; q++) { const IntegrationPoint &ip = ir.IntPoint(q); @@ -2879,24 +2913,15 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x, metric_energy += weight * metric->EvalW(Jpt); lim_energy += weight; - } - // Non-variation contribution from the surface alignment term. - if (sigma) - { - Array dofs; - Vector sigma_e; - sigma->FESpace()->GetElementDofs(i, dofs); - sigma->GetSubVector(dofs, sigma_e); - for (int s = 0; s < dofs.Size(); s++) + // Normalization of the surface fitting term. + if (sigma) { - if ((*sigma_marker)[dofs[s]] == true) - { - sigma_energy += sigma_e(s) * sigma_e(s); - } + sigma_energy += weight * sigma_bar_q(q) * sigma_bar_q(q); } } } + if (targetC->ContainsVolumeInfo() == false) { // Special case when the targets don't contain volumetric information. @@ -2945,6 +2970,11 @@ void TMOP_Integrator::UpdateAfterMeshChange(const Vector &new_x) if (sigma) { sigma_eval->ComputeAtNewPosition(new_x, *sigma); + // Update the restricted sigma. + for (int i = 0; i < sigma_marker->Size(); i++) + { + (*sigma_bar)(i) = ((*sigma_marker)[i] == true) ? (*sigma)(i) : 0.0; + } } } diff --git a/fem/tmop.hpp b/fem/tmop.hpp index aaa7f78ea5..0299403dd4 100644 --- a/fem/tmop.hpp +++ b/fem/tmop.hpp @@ -918,7 +918,7 @@ protected: AdaptivityEvaluator *adapt_eval; // Not owned. // Surface fitting. - GridFunction *sigma; // Owned. Updated by sigma_eval. + GridFunction *sigma, *sigma_bar; // Owned. Updated by sigma_eval. const Array *sigma_marker; // Not owned. Coefficient *coeff_sigma; // Not owned. AdaptivityEvaluator *sigma_eval; // Not owned. @@ -985,16 +985,16 @@ protected: IsoparametricTransformation &Tpr, const IntegrationRule &ir, DenseMatrix &m); - void AssembleElemVecSurfAlign(const GridFunction &sigma, - const Array &sigma_marker, - Coefficient &coeff, const FiniteElement &el_x, - IsoparametricTransformation &Tpr, - double normalization, DenseMatrix &mat); - void AssembleElemGradSurfAlign(const GridFunction &sigma, - const Array &sigma_marker, - Coefficient &coeff, const FiniteElement &el_x, - IsoparametricTransformation &Tpr, - double normalization, DenseMatrix &mat); + // First derivative of the surface fitting term. + void AssembleElemVecSurfFit(const FiniteElement &el_x, + IsoparametricTransformation &Tpr, + const IntegrationRule &ir_quad, + const Vector &weights, DenseMatrix &mat); + // Second derivative of the surface fitting term. + void AssembleElemGradSurfFit(const FiniteElement &el_x, + IsoparametricTransformation &Tpr, + const IntegrationRule &ir_quad, + const Vector &weights, DenseMatrix &mat); double GetFDDerivative(const FiniteElement &el, ElementTransformation &T, @@ -1044,7 +1044,7 @@ public: nodes0(NULL), coeff0(NULL), lim_dist(NULL), lim_func(NULL), lim_normal(1.0), zeta_0(NULL), zeta(NULL), coeff_zeta(NULL), adapt_eval(NULL), - sigma(NULL), sigma_marker(NULL), coeff_sigma(NULL), + sigma(NULL), sigma_bar(NULL), sigma_marker(NULL), coeff_sigma(NULL), sigma_eval(NULL), sigma_normal(1.0), discr_tc(dynamic_cast(tc)), fdflag(false), dxscale(1.0e3), fd_call_flag(false), exact_action(false) diff --git a/miniapps/meshing/mesh-optimizer.hpp b/miniapps/meshing/mesh-optimizer.hpp index 77b8ba256f..46dc85d45f 100644 --- a/miniapps/meshing/mesh-optimizer.hpp +++ b/miniapps/meshing/mesh-optimizer.hpp @@ -225,7 +225,7 @@ double adapt_lim_fun(const Vector &x) // Used for exact surface alignment double surface_level_set(const Vector &x) { - const int type = 0; + const int type = 1; if (type == 0) { const double sine = 0.25 * std::sin(4 * M_PI * x(0)); From 69f70eca5ed727cb3a3e77ef4b4607c43811ab9b Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Tue, 29 Dec 2020 15:10:10 -0800 Subject: [PATCH 005/198] Found a bug. --- fem/tmop.cpp | 14 +++++++------- miniapps/meshing/pmesh-optimizer.cpp | 6 ++++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/fem/tmop.cpp b/fem/tmop.cpp index 8291490e50..8630901018 100644 --- a/fem/tmop.cpp +++ b/fem/tmop.cpp @@ -2186,10 +2186,7 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, } Vector sigma_bar_q; - if (surface_fit) - { - sigma_bar->GetValues(el_id, ir, sigma_bar_q); - } + if (surface_fit) { sigma_bar->GetValues(el_id, ir, sigma_bar_q); } for (int i = 0; i < ir.GetNPoints(); i++) { @@ -2682,6 +2679,7 @@ void TMOP_Integrator::AssembleElemVecSurfFit(const FiniteElement &el_x, for (int d = 0; d < dim; d++) { + // Grad of sigma must be taken at the active DOFs. grad_q(d) += sigma_grad_e(s, d) * shape_s(s); } } @@ -2701,7 +2699,7 @@ void TMOP_Integrator::AssembleElemGradSurfFit(const FiniteElement &el_x, DenseMatrix &mat) { const int el_id = Tpr.ElementNo, nqp = ir_quad.GetNPoints(); - const FiniteElement &el_s = *sigma->FESpace()->GetFE(Tpr.ElementNo); + const FiniteElement &el_s = *sigma->FESpace()->GetFE(el_id); const int dof_x = el_x.GetDof(), dim = el_x.GetDim(), dof_s = el_s.GetDof(); @@ -2751,6 +2749,7 @@ void TMOP_Integrator::AssembleElemGradSurfFit(const FiniteElement &el_x, Tpr.SetIntPoint(&ip); el_s.CalcShape(ip, shape_s); el_x.CalcShape(ip, shape_x); + // We could reuse grad_phys, but this is more accurate. el_s.CalcPhysDShape(Tpr, dshape_s); // Grad of sigma_bar at the current quad point. @@ -2780,10 +2779,10 @@ void TMOP_Integrator::AssembleElemGradSurfFit(const FiniteElement &el_x, Di += sigma_grad_e(s, idim) * shape_s(s); Dj += sigma_grad_e(s, jdim) * shape_s(s); DD += sigma_grad_e(s, idim) * dshape_s(s, jdim) + - sigma_grad_grad_e(s*dim + idim, jdim) * shape_s(s) + + sigma_grad_grad_e(dof_s * idim + s, jdim) * shape_s(s) + sigma_grad_e(s, jdim) * dshape_s(s, idim); } - const double entry = w *(Di * Dj + sigma_bar_q(q) * DD) * + const double entry = w * (Di * Dj + sigma_bar_q(q) * DD) * shape_x(idof) * shape_x(jdof); mat(i, j) += entry; @@ -3097,6 +3096,7 @@ void TMOP_Integrator::UpdateAfterMeshChange(const Vector &new_x) { // Update zeta if adaptive limiting is enabled. if (zeta) { adapt_eval->ComputeAtNewPosition(new_x, *zeta); } + // Update sigma if surface fitting is enabled. if (sigma) { diff --git a/miniapps/meshing/pmesh-optimizer.cpp b/miniapps/meshing/pmesh-optimizer.cpp index d9735a9818..50b27ce56d 100644 --- a/miniapps/meshing/pmesh-optimizer.cpp +++ b/miniapps/meshing/pmesh-optimizer.cpp @@ -684,10 +684,12 @@ int main (int argc, char *argv[]) // Surface alignment. L2_FECollection mat_coll(0, dim); + H1_FECollection sigma_fec(mesh_poly_deg, dim); + ParFiniteElementSpace sigma_fes(pmesh, &sigma_fec); ParFiniteElementSpace mat_fes(pmesh, &mat_coll); ParGridFunction mat(&mat_fes); - ParGridFunction marker_gf(&ind_fes); - ParGridFunction ls_0(&ind_fes); + ParGridFunction marker_gf(&sigma_fes); + ParGridFunction ls_0(&sigma_fes); Array marker(ls_0.Size()); ConstantCoefficient coef_ls(surface_const); AdaptivityEvaluator *adapt_surface = NULL; From caeecc0e6d6a31da4135b465d4e0dc0e2d3b29ee Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Wed, 30 Dec 2020 22:12:43 -0800 Subject: [PATCH 006/198] Improved function arguments. --- fem/tmop.cpp | 62 +++++++++++++++------------------------------------- fem/tmop.hpp | 14 +++++------- 2 files changed, 24 insertions(+), 52 deletions(-) diff --git a/fem/tmop.cpp b/fem/tmop.cpp index 8630901018..e6a6783400 100644 --- a/fem/tmop.cpp +++ b/fem/tmop.cpp @@ -2395,11 +2395,7 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el, } } - if (zeta) - { - AssembleElemVecAdaptLim(*zeta, zeta_0, *coeff_zeta, - el, weights, lim_normal, *Tpr, ir, PMatO); - } + if (zeta) { AssembleElemVecAdaptLim(el, *Tpr, ir, weights, PMatO); } if (sigma) { AssembleElemVecSurfFit(el, *Tpr, ir, weights, PMatO); } delete Tpr; @@ -2506,38 +2502,26 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el, } } - if (zeta) - { - AssembleElemGradAdaptLim(*zeta, zeta_0, *coeff_zeta, - el, weights, lim_normal, *Tpr, ir, elmat); - } + if (zeta) { AssembleElemGradAdaptLim(el, *Tpr, ir, weights, elmat); } if (sigma) { AssembleElemGradSurfFit(el, *Tpr, ir, weights, elmat); } delete Tpr; } -void TMOP_Integrator::AssembleElemVecAdaptLim(const GridFunction &g, - const GridFunction *g0, - Coefficient &coeff, - const FiniteElement &el, - const Vector &weights, - double normalization, +void TMOP_Integrator::AssembleElemVecAdaptLim(const FiniteElement &el, IsoparametricTransformation &Tpr, const IntegrationRule &ir, + const Vector &weights, DenseMatrix &mat) { const int dof = el.GetDof(), dim = el.GetDim(), nqp = weights.Size(); Vector shape(dof), zeta_e, zeta_q, zeta0_q(nqp); Array dofs; - g.FESpace()->GetElementDofs(Tpr.ElementNo, dofs); - g.GetSubVector(dofs, zeta_e); - g.GetValues(Tpr.ElementNo, ir, zeta_q); - if (g0) - { - g0->GetValues(Tpr.ElementNo, ir, zeta0_q); - } - else { zeta0_q = 0.0; } + zeta->FESpace()->GetElementDofs(Tpr.ElementNo, dofs); + zeta->GetSubVector(dofs, zeta_e); + zeta->GetValues(Tpr.ElementNo, ir, zeta_q); + zeta_0->GetValues(Tpr.ElementNo, ir, zeta0_q); // Project the gradient of zeta in the same space. // The FE coefficients of the gradient go in zeta_grad_e. @@ -2555,33 +2539,25 @@ void TMOP_Integrator::AssembleElemVecAdaptLim(const GridFunction &g, el.CalcShape(ip, shape); zeta_grad_e.MultTranspose(shape, zeta_grad_q); zeta_grad_q *= 2.0 * (zeta_q(q) - zeta0_q(q)); - zeta_grad_q *= weights(q) * normalization * coeff.Eval(Tpr, ip); + zeta_grad_q *= weights(q) * lim_normal * coeff_zeta->Eval(Tpr, ip); AddMultVWt(shape, zeta_grad_q, mat); } } -void TMOP_Integrator::AssembleElemGradAdaptLim(const GridFunction &g, - const GridFunction *g0, - Coefficient &coeff, - const FiniteElement &el, - const Vector &weights, - double normalization, +void TMOP_Integrator::AssembleElemGradAdaptLim(const FiniteElement &el, IsoparametricTransformation &Tpr, const IntegrationRule &ir, + const Vector &weights, DenseMatrix &mat) { const int dof = el.GetDof(), dim = el.GetDim(), nqp = weights.Size(); Vector shape(dof), zeta_e, zeta_q, zeta0_q(nqp); Array dofs; - g.FESpace()->GetElementDofs(Tpr.ElementNo, dofs); - g.GetSubVector(dofs, zeta_e); - g.GetValues(Tpr.ElementNo, ir, zeta_q); - if (g0) - { - g0->GetValues(Tpr.ElementNo, ir, zeta0_q); - } - else { zeta0_q = 0.0; } + zeta->FESpace()->GetElementDofs(Tpr.ElementNo, dofs); + zeta->GetSubVector(dofs, zeta_e); + zeta->GetValues(Tpr.ElementNo, ir, zeta_q); + zeta_0->GetValues(Tpr.ElementNo, ir, zeta0_q); // Project the gradient of zeta in the same space. // The FE coefficients of the gradient go in zeta_grad_e. @@ -2610,7 +2586,7 @@ void TMOP_Integrator::AssembleElemGradAdaptLim(const GridFunction &g, Vector gg_ptr(zeta_grad_grad_q.GetData(), dim*dim); zeta_grad_grad_e.MultTranspose(shape, gg_ptr); - const double w = weights(q) * normalization * coeff.Eval(Tpr, ip); + const double w = weights(q) * lim_normal * coeff_zeta->Eval(Tpr, ip); for (int i = 0; i < dof * dim; i++) { const int idof = i % dof, idim = i / dof; @@ -2874,8 +2850,7 @@ void TMOP_Integrator::AssembleElementVectorFD(const FiniteElement &el, } PMatO.UseExternalData(elvect.GetData(), dof, dim); - AssembleElemVecAdaptLim(*zeta, zeta_0, *coeff_zeta, el, - weights, lim_normal, Tpr, ir, PMatO); + AssembleElemVecAdaptLim(el, Tpr, ir, weights, PMatO); } } @@ -2970,8 +2945,7 @@ void TMOP_Integrator::AssembleElementGradFD(const FiniteElement &el, weights(q) = ir.IntPoint(q).weight * Jtr(q).Det(); } - AssembleElemGradAdaptLim(*zeta, zeta_0, *coeff_zeta, el, - weights, lim_normal, Tpr, ir, elmat); + AssembleElemGradAdaptLim(el, Tpr, ir, weights, elmat); } } diff --git a/fem/tmop.hpp b/fem/tmop.hpp index 2d4ad328bf..2a52a0d4d3 100644 --- a/fem/tmop.hpp +++ b/fem/tmop.hpp @@ -1132,16 +1132,14 @@ protected: ElementTransformation &T, const Vector &elfun, DenseMatrix &elmat); - void AssembleElemVecAdaptLim(const GridFunction &g, const GridFunction *g0, - Coefficient &coeff, const FiniteElement &el, - const Vector &weights, double normalization, + void AssembleElemVecAdaptLim(const FiniteElement &el, IsoparametricTransformation &Tpr, - const IntegrationRule &ir, DenseMatrix &m); - void AssembleElemGradAdaptLim(const GridFunction &g, const GridFunction *g0, - Coefficient &coeff, const FiniteElement &el, - const Vector &weights, double normalization, + const IntegrationRule &ir, + const Vector &weights, DenseMatrix &mat); + void AssembleElemGradAdaptLim(const FiniteElement &el, IsoparametricTransformation &Tpr, - const IntegrationRule &ir, DenseMatrix &m); + const IntegrationRule &ir, + const Vector &weights, DenseMatrix &m); // First derivative of the surface fitting term. void AssembleElemVecSurfFit(const FiniteElement &el_x, From dd1fe9e7c3258e7a044ef29bd10cb273bc9156cd Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Wed, 30 Dec 2020 22:39:39 -0800 Subject: [PATCH 007/198] Added FD support for surface fitting. --- fem/tmop.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fem/tmop.cpp b/fem/tmop.cpp index e6a6783400..cc37b6e4fc 100644 --- a/fem/tmop.cpp +++ b/fem/tmop.cpp @@ -2828,8 +2828,8 @@ void TMOP_Integrator::AssembleElementVectorFD(const FiniteElement &el, } fd_call_flag = false; - // Contributions from adaptive limiting (exact derivatives). - if (zeta) + // Contributions from adaptive limiting, surface fitting (exact derivatives). + if (zeta || sigma) { const IntegrationRule &ir = ActionIntegrationRule(el); const int nqp = ir.GetNPoints(); @@ -2850,7 +2850,8 @@ void TMOP_Integrator::AssembleElementVectorFD(const FiniteElement &el, } PMatO.UseExternalData(elvect.GetData(), dof, dim); - AssembleElemVecAdaptLim(el, Tpr, ir, weights, PMatO); + if (zeta) { AssembleElemVecAdaptLim(el, Tpr, ir, weights, PMatO); } + if (sigma) { AssembleElemVecSurfFit(el, Tpr, ir, weights, PMatO); } } } @@ -2925,7 +2926,7 @@ void TMOP_Integrator::AssembleElementGradFD(const FiniteElement &el, fd_call_flag = false; // Contributions from adaptive limiting. - if (zeta) + if (zeta || sigma) { const IntegrationRule &ir = GradientIntegrationRule(el); const int nqp = ir.GetNPoints(); @@ -2945,7 +2946,8 @@ void TMOP_Integrator::AssembleElementGradFD(const FiniteElement &el, weights(q) = ir.IntPoint(q).weight * Jtr(q).Det(); } - AssembleElemGradAdaptLim(el, Tpr, ir, weights, elmat); + if (zeta) { AssembleElemGradAdaptLim(el, Tpr, ir, weights, elmat); } + if (sigma) { AssembleElemGradSurfFit(el, Tpr, ir, weights, elmat); } } } From 94e16907c905f01a519c224ce9ace4366b36fb8c Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Wed, 30 Dec 2020 22:45:51 -0800 Subject: [PATCH 008/198] Minor. --- miniapps/meshing/pmesh-optimizer.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/miniapps/meshing/pmesh-optimizer.cpp b/miniapps/meshing/pmesh-optimizer.cpp index 50b27ce56d..435f00282f 100644 --- a/miniapps/meshing/pmesh-optimizer.cpp +++ b/miniapps/meshing/pmesh-optimizer.cpp @@ -110,7 +110,7 @@ int main (int argc, char *argv[]) int target_id = 1; double lim_const = 0.0; double adapt_lim_const = 0.0; - double surface_const = 0.0; + double surface_fit_const = 0.0; int quad_type = 1; int quad_order = 8; int solver_type = 0; @@ -181,7 +181,7 @@ int main (int argc, char *argv[]) args.AddOption(&lim_const, "-lc", "--limit-const", "Limiting constant."); args.AddOption(&adapt_lim_const, "-alc", "--adapt-limit-const", "Adaptive limiting coefficient constant."); - args.AddOption(&surface_const, "-sc", "--surface-const", + args.AddOption(&surface_fit_const, "-sfc", "--surface-fit-const", "Surface preservation constant."); args.AddOption(&quad_type, "-qt", "--quad-type", "Quadrature rule type:\n\t" @@ -691,9 +691,9 @@ int main (int argc, char *argv[]) ParGridFunction marker_gf(&sigma_fes); ParGridFunction ls_0(&sigma_fes); Array marker(ls_0.Size()); - ConstantCoefficient coef_ls(surface_const); + ConstantCoefficient coef_ls(surface_fit_const); AdaptivityEvaluator *adapt_surface = NULL; - if (surface_const > 0.0) + if (surface_fit_const > 0.0) { FunctionCoefficient ls_coeff(surface_level_set); ls_0.ProjectCoefficient(ls_coeff); @@ -969,7 +969,7 @@ int main (int argc, char *argv[]) // 17. Compute the amount of energy decrease. const double fin_energy = a.GetParGridFunctionEnergy(x); double metric_part = fin_energy; - if (lim_const > 0.0 || adapt_lim_const > 0.0 || surface_const > 0.0) + if (lim_const > 0.0 || adapt_lim_const > 0.0 || surface_fit_const > 0.0) { lim_coeff.constant = 0.0; coef_zeta.constant = 0.0; @@ -977,7 +977,7 @@ int main (int argc, char *argv[]) metric_part = a.GetParGridFunctionEnergy(x); lim_coeff.constant = lim_const; coef_zeta.constant = adapt_lim_const; - coef_ls.constant = surface_const; + coef_ls.constant = surface_fit_const; } if (myid == 0) { From be26f523858dfad19df276275a9bdce04730cd77 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Thu, 31 Dec 2020 11:42:30 -0800 Subject: [PATCH 009/198] Improved the output for initial-vs-final energy. --- miniapps/meshing/pmesh-optimizer.cpp | 35 +++++++++++++++++++--------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/miniapps/meshing/pmesh-optimizer.cpp b/miniapps/meshing/pmesh-optimizer.cpp index 435f00282f..ea43dccf2b 100644 --- a/miniapps/meshing/pmesh-optimizer.cpp +++ b/miniapps/meshing/pmesh-optimizer.cpp @@ -682,7 +682,7 @@ int main (int argc, char *argv[]) } } - // Surface alignment. + // Surface fitting. L2_FECollection mat_coll(0, dim); H1_FECollection sigma_fec(mesh_poly_deg, dim); ParFiniteElementSpace sigma_fes(pmesh, &sigma_fec); @@ -799,7 +799,19 @@ int main (int argc, char *argv[]) } else { a.AddDomainIntegrator(he_nlf_integ); } + // Compute the initial energy of the functional. const double init_energy = a.GetParGridFunctionEnergy(x); + double init_metric_energy = init_energy; + if (lim_const > 0.0 || adapt_lim_const > 0.0 || surface_fit_const > 0.0) + { + lim_coeff.constant = 0.0; + coef_zeta.constant = 0.0; + coef_ls.constant = 0.0; + init_metric_energy = a.GetParGridFunctionEnergy(x); + lim_coeff.constant = lim_const; + coef_zeta.constant = adapt_lim_const; + coef_ls.constant = surface_fit_const; + } // Visualize the starting mesh and metric values. // Note that for combinations of metrics, this only shows the first metric. @@ -966,28 +978,29 @@ int main (int argc, char *argv[]) 900, 900, 300, 300); } - // 17. Compute the amount of energy decrease. + // Compute the final energy of the functional. const double fin_energy = a.GetParGridFunctionEnergy(x); - double metric_part = fin_energy; + double fin_metric_energy = fin_energy; if (lim_const > 0.0 || adapt_lim_const > 0.0 || surface_fit_const > 0.0) { lim_coeff.constant = 0.0; coef_zeta.constant = 0.0; - coef_ls.constant = 0.0; - metric_part = a.GetParGridFunctionEnergy(x); + coef_ls.constant = 0.0; + fin_metric_energy = a.GetParGridFunctionEnergy(x); lim_coeff.constant = lim_const; coef_zeta.constant = adapt_lim_const; - coef_ls.constant = surface_fit_const; + coef_ls.constant = surface_fit_const; } if (myid == 0) { + std::cout << std::scientific << std::setprecision(4); cout << "Initial strain energy: " << init_energy - << " = metrics: " << init_energy - << " + limiting term: " << 0.0 << endl; + << " = metrics: " << init_metric_energy + << " + extra terms: " << init_energy - init_metric_energy << endl; cout << " Final strain energy: " << fin_energy - << " = metrics: " << metric_part - << " + limiting term: " << fin_energy - metric_part << endl; - cout << "The strain energy decreased by: " << setprecision(12) + << " = metrics: " << fin_metric_energy + << " + extra terms: " << fin_energy - fin_metric_energy << endl; + cout << "The strain energy decreased by: " << (init_energy - fin_energy) * 100.0 / init_energy << " %." << endl; } From 438fd06639988344d31b02b92dbd4d4a29d72885 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Fri, 1 Jan 2021 23:28:50 -0800 Subject: [PATCH 010/198] Minor. --- miniapps/meshing/pmesh-optimizer.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/miniapps/meshing/pmesh-optimizer.cpp b/miniapps/meshing/pmesh-optimizer.cpp index ea43dccf2b..fb01bb006b 100644 --- a/miniapps/meshing/pmesh-optimizer.cpp +++ b/miniapps/meshing/pmesh-optimizer.cpp @@ -741,11 +741,6 @@ int main (int argc, char *argv[]) 600, 600, 300, 300); common::VisualizeField(vis3, "localhost", 19916, marker_gf, "Dofs to Move", 900, 600, 300, 300); - - socketstream vis4; - common::VisualizeField(vis4, "localhost", 19916, ls_0, "Level Set 0", - 300, 600, 300, 300); - } } From 06b40d8f9edfedc033c5d2941e26b0fa43e6ffce Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Tue, 5 Jan 2021 15:10:55 -0800 Subject: [PATCH 011/198] Added a 2D triangle mesh. --- miniapps/meshing/square01_tri.mesh | 65 ++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 miniapps/meshing/square01_tri.mesh diff --git a/miniapps/meshing/square01_tri.mesh b/miniapps/meshing/square01_tri.mesh new file mode 100644 index 0000000000..013a419ca8 --- /dev/null +++ b/miniapps/meshing/square01_tri.mesh @@ -0,0 +1,65 @@ +MFEM mesh v1.0 + +# +# MFEM Geometry Types (see mesh/geom.hpp): +# +# POINT = 0 +# SEGMENT = 1 +# TRIANGLE = 2 +# SQUARE = 3 +# TETRAHEDRON = 4 +# CUBE = 5 +# + +dimension +2 + +elements +8 +1 2 0 1 4 +1 2 4 3 0 +1 2 1 2 5 +1 2 5 4 1 +1 2 3 4 7 +1 2 7 6 3 +1 2 4 5 8 +1 2 8 7 4 + +boundary +8 +2 1 0 1 +2 1 1 2 +2 1 7 6 +2 1 8 7 +1 1 3 0 +1 1 6 3 +1 1 2 5 +1 1 5 8 + +vertices +9 + +nodes +FiniteElementSpace +FiniteElementCollection: Linear +VDim: 2 +Ordering: 0 + +0 +0.5 +1 +0 +0.5 +1 +0 +0.5 +1 +0 +0 +0 +0.5 +0.5 +0.5 +1 +1 +1 From fbcc7406a7068fccb2dd71d07ae1fe0f8b77c688 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Sun, 10 Jan 2021 16:36:24 -0800 Subject: [PATCH 012/198] Another option to diffuse fields. --- miniapps/meshing/mesh-optimizer.hpp | 67 ++++++++++++++++++++++++++++ miniapps/meshing/pmesh-optimizer.cpp | 1 + 2 files changed, 68 insertions(+) diff --git a/miniapps/meshing/mesh-optimizer.hpp b/miniapps/meshing/mesh-optimizer.hpp index a2a8ad6f17..9b9ee8cad6 100644 --- a/miniapps/meshing/mesh-optimizer.hpp +++ b/miniapps/meshing/mesh-optimizer.hpp @@ -316,3 +316,70 @@ void DiffuseField(ParGridFunction &field, int smooth_steps) delete Lap; } #endif + +void DiffuseField2(ParGridFunction &field, double coeff) +{ + ParFiniteElementSpace &pfes = *field.ParFESpace(); + + // Compute average mesh size (assumes similar cells). + double loc_area = 0.0, dx; + ParMesh &pmesh = *pfes.GetParMesh(); + for (int i = 0; i < pmesh.GetNE(); i++) + { + loc_area += pmesh.GetElementVolume(i); + } + double glob_area; + MPI_Allreduce(&loc_area, &glob_area, 1, MPI_DOUBLE, + MPI_SUM, pfes.GetComm()); + + const int glob_zones = pmesh.GetGlobalNE(); + switch (pmesh.GetElementBaseGeometry(0)) + { + case Geometry::SEGMENT: + dx = glob_area / glob_zones; break; + case Geometry::SQUARE: + dx = sqrt(glob_area / glob_zones); break; + case Geometry::TRIANGLE: + dx = sqrt(2.0 * glob_area / glob_zones); break; + case Geometry::CUBE: + dx = pow(glob_area / glob_zones, 1.0/3.0); break; + case Geometry::TETRAHEDRON: + dx = pow(6.0 * glob_area / glob_zones, 1.0/3.0); break; + default: MFEM_ABORT("Unknown zone type!"); + } + dx /= pfes.GetOrder(0); + + // Set up RHS. + ParLinearForm b(&pfes); + GridFunctionCoefficient src_coeff(&field); + b.AddDomainIntegrator(new DomainLFIntegrator(src_coeff)); + b.Assemble(); + + // Diffusion and mass terms in the LHS. + ParBilinearForm a(&pfes); + a.AddDomainIntegrator(new MassIntegrator); + ConstantCoefficient diffuse_coeff(coeff * dx * dx); + a.AddDomainIntegrator(new DiffusionIntegrator(diffuse_coeff)); + a.Assemble(); + + // Solve with Neumann BC. + ParGridFunction u_neumann(&pfes); + Array ess_tdof_list; + ess_tdof_list.DeleteAll(); + // Solver. + CGSolver cg(MPI_COMM_WORLD); + cg.SetRelTol(1e-12); + cg.SetMaxIter(100); + cg.SetPrintLevel(1); + OperatorPtr A; + Vector B, X; + a.FormLinearSystem(ess_tdof_list, u_neumann, b, A, X, B); + Solver *prec = new HypreBoomerAMG; + cg.SetPreconditioner(*prec); + cg.SetOperator(*A); + cg.Mult(B, X); + a.RecoverFEMSolution(X, b, u_neumann); + delete prec; + + field = u_neumann; +} diff --git a/miniapps/meshing/pmesh-optimizer.cpp b/miniapps/meshing/pmesh-optimizer.cpp index 0314b7e88c..4c0fd01497 100644 --- a/miniapps/meshing/pmesh-optimizer.cpp +++ b/miniapps/meshing/pmesh-optimizer.cpp @@ -708,6 +708,7 @@ int main (int argc, char *argv[]) FunctionCoefficient ls_coeff(surface_level_set); ls_0.ProjectCoefficient(ls_coeff); DiffuseField(ls_0, 10); + //DiffuseField2(ls_0, 10.0); for (int i = 0; i < pmesh->GetNE(); i++) { From 75880768532995639279e1be0ac59ef95b21c49e Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Sun, 10 Jan 2021 17:06:42 -0800 Subject: [PATCH 013/198] 3D surface fitting test. --- fem/tmop.cpp | 3 ++- miniapps/meshing/mesh-optimizer.hpp | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/fem/tmop.cpp b/fem/tmop.cpp index d3e8ea7a20..8f7387c964 100644 --- a/fem/tmop.cpp +++ b/fem/tmop.cpp @@ -2267,6 +2267,8 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, for (int i = 0; i < ir.GetNPoints(); i++) { const IntegrationPoint &ip = ir.IntPoint(i); + Tpr->SetIntPoint(&ip); + const DenseMatrix &Jtr_i = Jtr(i); metric->SetTargetJacobian(Jtr_i); CalcInverse(Jtr_i, Jrt); @@ -2306,7 +2308,6 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, } delete Tpr; - return energy; } void TMOP_Integrator::AssembleElementVector(const FiniteElement &el, diff --git a/miniapps/meshing/mesh-optimizer.hpp b/miniapps/meshing/mesh-optimizer.hpp index 9b9ee8cad6..fb4eac51be 100644 --- a/miniapps/meshing/mesh-optimizer.hpp +++ b/miniapps/meshing/mesh-optimizer.hpp @@ -237,6 +237,8 @@ double adapt_lim_fun(const Vector &x) double surface_level_set(const Vector &x) { const int type = 1; + + const int dim = x.Size(); if (type == 0) { const double sine = 0.25 * std::sin(4 * M_PI * x(0)); @@ -244,9 +246,18 @@ double surface_level_set(const Vector &x) } else { - const double xc = x(0) - 0.5, yc = x(1) - 0.5; - const double r = sqrt(xc*xc + yc*yc); - return (r > 0.2) ? 1.0 : -1.0; + if (dim == 2) + { + const double xc = x(0) - 0.5, yc = x(1) - 0.5; + const double r = sqrt(xc*xc + yc*yc); + return (r > 0.2) ? 1.0 : -1.0; + } + else + { + const double xc = x(0) - 0.5, yc = x(1) - 0.5, zc = x(2) - 0.5; + const double r = sqrt(xc*xc + yc*yc + zc*zc); + return (r > 0.3) ? 1.0 : -1.0; + } } } From 5e296c39c61556e3974c14bbe6a8f79691320e72 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Tue, 12 Jan 2021 09:48:57 -0800 Subject: [PATCH 014/198] Minor. --- fem/tmop.cpp | 1 - miniapps/meshing/pmesh-optimizer.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/fem/tmop.cpp b/fem/tmop.cpp index 8f7387c964..82e448bb8c 100644 --- a/fem/tmop.cpp +++ b/fem/tmop.cpp @@ -2267,7 +2267,6 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, for (int i = 0; i < ir.GetNPoints(); i++) { const IntegrationPoint &ip = ir.IntPoint(i); - Tpr->SetIntPoint(&ip); const DenseMatrix &Jtr_i = Jtr(i); metric->SetTargetJacobian(Jtr_i); diff --git a/miniapps/meshing/pmesh-optimizer.cpp b/miniapps/meshing/pmesh-optimizer.cpp index ac7b91c654..d87f00ce80 100644 --- a/miniapps/meshing/pmesh-optimizer.cpp +++ b/miniapps/meshing/pmesh-optimizer.cpp @@ -1004,7 +1004,7 @@ int main (int argc, char *argv[]) pmesh->PrintAsOne(mesh_ofs); } - if (visualization) + if (visualization && surface_fit_const > 0.0) { socketstream vis2, vis3; common::VisualizeField(vis2, "localhost", 19916, mat, "Materials", From feca2f59dc041ba0dfa576e34723fa12ae5ad141 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Thu, 14 Jan 2021 12:26:28 -0800 Subject: [PATCH 015/198] Minor. --- miniapps/meshing/mesh-optimizer.hpp | 4 ++-- miniapps/meshing/pmesh-optimizer.cpp | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/miniapps/meshing/mesh-optimizer.hpp b/miniapps/meshing/mesh-optimizer.hpp index fb4eac51be..cfe34d69b6 100644 --- a/miniapps/meshing/mesh-optimizer.hpp +++ b/miniapps/meshing/mesh-optimizer.hpp @@ -250,13 +250,13 @@ double surface_level_set(const Vector &x) { const double xc = x(0) - 0.5, yc = x(1) - 0.5; const double r = sqrt(xc*xc + yc*yc); - return (r > 0.2) ? 1.0 : -1.0; + return std::tanh(2.0*(r-0.2)); } else { const double xc = x(0) - 0.5, yc = x(1) - 0.5, zc = x(2) - 0.5; const double r = sqrt(xc*xc + yc*yc + zc*zc); - return (r > 0.3) ? 1.0 : -1.0; + return std::tanh(2.0*(r-0.3)); } } } diff --git a/miniapps/meshing/pmesh-optimizer.cpp b/miniapps/meshing/pmesh-optimizer.cpp index d87f00ce80..bc259fde9d 100644 --- a/miniapps/meshing/pmesh-optimizer.cpp +++ b/miniapps/meshing/pmesh-optimizer.cpp @@ -713,12 +713,11 @@ int main (int argc, char *argv[]) { FunctionCoefficient ls_coeff(surface_level_set); ls_0.ProjectCoefficient(ls_coeff); - DiffuseField(ls_0, 10); - //DiffuseField2(ls_0, 10.0); for (int i = 0; i < pmesh->GetNE(); i++) { mat(i) = material_id(i, ls_0); + pmesh->SetAttribute(i, mat(i) + 1); } GridFunctionCoefficient coeff_mat(&mat); From 7d2ce53a636ed9b010cc2dd9b6a1cd2f8b5618c7 Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Mon, 25 Jan 2021 09:26:28 -0800 Subject: [PATCH 016/198] write element attribute in mesh file --- mesh/pmesh.cpp | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/mesh/pmesh.cpp b/mesh/pmesh.cpp index bf872fc685..895b9d33fe 100644 --- a/mesh/pmesh.cpp +++ b/mesh/pmesh.cpp @@ -4309,11 +4309,13 @@ static void dump_element(const Element* elem, Array &data) void ParMesh::PrintAsOne(std::ostream &out) { - int i, j, k, p, nv_ne[2], &nv = nv_ne[0], &ne = nv_ne[1], vc; + int i, j, k, p, nv_ne[3], &nv = nv_ne[0], &ne = nv_ne[1], &nc = nv_ne[2], vc; const int *v; MPI_Status status; Array vert; Array ints; + Array attr_ne; + int attr; if (MyRank == 0) { @@ -4335,14 +4337,17 @@ void ParMesh::PrintAsOne(std::ostream &out) } nv = NumOfElements; + nc = NumOfElements; MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm); if (MyRank == 0) { out << "\n\nelements\n" << ne << '\n'; for (i = 0; i < NumOfElements; i++) { + attr = elements[i]->GetAttribute(); // processor number + 1 as attribute and geometry type - out << 1 << ' ' << elements[i]->GetGeometryType(); + //out << 1 << ' ' << elements[i]->GetGeometryType(); + out << attr << ' ' << elements[i]->GetGeometryType(); // vertices nv = elements[i]->GetNVertices(); v = elements[i]->GetVertices(); @@ -4355,16 +4360,24 @@ void ParMesh::PrintAsOne(std::ostream &out) vc = NumOfVertices; for (p = 1; p < NRanks; p++) { - MPI_Recv(nv_ne, 2, MPI_INT, p, 444, MyComm, &status); + MPI_Recv(nv_ne, 3, MPI_INT, p, 444, MyComm, &status); ints.SetSize(ne); + attr_ne.SetSize(nc); if (ne) { MPI_Recv(&ints[0], ne, MPI_INT, p, 445, MyComm, &status); } + if (nc) + { + MPI_Recv(&attr_ne[0], nc, MPI_INT, p, 446, MyComm, &status); + } + + int j = 0; for (i = 0; i < ne; ) { // processor number + 1 as attribute and geometry type - out << p+1 << ' ' << ints[i]; + // out << p+1 << ' ' << ints[i]; + out << attr_ne[j] << ' ' << ints[i]; // vertices k = Geometries.GetVertices(ints[i++])->GetNPoints(); for (j = 0; j < k; j++) @@ -4374,6 +4387,7 @@ void ParMesh::PrintAsOne(std::ostream &out) out << '\n'; } vc += nv; + j++; } } else @@ -4385,7 +4399,7 @@ void ParMesh::PrintAsOne(std::ostream &out) ne += 1 + elements[i]->GetNVertices(); } nv = NumOfVertices; - MPI_Send(nv_ne, 2, MPI_INT, 0, 444, MyComm); + MPI_Send(nv_ne, 3, MPI_INT, 0, 444, MyComm); ints.Reserve(ne); ints.SetSize(0); @@ -4398,6 +4412,17 @@ void ParMesh::PrintAsOne(std::ostream &out) { MPI_Send(&ints[0], ne, MPI_INT, 0, 445, MyComm); } + + attr_ne.SetSize(nc); + for (i = 0; i < NumOfElements; i++) + { + attr_ne[i] = elements[i]->GetAttribute(); + } + MFEM_ASSERT(attr_ne.Size() == nc, ""); + if (ne) + { + MPI_Send(&attr_ne[0], nc, MPI_INT, 0, 446, MyComm); + } } // boundary + shared boundary From a39f7635f5b92190908337b2167d726e4f03bf22 Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Mon, 25 Jan 2021 10:42:04 -0800 Subject: [PATCH 017/198] bug fix for writing attribute --- mesh/pmesh.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mesh/pmesh.cpp b/mesh/pmesh.cpp index 895b9d33fe..f0114c20b3 100644 --- a/mesh/pmesh.cpp +++ b/mesh/pmesh.cpp @@ -4339,6 +4339,7 @@ void ParMesh::PrintAsOne(std::ostream &out) nv = NumOfElements; nc = NumOfElements; MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm); + MPI_Allreduce(&nv, &nc, 1, MPI_INT, MPI_SUM, MyComm); if (MyRank == 0) { out << "\n\nelements\n" << ne << '\n'; @@ -4372,12 +4373,12 @@ void ParMesh::PrintAsOne(std::ostream &out) MPI_Recv(&attr_ne[0], nc, MPI_INT, p, 446, MyComm, &status); } - int j = 0; + int m = 0; for (i = 0; i < ne; ) { // processor number + 1 as attribute and geometry type // out << p+1 << ' ' << ints[i]; - out << attr_ne[j] << ' ' << ints[i]; + out << attr_ne[m] << ' ' << ints[i]; // vertices k = Geometries.GetVertices(ints[i++])->GetNPoints(); for (j = 0; j < k; j++) @@ -4385,9 +4386,9 @@ void ParMesh::PrintAsOne(std::ostream &out) out << ' ' << vc + ints[i++]; } out << '\n'; + m++; } vc += nv; - j++; } } else @@ -4419,7 +4420,7 @@ void ParMesh::PrintAsOne(std::ostream &out) attr_ne[i] = elements[i]->GetAttribute(); } MFEM_ASSERT(attr_ne.Size() == nc, ""); - if (ne) + if (nc) { MPI_Send(&attr_ne[0], nc, MPI_INT, 0, 446, MyComm); } From ff6dc50d7ffdd18bbcf34aebcdcf9e355886d243 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Tue, 2 Feb 2021 22:51:03 -0800 Subject: [PATCH 018/198] Computation of surface fitting errors. Changed the normalization. --- fem/tmop.cpp | 26 +++++++++++++++++++++++++- fem/tmop.hpp | 1 + 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/fem/tmop.cpp b/fem/tmop.cpp index 82e448bb8c..3f8c6620da 100644 --- a/fem/tmop.cpp +++ b/fem/tmop.cpp @@ -2186,6 +2186,29 @@ void TMOP_Integrator::EnableSurfaceFitting(const ParGridFunction &s0, } #endif +void TMOP_Integrator::GetSurfaceFittingErrors(double &err_avg, double &err_max) +{ + MFEM_VERIFY(sigma, "Surface fitting has not been enabled."); + + int loc_cnt = 0; + double loc_max = 0.0, loc_sum = 0.0; + for (int i = 0; i < sigma_marker->Size(); i++) + { + if ((*sigma_marker)[i] == true) + { + loc_cnt++; + loc_max = std::max(loc_max, (*sigma_bar)(i)); + loc_sum += std::abs((*sigma_bar)(i)); + } + } + + int glob_cnt; + MPI_Allreduce(&loc_max, &err_max, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); + MPI_Allreduce(&loc_cnt, &glob_cnt, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(&loc_sum, &err_avg, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + err_avg = err_avg / glob_cnt; +} + double TMOP_Integrator::GetElementEnergy(const FiniteElement &el, ElementTransformation &T, const Vector &elfun) @@ -3044,7 +3067,8 @@ void TMOP_Integrator::ParEnableNormalization(const ParGridFunction &x) MPI_Allreduce(loc, rdc, 3, MPI_DOUBLE, MPI_SUM, x.ParFESpace()->GetComm()); metric_normal = 1.0 / rdc[0]; lim_normal = 1.0 / rdc[1]; - if (sigma) { sigma_normal = 1.0 / rdc[2]; } + // if (sigma) { sigma_normal = 1.0 / rdc[2]; } + if (sigma) { sigma_normal = lim_normal; } } #endif diff --git a/fem/tmop.hpp b/fem/tmop.hpp index be09670557..0930a5e3f7 100644 --- a/fem/tmop.hpp +++ b/fem/tmop.hpp @@ -1305,6 +1305,7 @@ public: const Array &smarker, Coefficient &coeff, AdaptivityEvaluator &ae); #endif + void GetSurfaceFittingErrors(double &err_avg, double &err_max); /// Update the original/reference nodes used for limiting. void SetLimitingNodes(const GridFunction &n0) { nodes0 = &n0; } From 966071f5ce6888b006a335c5f408baab1ed9081a Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Tue, 2 Feb 2021 22:55:28 -0800 Subject: [PATCH 019/198] Minor. --- fem/tmop.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fem/tmop.cpp b/fem/tmop.cpp index 3f8c6620da..37954edd05 100644 --- a/fem/tmop.cpp +++ b/fem/tmop.cpp @@ -2197,7 +2197,7 @@ void TMOP_Integrator::GetSurfaceFittingErrors(double &err_avg, double &err_max) if ((*sigma_marker)[i] == true) { loc_cnt++; - loc_max = std::max(loc_max, (*sigma_bar)(i)); + loc_max = std::max(loc_max, std::abs((*sigma_bar)(i))); loc_sum += std::abs((*sigma_bar)(i)); } } From ec9c967b8827bb876844c4a97e49c7282d361013 Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Thu, 4 Feb 2021 10:03:43 -0800 Subject: [PATCH 020/198] adding 3D combo metrics --- fem/tmop.hpp | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/fem/tmop.hpp b/fem/tmop.hpp index be09670557..5327dce9ae 100644 --- a/fem/tmop.hpp +++ b/fem/tmop.hpp @@ -571,6 +571,69 @@ public: const double weight, DenseMatrix &A) const; }; +/// 3D barrier Shape+Size (VS) metric (polyconvex). +class TMOP_Metric_328 : public TMOP_Combo_QualityMetric +{ +protected: + mutable InvariantsEvaluator2D ie; + double gamma; + TMOP_QualityMetric *sh_metric, *sz_metric; + +public: + TMOP_Metric_328(double gamma_) : gamma(gamma_), + sh_metric(new TMOP_Metric_301), + sz_metric(new TMOP_Metric_316) + { + // (1-gamma) mu_301 + gamma mu_316 + AddQualityMetric(sh_metric, 1.-gamma_); + AddQualityMetric(sz_metric, gamma_); + } + + virtual ~TMOP_Metric_328() { delete sh_metric; delete sz_metric; } +}; + +/// 3D barrier Shape+Size (VS) metric (polyconvex). +class TMOP_Metric_333 : public TMOP_Combo_QualityMetric +{ +protected: + mutable InvariantsEvaluator2D ie; + double gamma; + TMOP_QualityMetric *sh_metric, *sz_metric; + +public: + TMOP_Metric_333(double gamma_) : gamma(gamma_), + sh_metric(new TMOP_Metric_302), + sz_metric(new TMOP_Metric_316) + { + // (1-gamma) mu_302 + gamma mu_316 + AddQualityMetric(sh_metric, 1.-gamma_); + AddQualityMetric(sz_metric, gamma_); + } + + virtual ~TMOP_Metric_333() { delete sh_metric; delete sz_metric; } +}; + +/// 3D barrier Shape+Size (VS) metric (polyconvex). +class TMOP_Metric_334 : public TMOP_Combo_QualityMetric +{ +protected: + mutable InvariantsEvaluator2D ie; + double gamma; + TMOP_QualityMetric *sh_metric, *sz_metric; + +public: + TMOP_Metric_334(double gamma_) : gamma(gamma_), + sh_metric(new TMOP_Metric_303), + sz_metric(new TMOP_Metric_316) + { + // (1-gamma) mu_303 + gamma mu_316 + AddQualityMetric(sh_metric, 1.-gamma_); + AddQualityMetric(sz_metric, gamma_); + } + + virtual ~TMOP_Metric_334() { delete sh_metric; delete sz_metric; } +}; + /// Shifted barrier form of 3D metric 16 (volume, ideal barrier metric), 3D class TMOP_Metric_352 : public TMOP_QualityMetric { From 6651c80b27172893aa97c9fc6133fc2c8cb7a073 Mon Sep 17 00:00:00 2001 From: Yohann Dudouit Date: Mon, 28 Jun 2021 17:35:25 -0700 Subject: [PATCH 021/198] Make Delete reset the Memory object. --- general/mem_manager.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/general/mem_manager.hpp b/general/mem_manager.hpp index a594a84885..5b92a553b9 100644 --- a/general/mem_manager.hpp +++ b/general/mem_manager.hpp @@ -362,8 +362,7 @@ public: be updated as described above. */ inline void SetDeviceMemoryType(MemoryType d_mt); - /** @brief Delete the owned pointers. The Memory is not reset by this method, - i.e. it will, generally, not be Empty() after this call. */ + /** @brief Delete the owned pointers and reset the Memory object. */ inline void Delete(); /** @brief Delete the device pointer, if owned. If @a copy_to_host is true @@ -940,6 +939,7 @@ inline void Memory::Delete() { if (flags & OWNS_HOST) { delete [] h_ptr; } } + Reset(); } template From 330f67cedcecd2c6da4948b75e9a42485dffc289 Mon Sep 17 00:00:00 2001 From: Yohann Dudouit Date: Wed, 30 Jun 2021 10:06:23 -0700 Subject: [PATCH 022/198] Reset with current MemoryType. --- general/mem_manager.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/general/mem_manager.hpp b/general/mem_manager.hpp index 5b92a553b9..5ed0ddad67 100644 --- a/general/mem_manager.hpp +++ b/general/mem_manager.hpp @@ -939,7 +939,7 @@ inline void Memory::Delete() { if (flags & OWNS_HOST) { delete [] h_ptr; } } - Reset(); + Reset(h_mt); } template From ec011048a0fb182322758a283755606b22990276 Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Fri, 25 Jun 2021 19:19:51 -0700 Subject: [PATCH 023/198] Minor Doxygen comments edits --- fem/lor.hpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/fem/lor.hpp b/fem/lor.hpp index 08aff63995..e80be4b3c1 100644 --- a/fem/lor.hpp +++ b/fem/lor.hpp @@ -35,7 +35,7 @@ private: /// Adds all the integrators from the BilinearForm @a a_from to @a a_to. If /// the mesh consists of tensor product elements, temporarily changes the /// integration rules of the integrators to use collocated quadrature for - /// better conditioning of the %LOR system. + /// better conditioning of the LOR system. void AddIntegrators(BilinearForm &a_from, BilinearForm &a_to, GetIntegratorsFn get_integrators, @@ -53,7 +53,7 @@ private: const IntegrationRule *ir); /// Resets the integration rules of the integrators of @a a to their original - /// values (after temporarily changing them for %LOR assembly). + /// values (after temporarily changing them for LOR assembly). void ResetIntegrationRules(GetIntegratorsFn get_integrators); static inline int absdof(int i) { return i < 0 ? -1-i : i; } @@ -75,7 +75,7 @@ protected: /// ConstructDofPermutation and GetDofPermutation). void ConstructLocalDofPermutation(Array &perm_) const; - /// Construct the permutation that maps %LOR DOFs to high-order DOFs. See + /// Construct the permutation that maps LOR DOFs to high-order DOFs. See /// GetDofPermutation. void ConstructDofPermutation() const; @@ -86,19 +86,19 @@ protected: /// Returns the type of finite element space: H1, ND, RT or L2. FESpaceType GetFESpaceType() const; - /// Returns the order of the %LOR space. 1 for H1 or ND, 0 for L2 or RT. + /// Returns the order of the LOR space. 1 for H1 or ND, 0 for L2 or RT. int GetLOROrder() const; LORBase(FiniteElementSpace &fes_ho_); public: - /// Returns the assembled %LOR system. + /// Returns the assembled LOR system. const OperatorHandle &GetAssembledSystem() const; /// Assembles the %LOR system. void AssembleSystem(BilinearForm &a_ho, const Array &ess_dofs); - /// @brief Returns the permutation that maps %LOR DOFs to high-order DOFs. + /// @brief Returns the permutation that maps LOR DOFs to high-order DOFs. /// /// This permutation is constructed the first time it is requested, and then /// is cached. For H1 and L2 finite element spaces (or for nonconforming @@ -108,11 +108,11 @@ public: /// /// For vector finite element spaces (ND and RT), the DOF permutation is /// nontrivial. Returns an array @a perm such that, given an index @a i of a - /// %LOR dof, @a perm[i] is the index of the corresponding HO dof. + /// LOR dof, @a perm[i] is the index of the corresponding HO dof. const Array &GetDofPermutation() const; - /// Returns true if the %LOR spaces requires a DOF permutation (if the - /// corresponding %LOR and HO DOFs are numbered differently), false + /// Returns true if the LOR spaces requires a DOF permutation (if the + /// corresponding LOR and HO DOFs are numbered differently), false /// otherwise. Note: permutations are not required in the case of /// nonconforming spaces, since the DOF numbering is incorporated into the /// prolongation operators. @@ -144,7 +144,7 @@ public: LORDiscretization(FiniteElementSpace &fes_ho, int ref_type=BasisType::GaussLobatto); - /// Return the assembled %LOR operator as a SparseMatrix. + /// Return the assembled LOR operator as a SparseMatrix. SparseMatrix &GetAssembledMatrix() const; }; @@ -170,10 +170,10 @@ public: ParLORDiscretization(ParFiniteElementSpace &fes_ho, int ref_type=BasisType::GaussLobatto); - /// Return the assembled %LOR operator as a HypreParMatrix. + /// Return the assembled LOR operator as a HypreParMatrix. HypreParMatrix &GetAssembledMatrix() const; - /// Return the %LOR ParFiniteElementSpace. + /// Return the LOR ParFiniteElementSpace. ParFiniteElementSpace &GetParFESpace() const; }; @@ -197,7 +197,7 @@ protected: mutable Vector px, py; public: /// @brief Create a solver of type @a SolverType, formed using the assembled - /// SparseMatrix of the %LOR version of @a a_ho. @see LORDiscretization + /// SparseMatrix of the LOR version of @a a_ho. @see LORDiscretization LORSolver(BilinearForm &a_ho, const Array &ess_tdof_list, int ref_type=BasisType::GaussLobatto) { @@ -207,7 +207,7 @@ public: #ifdef MFEM_USE_MPI /// @brief Create a solver of type @a SolverType, formed using the assembled - /// HypreParMatrix of the %LOR version of @a a_ho. @see ParLORDiscretization + /// HypreParMatrix of the LOR version of @a a_ho. @see ParLORDiscretization LORSolver(ParBilinearForm &a_ho, const Array &ess_tdof_list, int ref_type=BasisType::GaussLobatto) { @@ -228,7 +228,7 @@ public: SetOperator(op); } - /// @brief Create a solver of type @a SolverType using the assembled %LOR + /// @brief Create a solver of type @a SolverType using the assembled LOR /// operator represented by @a lor_. /// /// The given @a args will be used as arguments to the solver constructor. @@ -270,9 +270,9 @@ public: /// @brief Enable or disable the DOF permutation (enabled by default). /// - /// The corresponding %LOR and high-order DOFs may not have the same + /// The corresponding LOR and high-order DOFs may not have the same /// numbering (for example, when using ND or RT spaces), and so a permutation - /// is required when applying the %LOR solver as a preconditioner for the + /// is required when applying the LOR solver as a preconditioner for the /// high-order problem. This permutation can be disabled (for example, in /// order to precondition the low-order problem directly). void UsePermutation(bool use_permutation_) From b69e30c2ab5d57cb23a298369846651ef19f0054 Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Fri, 25 Jun 2021 19:20:07 -0700 Subject: [PATCH 024/198] Add accessor for LORBase object from LORSolver --- fem/lor.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fem/lor.hpp b/fem/lor.hpp index e80be4b3c1..9494ab7661 100644 --- a/fem/lor.hpp +++ b/fem/lor.hpp @@ -286,6 +286,9 @@ public: /// Access the underlying solver. const SolverType &GetSolver() const { return solver; } + /// Access the LOR discretization object. + const LORBase &GetLOR() const { return *lor; } + ~LORSolver() { if (own_lor) { delete lor; } } }; From c2d96bebba9b39341f60f70df1be7c577b9fce20 Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Fri, 25 Jun 2021 19:21:11 -0700 Subject: [PATCH 025/198] Refactor LOR AssembleSystem --- fem/lor.cpp | 20 +++++++++++++++++--- fem/lor.hpp | 14 +++++++++++--- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/fem/lor.cpp b/fem/lor.cpp index 7428552764..5e530cdad2 100644 --- a/fem/lor.cpp +++ b/fem/lor.cpp @@ -238,7 +238,7 @@ const OperatorHandle &LORBase::GetAssembledSystem() const return A; } -void LORBase::AssembleSystem(BilinearForm &a_ho, const Array &ess_dofs) +void LORBase::AssembleSystem_(BilinearForm &a_ho, const Array &ess_dofs) { a->UseExternalIntegrators(); AddIntegrators(a_ho, *a, &BilinearForm::GetDBFI, @@ -373,7 +373,6 @@ LORDiscretization::LORDiscretization(BilinearForm &a_ho_, int ref_type) : LORDiscretization(*a_ho_.FESpace(), ref_type) { - a = new BilinearForm(fes); AssembleSystem(a_ho_, ess_tdof_list); } @@ -399,6 +398,14 @@ LORDiscretization::LORDiscretization(FiniteElementSpace &fes_ho, A.SetType(Operator::MFEM_SPARSEMAT); } +void LORDiscretization::AssembleSystem(BilinearForm &a_ho, + const Array &ess_dofs) +{ + delete a; + a = new BilinearForm(&GetFESpace()); + AssembleSystem_(a_ho, ess_dofs); +} + SparseMatrix &LORDiscretization::GetAssembledMatrix() const { MFEM_VERIFY(a != NULL && A.Ptr() != NULL, "No LOR system assembled"); @@ -412,7 +419,6 @@ ParLORDiscretization::ParLORDiscretization(ParBilinearForm &a_ho_, int ref_type) : ParLORDiscretization(*a_ho_.ParFESpace(), ref_type) { - a = new ParBilinearForm(static_cast(fes)); AssembleSystem(a_ho_, ess_tdof_list); } @@ -439,6 +445,14 @@ ParLORDiscretization::ParLORDiscretization(ParFiniteElementSpace &fes_ho, A.SetType(Operator::Hypre_ParCSR); } +void ParLORDiscretization::AssembleSystem(ParBilinearForm &a_ho, + const Array &ess_dofs) +{ + delete a; + a = new ParBilinearForm(&GetParFESpace()); + AssembleSystem_(a_ho, ess_dofs); +} + HypreParMatrix &ParLORDiscretization::GetAssembledMatrix() const { MFEM_VERIFY(a != NULL && A.Ptr() != NULL, "No LOR system assembled"); diff --git a/fem/lor.hpp b/fem/lor.hpp index 9494ab7661..4c4afc096e 100644 --- a/fem/lor.hpp +++ b/fem/lor.hpp @@ -89,15 +89,17 @@ protected: /// Returns the order of the LOR space. 1 for H1 or ND, 0 for L2 or RT. int GetLOROrder() const; + /// Assembles the LOR system (used internally by + /// LORDiscretization::AssembleSystem and + /// ParLORDiscretization::AssembleSystem). + void AssembleSystem_(BilinearForm &a_ho, const Array &ess_dofs); + LORBase(FiniteElementSpace &fes_ho_); public: /// Returns the assembled LOR system. const OperatorHandle &GetAssembledSystem() const; - /// Assembles the %LOR system. - void AssembleSystem(BilinearForm &a_ho, const Array &ess_dofs); - /// @brief Returns the permutation that maps LOR DOFs to high-order DOFs. /// /// This permutation is constructed the first time it is requested, and then @@ -144,6 +146,9 @@ public: LORDiscretization(FiniteElementSpace &fes_ho, int ref_type=BasisType::GaussLobatto); + /// Assembles the LOR system corresponding to @a a_ho. + void AssembleSystem(BilinearForm &a_ho, const Array &ess_dofs); + /// Return the assembled LOR operator as a SparseMatrix. SparseMatrix &GetAssembledMatrix() const; }; @@ -170,6 +175,9 @@ public: ParLORDiscretization(ParFiniteElementSpace &fes_ho, int ref_type=BasisType::GaussLobatto); + /// Assembles the LOR system corresponding to @a a_ho. + void AssembleSystem(ParBilinearForm &a_ho, const Array &ess_dofs); + /// Return the assembled LOR operator as a HypreParMatrix. HypreParMatrix &GetAssembledMatrix() const; From 2657585cfe90b28a872f947e76f50bf71af8d7b9 Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Fri, 25 Jun 2021 19:21:28 -0700 Subject: [PATCH 026/198] Enable LOR for variable-order spaces (in serial) --- fem/lor.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/fem/lor.cpp b/fem/lor.cpp index 5e530cdad2..9f9328f458 100644 --- a/fem/lor.cpp +++ b/fem/lor.cpp @@ -381,15 +381,16 @@ LORDiscretization::LORDiscretization(FiniteElementSpace &fes_ho, { CheckBasisType(fes_ho); - // TODO: support variable-order spaces - MFEM_VERIFY(!fes_ho.IsVariableOrder(), - "Cannot construct LOR operators on variable-order spaces"); - - int order = fes_ho.GetMaxElementOrder(); - if (GetFESpaceType() == L2) { ++order; } - Mesh &mesh_ho = *fes_ho.GetMesh(); - mesh = new Mesh(Mesh::MakeRefined(mesh_ho, order, ref_type)); + // For H1, ND and RT spaces, use refinement = element order, for DG spaces, + // use refinement = element order + 1 (since LOR is p = 0 in this case). + int increment = (GetFESpaceType() == L2) ? 1 : 0; + Array refinements(mesh_ho.GetNE()); + for (int i=0; iClone(GetLOROrder()); fes = new FiniteElementSpace(mesh, fec); @@ -426,7 +427,7 @@ ParLORDiscretization::ParLORDiscretization(ParFiniteElementSpace &fes_ho, int ref_type) : LORBase(fes_ho) { if (fes_ho.GetMyRank() == 0) { CheckBasisType(fes_ho); } - // TODO: support variable-order spaces + // TODO: support variable-order spaces in parallel MFEM_VERIFY(!fes_ho.IsVariableOrder(), "Cannot construct LOR operators on variable-order spaces"); From 0a9b0c681d83a73c30e1c9816271045828a186ba Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Sat, 26 Jun 2021 18:27:47 -0700 Subject: [PATCH 027/198] Handle LOR permutation construction in variable-order case --- fem/fespace.cpp | 5 ++++- fem/lor.cpp | 17 ++++++++++++++++- fem/pfespace.cpp | 5 ++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/fem/fespace.cpp b/fem/fespace.cpp index d55c135e6c..9743726754 100644 --- a/fem/fespace.cpp +++ b/fem/fespace.cpp @@ -102,8 +102,11 @@ void FiniteElementSpace::CopyProlongationAndRestriction( SparseMatrix *perm_mat = NULL, *perm_mat_tr = NULL; if (perm) { + // Note: although n and fes.GetVSize() are typically equal, in + // variable-order spaces they may differ, since nonconforming edges/faces + // my have fictitious DOFs. int n = perm->Size(); - perm_mat = new SparseMatrix(n, n); + perm_mat = new SparseMatrix(n, fes.GetVSize()); for (int i=0; i &perm_) const int dim = mesh_lor.Dimension(); const CoarseFineTransformations &cf_tr = mesh_lor.GetRefinementTransforms(); + using GeomRef = std::pair; + std::map point_matrices_offsets; perm_.SetSize(fes_lor.GetVSize()); Array vdof_ho, vdof_lor; for (int ilor=0; ilor &perm_) const continue; } - int p = fes_ho.GetOrder(iho); int p1 = p+1; int ndof_per_dim = (dim == 2) ? p*p1 : type == ND ? p*p1*p1 : p*p*p1; diff --git a/fem/pfespace.cpp b/fem/pfespace.cpp index a231a7a44e..dcd15106bc 100644 --- a/fem/pfespace.cpp +++ b/fem/pfespace.cpp @@ -2939,8 +2939,11 @@ void ParFiniteElementSpace::CopyProlongationAndRestriction( SparseMatrix *perm_mat = NULL, *perm_mat_tr = NULL; if (perm) { + // Note: although n and fes.GetVSize() are typically equal, in + // variable-order spaces they may differ, since nonconforming edges/faces + // my have fictitious DOFs. int n = perm->Size(); - perm_mat = new SparseMatrix(n, n); + perm_mat = new SparseMatrix(n, fes.GetVSize()); for (int i=0; i Date: Sun, 27 Jun 2021 12:23:06 -0700 Subject: [PATCH 028/198] Handle better support of LOR for nonconforming spaces --- fem/lor.cpp | 13 +++++++++---- fem/lor.hpp | 22 +++++++++++++++++----- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/fem/lor.cpp b/fem/lor.cpp index 9a2d330ada..52c2bbc73c 100644 --- a/fem/lor.cpp +++ b/fem/lor.cpp @@ -241,10 +241,15 @@ const Array &LORBase::GetDofPermutation() const return perm; } -bool LORBase::RequiresDofPermutation() const +bool LORBase::HasSameDofNumbering() const { FESpaceType type = GetFESpaceType(); - return (type == H1 || type == L2 || nonconforming) ? false : true; + return type == H1 || type == L2; +} + +bool LORBase::RequiresDofPermutation() const +{ + return (HasSameDofNumbering() || nonconforming) ? false : true; } const OperatorHandle &LORBase::GetAssembledSystem() const @@ -295,7 +300,7 @@ void LORBase::AssembleSystem_(BilinearForm &a_ho, const Array &ess_dofs) void LORBase::SetupNonconforming() { - if (RequiresDofPermutation()) + if (!HasSameDofNumbering()) { Array p; ConstructLocalDofPermutation(p); @@ -305,7 +310,7 @@ void LORBase::SetupNonconforming() { fes->CopyProlongationAndRestriction(fes_ho, NULL); } - nonconforming = true; + nonconforming = fes->Nonconforming(); } template diff --git a/fem/lor.hpp b/fem/lor.hpp index 4c4afc096e..b433858419 100644 --- a/fem/lor.hpp +++ b/fem/lor.hpp @@ -79,6 +79,10 @@ protected: /// GetDofPermutation. void ConstructDofPermutation() const; + /// Returns true if the LOR space and HO space have the same DOF numbering + /// (H1 or L2 spaces), false otherwise (ND or RT spaces). + bool HasSameDofNumbering() const; + /// Sets up the prolongation and restriction operators required for /// nonconforming spaces. void SetupNonconforming(); @@ -113,11 +117,19 @@ public: /// LOR dof, @a perm[i] is the index of the corresponding HO dof. const Array &GetDofPermutation() const; - /// Returns true if the LOR spaces requires a DOF permutation (if the - /// corresponding LOR and HO DOFs are numbered differently), false - /// otherwise. Note: permutations are not required in the case of - /// nonconforming spaces, since the DOF numbering is incorporated into the - /// prolongation operators. + /// @brief Returns true if the LOR space requires a DOF permutation, false + /// otherwise. + /// + /// DOF permutations are required if all of the following conditions are + /// true: + /// * The spaces have different DOF numberings (which occurs for ND and RT + /// spaces, in this case @ref HasSameDofNumbering returns false). + /// * The mesh is conforming. + /// * The space is not variable degree. + /// + /// Note: permutations are not required in the case of nonconforming meshes + /// or variable polynomial degrees, since in these cases the DOF numbering is + /// incorporated into the prolongation operators. bool RequiresDofPermutation() const; /// Returns the low-order refined finite element space. From 74710220a17e0b80f1b2c49ec3624bd90a58fc19 Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Wed, 30 Jun 2021 14:11:24 -0700 Subject: [PATCH 029/198] Remove need (mostly) for LOR DOF permutations Build the permutation into the P and R operators of the LOR space, so that the true DOF numbering corresponds to the same true DOF number of the high-order space. This means the LORSolver does not need to perform any permutation, since it is incorporated into the RAP. --- fem/fespace.cpp | 11 +++++++++++ fem/lor.cpp | 35 +++++------------------------------ fem/lor.hpp | 47 ++++------------------------------------------- fem/pfespace.cpp | 15 +++++++++++++++ 4 files changed, 35 insertions(+), 73 deletions(-) diff --git a/fem/fespace.cpp b/fem/fespace.cpp index 9743726754..850cfb9827 100644 --- a/fem/fespace.cpp +++ b/fem/fespace.cpp @@ -123,11 +123,22 @@ void FiniteElementSpace::CopyProlongationAndRestriction( else { cP = new SparseMatrix(*fes.GetConformingProlongation()); } cP_is_set = true; } + else if (perm != NULL) + { + cP = perm_mat; + cP_is_set = true; + perm_mat = NULL; + } if (fes.GetConformingRestriction() != NULL) { if (perm) { cR = Mult(*fes.GetConformingRestriction(), *perm_mat_tr); } else { cR = new SparseMatrix(*fes.GetConformingRestriction()); } } + else if (perm != NULL) + { + cR = perm_mat_tr; + perm_mat_tr = NULL; + } delete perm_mat; delete perm_mat_tr; diff --git a/fem/lor.cpp b/fem/lor.cpp index 52c2bbc73c..80a15bf48e 100644 --- a/fem/lor.cpp +++ b/fem/lor.cpp @@ -196,7 +196,7 @@ void LORBase::ConstructLocalDofPermutation(Array &perm_) const void LORBase::ConstructDofPermutation() const { FESpaceType type = GetFESpaceType(); - if (type == H1 || type == L2 || nonconforming) + if (type == H1 || type == L2) { // H1 and L2: no permutation necessary, return identity perm.SetSize(fes->GetTrueVSize()); @@ -247,11 +247,6 @@ bool LORBase::HasSameDofNumbering() const return type == H1 || type == L2; } -bool LORBase::RequiresDofPermutation() const -{ - return (HasSameDofNumbering() || nonconforming) ? false : true; -} - const OperatorHandle &LORBase::GetAssembledSystem() const { MFEM_VERIFY(a != NULL && A.Ptr() != NULL, "No LOR system assembled"); @@ -272,33 +267,14 @@ void LORBase::AssembleSystem_(BilinearForm &a_ho, const Array &ess_dofs) &BilinearForm::GetBFBFI_Marker, &BilinearForm::AddBdrFaceIntegrator, ir_face); a->Assemble(); - if (RequiresDofPermutation()) - { - const Array &p = GetDofPermutation(); - // Form inverse permutation: given high-order dof i, pi[i] is corresp. LO - Array pi(p.Size()); - for (int i=0; i ess_dofs_perm(ess_dofs.Size()); - for (int i=0; iFormSystemMatrix(ess_dofs_perm, A); - } - else - { - a->FormSystemMatrix(ess_dofs, A); - } + a->FormSystemMatrix(ess_dofs, A); ResetIntegrationRules(&BilinearForm::GetDBFI); ResetIntegrationRules(&BilinearForm::GetFBFI); ResetIntegrationRules(&BilinearForm::GetBBFI); ResetIntegrationRules(&BilinearForm::GetBFBFI); } -void LORBase::SetupNonconforming() +void LORBase::SetupProlongationAndRestriction() { if (!HasSameDofNumbering()) { @@ -310,7 +286,6 @@ void LORBase::SetupNonconforming() { fes->CopyProlongationAndRestriction(fes_ho, NULL); } - nonconforming = fes->Nonconforming(); } template @@ -414,7 +389,7 @@ LORDiscretization::LORDiscretization(FiniteElementSpace &fes_ho, fec = fes_ho.FEColl()->Clone(GetLOROrder()); fes = new FiniteElementSpace(mesh, fec); - if (fes_ho.Nonconforming()) { SetupNonconforming(); } + SetupProlongationAndRestriction(); A.SetType(Operator::MFEM_SPARSEMAT); } @@ -461,7 +436,7 @@ ParLORDiscretization::ParLORDiscretization(ParFiniteElementSpace &fes_ho, fec = fes_ho.FEColl()->Clone(GetLOROrder()); ParFiniteElementSpace *pfes = new ParFiniteElementSpace(pmesh, fec); fes = pfes; - if (fes_ho.Nonconforming()) { SetupNonconforming(); } + SetupProlongationAndRestriction(); A.SetType(Operator::Hypre_ParCSR); } diff --git a/fem/lor.hpp b/fem/lor.hpp index b433858419..77e02bc22c 100644 --- a/fem/lor.hpp +++ b/fem/lor.hpp @@ -68,7 +68,6 @@ protected: BilinearForm *a; OperatorHandle A; mutable Array perm; - bool nonconforming = false; /// Constructs the local DOF (ldof) permutation. In parallel this is used as /// an intermediate step in computing the DOF permutation (see @@ -83,9 +82,9 @@ protected: /// (H1 or L2 spaces), false otherwise (ND or RT spaces). bool HasSameDofNumbering() const; - /// Sets up the prolongation and restriction operators required for - /// nonconforming spaces. - void SetupNonconforming(); + /// Sets up the prolongation and restriction operators required in the case + /// of different DOF numberings (ND or RT spaces) or nonconforming spaces. + void SetupProlongationAndRestriction(); /// Returns the type of finite element space: H1, ND, RT or L2. FESpaceType GetFESpaceType() const; @@ -117,21 +116,6 @@ public: /// LOR dof, @a perm[i] is the index of the corresponding HO dof. const Array &GetDofPermutation() const; - /// @brief Returns true if the LOR space requires a DOF permutation, false - /// otherwise. - /// - /// DOF permutations are required if all of the following conditions are - /// true: - /// * The spaces have different DOF numberings (which occurs for ND and RT - /// spaces, in this case @ref HasSameDofNumbering returns false). - /// * The mesh is conforming. - /// * The space is not variable degree. - /// - /// Note: permutations are not required in the case of nonconforming meshes - /// or variable polynomial degrees, since in these cases the DOF numbering is - /// incorporated into the prolongation operators. - bool RequiresDofPermutation() const; - /// Returns the low-order refined finite element space. FiniteElementSpace &GetFESpace() const { return *fes; } @@ -263,30 +247,7 @@ public: height = solver.Height(); } - void Mult(const Vector &x, Vector &y) const - { - if (use_permutation && lor->RequiresDofPermutation()) - { - const Array &p = lor->GetDofPermutation(); - px.SetSize(x.Size()); - py.SetSize(y.Size()); - for (int i=0; iP); } nonconf_P = true; } + else if (perm != NULL) + { + HYPRE_BigInt glob_nrows = GlobalVSize(); + HYPRE_BigInt glob_ncols = GlobalTrueVSize(); + HYPRE_BigInt *col_starts = GetTrueDofOffsets(); + HYPRE_BigInt *row_starts = GetDofOffsets(); + P = new HypreParMatrix(MyComm, glob_nrows, glob_ncols, row_starts, + col_starts, perm_mat); + nonconf_P = true; + } if (pfes->R != NULL) { if (perm) { R = Mult(*pfes->R, *perm_mat_tr); } else { R = new SparseMatrix(*pfes->R); } } + else if (perm != NULL) + { + R = perm_mat_tr; + perm_mat_tr = NULL; + } delete perm_mat; delete perm_mat_tr; From 06032d93402dd6fe4fa7f0953ec8be2697d4cd04 Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Wed, 30 Jun 2021 18:30:25 -0700 Subject: [PATCH 030/198] Add HostRead in BilinearForm::EliminateVDofs --- fem/bilinearform.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/fem/bilinearform.cpp b/fem/bilinearform.cpp index 18447ddadc..d63c4e1355 100644 --- a/fem/bilinearform.cpp +++ b/fem/bilinearform.cpp @@ -953,6 +953,7 @@ void BilinearForm::EliminateVDofs(const Array &vdofs, const Vector &sol, Vector &rhs, DiagonalPolicy dpolicy) { + vdofs.HostRead(); for (int i = 0; i < vdofs.Size(); i++) { int vdof = vdofs[i]; From e20ae257e52bb57862c7bbca6b44b5041c5f39ca Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Wed, 7 Jul 2021 14:25:56 -0700 Subject: [PATCH 031/198] Don't need LORSolver permutation anymore --- fem/lor.hpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/fem/lor.hpp b/fem/lor.hpp index 77e02bc22c..186b2effca 100644 --- a/fem/lor.hpp +++ b/fem/lor.hpp @@ -196,7 +196,6 @@ class LORSolver : public Solver protected: LORBase *lor; bool own_lor = true; - bool use_permutation = true; SolverType solver; mutable Vector px, py; public: @@ -222,8 +221,6 @@ public: /// @brief Create a solver of type @a SolverType using Operator @a op and /// arguments @a args. - /// - /// The object @a lor_ will be used for DOF permutations. template LORSolver(const Operator &op, LORBase &lor_, Args&&... args) : solver(args...) { @@ -249,18 +246,6 @@ public: void Mult(const Vector &x, Vector &y) const { solver.Mult(x, y); } - /// @brief Enable or disable the DOF permutation (enabled by default). - /// - /// The corresponding LOR and high-order DOFs may not have the same - /// numbering (for example, when using ND or RT spaces), and so a permutation - /// is required when applying the LOR solver as a preconditioner for the - /// high-order problem. This permutation can be disabled (for example, in - /// order to precondition the low-order problem directly). - void UsePermutation(bool use_permutation_) - { - use_permutation = use_permutation_; - } - /// Access the underlying solver. SolverType &GetSolver() { return solver; } From 1d5ff54f4cf4750d1457a8b538b9cab65bfda88a Mon Sep 17 00:00:00 2001 From: Cameron Smith Date: Tue, 13 Jul 2021 20:05:57 +0000 Subject: [PATCH 032/198] install examples --- config/cmake/modules/MfemCmakeUtilities.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/cmake/modules/MfemCmakeUtilities.cmake b/config/cmake/modules/MfemCmakeUtilities.cmake index acd05c4f1a..0edabb7e28 100644 --- a/config/cmake/modules/MfemCmakeUtilities.cmake +++ b/config/cmake/modules/MfemCmakeUtilities.cmake @@ -100,6 +100,8 @@ macro(add_mfem_examples EXE_SRCS) string(REPLACE ".cpp" "" EXE_NAME "${EXE_PREFIX}${SRC_FILENAME}") mfem_add_executable(${EXE_NAME} ${SRC_FILE}) + install(TARGETS ${EXE_NAME} + DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) add_dependencies(${MFEM_ALL_EXAMPLES_TARGET_NAME} ${EXE_NAME}) if (EXE_NEEDED_BY) add_dependencies(${EXE_NEEDED_BY} ${EXE_NAME}) From afee0acf50f16289855e7781efbf4d8972c4fe0d Mon Sep 17 00:00:00 2001 From: Cameron Smith Date: Wed, 14 Jul 2021 13:18:04 +0000 Subject: [PATCH 033/198] exes are RUNTIME comps, cmake sets the prefix --- config/cmake/modules/MfemCmakeUtilities.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/cmake/modules/MfemCmakeUtilities.cmake b/config/cmake/modules/MfemCmakeUtilities.cmake index 0edabb7e28..2efabf1bbd 100644 --- a/config/cmake/modules/MfemCmakeUtilities.cmake +++ b/config/cmake/modules/MfemCmakeUtilities.cmake @@ -101,7 +101,7 @@ macro(add_mfem_examples EXE_SRCS) string(REPLACE ".cpp" "" EXE_NAME "${EXE_PREFIX}${SRC_FILENAME}") mfem_add_executable(${EXE_NAME} ${SRC_FILE}) install(TARGETS ${EXE_NAME} - DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) + RUNTIME DESTINATION bin) add_dependencies(${MFEM_ALL_EXAMPLES_TARGET_NAME} ${EXE_NAME}) if (EXE_NEEDED_BY) add_dependencies(${EXE_NEEDED_BY} ${EXE_NAME}) From a10fb1f421702506bc0c86d96f5996d0551bfd1a Mon Sep 17 00:00:00 2001 From: Cameron Smith Date: Wed, 14 Jul 2021 16:14:01 +0000 Subject: [PATCH 034/198] install examples to examples dir --- config/cmake/modules/MfemCmakeUtilities.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/cmake/modules/MfemCmakeUtilities.cmake b/config/cmake/modules/MfemCmakeUtilities.cmake index 2efabf1bbd..055e0c2948 100644 --- a/config/cmake/modules/MfemCmakeUtilities.cmake +++ b/config/cmake/modules/MfemCmakeUtilities.cmake @@ -101,7 +101,7 @@ macro(add_mfem_examples EXE_SRCS) string(REPLACE ".cpp" "" EXE_NAME "${EXE_PREFIX}${SRC_FILENAME}") mfem_add_executable(${EXE_NAME} ${SRC_FILE}) install(TARGETS ${EXE_NAME} - RUNTIME DESTINATION bin) + RUNTIME DESTINATION examples) add_dependencies(${MFEM_ALL_EXAMPLES_TARGET_NAME} ${EXE_NAME}) if (EXE_NEEDED_BY) add_dependencies(${EXE_NEEDED_BY} ${EXE_NAME}) From 4d51a907cf75a148b7d45157b04fb61b7845edda Mon Sep 17 00:00:00 2001 From: Cameron Smith Date: Wed, 14 Jul 2021 16:16:58 +0000 Subject: [PATCH 035/198] install examples if enabled I don't think this has other significant side effects... --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c53d27088c..e99c11babb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -550,7 +550,11 @@ add_custom_target(${MFEM_EXEC_PREREQUISITES_TARGET_NAME}) # Create a target for all examples and, optionally, enable it. set(MFEM_ALL_EXAMPLES_TARGET_NAME examples) add_mfem_target(${MFEM_ALL_EXAMPLES_TARGET_NAME} ${MFEM_ENABLE_EXAMPLES}) -add_subdirectory(examples EXCLUDE_FROM_ALL) +if (MFEM_ENABLE_EXAMPLES) + add_subdirectory(examples) #install examples if enabled +else() + add_subdirectory(examples EXCLUDE_FROM_ALL) +endif() # Create a target for all miniapps and, optionally, enable it. set(MFEM_ALL_MINIAPPS_TARGET_NAME miniapps) From 1f35f2580de54c34bbc5b98ddd3b0f54da91a3a9 Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Wed, 14 Jul 2021 12:47:29 -0700 Subject: [PATCH 036/198] WIP - initial commit --- miniapps/shifted/diffusion.cpp | 39 ++++++++++++++++++++++------ miniapps/shifted/marking.cpp | 32 ++++++++++++----------- miniapps/shifted/marking.hpp | 12 ++++++--- miniapps/shifted/quad.mesh | 7 ++++++ miniapps/shifted/sbm_aux.hpp | 46 +++++++++++++++++++++++++++++++++- 5 files changed, 109 insertions(+), 27 deletions(-) create mode 100644 miniapps/shifted/quad.mesh diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index c98423a91e..b52ee85060 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -169,21 +169,38 @@ int main(int argc, char *argv[]) // corresponding to pfespace. ParGridFunction x(&pfespace); // ParGridFunction for level_set_value. - ParGridFunction level_set_val(&pfespace); + ParGridFunction dirichlet_level_set_val(&pfespace); + ParGridFunction neumann_level_set_val(&pfespace); + ParGridFunction combo_level_set_val(&pfespace); + + int dirichlet_level_set_type = level_set_type == 5 ? 1 : level_set_type; // Determine if each element in the ParMesh is inside the actual domain, // partially cut by its boundary, or completely outside the domain. - Dist_Level_Set_Coefficient dist_fun_level_coef(level_set_type); - level_set_val.ProjectCoefficient(dist_fun_level_coef); + Dist_Level_Set_Coefficient dirichlet_dist_coef(dirichlet_level_set_type); + dirichlet_level_set_val.ProjectCoefficient(dirichlet_dist_coef); // Exchange information for ghost elements i.e. elements that share a face // with element on the current processor, but belong to another processor. - level_set_val.ExchangeFaceNbrData(); + dirichlet_level_set_val.ExchangeFaceNbrData(); // Setup the class to mark all elements based on whether they are located // inside or outside the true domain, or intersected by the true boundary. - ShiftedFaceMarker marker(pmesh, level_set_val, pfespace, include_cut_cell); + ShiftedFaceMarker marker(pmesh, dirichlet_level_set_val, pfespace, include_cut_cell); Array elem_marker; marker.MarkElements(elem_marker); + // Setup the Neumann level set grid function + Dist_Level_Set_Coefficient neumann_dist_coef(level_set_type); + neumann_level_set_val.ProjectCoefficient(neumann_dist_coef); + neumann_level_set_val.ExchangeFaceNbrData(); + marker.SetLevelSetFunction(neumann_level_set_val); + marker.MarkElements(elem_marker); + + // Create a Combo level set coefficient + Combo_Level_Set_Coefficient combo_dist_coef; + combo_dist_coef.Add_Level_Set_Coefficient(dirichlet_dist_coef); + combo_dist_coef.Add_Level_Set_Coefficient(neumann_dist_coef); + + // Visualize the element markers. if (visualization) { @@ -237,13 +254,18 @@ int main(int argc, char *argv[]) VectorCoefficient *dist_vec = NULL; // Compute the distance field using the HeatDistanceSolver for // level_set_type == 4 or analytically for all other level set types. - if (level_set_type == 4) + if (level_set_type == 4 || level_set_type == 5) { // Discrete distance vector. double dx = AvgElementSize(pmesh); ParGridFunction filt_gf(&pfespace); PDEFilter *filter = new PDEFilter(pmesh, 2.0 * dx); - filter->Filter(dist_fun_level_coef, filt_gf); + if (level_set_type == 4) { + filter->Filter(dirichlet_dist_coef, filt_gf); + } + else { + filter->Filter(combo_dist_coef, filt_gf); + } delete filter; GridFunctionCoefficient ls_filt_coeff(&filt_gf); @@ -279,6 +301,7 @@ int main(int argc, char *argv[]) "Distance Vector", s, s, s, s, "Rjmmpcvv", 1); } + // Set up a list to indicate element attributes to be included in assembly, // so that inactive elements are excluded. const int max_elem_attr = pmesh.attributes.Max(); @@ -289,7 +312,7 @@ int main(int argc, char *argv[]) { if (!include_cut_cell && (elem_marker[i] == ShiftedFaceMarker::SBElementType::OUTSIDE || - elem_marker[i] == ShiftedFaceMarker::SBElementType::CUT)) + elem_marker[i] >= ShiftedFaceMarker::SBElementType::CUT)) { pmesh.SetAttribute(i, max_elem_attr+1); inactive_elements = true; diff --git a/miniapps/shifted/marking.cpp b/miniapps/shifted/marking.cpp index da8a8fd742..8dcb391ac2 100644 --- a/miniapps/shifted/marking.cpp +++ b/miniapps/shifted/marking.cpp @@ -14,10 +14,11 @@ namespace mfem { -void ShiftedFaceMarker::MarkElements(Array &elem_marker) const +void ShiftedFaceMarker::MarkElements(Array &elem_marker) { elem_marker.SetSize(pmesh.GetNE() + pmesh.GetNSharedFaces()); - elem_marker = SBElementType::INSIDE; + if (!initial_marking) { elem_marker = SBElementType::INSIDE; } + else { level_set_index += 1; } IntegrationRules IntRulesLo(0, Quadrature1D::GaussLobatto); @@ -42,7 +43,7 @@ void ShiftedFaceMarker::MarkElements(Array &elem_marker) const } else if (count > 0) // partially outside { - elem_marker[i] = SBElementType::CUT; + elem_marker[i] = SBElementType::CUT + level_set_index; } } @@ -76,9 +77,10 @@ void ShiftedFaceMarker::MarkElements(Array &elem_marker) const } else if (count > 0) // partially outside { - elem_marker[i] = SBElementType::CUT; + elem_marker[i] = SBElementType::CUT + level_set_index; } } + initial_marking = true; } void ShiftedFaceMarker::ListShiftedFaceDofs(const Array &elem_marker, @@ -102,25 +104,25 @@ void ShiftedFaceMarker::ListShiftedFaceDofs(const Array &elem_marker, { int te1 = elem_marker[tr->Elem1No], te2 = elem_marker[tr->Elem2No]; if (!include_cut_cell && - te1 == ShiftedFaceMarker::CUT && te2 == ShiftedFaceMarker::INSIDE) + te1 >= ShiftedFaceMarker::CUT && te2 == ShiftedFaceMarker::INSIDE) { pfes_sltn.GetFaceDofs(f, dofs); sface_dof_list.Append(dofs); } if (!include_cut_cell && - te1 == ShiftedFaceMarker::INSIDE && te2 == ShiftedFaceMarker::CUT) + te1 == ShiftedFaceMarker::INSIDE && te2 >= ShiftedFaceMarker::CUT) { pfes_sltn.GetFaceDofs(f, dofs); sface_dof_list.Append(dofs); } if (include_cut_cell && - te1 == SBElementType::CUT && te2 == SBElementType::OUTSIDE) + te1 >= SBElementType::CUT && te2 == SBElementType::OUTSIDE) { pfes_sltn.GetFaceDofs(f, dofs); sface_dof_list.Append(dofs); } if (include_cut_cell && - te1 == SBElementType::OUTSIDE && te2 == SBElementType::CUT) + te1 == SBElementType::OUTSIDE && te2 >= SBElementType::CUT) { pfes_sltn.GetFaceDofs(f, dofs); sface_dof_list.Append(dofs); @@ -136,7 +138,7 @@ void ShiftedFaceMarker::ListShiftedFaceDofs(const Array &elem_marker, FaceElementTransformations *tr = pmesh.GetBdrFaceTransformations(i); if (tr != NULL) { - if (elem_marker[tr->Elem1No] == SBElementType::CUT) + if (elem_marker[tr->Elem1No] >= SBElementType::CUT) { pfes_sltn.GetFaceDofs(pmesh.GetBdrFace(i), dofs); sface_dof_list.Append(dofs); @@ -158,13 +160,13 @@ void ShiftedFaceMarker::ListShiftedFaceDofs(const Array &elem_marker, // Add if the element on this MPI rank is completely inside the domain // and the element on other MPI rank is not. if (!include_cut_cell && - te2 == ShiftedFaceMarker::CUT && te1 == ShiftedFaceMarker::INSIDE) + te2 >= ShiftedFaceMarker::CUT && te1 == ShiftedFaceMarker::INSIDE) { pfes_sltn.GetFaceDofs(faceno, dofs); sface_dof_list.Append(dofs); } if (include_cut_cell && - te2 == SBElementType::OUTSIDE && te1 == SBElementType::CUT) + te2 == SBElementType::OUTSIDE && te1 >= SBElementType::CUT) { pfes_sltn.GetFaceDofs(faceno, dofs); sface_dof_list.Append(dofs); @@ -198,7 +200,7 @@ void ShiftedFaceMarker::ListEssentialTDofs(const Array &elem_marker, FaceElementTransformations *tr = pmesh.GetBdrFaceTransformations(i); if (tr != NULL) { - if (elem_marker[tr->Elem1No] == SBElementType::CUT) + if (elem_marker[tr->Elem1No] >= SBElementType::CUT) { pmesh.SetBdrAttribute(i, pmesh_bdr_attr_max+1); sbm_at_true_boundary = true; @@ -235,7 +237,7 @@ void ShiftedFaceMarker::ListEssentialTDofs(const Array &elem_marker, { if (!include_cut_cell && (elem_marker[e] == SBElementType::OUTSIDE || - elem_marker[e] == SBElementType::CUT)) + elem_marker[e] >= SBElementType::CUT)) { pfes_sltn.GetElementVDofs(e, dofs); for (int i = 0; i < dofs.Size(); i++) @@ -295,7 +297,7 @@ void ShiftedFaceMarker::ListShiftedFaceDofs2(const Array &elem_marker, mat(i) = 0.0; if (elem_marker[i] == SBElementType::OUTSIDE) { mat(i) = 1.0; } - if (elem_marker[i] == SBElementType::CUT && include_cut_cell == false) + if (elem_marker[i] >= SBElementType::CUT && include_cut_cell == false) { mat(i) = 1.0; } } @@ -319,7 +321,7 @@ void ShiftedFaceMarker::ListShiftedFaceDofs2(const Array &elem_marker, FaceElementTransformations *tr = pmesh.GetBdrFaceTransformations(i); if (tr != NULL) { - if (elem_marker[tr->Elem1No] == SBElementType::CUT) + if (elem_marker[tr->Elem1No] >= SBElementType::CUT) { pfes_sltn.GetFaceDofs(pmesh.GetBdrFace(i), dofs); sface_dof_list.Append(dofs); diff --git a/miniapps/shifted/marking.hpp b/miniapps/shifted/marking.hpp index 46853780b5..8763a2c732 100644 --- a/miniapps/shifted/marking.hpp +++ b/miniapps/shifted/marking.hpp @@ -26,6 +26,7 @@ protected: ParGridFunction &ls_func; ParFiniteElementSpace &pfes_sltn; bool include_cut_cell; + bool initial_marking; // Marking of face dofs by using an averaged continuous GridFunction. const bool func_dof_marking = false; @@ -34,18 +35,21 @@ protected: void ListShiftedFaceDofs2(const Array &elem_marker, Array &sface_dof_list) const; +private: + int level_set_index; public: /// Element type related to shifted boundaries (not interfaces). - enum SBElementType {INSIDE, OUTSIDE, CUT}; + enum SBElementType {INSIDE = 0, OUTSIDE = 1, CUT = 2}; ShiftedFaceMarker(ParMesh &pm, ParGridFunction &ls, ParFiniteElementSpace &space_sltn, bool include_cut_cell_) : pmesh(pm), ls_func(ls), pfes_sltn(space_sltn), - include_cut_cell(include_cut_cell_) { } + include_cut_cell(include_cut_cell_), initial_marking(false), + level_set_index(0) { } /// Mark all the elements in the mesh using the @a SBElementType - void MarkElements(Array &elem_marker) const; + void MarkElements(Array &elem_marker); /// List dofs associated with the surrogate boundary. /// If @a include_cut_cell = false, the surrogate boundary includes faces @@ -66,6 +70,8 @@ public: const Array &sface_dof_list, Array &ess_tdof_list, Array &ess_shift_bdr) const; + + void SetLevelSetFunction(ParGridFunction &ls) { ls_func = ls; } }; } // namespace mfem diff --git a/miniapps/shifted/quad.mesh b/miniapps/shifted/quad.mesh new file mode 100644 index 0000000000..5194ead47f --- /dev/null +++ b/miniapps/shifted/quad.mesh @@ -0,0 +1,7 @@ +MFEM INLINE mesh v1.0 + +type = quad +nx = 8 +ny = 4 +sx = 2.0 +sy = 1.0 diff --git a/miniapps/shifted/sbm_aux.hpp b/miniapps/shifted/sbm_aux.hpp index 9ec5425795..70fdcf39bd 100644 --- a/miniapps/shifted/sbm_aux.hpp +++ b/miniapps/shifted/sbm_aux.hpp @@ -65,6 +65,19 @@ double dist_value(const Vector &x, const int type) if (0.3 <= xc && xc <= 0.8 && 0.15 <= yc && yc <= 0.2) { return 1.0; } return -1.0; } + if (type == 5) // circle of radius 0.2 - centered at 1.5, 0.5 + { + double dx = x(0) - 1.5, + dy = x(1) - 0.5, + rv = dx*dx + dy*dy; + if (x.Size() == 3) + { + double dz = x(2) - 0.5; + rv += dz*dz; + } + rv = rv > 0 ? pow(rv, 0.5) : 0; + return rv - ring_radius; // positive is the domain + } else { MFEM_ABORT(" Function type not implement yet."); @@ -92,6 +105,31 @@ public: } }; +/// Level set coefficient - +1 inside the domain, -1 outside, 0 at the boundary. +class Combo_Level_Set_Coefficient : public Coefficient +{ +private: + Array dls; + +public: + Combo_Level_Set_Coefficient() : Coefficient() { } + + virtual void Add_Level_Set_Coefficient(Dist_Level_Set_Coefficient &dls_) + { dls.Append(&dls_); } + + virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip) + { + MFEM_VERIFY(dls.Size() > 0, "Add at-least 1 Dist_level_Set_Coefficient to" + " the Combo."); + double dist = dls[0]->Eval(T, ip); + for (int j = 1; j < dls.Size(); j++) { + dist = min(dist, dls[j]->Eval(T, ip)); + } + if (dist >= 0.) { return 1.; } + else { return -1.; } + } +}; + /// Distance vector to the zero level-set. class Dist_Vector_Coefficient : public VectorCoefficient { @@ -125,7 +163,7 @@ public: } }; -/// Boundary conditions +/// Boundary conditions - Dirichlet double dirichlet_velocity_circle(const Vector &x) { return 0.; @@ -142,6 +180,12 @@ double dirichlet_velocity_xy_sinusoidal(const Vector &x) return 1./(M_PI*M_PI)*std::sin(M_PI*x(0)*x(1)); } +/// Boundary conditions - Neumann +double neumann_velocity_circle(const Vector &x) +{ + return 0.; +} + /// `f` for the Poisson problem (-nabla^2 u = f). double rhs_fun_circle(const Vector &x) From 1d84ae004c7d91274578ad41ec32f587a0d48662 Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Mon, 19 Jul 2021 10:51:02 -0700 Subject: [PATCH 037/198] working dirichlet+neumann --- miniapps/shifted/diffusion.cpp | 45 ++- miniapps/shifted/sbm_aux.hpp | 9 + miniapps/shifted/sbm_solver.cpp | 501 +++++++++++++++++++++++++++++++- miniapps/shifted/sbm_solver.hpp | 149 +++++++++- 4 files changed, 688 insertions(+), 16 deletions(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index b52ee85060..03ca415ae0 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -57,6 +57,10 @@ // Solves -nabla^2 u = 1 with homogeneous boundary conditions. // mpirun -np 4 diffusion -rs 5 -lst 4 -alpha 2 +// Problem 5: Circular hole of radius 0.2 at [0.5, 0.5] and [1.5, 0.5] +// Solves -nabla^2 u = 1 with homogeneous Dirichlet and Neumann boundary conditions. +// mpirun -np 1 diffusion -m quad.mesh -rs 3 -o 1 -vis -lst 5 -ho 1 + #include "mfem.hpp" #include "../common/mfem-common.hpp" #include "sbm_aux.hpp" @@ -334,7 +338,7 @@ int main(int argc, char *argv[]) // the FEM linear system. ParLinearForm b(&pfespace); FunctionCoefficient *rhs_f = NULL; - if (level_set_type == 1 || level_set_type == 4) + if (level_set_type == 1 || level_set_type == 4 || level_set_type == 5) { rhs_f = new FunctionCoefficient(rhs_fun_circle); } @@ -351,7 +355,7 @@ int main(int argc, char *argv[]) // Dirichlet BC that must be imposed on the true boundary. ShiftedFunctionCoefficient *dbcCoef = NULL; - if (level_set_type == 1 || level_set_type == 4) + if (level_set_type == 1 || level_set_type == 4 || level_set_type == 5) { dbcCoef = new ShiftedFunctionCoefficient(dirichlet_velocity_circle); } @@ -367,17 +371,39 @@ int main(int argc, char *argv[]) { MFEM_ABORT("Dirichlet velocity function not set for level set type.\n"); } - // Add integrators corresponding to the shifted boundary method (SBM). + + ShiftedFunctionCoefficient *nbcCoef = NULL; + ShiftedVectorFunctionCoefficient *normalbcCoef = NULL; + if (level_set_type == 5) { + nbcCoef = new ShiftedFunctionCoefficient(neumann_velocity_circle); + normalbcCoef = new ShiftedVectorFunctionCoefficient(dim, normal_vector); + } + + // Add integrators corresponding to the shifted boundary method (SBM) + // for Dirichlet boundaries. + int dirichlet_cut_marker_offset = 0; b.AddInteriorFaceIntegrator(new SBM2DirichletLFIntegrator(&pmesh, *dbcCoef, alpha, *dist_vec, elem_marker, include_cut_cell, - ho_terms)); + ho_terms, + dirichlet_cut_marker_offset)); b.AddBdrFaceIntegrator(new SBM2DirichletLFIntegrator(&pmesh, *dbcCoef, alpha, *dist_vec, elem_marker, include_cut_cell, - ho_terms), ess_shift_bdr); + ho_terms, + dirichlet_cut_marker_offset), ess_shift_bdr); + + // Add integrators corresponding to the shifted boundary method (SBM) + // for Neumann boundaries. + int neumann_cut_marker_offset = 1; + if (level_set_type == 5) { + b.AddInteriorFaceIntegrator(new SBM2NeumannLFIntegrator( + &pmesh, *nbcCoef, alpha, *dist_vec, *normalbcCoef, + elem_marker, include_cut_cell, ho_terms, neumann_cut_marker_offset)); + } + b.Assemble(); // Set up the bilinear form a(.,.) on the finite element space corresponding @@ -396,6 +422,15 @@ int main(int argc, char *argv[]) include_cut_cell, ho_terms), ess_shift_bdr); + // Add neumann bilinearform integrator + a.AddInteriorFaceIntegrator(new SBM2NeumannIntegrator(&pmesh, alpha, + *dist_vec, + *normalbcCoef, + elem_marker, + include_cut_cell, + ho_terms, + neumann_cut_marker_offset)); + // Assemble the bilinear form and the corresponding linear system, // applying any necessary transformations. a.Assemble(); diff --git a/miniapps/shifted/sbm_aux.hpp b/miniapps/shifted/sbm_aux.hpp index 70fdcf39bd..40f1c62474 100644 --- a/miniapps/shifted/sbm_aux.hpp +++ b/miniapps/shifted/sbm_aux.hpp @@ -186,6 +186,15 @@ double neumann_velocity_circle(const Vector &x) return 0.; } +/// Normal vector for level_set_type = 5. Circle centered at [0.5 , 0.5] +void normal_vector(const Vector &x, Vector &p) { + p.SetSize(x.Size()); + p(0) = x(0)-0.5; + p(1) = x(1)-0.5; //center of circle at [0.5, 0.5] + p /= p.Norml2(); + p *= -1; +} + /// `f` for the Poisson problem (-nabla^2 u = f). double rhs_fun_circle(const Vector &x) diff --git a/miniapps/shifted/sbm_solver.cpp b/miniapps/shifted/sbm_solver.cpp index 75d23a846d..33ba49926b 100644 --- a/miniapps/shifted/sbm_solver.cpp +++ b/miniapps/shifted/sbm_solver.cpp @@ -9,7 +9,6 @@ // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. -#include "marking.hpp" #include "sbm_solver.hpp" #include "mfem.hpp" @@ -30,6 +29,21 @@ double ShiftedFunctionCoefficient::Eval(ElementTransformation & T, return Function(transip); } +void ShiftedVectorFunctionCoefficient::Eval(Vector &V, + ElementTransformation & T, + const IntegrationPoint & ip, + const Vector &D) +{ + Vector transip; + T.Transform(ip, transip); + for (int i = 0; i < D.Size(); i++) + { + transip(i) += D(i); + } + + Function(transip, V); +} + void SBM2DirichletIntegrator::AssembleFaceMatrix( const FiniteElement &el1, const FiniteElement &el2, FaceElementTransformations &Trans, DenseMatrix &elmat) @@ -381,14 +395,16 @@ void SBM2DirichletLFIntegrator::AssembleRHSElementVect( { // 1 is inside and 2 is cut or 1 is a boundary element. if ( marker1 == ShiftedFaceMarker::SBElementType::INSIDE && - (marker2 == ShiftedFaceMarker::SBElementType::CUT || + (marker2 == ShiftedFaceMarker::SBElementType::CUT + + cut_cell_marker_offset || Tr.ElementType == ElementTransformation::BDR_FACE)) { elem1f = true; ndof = ndof1; } // 1 is cut, 2 is inside - else if (marker1 == ShiftedFaceMarker::SBElementType::CUT && + else if (marker1 == ShiftedFaceMarker::SBElementType::CUT + + cut_cell_marker_offset && marker2 == ShiftedFaceMarker::SBElementType::INSIDE) { if (Tr.Elem2No >= NEproc) { return; } @@ -403,7 +419,8 @@ void SBM2DirichletLFIntegrator::AssembleRHSElementVect( else { // 1 is cut and 2 is outside or 1 is a boundary element. - if (marker1 == ShiftedFaceMarker::SBElementType::CUT && + if (marker1 == ShiftedFaceMarker::SBElementType::CUT + + cut_cell_marker_offset && (marker2 == ShiftedFaceMarker::SBElementType::OUTSIDE || Tr.ElementType == ElementTransformation::BDR_FACE)) { @@ -412,7 +429,8 @@ void SBM2DirichletLFIntegrator::AssembleRHSElementVect( } // 1 is outside, 2 is cut else if (marker1 == ShiftedFaceMarker::SBElementType::OUTSIDE && - marker2 == ShiftedFaceMarker::SBElementType::CUT) + marker2 == ShiftedFaceMarker::SBElementType::CUT + + cut_cell_marker_offset) { if (Tr.Elem2No >= NEproc) { return; } elem1f = false; @@ -641,4 +659,477 @@ void SBM2DirichletLFIntegrator::AssembleRHSElementVect( } } + +void SBM2NeumannIntegrator::AssembleFaceMatrix( + const FiniteElement &el1, const FiniteElement &el2, + FaceElementTransformations &Trans, DenseMatrix &elmat) +{ + int dim, ndof1, ndof2, ndof, ndoftotal; + double w; + DenseMatrix temp_elmat; + + dim = el1.GetDim(); + ndof1 = el1.GetDof(); + ndof2 = el2.GetDof(); + ndoftotal = Trans.ElementType == ElementTransformation::BDR_FACE ? + ndof1 : ndof1 + ndof2; + + elmat.SetSize(ndoftotal); + elmat = 0.0; + + bool elem1f = true; // flag indicating whether Trans.Elem1No is part of the + // surrogate domain or not. + int elem1 = Trans.Elem1No, + elem2 = Trans.Elem2No, + marker1 = (*elem_marker)[elem1]; + + int marker2; + + if (Trans.Elem2No >= NEproc) + { + marker2 = (*elem_marker)[NEproc+par_shared_face_count]; + par_shared_face_count++; + } + else if (Trans.ElementType == ElementTransformation::BDR_FACE) + { + marker2 = marker1; + } + else + { + marker2 = (*elem_marker)[elem2]; + } + + if (!include_cut_cell) + { + // 1 is inside and 2 is cut or 1 is a boundary element. + if (marker1 == ShiftedFaceMarker::SBElementType::INSIDE && + (marker2 == ShiftedFaceMarker::SBElementType::CUT || + Trans.ElementType == ElementTransformation::BDR_FACE)) + { + elem1f = true; + } + // 1 is cut, 2 is inside + else if (marker1 == ShiftedFaceMarker::SBElementType::CUT && + marker2 == ShiftedFaceMarker::SBElementType::INSIDE) + { + if (Trans.Elem2No >= NEproc) { return; } + elem1f = false; + } + else + { + return; + } + } + else + { + // 1 is cut and 2 is outside or 1 is a boundary element. + if (marker1 == ShiftedFaceMarker::SBElementType::CUT && + (marker2 == ShiftedFaceMarker::SBElementType::OUTSIDE || + Trans.ElementType == ElementTransformation::BDR_FACE)) + { + elem1f = true; + } + // 1 is outside, 2 is cut + else if (marker1 == ShiftedFaceMarker::SBElementType::OUTSIDE && + marker2 == ShiftedFaceMarker::SBElementType::CUT) + { + if (Trans.Elem2No >= NEproc) { return; } + elem1f = false; + } + else + { + return; + } + } + + ndof = elem1f ? ndof1 : ndof2; + + temp_elmat.SetSize(ndof); + temp_elmat = 0.; + + nor.SetSize(dim); + nh.SetSize(dim); + ni.SetSize(dim); + adjJ.SetSize(dim); + + shape.SetSize(ndof1); + dshape.SetSize(ndof1, dim); + dshapephys.SetSize(ndof1, dim); + dshapedn.SetSize(ndof1); + Vector wrk = shape; + + const IntegrationRule *ir = IntRule; + if (ir == NULL) + { + int order = elem1f ? 4*el1.GetOrder() : 4*el2.GetOrder(); + ir = &IntRules.Get(Trans.GetGeometryType(), order); + } + + MFEM_VERIFY(nterms == 1, " nterms must be 1 for Neumann.\n"); + + Array dkphi_dxk; + DenseMatrix grad_phys; + Vector Factorial; + Array grad_phys_dir; + + if (nterms > 0) + { + if (elem1f) + { + el1.ProjectGrad(el1, *Trans.Elem1, grad_phys); + } + else + { + el2.ProjectGrad(el2, *Trans.Elem2, grad_phys); + } + + DenseMatrix grad_work; + grad_phys_dir.SetSize(dim); // NxN matrices for derivative in each direction + for (int i = 0; i < dim; i++) + { + grad_phys_dir[i] = new DenseMatrix(ndof, ndof); + grad_phys_dir[i]->CopyRows(grad_phys, i*ndof, (i+1)*ndof-1); + } + + DenseMatrix grad_phys_work = grad_phys; + grad_phys_work.SetSize(ndof, ndof*dim); + + dkphi_dxk.SetSize(nterms); + + for (int i = 0; i < nterms; i++) + { + int sz1 = pow(dim, i+1); + dkphi_dxk[i] = new DenseMatrix(ndof, ndof*sz1*dim); + int loc_col_per_dof = sz1; + int tot_col_per_dof = loc_col_per_dof*dim; + for (int k = 0; k < dim; k++) + { + grad_work.SetSize(ndof, ndof*sz1); + // grad_work[k] has derivative in kth direction for each DOF. + // grad_work[0] has d^2phi/dx^2 and d^2phi/dxdy terms and + // grad_work[1] has d^2phi/dydx and d^2phi/dy2 terms for each dof + if (i == 0) + { + Mult(*grad_phys_dir[k], grad_phys_work, grad_work); + } + else + { + Mult(*grad_phys_dir[k], *dkphi_dxk[i-1], grad_work); + } + // Now we must place columns for each dof together so that they are + // in order: d^2phi/dx^2, d^2phi/dxdy, d^2phi/dydx, d^2phi/dy2. + for (int j = 0; j < ndof; j++) + { + for (int d = 0; d < loc_col_per_dof; d++) + { + Vector col; + grad_work.GetColumn(j*loc_col_per_dof+d, col); + dkphi_dxk[i]->SetCol(j*tot_col_per_dof+k*loc_col_per_dof+d, col); + } + } + } + } + + for (int i = 0; i < grad_phys_dir.Size(); i++) + { + delete grad_phys_dir[i]; + } + + Factorial.SetSize(nterms); + Factorial(0) = 2; + for (int i = 1; i < nterms; i++) + { + Factorial(i) = Factorial(i-1)*(i+2); + } + } + + + DenseMatrix q_hess_dn(dim, ndof1); + Vector q_hess_dn_work(q_hess_dn.GetData(), ndof1*dim); + Vector q_hess_dot_d(ndof1); + + Vector D(vD->GetVDim()); + Vector N(vN->GetVDim()); + // assemble: -< \nabla u.n, w > + // -< u + \nabla u.d + h.o.t, \nabla w.n> + // - + for (int p = 0; p < ir->GetNPoints(); p++) + { + const IntegrationPoint &ip = ir->IntPoint(p); + + // Set the integration point in the face and the neighboring elements + Trans.SetAllIntPoints(&ip); + + // Access the neighboring elements' integration points + // Note: eip2 will only contain valid data if Elem2 exists + const IntegrationPoint &eip1 = Trans.GetElement1IntPoint(); + const IntegrationPoint &eip2 = Trans.GetElement2IntPoint(); + + if (dim == 1) + { + nor(0) = 2*eip1.x - 1.0; + } + else + { + // Note: this normal accounts for the weight of the surface transformation + // Jacobian i.e. nor = nhat*det(J) + CalcOrtho(Trans.Jacobian(), nor); + } + vD->Eval(D, Trans, ip); + vN->Eval(N, Trans, ip, D); + + double nor_dot_d = nor*D; + // If we are clipping inside the domain, ntilde and d vector should be + // aligned. + if (!include_cut_cell && nor_dot_d < 0) { nor *= -1; } + if (include_cut_cell && nor_dot_d > 0) { nor *= -1; } + + if (elem1f) + { + el1.CalcShape(eip1, shape); + el1.CalcDShape(eip1, dshape); + w = ip.weight/Trans.Elem1->Weight(); + CalcAdjugate(Trans.Elem1->Jacobian(), adjJ); + } + else + { + el1.CalcShape(eip2, shape); + el1.CalcDShape(eip2, dshape); + w = ip.weight/Trans.Elem2->Weight(); + CalcAdjugate(Trans.Elem2->Jacobian(), adjJ); + } + + ni.Set(w, nor); // alpha_k*nor/det(J) + adjJ.Mult(ni, nh); + dshape.Mult(nh, dshapedn); //dphi/dn * Jinv * alpha_k * nor + + // - - Term 2 + AddMult_a_VWt(-1., shape, dshapedn, temp_elmat); + + // -MultTranspose(shape, T1_wrk); + + DenseMatrix T2; + Vector T2_wrk; + for (int j = 0; j < i+1; j++) + { + int sz2 = pow(dim, i-j); + T2.SetSize(dim, ndof1*sz2); + T2_wrk.SetDataAndSize(T2.GetData(), dim*ndof1*sz2); + T1.MultTranspose(D, T2_wrk); + T1 = T2; + } + Vector q_hess_dot_d_work(ndof1); + T1.MultTranspose(N, q_hess_dot_d_work); + q_hess_dot_d += q_hess_dot_d_work; + } + + wrk = q_hess_dot_d; + wrk *= ip.weight * n_dot_ntilde; + + AddMult_a_VWt(1., shape, wrk, temp_elmat); + int offset = elem1f ? 0 : ndof1; + elmat.CopyMN(temp_elmat, offset, offset); + } //p < ir->GetNPoints() + + for (int i = 0; i < dkphi_dxk.Size(); i++) + { + delete dkphi_dxk[i]; + } +} + +void SBM2NeumannLFIntegrator::AssembleRHSElementVect( + const FiniteElement &el, ElementTransformation &Tr, Vector &elvect) +{ + mfem_error("SBM2NeumannLFIntegrator::AssembleRHSElementVect"); +} + +void SBM2NeumannLFIntegrator::AssembleRHSElementVect( + const FiniteElement &el, FaceElementTransformations &Tr, Vector &elvect) +{ + AssembleRHSElementVect(el, el, Tr, elvect); +} + +void SBM2NeumannLFIntegrator::AssembleRHSElementVect( + const FiniteElement &el1, const FiniteElement &el2, + FaceElementTransformations &Tr, Vector &elvect) +{ + + int dim, ndof1, ndof2, ndof, ndoftotal; + double w; + Vector temp_elvect; + + dim = el1.GetDim(); + ndof1 = el1.GetDof(); + ndof2 = el2.GetDof(); + ndoftotal = ndof1 + ndof2; + if (Tr.Elem2No >= NEproc || + Tr.ElementType == ElementTransformation::BDR_FACE) + { + ndoftotal = ndof1; + } + + elvect.SetSize(ndoftotal); + elvect = 0.0; + + bool elem1f = true; + int elem1 = Tr.Elem1No, + elem2 = Tr.Elem2No, + marker1 = (*elem_marker)[elem1]; + + int marker2; + if (Tr.Elem2No >= NEproc) + { + marker2 = (*elem_marker)[NEproc+par_shared_face_count]; + par_shared_face_count++; + } + else if (Tr.ElementType == ElementTransformation::BDR_FACE) + { + marker2 = marker1; + } + else + { + marker2 = (*elem_marker)[elem2]; + } + if (!include_cut_cell) + { + // 1 is inside and 2 is cut or 1 is a boundary element. + if ( marker1 == ShiftedFaceMarker::SBElementType::INSIDE && + (marker2 == ShiftedFaceMarker::SBElementType::CUT + + cut_cell_marker_offset || + Tr.ElementType == ElementTransformation::BDR_FACE)) + { + elem1f = true; + ndof = ndof1; + } + // 1 is cut, 2 is inside + else if (marker1 == ShiftedFaceMarker::SBElementType::CUT + + cut_cell_marker_offset && + marker2 == ShiftedFaceMarker::SBElementType::INSIDE) + { + if (Tr.Elem2No >= NEproc) { return; } + elem1f = false; + ndof = ndof2; + } + else + { + return; + } + } + else + { + // 1 is cut and 2 is outside or 1 is a boundary element. + if (marker1 == ShiftedFaceMarker::SBElementType::CUT + + cut_cell_marker_offset && + (marker2 == ShiftedFaceMarker::SBElementType::OUTSIDE || + Tr.ElementType == ElementTransformation::BDR_FACE)) + { + elem1f = true; + ndof = ndof1; + } + // 1 is outside, 2 is cut + else if (marker1 == ShiftedFaceMarker::SBElementType::OUTSIDE && + marker2 == ShiftedFaceMarker::SBElementType::CUT + + cut_cell_marker_offset) + { + if (Tr.Elem2No >= NEproc) { return; } + elem1f = false; + ndof = ndof2; + } + else + { + return; + } + } + + temp_elvect.SetSize(ndof); + temp_elvect = 0.0; + + + nor.SetSize(dim); + nh.SetSize(dim); + ni.SetSize(dim); + adjJ.SetSize(dim); + + shape.SetSize(ndof); + + const IntegrationRule *ir = IntRule; + if (ir == NULL) + { + // a simple choice for the integration order; is this OK? + int order = elem1f ? 4*el1.GetOrder() : 4*el2.GetOrder(); + ir = &IntRules.Get(Tr.GetGeometryType(), order); + } + + Vector D(vD->GetVDim()); + Vector N(vN->GetVDim()); + Vector wrk = shape; + for (int p = 0; p < ir->GetNPoints(); p++) + { + const IntegrationPoint &ip = ir->IntPoint(p); + + // Set the integration point in the face and the neighboring element + Tr.SetAllIntPoints(&ip); + + // Access the neighboring element's integration point + const IntegrationPoint &eip = Tr.GetElement1IntPoint(); + const IntegrationPoint &eip1 = Tr.GetElement1IntPoint(); + const IntegrationPoint &eip2 = Tr.GetElement2IntPoint(); + + if (dim == 1) + { + nor(0) = 2*eip.x - 1.0; + } + else + { + CalcOrtho(Tr.Jacobian(), nor); + } + vD->Eval(D, Tr, ip); + vN->Eval(N, Tr, ip, D); + + double nor_dot_d = nor*D; + if (!include_cut_cell && nor_dot_d < 0) { nor *= -1; } + if (include_cut_cell && nor_dot_d > 0) { nor *= -1; } + // note here that if we are clipping outside the domain, we will have to + // flip the sign if nor_dot_d is +ve. + + if (elem1f) + { + el1.CalcShape(eip1, shape); + w = ip.weight * uN->Eval(Tr, ip, D); + } + else + { + el2.CalcShape(eip2, shape); + w = ip.weight * uN->Eval(Tr, ip, D); + } + + double n_dot_ntilde = (nor*N); //nor and N are pointing in opposite direction + wrk.Set(n_dot_ntilde*w, shape); + // Function; + +public: + ShiftedVectorFunctionCoefficient(int dim, + std::function F) + : VectorCoefficient(dim), Function(std::move(F)) { } + + using VectorCoefficient::Eval; + virtual void Eval(Vector &V, ElementTransformation &T, + const IntegrationPoint &ip) + { + Vector D(vdim); + D = 0.; + return (this)->Eval(V, T, ip, D); + } + + /// Evaluate the coefficient at @a ip + @a D. + void Eval(Vector &V, + ElementTransformation &T, + const IntegrationPoint &ip, + const Vector &D); +}; + /// BilinearFormIntegrator for the high-order extension of shifted boundary /// method. /// A(u, w) = - @@ -66,6 +93,8 @@ protected: int NEproc; //Number of elements on the current MPI rank int par_shared_face_count; // + int cut_cell_marker_offset; + // these are not thread-safe! Vector shape, dshapedn, dshapephysdn, nor, nh, ni; DenseMatrix jmat, dshape, dshapephys, adjJ; @@ -77,13 +106,15 @@ public: VectorCoefficient &vD_, Array &elem_marker_, bool include_cut_cell_ = false, - int nterms_ = 0) + int nterms_ = 0, + int cut_cell_marker_offset_ = 0) : alpha(a), vD(&vD_), elem_marker(&elem_marker_), include_cut_cell(include_cut_cell_), nterms(nterms_), NEproc(pmesh->GetNE()), - par_shared_face_count(0) { } + par_shared_face_count(0), + cut_cell_marker_offset(cut_cell_marker_offset_) { } using BilinearFormIntegrator::AssembleFaceMatrix; virtual void AssembleFaceMatrix(const FiniteElement &el1, @@ -123,6 +154,8 @@ protected: int NEproc; //Number of elements on the current MPI rank int par_shared_face_count; // + int cut_cell_marker_offset; + // these are not thread-safe! Vector shape, dshape_dd, dshape_dn, nor, nh, ni; DenseMatrix dshape, mq, adjJ; @@ -130,17 +163,19 @@ protected: public: SBM2DirichletLFIntegrator(const ParMesh *pmesh, ShiftedFunctionCoefficient &u, - const double a, + const double alpha_, VectorCoefficient &vD_, Array &elem_marker_, bool include_cut_cell_ = false, - int nterms_ = 0) - : uD(&u), alpha(a), vD(&vD_), + int nterms_ = 0, + int cut_cell_marker_offset_ = 0) + : uD(&u), alpha(alpha_), vD(&vD_), elem_marker(&elem_marker_), include_cut_cell(include_cut_cell_), nterms(nterms_), NEproc(pmesh->GetNE()), - par_shared_face_count(0) { } + par_shared_face_count(0), + cut_cell_marker_offset(cut_cell_marker_offset_) { } virtual void AssembleRHSElementVect(const FiniteElement &el, ElementTransformation &Tr, @@ -154,6 +189,108 @@ public: Vector &elvect); }; + +// +class SBM2NeumannIntegrator : public BilinearFormIntegrator +{ +protected: + double alpha; + VectorCoefficient *vD; // Distance function coefficient + ShiftedVectorFunctionCoefficient *vN; // Normal function coefficient + Array *elem_marker; //marker indicating whether element is inside, + //cut, or outside the domain. + bool include_cut_cell; + int nterms; // Number of terms in addition to the gradient + // term from Taylor expansion that should be included. (0 by default). + int NEproc; //Number of elements on the current MPI rank + int par_shared_face_count; // + + int cut_cell_marker_offset; + + + // these are not thread-safe! + Vector shape, dshapedn, dshapephysdn, nor, nh, ni; + DenseMatrix jmat, dshape, dshapephys, adjJ; + + +public: + SBM2NeumannIntegrator(const ParMesh *pmesh, + const double alpha_, + VectorCoefficient &vD_, + ShiftedVectorFunctionCoefficient &vN_, + Array &elem_marker_, + bool include_cut_cell_ = false, + int nterms_ = 0, + int cut_cell_marker_offset_ = 0) + : alpha(alpha_), vD(&vD_), vN(&vN_), + elem_marker(&elem_marker_), + include_cut_cell(include_cut_cell_), + nterms(nterms_), + NEproc(pmesh->GetNE()), + par_shared_face_count(0), + cut_cell_marker_offset(cut_cell_marker_offset_) { } + + using BilinearFormIntegrator::AssembleFaceMatrix; + virtual void AssembleFaceMatrix(const FiniteElement &el1, + const FiniteElement &el2, + FaceElementTransformations &Trans, + DenseMatrix &elmat); + + bool GetTrimFlag() { return include_cut_cell; } + + virtual ~SBM2NeumannIntegrator() { } +}; + +class SBM2NeumannLFIntegrator : public LinearFormIntegrator +{ +protected: + ShiftedFunctionCoefficient *uN; //Neumann condition on true boundary + VectorCoefficient *vD; // Distance function coefficient + ShiftedVectorFunctionCoefficient *vN; // Normal function coefficient + Array *elem_marker; //marker indicating whether element is inside, + double alpha; // Nitsche parameter + int nterms; //Number of terms in addition to the gradient term from Taylor + //expansion that should be included. (0 by default). + bool include_cut_cell; + int NEproc; //Number of elements on the current MPI rank + int par_shared_face_count; // + int cut_cell_marker_offset; + + // these are not thread-safe! + Vector shape, dshape_dd, dshape_dn, nor, nh, ni; + DenseMatrix dshape, mq, adjJ; + +public: + SBM2NeumannLFIntegrator(const ParMesh *pmesh, + ShiftedFunctionCoefficient &u, + const double alpha_, + VectorCoefficient &vD_, + ShiftedVectorFunctionCoefficient &vN_, + Array &elem_marker_, + int nterms_ = 0, + bool include_cut_cell_ = true, + int cut_cell_marker_offset_ = 0) + : uN(&u), vD(&vD_), vN(&vN_), + elem_marker(&elem_marker_), + alpha(alpha_), nterms(nterms_), + include_cut_cell(include_cut_cell_), + NEproc(pmesh->GetNE()), + par_shared_face_count(0), + cut_cell_marker_offset(cut_cell_marker_offset_) { } + + virtual void AssembleRHSElementVect(const FiniteElement &el, + ElementTransformation &Tr, + Vector &elvect); + virtual void AssembleRHSElementVect(const FiniteElement &el, + FaceElementTransformations &Tr, + Vector &elvect); + virtual void AssembleRHSElementVect(const FiniteElement &el1, + const FiniteElement &el2, + FaceElementTransformations &Tr, + Vector &elvect); + bool GetTrimFlag() { return include_cut_cell; } +}; + } // namespace mfem #endif From a6270b25824f063ebd8a46daf55ed41b3d368fa3 Mon Sep 17 00:00:00 2001 From: Keith Date: Mon, 19 Jul 2021 19:52:33 -0700 Subject: [PATCH 038/198] update osc file --- examples/osc.cpp | 375 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 examples/osc.cpp diff --git a/examples/osc.cpp b/examples/osc.cpp new file mode 100644 index 0000000000..25841dbbc4 --- /dev/null +++ b/examples/osc.cpp @@ -0,0 +1,375 @@ +// MFEM Example 6 +// +// Compile with: make ex6 +// +// Sample runs: ex6 -m ../data/square-disc.mesh -o 1 +// ex6 -m ../data/square-disc.mesh -o 2 +// ex6 -m ../data/square-disc-nurbs.mesh -o 2 +// ex6 -m ../data/star.mesh -o 3 +// ex6 -m ../data/escher.mesh -o 2 +// ex6 -m ../data/fichera.mesh -o 2 +// ex6 -m ../data/disc-nurbs.mesh -o 2 +// ex6 -m ../data/ball-nurbs.mesh +// ex6 -m ../data/pipe-nurbs.mesh +// ex6 -m ../data/star-surf.mesh -o 2 +// ex6 -m ../data/square-disc-surf.mesh -o 2 +// ex6 -m ../data/amr-quad.mesh +// ex6 -m ../data/inline-segment.mesh -o 1 -md 100 +// +// Device sample runs: +// ex6 -pa -d cuda +// ex6 -pa -d occa-cuda +// ex6 -pa -d raja-omp +// ex6 -pa -d ceed-cpu +// * ex6 -pa -d ceed-cuda +// ex6 -pa -d ceed-cuda:/gpu/cuda/shared +// +// Description: This is a version of Example 1 with a simple adaptive mesh +// refinement loop. The problem being solved is again the Laplace +// equation -Delta u = 1 with homogeneous Dirichlet boundary +// conditions. The problem is solved on a sequence of meshes which +// are locally refined in a conforming (triangles, tetrahedrons) +// or non-conforming (quadrilaterals, hexahedra) manner according +// to a simple ZZ error estimator. +// +// The example demonstrates MFEM's capability to work with both +// conforming and nonconforming refinements, in 2D and 3D, on +// linear, curved and surface meshes. Interpolation of functions +// from coarse to fine meshes, as well as persistent GLVis +// visualization are also illustrated. +// +// We recommend viewing Example 1 before viewing this example. + +#include "mfem.hpp" +#include +#include + +using namespace std; +using namespace mfem; + +double wavefront_exsol(const Vector &p) +{ + double x = p(0), y = p(1); + double alpha = 1000.0; + // double xc = -0.05, yc = -0.05; + double xc = 0.0, yc = 0.0; + double r0 = 0.7; + double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0)); + return atan(alpha * (r - r0)); +} + +void wavefront_exgrad(const Vector &p, Vector &grad) +{ + double x = p(0), y = p(1); + double alpha = 1000.0; + double xc = -0.05, yc = -0.05; + double r0 = 0.7; + grad(0) = 0.0; + grad(1) = 0.0; +} + +double wavefront_laplace(const Vector &p) +{ + double x = p(0), y = p(1); + double alpha = 1000.0; + // double xc = -0.05, yc = -0.05; + double xc = 0.0, yc = 0.0; + double r0 = 0.7; + double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0)); + double num = - ( alpha - pow(alpha,3) * (pow(r,2) - pow(r0,2)) ); + double denom = pow(r * ( pow(alpha,2) * pow(r0,2) + pow(alpha,2) * pow(r,2) \ + - 2 * pow(alpha,2) * r0 * r + 1.0 ),2); + denom = max(denom,1e-8); + // return num / denom; + if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1; } + if (p.Normlp(2.0) < 0.4 || p.Normlp(2.0) > 0.6) { return 2; } + return 0; +} + +int main(int argc, char *argv[]) +{ + // 1. Parse command-line options. + const char *mesh_file = "../data/star.mesh"; + int order = 1; + bool pa = false; + const char *device_config = "cpu"; + int max_dofs = 50000; + bool visualization = true; + + OptionsParser args(argc, argv); + args.AddOption(&mesh_file, "-m", "--mesh", + "Mesh file to use."); + args.AddOption(&order, "-o", "--order", + "Finite element order (polynomial degree)."); + args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa", + "--no-partial-assembly", "Enable Partial Assembly."); + args.AddOption(&device_config, "-d", "--device", + "Device configuration string, see Device::Configure()."); + args.AddOption(&max_dofs, "-md", "--max-dofs", + "Stop after reaching this many degrees of freedom."); + args.AddOption(&visualization, "-vis", "--visualization", "-no-vis", + "--no-visualization", + "Enable or disable GLVis visualization."); + args.Parse(); + if (!args.Good()) + { + args.PrintUsage(cout); + return 1; + } + args.PrintOptions(cout); + + // 2. Enable hardware devices such as GPUs, and programming models such as + // CUDA, OCCA, RAJA and OpenMP based on command line options. + Device device(device_config); + device.Print(); + + // 3. Read the mesh from the given mesh file. We can handle triangular, + // quadrilateral, tetrahedral, hexahedral, surface and volume meshes with + // the same code. + Mesh mesh(mesh_file, 1, 1); + int dim = mesh.Dimension(); + int sdim = mesh.SpaceDimension(); + + // 4. Since a NURBS mesh can currently only be refined uniformly, we need to + // convert it to a piecewise-polynomial curved mesh. First we refine the + // NURBS mesh a bit more and then project the curvature to quadratic Nodes. + if (mesh.NURBSext) + { + for (int i = 0; i < 2; i++) + { + mesh.UniformRefinement(); + } + mesh.SetCurvature(2); + } + + // 5. Define a finite element space on the mesh. The polynomial order is + // one (linear) by default, but this can be changed on the command line. + H1_FECollection fec(order, dim); + FiniteElementSpace fespace(&mesh, &fec); + + // 6. As in Example 1, we set up bilinear and linear forms corresponding to + // the Laplace problem -\Delta u = 1. We don't assemble the discrete + // problem yet, this will be done in the main loop. + BilinearForm a(&fespace); + if (pa) + { + a.SetAssemblyLevel(AssemblyLevel::PARTIAL); + a.SetDiagonalPolicy(Operator::DIAG_ONE); + } + LinearForm b(&fespace); + + ConstantCoefficient one(1.0); + ConstantCoefficient zero(0.0); + Coefficient * exsol = nullptr; + Coefficient * rhs = nullptr; + exsol = new FunctionCoefficient(wavefront_exsol); + rhs = new FunctionCoefficient(wavefront_laplace); + + BilinearFormIntegrator *integ = new DiffusionIntegrator(one); + a.AddDomainIntegrator(integ); + // b.AddDomainIntegrator(new DomainLFIntegrator(one)); + b.AddDomainIntegrator(new DomainLFIntegrator(*rhs)); + + // 7. The solution vector x and the associated finite element grid function + // will be maintained over the AMR iterations. We initialize it to zero. + GridFunction x(&fespace); + x = 0.0; + + // 8. All boundary attributes will be used for essential (Dirichlet) BC. + MFEM_VERIFY(mesh.bdr_attributes.Size() > 0, + "Boundary attributes required in the mesh."); + Array ess_bdr(mesh.bdr_attributes.Max()); + ess_bdr = 1; + + // 9. Connect to GLVis. + char vishost[] = "localhost"; + int visport = 19916; + socketstream sol_sock; + if (visualization) + { + sol_sock.open(vishost, visport); + } + + // 10. Set up an error estimator. Here we use the Zienkiewicz-Zhu estimator + // that uses the ComputeElementFlux method of the DiffusionIntegrator to + // recover a smoothed flux (gradient) that is subtracted from the element + // flux to get an error indicator. We need to supply the space for the + // smoothed flux: an (H1)^sdim (i.e., vector-valued) space is used here. + FiniteElementSpace flux_fespace(&mesh, &fec, sdim); + ZienkiewiczZhuEstimator estimator(*integ, x, flux_fespace); + estimator.SetAnisotropic(); + + // 11. A refiner selects and refines elements based on a refinement strategy. + // The strategy here is to refine elements with errors larger than a + // fraction of the maximum element error. Other strategies are possible. + // The refiner will call the given error estimator. + ThresholdRefiner refiner(estimator); + refiner.SetTotalErrorFraction(0.7); + + + + // 11.5. Preprocess mesh to control osc + L2_FECollection l2fec(order, dim); + FiniteElementSpace l2fes(&mesh, &l2fec); + GridFunction load(&l2fes); + int order_quad = std::max(2, 2*order+1); + const IntegrationRule *irs[Geometry::NumGeom]; + for (int i=0; i < Geometry::NumGeom; ++i) + { + irs[i] = &(IntRules.Get(i, order_quad)); + } + + double osc_tol = 1e-3; + while (true) + { + bool h_refined = false; + Array mesh_refinements; + // get L2-norm of load ( f ) + load.ProjectCoefficient(*rhs); + // load.ProjectDiscCoefficient(*rhs,mfem::GridFunction::AvgType::ARITHMETIC); + double norm_of_load = ComputeLpNorm(2.0,*rhs,mesh,irs); + + // construct h * (I - Pi) f + GridFunction osc_fun; + double NE = l2fes.GetNE(); + double av_norm_of_load = norm_of_load / sqrt(NE); + Vector norm_of_fine_scale(NE); + load.ComputeElementL2Errors(*rhs,norm_of_fine_scale); + // osc.Print(); + // break; + + + for (int i = 0; i < NE; i++) + { + double h = mesh.GetElementSize(i); + double local_osc = h * norm_of_fine_scale(i); + if ( local_osc > osc_tol * av_norm_of_load ) + { + h_refined = true; + mesh_refinements.Append(i); + } + } + if (h_refined) + { + int nonconforming = -1; + int nc_limit = 1; + + mesh.GeneralRefinement(mesh_refinements, nonconforming, nc_limit); + l2fes.Update(false); + load.Update(); + } + else + { + break; + } + + sol_sock.precision(8); + sol_sock << "mesh\n" << mesh << flush; + } + + + + cout << "press any key" << endl; + cin.get(); + fespace.Update(false); + b.Update(); + a.Update(); + x.Update(); + + // 12. The main AMR loop. In each iteration we solve the problem on the + // current mesh, visualize the solution, and refine the mesh. + for (int it = 0; ; it++) + { + int cdofs = fespace.GetTrueVSize(); + cout << "\nAMR iteration " << it << endl; + cout << "Number of unknowns: " << cdofs << endl; + + // 13. Assemble the right-hand side. + b.Assemble(); + + // 14. Set Dirichlet boundary values in the GridFunction x. + // Determine the list of Dirichlet true DOFs in the linear system. + Array ess_tdof_list; + x.ProjectBdrCoefficient(*exsol, ess_bdr); + // x.ProjectBdrCoefficient(zero, ess_bdr); + fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list); + + // 15. Assemble the stiffness matrix. + a.Assemble(); + + // 16. Create the linear system: eliminate boundary conditions, constrain + // hanging nodes and possibly apply other transformations. The system + // will be solved for true (unconstrained) DOFs only. + OperatorPtr A; + Vector B, X; + + const int copy_interior = 1; + a.FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior); + + // 17. Solve the linear system A X = B. + if (!pa) + { +#ifndef MFEM_USE_SUITESPARSE + // Use a simple symmetric Gauss-Seidel preconditioner with PCG. + GSSmoother M((SparseMatrix&)(*A)); + PCG(*A, M, B, X, 3, 200, 1e-12, 0.0); +#else + // If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system. + UMFPackSolver umf_solver; + umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS; + umf_solver.SetOperator(*A); + umf_solver.Mult(B, X); +#endif + } + else // Diagonal preconditioning in partial assembly mode. + { + OperatorJacobiSmoother M(a, ess_tdof_list); + PCG(*A, M, B, X, 3, 2000, 1e-12, 0.0); + } + + // 18. After solving the linear system, reconstruct the solution as a + // finite element GridFunction. Constrained nodes are interpolated + // from true DOFs (it may therefore happen that x.Size() >= X.Size()). + a.RecoverFEMSolution(X, b, x); + + // 19. Send solution by socket to the GLVis server. + if (visualization && sol_sock.good()) + { + sol_sock.precision(8); + sol_sock << "solution\n" << mesh << x << flush; + } + + if (cdofs > max_dofs) + { + cout << "Reached the maximum number of dofs. Stop." << endl; + break; + } + + // 20. Call the refiner to modify the mesh. The refiner calls the error + // estimator to obtain element errors, then it selects elements to be + // refined and finally it modifies the mesh. The Stop() method can be + // used to determine if a stopping criterion was met. + refiner.Apply(mesh); + if (refiner.Stop()) + { + cout << "Stopping criterion satisfied. Stop." << endl; + break; + } + + // 21. Update the space to reflect the new state of the mesh. Also, + // interpolate the solution x so that it lies in the new space but + // represents the same function. This saves solver iterations later + // since we'll have a good initial guess of x in the next step. + // Internally, FiniteElementSpace::Update() calculates an + // interpolation matrix which is then used by GridFunction::Update(). + fespace.Update(); + x.Update(); + + // 22. Inform also the bilinear and linear forms that the space has + // changed. + a.Update(); + b.Update(); + } + + return 0; +} From 31ce22595b928234e664666f191fe2d67334d116 Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Thu, 22 Jul 2021 10:02:09 -0700 Subject: [PATCH 039/198] work for multiple Dirichlet and Neumann conditions --- miniapps/shifted/diffusion.cpp | 289 +++++++++++++++++++++----------- miniapps/shifted/marking.cpp | 49 +++--- miniapps/shifted/marking.hpp | 23 ++- miniapps/shifted/sbm_aux.hpp | 115 +++++++++---- miniapps/shifted/sbm_solver.cpp | 22 +-- miniapps/shifted/sbm_solver.hpp | 16 +- 6 files changed, 344 insertions(+), 170 deletions(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index 03ca415ae0..56cbd90ef7 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -58,9 +58,13 @@ // mpirun -np 4 diffusion -rs 5 -lst 4 -alpha 2 // Problem 5: Circular hole of radius 0.2 at [0.5, 0.5] and [1.5, 0.5] -// Solves -nabla^2 u = 1 with homogeneous Dirichlet and Neumann boundary conditions. -// mpirun -np 1 diffusion -m quad.mesh -rs 3 -o 1 -vis -lst 5 -ho 1 +// Solves -nabla^2 u = 1 with homogeneous Neumann boundary conditions. +// mpirun -np 1 diffusion -m ../../data/inline-quad.mesh -rs 3 -o 1 -vis -lst -1 -ho 1 -nlst 1 +// Problem 6: Circular hole with homogeneous Neumann, triangular hole with +// inhomogeneous Dirichlet, and square hole with homogeneous Dirichlet +// boundary condition. +// mpirun -np 1 diffusion -m ../../data/inline-quad.mesh -rs 3 -o 1 -vis -lst 5 -ho 1 -nlst 7 -alpha 10.0 -dlstc 6 #include "mfem.hpp" #include "../common/mfem-common.hpp" #include "sbm_aux.hpp" @@ -84,7 +88,9 @@ int main(int argc, char *argv[]) int order = 2; bool visualization = true; int ser_ref_levels = 0; - int level_set_type = 1; + int dirichlet_level_set_type = 1; + int dirichlet_level_set_type_combo = -1; + int neumann_level_set_type = -1; int ho_terms = 0; double alpha = 1; bool include_cut_cell = false; @@ -100,8 +106,10 @@ int main(int argc, char *argv[]) "Enable or disable GLVis visualization."); args.AddOption(&ser_ref_levels, "-rs", "--refine-serial", "Number of times to refine the mesh uniformly in serial."); - args.AddOption(&level_set_type, "-lst", "--level-set-type", - "level-set-type:"); + args.AddOption(&dirichlet_level_set_type, "-lst", "--level-set-type", + "level-set-type."); + args.AddOption(&neumann_level_set_type, "-nlst", "--neumann-level-set-type", + "neumann-level-set-type."); args.AddOption(&ho_terms, "-ho", "--high-order", "Additional high-order terms to include"); args.AddOption(&alpha, "-alpha", "--alpha", @@ -109,6 +117,8 @@ int main(int argc, char *argv[]) args.AddOption(&include_cut_cell, "-cut", "--cut", "-no-cut-cell", "--no-cut-cell", "Include or not include elements cut by true boundary."); + args.AddOption(&dirichlet_level_set_type_combo, "-dlstc", + "--level-set-type-combo", "level-set-type-combo."); args.Parse(); if (!args.Good()) @@ -145,7 +155,7 @@ int main(int argc, char *argv[]) Vector vxyz; // Set the nodal grid function for the mesh, and modify the nodal positions - // for level_set_type = 3 such that some of the mesh elements are intersected + // for dirichlet_level_set_type = 3 such that some of the mesh elements are intersected // by the true boundary (y = 0). ParFiniteElementSpace pfespace_mesh(&pmesh, &fec, dim); pmesh.SetNodalFESpace(&pfespace_mesh); @@ -153,7 +163,7 @@ int main(int argc, char *argv[]) pmesh.SetNodalGridFunction(&x_mesh); vxyz = *pmesh.GetNodes(); int nodes_cnt = vxyz.Size()/dim; - if (level_set_type == 3) + if (dirichlet_level_set_type == 3) { for (int i = 0; i < nodes_cnt; i++) { @@ -172,38 +182,58 @@ int main(int argc, char *argv[]) // Define the solution vector x as a finite element grid function // corresponding to pfespace. ParGridFunction x(&pfespace); - // ParGridFunction for level_set_value. - ParGridFunction dirichlet_level_set_val(&pfespace); - ParGridFunction neumann_level_set_val(&pfespace); ParGridFunction combo_level_set_val(&pfespace); - int dirichlet_level_set_type = level_set_type == 5 ? 1 : level_set_type; - // Determine if each element in the ParMesh is inside the actual domain, // partially cut by its boundary, or completely outside the domain. - Dist_Level_Set_Coefficient dirichlet_dist_coef(dirichlet_level_set_type); - dirichlet_level_set_val.ProjectCoefficient(dirichlet_dist_coef); - // Exchange information for ghost elements i.e. elements that share a face - // with element on the current processor, but belong to another processor. - dirichlet_level_set_val.ExchangeFaceNbrData(); - // Setup the class to mark all elements based on whether they are located - // inside or outside the true domain, or intersected by the true boundary. - ShiftedFaceMarker marker(pmesh, dirichlet_level_set_val, pfespace, include_cut_cell); - Array elem_marker; - marker.MarkElements(elem_marker); - - // Setup the Neumann level set grid function - Dist_Level_Set_Coefficient neumann_dist_coef(level_set_type); - neumann_level_set_val.ProjectCoefficient(neumann_dist_coef); - neumann_level_set_val.ExchangeFaceNbrData(); - marker.SetLevelSetFunction(neumann_level_set_val); - marker.MarkElements(elem_marker); - + Dist_Level_Set_Coefficient *dirichlet_dist_coef = NULL; + Dist_Level_Set_Coefficient *dirichlet_dist_coef_2 = NULL; + Dist_Level_Set_Coefficient *neumann_dist_coef = NULL; // Create a Combo level set coefficient Combo_Level_Set_Coefficient combo_dist_coef; - combo_dist_coef.Add_Level_Set_Coefficient(dirichlet_dist_coef); - combo_dist_coef.Add_Level_Set_Coefficient(neumann_dist_coef); + ShiftedFaceMarker marker(pmesh, pfespace, include_cut_cell); + Array elem_marker; + + if (dirichlet_level_set_type > 0) + { + // ParGridFunction for level_set_value. + ParGridFunction dirichlet_level_set_val(&pfespace); + dirichlet_dist_coef = new Dist_Level_Set_Coefficient(dirichlet_level_set_type); + dirichlet_level_set_val.ProjectCoefficient(*dirichlet_dist_coef); + // Exchange information for ghost elements i.e. elements that share a face + // with element on the current processor, but belong to another processor. + dirichlet_level_set_val.ExchangeFaceNbrData(); + // Setup the class to mark all elements based on whether they are located + // inside or outside the true domain, or intersected by the true boundary. + marker.MarkElements(dirichlet_level_set_val, elem_marker); + combo_dist_coef.Add_Level_Set_Coefficient(*dirichlet_dist_coef); + } + + if (dirichlet_level_set_type_combo == 6) + { + MFEM_VERIFY(dirichlet_level_set_type == 5, + " The combo level set example has been only set for" + " dirichlet_level_set_type == 5."); + ParGridFunction dirichlet_level_set_val(&pfespace); + dirichlet_dist_coef_2 = new Dist_Level_Set_Coefficient( + dirichlet_level_set_type_combo); + dirichlet_level_set_val.ProjectCoefficient(*dirichlet_dist_coef_2); + dirichlet_level_set_val.ExchangeFaceNbrData(); + marker.MarkElements(dirichlet_level_set_val, elem_marker); + combo_dist_coef.Add_Level_Set_Coefficient(*dirichlet_dist_coef_2); + } + + // Setup the Neumann level set grid function + if (neumann_level_set_type > 0) + { + ParGridFunction neumann_level_set_val(&pfespace); + neumann_dist_coef = new Dist_Level_Set_Coefficient(neumann_level_set_type); + neumann_level_set_val.ProjectCoefficient(*neumann_dist_coef); + neumann_level_set_val.ExchangeFaceNbrData(); + marker.MarkElements(neumann_level_set_val, elem_marker); + combo_dist_coef.Add_Level_Set_Coefficient(*neumann_dist_coef); + } // Visualize the element markers. if (visualization) @@ -258,17 +288,26 @@ int main(int argc, char *argv[]) VectorCoefficient *dist_vec = NULL; // Compute the distance field using the HeatDistanceSolver for // level_set_type == 4 or analytically for all other level set types. - if (level_set_type == 4 || level_set_type == 5) + if (dirichlet_level_set_type == 1 || dirichlet_level_set_type == 2 || + dirichlet_level_set_type == 3) + { + // Analytic distance vector. + dist_vec = new Dist_Vector_Coefficient(dim, dirichlet_level_set_type); + distance.ProjectDiscCoefficient(*dist_vec); + } + else { // Discrete distance vector. double dx = AvgElementSize(pmesh); ParGridFunction filt_gf(&pfespace); PDEFilter *filter = new PDEFilter(pmesh, 2.0 * dx); - if (level_set_type == 4) { - filter->Filter(dirichlet_dist_coef, filt_gf); + if (dirichlet_level_set_type == 4) + { + filter->Filter(*dirichlet_dist_coef, filt_gf); } - else { - filter->Filter(combo_dist_coef, filt_gf); + else + { + filter->Filter(combo_dist_coef, filt_gf); } delete filter; GridFunctionCoefficient ls_filt_coeff(&filt_gf); @@ -288,12 +327,6 @@ int main(int argc, char *argv[]) dist_func.ComputeVectorDistance(ls_filt_coeff, distance); dist_vec = new VectorGridFunctionCoefficient(&distance); } - else - { - // Analytic distance vector. - dist_vec = new Dist_Vector_Coefficient(dim, level_set_type); - distance.ProjectDiscCoefficient(*dist_vec); - } // Visualize the distance vector. if (visualization) @@ -338,15 +371,17 @@ int main(int argc, char *argv[]) // the FEM linear system. ParLinearForm b(&pfespace); FunctionCoefficient *rhs_f = NULL; - if (level_set_type == 1 || level_set_type == 4 || level_set_type == 5) + if (dirichlet_level_set_type == 1 || dirichlet_level_set_type == 4 || + dirichlet_level_set_type == 5 || dirichlet_level_set_type == 6 || + neumann_level_set_type == 1 || neumann_level_set_type == 7) { rhs_f = new FunctionCoefficient(rhs_fun_circle); } - else if (level_set_type == 2) + else if (dirichlet_level_set_type == 2) { rhs_f = new FunctionCoefficient(rhs_fun_xy_exponent); } - else if (level_set_type == 3) + else if (dirichlet_level_set_type == 3) { rhs_f = new FunctionCoefficient(rhs_fun_xy_sinusoidal); } @@ -355,88 +390,154 @@ int main(int argc, char *argv[]) // Dirichlet BC that must be imposed on the true boundary. ShiftedFunctionCoefficient *dbcCoef = NULL; - if (level_set_type == 1 || level_set_type == 4 || level_set_type == 5) + if (dirichlet_level_set_type == 1 || dirichlet_level_set_type >= 4) { dbcCoef = new ShiftedFunctionCoefficient(dirichlet_velocity_circle); } - else if (level_set_type == 2) + else if (dirichlet_level_set_type == 2) { dbcCoef = new ShiftedFunctionCoefficient(dirichlet_velocity_xy_exponent); } - else if (level_set_type == 3) + else if (dirichlet_level_set_type == 3) { dbcCoef = new ShiftedFunctionCoefficient(dirichlet_velocity_xy_sinusoidal); } - else + + ShiftedFunctionCoefficient *dbcCoefCombo = NULL; + if (dirichlet_level_set_type_combo == 6) { - MFEM_ABORT("Dirichlet velocity function not set for level set type.\n"); + dbcCoefCombo = new ShiftedFunctionCoefficient(unity); } - ShiftedFunctionCoefficient *nbcCoef = NULL; + // Homogeneous Neumann boundary condition coefficient + ShiftedFunctionCoefficient nbcCoef(neumann_velocity_circle); ShiftedVectorFunctionCoefficient *normalbcCoef = NULL; - if (level_set_type == 5) { - nbcCoef = new ShiftedFunctionCoefficient(neumann_velocity_circle); - normalbcCoef = new ShiftedVectorFunctionCoefficient(dim, normal_vector); + if (neumann_level_set_type == 1) + { + normalbcCoef = new ShiftedVectorFunctionCoefficient(dim, normal_vector); + } + else if (neumann_level_set_type == 7) + { + normalbcCoef = new ShiftedVectorFunctionCoefficient(dim, normal_vector2); + } + else if (neumann_level_set_type > 0) + { + MFEM_ABORT(" Normal vector coefficient not implemented for level set."); } - // Add integrators corresponding to the shifted boundary method (SBM) // for Dirichlet boundaries. - int dirichlet_cut_marker_offset = 0; - b.AddInteriorFaceIntegrator(new SBM2DirichletLFIntegrator(&pmesh, *dbcCoef, - alpha, *dist_vec, - elem_marker, - include_cut_cell, - ho_terms, - dirichlet_cut_marker_offset)); - b.AddBdrFaceIntegrator(new SBM2DirichletLFIntegrator(&pmesh, *dbcCoef, - alpha, *dist_vec, - elem_marker, - include_cut_cell, - ho_terms, - dirichlet_cut_marker_offset), ess_shift_bdr); + int cut_marker_offset = 0; + Array bilinear_dirichlet_marker(0), + bilinear_neumann_marker(0); + + if (dirichlet_level_set_type > 0) + { + b.AddInteriorFaceIntegrator(new SBM2DirichletLFIntegrator(&pmesh, *dbcCoef, + alpha, *dist_vec, + elem_marker, + include_cut_cell, + ho_terms, + cut_marker_offset)); + b.AddBdrFaceIntegrator(new SBM2DirichletLFIntegrator(&pmesh, *dbcCoef, + alpha, *dist_vec, + elem_marker, + include_cut_cell, + ho_terms, + cut_marker_offset), ess_shift_bdr); + bilinear_dirichlet_marker.Append(ShiftedFaceMarker::SBElementType::CUT + +cut_marker_offset); + cut_marker_offset += 1; + } + + if (dirichlet_level_set_type_combo == 6) + { + b.AddInteriorFaceIntegrator(new SBM2DirichletLFIntegrator(&pmesh, *dbcCoefCombo, + alpha, *dist_vec, + elem_marker, + include_cut_cell, + ho_terms, + cut_marker_offset)); + bilinear_dirichlet_marker.Append(ShiftedFaceMarker::SBElementType::CUT + +cut_marker_offset); + cut_marker_offset += 1; + } // Add integrators corresponding to the shifted boundary method (SBM) // for Neumann boundaries. - int neumann_cut_marker_offset = 1; - if (level_set_type == 5) { - b.AddInteriorFaceIntegrator(new SBM2NeumannLFIntegrator( - &pmesh, *nbcCoef, alpha, *dist_vec, *normalbcCoef, - elem_marker, include_cut_cell, ho_terms, neumann_cut_marker_offset)); + if (neumann_level_set_type > 0) + { + b.AddInteriorFaceIntegrator(new SBM2NeumannLFIntegrator( + &pmesh, nbcCoef, alpha, *dist_vec, *normalbcCoef, + elem_marker, include_cut_cell, ho_terms, cut_marker_offset)); + bilinear_neumann_marker.Append(ShiftedFaceMarker::SBElementType::CUT + +cut_marker_offset); + cut_marker_offset += 1; } b.Assemble(); + // elem_marker.Print(); + // bilinear_dirichlet_marker.Print(); + // bilinear_neumann_marker.Print(); + // MFEM_ABORT(" "); + // Set up the bilinear form a(.,.) on the finite element space corresponding // to the Laplacian operator -Delta, by adding the Diffusion domain // integrator and SBM integrator. ParBilinearForm a(&pfespace); ConstantCoefficient one(1.); a.AddDomainIntegrator(new DiffusionIntegrator(one), ess_elem); - a.AddInteriorFaceIntegrator(new SBM2DirichletIntegrator(&pmesh, alpha, - *dist_vec, - elem_marker, - include_cut_cell, - ho_terms)); - a.AddBdrFaceIntegrator(new SBM2DirichletIntegrator(&pmesh, alpha, *dist_vec, - elem_marker, - include_cut_cell, - ho_terms), ess_shift_bdr); + if (dirichlet_level_set_type > 0) + { + a.AddInteriorFaceIntegrator(new SBM2DirichletIntegrator(&pmesh, alpha, + *dist_vec, + elem_marker, + bilinear_dirichlet_marker, + include_cut_cell, + ho_terms)); + a.AddBdrFaceIntegrator(new SBM2DirichletIntegrator(&pmesh, alpha, *dist_vec, + elem_marker, + bilinear_dirichlet_marker, + include_cut_cell, + ho_terms), ess_shift_bdr); + } // Add neumann bilinearform integrator - a.AddInteriorFaceIntegrator(new SBM2NeumannIntegrator(&pmesh, alpha, - *dist_vec, - *normalbcCoef, - elem_marker, - include_cut_cell, - ho_terms, - neumann_cut_marker_offset)); + if (neumann_level_set_type > 0) + { + a.AddInteriorFaceIntegrator(new SBM2NeumannIntegrator(&pmesh, alpha, + *dist_vec, + *normalbcCoef, + elem_marker, + bilinear_neumann_marker, + include_cut_cell, + ho_terms)); + } // Assemble the bilinear form and the corresponding linear system, // applying any necessary transformations. a.Assemble(); // Project the exact solution as an initial condition for Dirichlet boundary. - x.ProjectCoefficient(*dbcCoef); + x = 0.0; + if (dirichlet_level_set_type > 0) + { + //x.ProjectCoefficient(*dbcCoef); + if (dirichlet_level_set_type_combo == 6) + { + x = 0.0; + } + } + + if (visualization) + { + char vishost[] = "localhost"; + int visport = 19916, s = 350; + socketstream sol_sock; + common::VisualizeField(sol_sock, vishost, visport, x, + "Solution", s, 0, s, s, "Rj"); + } + // Form the linear system and solve it. OperatorPtr A; @@ -487,7 +588,7 @@ int main(int argc, char *argv[]) } // Construct an error grid function if the exact solution is known. - if (level_set_type == 2 || level_set_type == 3) + if (dirichlet_level_set_type == 2 || dirichlet_level_set_type == 3) { ParGridFunction err(x); Vector pxyz(dim); @@ -497,11 +598,11 @@ int main(int argc, char *argv[]) pxyz(0) = vxyz(i); pxyz(1) = vxyz(i+nodes_cnt); double exact_val = 0.; - if (level_set_type == 2) + if (dirichlet_level_set_type == 2) { exact_val = dirichlet_velocity_xy_exponent(pxyz); } - else if (level_set_type == 3) + else if (dirichlet_level_set_type == 3) { exact_val = dirichlet_velocity_xy_sinusoidal(pxyz); } diff --git a/miniapps/shifted/marking.cpp b/miniapps/shifted/marking.cpp index 8dcb391ac2..f659e316a9 100644 --- a/miniapps/shifted/marking.cpp +++ b/miniapps/shifted/marking.cpp @@ -17,7 +17,7 @@ namespace mfem void ShiftedFaceMarker::MarkElements(Array &elem_marker) { elem_marker.SetSize(pmesh.GetNE() + pmesh.GetNSharedFaces()); - if (!initial_marking) { elem_marker = SBElementType::INSIDE; } + if (!initial_marking_done) { elem_marker = SBElementType::INSIDE; } else { level_set_index += 1; } IntegrationRules IntRulesLo(0, Quadrature1D::GaussLobatto); @@ -29,7 +29,7 @@ void ShiftedFaceMarker::MarkElements(Array &elem_marker) ElementTransformation *Tr = pmesh.GetElementTransformation(i); const IntegrationRule &ir = IntRulesLo.Get(pmesh.GetElementBaseGeometry(i), 4*Tr->OrderJ()); - ls_func.GetValues(i, ir, vals); + ls_func->GetValues(i, ir, vals); int count = 0; for (int j = 0; j < ir.GetNPoints(); j++) @@ -43,6 +43,8 @@ void ShiftedFaceMarker::MarkElements(Array &elem_marker) } else if (count > 0) // partially outside { + MFEM_VERIFY(elem_marker[i] <= SBElementType::OUTSIDE, + " One element cut by multiple level-sets."); elem_marker[i] = SBElementType::CUT + level_set_index; } } @@ -67,7 +69,7 @@ void ShiftedFaceMarker::MarkElements(Array &elem_marker) for (int j = 0; j < nip; j++) { const IntegrationPoint &ip = ir.IntPoint(j); - vals[j] = ls_func.GetValue(tr->Elem2No, ip); + vals[j] = ls_func->GetValue(tr->Elem2No, ip); if (vals[j] <= 0.) { count++; } } @@ -77,10 +79,19 @@ void ShiftedFaceMarker::MarkElements(Array &elem_marker) } else if (count > 0) // partially outside { + MFEM_VERIFY(elem_marker[i] <= SBElementType::OUTSIDE, + " One element cut by multiple level-sets."); elem_marker[i] = SBElementType::CUT + level_set_index; } } - initial_marking = true; + initial_marking_done = true; +} + +void ShiftedFaceMarker::MarkElements(ParGridFunction &ls, + Array &elem_marker) +{ + SetLevelSetFunction(ls); + MarkElements(elem_marker); } void ShiftedFaceMarker::ListShiftedFaceDofs(const Array &elem_marker, @@ -106,25 +117,25 @@ void ShiftedFaceMarker::ListShiftedFaceDofs(const Array &elem_marker, if (!include_cut_cell && te1 >= ShiftedFaceMarker::CUT && te2 == ShiftedFaceMarker::INSIDE) { - pfes_sltn.GetFaceDofs(f, dofs); + pfes_sltn->GetFaceDofs(f, dofs); sface_dof_list.Append(dofs); } if (!include_cut_cell && te1 == ShiftedFaceMarker::INSIDE && te2 >= ShiftedFaceMarker::CUT) { - pfes_sltn.GetFaceDofs(f, dofs); + pfes_sltn->GetFaceDofs(f, dofs); sface_dof_list.Append(dofs); } if (include_cut_cell && te1 >= SBElementType::CUT && te2 == SBElementType::OUTSIDE) { - pfes_sltn.GetFaceDofs(f, dofs); + pfes_sltn->GetFaceDofs(f, dofs); sface_dof_list.Append(dofs); } if (include_cut_cell && te1 == SBElementType::OUTSIDE && te2 >= SBElementType::CUT) { - pfes_sltn.GetFaceDofs(f, dofs); + pfes_sltn->GetFaceDofs(f, dofs); sface_dof_list.Append(dofs); } } @@ -140,7 +151,7 @@ void ShiftedFaceMarker::ListShiftedFaceDofs(const Array &elem_marker, { if (elem_marker[tr->Elem1No] >= SBElementType::CUT) { - pfes_sltn.GetFaceDofs(pmesh.GetBdrFace(i), dofs); + pfes_sltn->GetFaceDofs(pmesh.GetBdrFace(i), dofs); sface_dof_list.Append(dofs); } } @@ -162,13 +173,13 @@ void ShiftedFaceMarker::ListShiftedFaceDofs(const Array &elem_marker, if (!include_cut_cell && te2 >= ShiftedFaceMarker::CUT && te1 == ShiftedFaceMarker::INSIDE) { - pfes_sltn.GetFaceDofs(faceno, dofs); + pfes_sltn->GetFaceDofs(faceno, dofs); sface_dof_list.Append(dofs); } if (include_cut_cell && te2 == SBElementType::OUTSIDE && te1 >= SBElementType::CUT) { - pfes_sltn.GetFaceDofs(faceno, dofs); + pfes_sltn->GetFaceDofs(faceno, dofs); sface_dof_list.Append(dofs); } } @@ -227,7 +238,7 @@ void ShiftedFaceMarker::ListEssentialTDofs(const Array &elem_marker, } } Array ess_vdofs_bdr; - pfes_sltn.GetEssentialVDofs(ess_bdr, ess_vdofs_bdr); + pfes_sltn->GetEssentialVDofs(ess_bdr, ess_vdofs_bdr); // Get all dofs associated with elements outside the domain or intersected by // the boundary. @@ -239,7 +250,7 @@ void ShiftedFaceMarker::ListEssentialTDofs(const Array &elem_marker, (elem_marker[e] == SBElementType::OUTSIDE || elem_marker[e] >= SBElementType::CUT)) { - pfes_sltn.GetElementVDofs(e, dofs); + pfes_sltn->GetElementVDofs(e, dofs); for (int i = 0; i < dofs.Size(); i++) { ess_vdofs[dofs[i]] = -1; @@ -248,7 +259,7 @@ void ShiftedFaceMarker::ListEssentialTDofs(const Array &elem_marker, if (include_cut_cell && elem_marker[e] == SBElementType::OUTSIDE) { - pfes_sltn.GetElementVDofs(e, dofs); + pfes_sltn->GetElementVDofs(e, dofs); for (int i = 0; i < dofs.Size(); i++) { ess_vdofs[dofs[i]] = -1; @@ -273,13 +284,13 @@ void ShiftedFaceMarker::ListEssentialTDofs(const Array &elem_marker, // Synchronize for (int i = 0; i < ess_vdofs.Size() ; i++) { ess_vdofs[i] += 1; } - pfes_sltn.Synchronize(ess_vdofs); + pfes_sltn->Synchronize(ess_vdofs); for (int i = 0; i < ess_vdofs.Size() ; i++) { ess_vdofs[i] -= 1; } // Convert to tdofs Array ess_tdofs; - pfes_sltn.GetRestrictionMatrix()->BooleanMult(ess_vdofs, ess_tdofs); - pfes_sltn.MarkerToList(ess_tdofs, ess_tdof_list); + pfes_sltn->GetRestrictionMatrix()->BooleanMult(ess_vdofs, ess_tdofs); + pfes_sltn->MarkerToList(ess_tdofs, ess_tdof_list); } void ShiftedFaceMarker::ListShiftedFaceDofs2(const Array &elem_marker, @@ -290,7 +301,7 @@ void ShiftedFaceMarker::ListShiftedFaceDofs2(const Array &elem_marker, L2_FECollection mat_coll(0, pmesh.Dimension()); ParFiniteElementSpace mat_fes(&pmesh, &mat_coll); ParGridFunction mat(&mat_fes); - ParGridFunction marker_gf(&pfes_sltn); + ParGridFunction marker_gf(pfes_sltn); for (int i = 0; i < pmesh.GetNE(); i++) { // 0 is inside, 1 is outside. @@ -323,7 +334,7 @@ void ShiftedFaceMarker::ListShiftedFaceDofs2(const Array &elem_marker, { if (elem_marker[tr->Elem1No] >= SBElementType::CUT) { - pfes_sltn.GetFaceDofs(pmesh.GetBdrFace(i), dofs); + pfes_sltn->GetFaceDofs(pmesh.GetBdrFace(i), dofs); sface_dof_list.Append(dofs); } } diff --git a/miniapps/shifted/marking.hpp b/miniapps/shifted/marking.hpp index 8763a2c732..18f2b13340 100644 --- a/miniapps/shifted/marking.hpp +++ b/miniapps/shifted/marking.hpp @@ -12,7 +12,7 @@ #ifndef MFEM_MARKING_HPP #define MFEM_MARKING_HPP -#include "mfem.hpp" +#include "../../mfem.hpp" namespace mfem { @@ -23,10 +23,10 @@ class ShiftedFaceMarker { protected: ParMesh &pmesh; - ParGridFunction &ls_func; - ParFiniteElementSpace &pfes_sltn; + ParGridFunction *ls_func; + ParFiniteElementSpace *pfes_sltn; bool include_cut_cell; - bool initial_marking; + bool initial_marking_done; // Marking of face dofs by using an averaged continuous GridFunction. const bool func_dof_marking = false; @@ -37,19 +37,26 @@ protected: private: int level_set_index; + public: /// Element type related to shifted boundaries (not interfaces). enum SBElementType {INSIDE = 0, OUTSIDE = 1, CUT = 2}; ShiftedFaceMarker(ParMesh &pm, ParGridFunction &ls, - ParFiniteElementSpace &space_sltn, + ParFiniteElementSpace &pfes, bool include_cut_cell_) + : pmesh(pm), ls_func(&ls), pfes_sltn(&pfes), + include_cut_cell(include_cut_cell_), initial_marking_done(false), + level_set_index(0) { } + + ShiftedFaceMarker(ParMesh &pm, ParFiniteElementSpace &pfes, bool include_cut_cell_) - : pmesh(pm), ls_func(ls), pfes_sltn(space_sltn), - include_cut_cell(include_cut_cell_), initial_marking(false), + : pmesh(pm), ls_func(NULL), pfes_sltn(&pfes), + include_cut_cell(include_cut_cell_), initial_marking_done(false), level_set_index(0) { } /// Mark all the elements in the mesh using the @a SBElementType void MarkElements(Array &elem_marker); + void MarkElements(ParGridFunction &ls, Array &elem_marker); /// List dofs associated with the surrogate boundary. /// If @a include_cut_cell = false, the surrogate boundary includes faces @@ -71,7 +78,7 @@ public: Array &ess_tdof_list, Array &ess_shift_bdr) const; - void SetLevelSetFunction(ParGridFunction &ls) { ls_func = ls; } + void SetLevelSetFunction(ParGridFunction &ls) { ls_func = &ls; } }; } // namespace mfem diff --git a/miniapps/shifted/sbm_aux.hpp b/miniapps/shifted/sbm_aux.hpp index 40f1c62474..2260d32686 100644 --- a/miniapps/shifted/sbm_aux.hpp +++ b/miniapps/shifted/sbm_aux.hpp @@ -16,23 +16,36 @@ using namespace std; using namespace mfem; +double point_inside_trigon(const Vector px, Vector p1, Vector p2, Vector p3) +{ + Vector v0 = p1; + Vector v1 = p2; v1 -=p1; + Vector v2 = p3; v2 -=p1; + double p, q; + p = ((px(0)*v2(1)-px(1)*v2(0))-(v0(0)*v2(1)-v0(1)*v2(0)))/(v1(0)*v2(1)-v1(1)*v2( + 0)); + q = -((px(0)*v1(1)-px(1)*v1(0))-(v0(0)*v1(1)-v0(1)*v1(0)))/(v1(0)*v2(1)-v1( + 1)*v2(0)); + + if (p > 0 && q > 0 && 1-p-q > 0) + { + return -1.0; + } + return 1.0; +} + /// Analytic distance to the 0 level set. Positive value if the point is inside /// the domain, and negative value if outside. double dist_value(const Vector &x, const int type) { + double ring_radius = 0.2; if (type == 1 || type == 2) // circle of radius 0.2 - centered at 0.5, 0.5 { - double dx = x(0) - 0.5, - dy = x(1) - 0.5, - rv = dx*dx + dy*dy; - if (x.Size() == 3) - { - double dz = x(2) - 0.5; - rv += dz*dz; - } - rv = rv > 0 ? pow(rv, 0.5) : 0; - return rv - ring_radius; // positive is the domain + Vector xc(x.Size()); + xc = 0.5; + xc -= x; + return xc.Norml2() - ring_radius; // positive is the domain } else if (type == 3) // walls at y = 0.0 { @@ -65,18 +78,36 @@ double dist_value(const Vector &x, const int type) if (0.3 <= xc && xc <= 0.8 && 0.15 <= yc && yc <= 0.2) { return 1.0; } return -1.0; } - if (type == 5) // circle of radius 0.2 - centered at 1.5, 0.5 + else if (type == 5) // square of side 0.2 centered at 0.75, 0.25 { - double dx = x(0) - 1.5, - dy = x(1) - 0.5, - rv = dx*dx + dy*dy; - if (x.Size() == 3) + double square_side = 0.2; + Vector xc(x.Size()); + xc = 0.75; xc(1) = 0.25; + xc -= x; + if (abs(xc(0)) > 0.5*square_side || abs(xc(1)) > 0.5*square_side) { - double dz = x(2) - 0.5; - rv += dz*dz; + return 1.0; } - rv = rv > 0 ? pow(rv, 0.5) : 0; - return rv - ring_radius; // positive is the domain + else + { + return -1.0; + } + return 0.0; + } + else if (type == 6) // Triangle + { + Vector p1(x.Size()), p2(x.Size()), p3(x.Size()); + p1(0) = 0.25; p1(1) = 0.4; + p2(0) = 0.1; p2(1) = 0.1; + p3(0) = 0.4; p3(1) = 0.1; + return point_inside_trigon(x, p1, p2, p3); + } + else if (type == 7) // circle of radius 0.2 - centered at 0.5, 0.6 + { + Vector xc(x.Size()); + xc = 0.5; xc(1) = 0.6; + xc -= x; + return xc.Norml2() - 0.2; } else { @@ -114,16 +145,19 @@ private: public: Combo_Level_Set_Coefficient() : Coefficient() { } - virtual void Add_Level_Set_Coefficient(Dist_Level_Set_Coefficient &dls_) + void Add_Level_Set_Coefficient(Dist_Level_Set_Coefficient &dls_) { dls.Append(&dls_); } + int GetNLevelSets() { return dls.Size(); } + virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip) { MFEM_VERIFY(dls.Size() > 0, "Add at-least 1 Dist_level_Set_Coefficient to" - " the Combo."); + " the Combo."); double dist = dls[0]->Eval(T, ip); - for (int j = 1; j < dls.Size(); j++) { - dist = min(dist, dls[j]->Eval(T, ip)); + for (int j = 1; j < dls.Size(); j++) + { + dist = min(dist, dls[j]->Eval(T, ip)); } if (dist >= 0.) { return 1.; } else { return -1.; } @@ -169,6 +203,16 @@ double dirichlet_velocity_circle(const Vector &x) return 0.; } +double unity(const Vector &x) +{ + return 0.015; +} + +double zero(const Vector &x) +{ + return 0.0; +} + double dirichlet_velocity_xy_exponent(const Vector &x) { double xy_p = 2.; // exponent for level set 2 where u = x^p+y^p; @@ -186,13 +230,24 @@ double neumann_velocity_circle(const Vector &x) return 0.; } -/// Normal vector for level_set_type = 5. Circle centered at [0.5 , 0.5] -void normal_vector(const Vector &x, Vector &p) { - p.SetSize(x.Size()); - p(0) = x(0)-0.5; - p(1) = x(1)-0.5; //center of circle at [0.5, 0.5] - p /= p.Norml2(); - p *= -1; +/// Normal vector for level_set_type = 1. Circle centered at [0.5 , 0.5] +void normal_vector(const Vector &x, Vector &p) +{ + p.SetSize(x.Size()); + p(0) = x(0)-0.5; + p(1) = x(1)-0.5; //center of circle at [0.5, 0.5] + p /= p.Norml2(); + p *= -1; +} + +/// Normal vector for level_set_type = 6. Circle centered at [0.75 , 0.25] +void normal_vector2(const Vector &x, Vector &p) +{ + p.SetSize(x.Size()); + p(0) = x(0)-0.5; + p(1) = x(1)-0.6; //center of circle at [0.5, 0.6] + p /= p.Norml2(); + p *= -1; } diff --git a/miniapps/shifted/sbm_solver.cpp b/miniapps/shifted/sbm_solver.cpp index 33ba49926b..a5edc25ffc 100644 --- a/miniapps/shifted/sbm_solver.cpp +++ b/miniapps/shifted/sbm_solver.cpp @@ -86,13 +86,13 @@ void SBM2DirichletIntegrator::AssembleFaceMatrix( { // 1 is inside and 2 is cut or 1 is a boundary element. if (marker1 == ShiftedFaceMarker::SBElementType::INSIDE && - (marker2 == ShiftedFaceMarker::SBElementType::CUT || + (cut_marker.Find(marker2) != -1 || Trans.ElementType == ElementTransformation::BDR_FACE)) { elem1f = true; } // 1 is cut, 2 is inside - else if (marker1 == ShiftedFaceMarker::SBElementType::CUT && + else if (cut_marker.Find(marker1) != -1 && marker2 == ShiftedFaceMarker::SBElementType::INSIDE) { if (Trans.Elem2No >= NEproc) { return; } @@ -106,7 +106,7 @@ void SBM2DirichletIntegrator::AssembleFaceMatrix( else { // 1 is cut and 2 is outside or 1 is a boundary element. - if (marker1 == ShiftedFaceMarker::SBElementType::CUT && + if (cut_marker.Find(marker1) != -1 && (marker2 == ShiftedFaceMarker::SBElementType::OUTSIDE || Trans.ElementType == ElementTransformation::BDR_FACE)) { @@ -114,7 +114,7 @@ void SBM2DirichletIntegrator::AssembleFaceMatrix( } // 1 is outside, 2 is cut else if (marker1 == ShiftedFaceMarker::SBElementType::OUTSIDE && - marker2 == ShiftedFaceMarker::SBElementType::CUT) + cut_marker.Find(marker2) != -1) { if (Trans.Elem2No >= NEproc) { return; } elem1f = false; @@ -420,7 +420,7 @@ void SBM2DirichletLFIntegrator::AssembleRHSElementVect( { // 1 is cut and 2 is outside or 1 is a boundary element. if (marker1 == ShiftedFaceMarker::SBElementType::CUT - + cut_cell_marker_offset && + + cut_cell_marker_offset && (marker2 == ShiftedFaceMarker::SBElementType::OUTSIDE || Tr.ElementType == ElementTransformation::BDR_FACE)) { @@ -703,13 +703,13 @@ void SBM2NeumannIntegrator::AssembleFaceMatrix( { // 1 is inside and 2 is cut or 1 is a boundary element. if (marker1 == ShiftedFaceMarker::SBElementType::INSIDE && - (marker2 == ShiftedFaceMarker::SBElementType::CUT || + (cut_marker.Find(marker2) != -1 || Trans.ElementType == ElementTransformation::BDR_FACE)) { elem1f = true; } // 1 is cut, 2 is inside - else if (marker1 == ShiftedFaceMarker::SBElementType::CUT && + else if (cut_marker.Find(marker1) != -1 && marker2 == ShiftedFaceMarker::SBElementType::INSIDE) { if (Trans.Elem2No >= NEproc) { return; } @@ -723,7 +723,7 @@ void SBM2NeumannIntegrator::AssembleFaceMatrix( else { // 1 is cut and 2 is outside or 1 is a boundary element. - if (marker1 == ShiftedFaceMarker::SBElementType::CUT && + if (cut_marker.Find(marker1) != -1 && (marker2 == ShiftedFaceMarker::SBElementType::OUTSIDE || Trans.ElementType == ElementTransformation::BDR_FACE)) { @@ -731,7 +731,7 @@ void SBM2NeumannIntegrator::AssembleFaceMatrix( } // 1 is outside, 2 is cut else if (marker1 == ShiftedFaceMarker::SBElementType::OUTSIDE && - marker2 == ShiftedFaceMarker::SBElementType::CUT) + cut_marker.Find(marker2) != -1) { if (Trans.Elem2No >= NEproc) { return; } elem1f = false; @@ -967,7 +967,7 @@ void SBM2NeumannLFIntegrator::AssembleRHSElementVect( void SBM2NeumannLFIntegrator::AssembleRHSElementVect( const FiniteElement &el1, const FiniteElement &el2, - FaceElementTransformations &Tr, Vector &elvect) + FaceElementTransformations &Tr, Vector &elvect) { int dim, ndof1, ndof2, ndof, ndoftotal; @@ -1035,7 +1035,7 @@ void SBM2NeumannLFIntegrator::AssembleRHSElementVect( { // 1 is cut and 2 is outside or 1 is a boundary element. if (marker1 == ShiftedFaceMarker::SBElementType::CUT - + cut_cell_marker_offset && + + cut_cell_marker_offset && (marker2 == ShiftedFaceMarker::SBElementType::OUTSIDE || Tr.ElementType == ElementTransformation::BDR_FACE)) { diff --git a/miniapps/shifted/sbm_solver.hpp b/miniapps/shifted/sbm_solver.hpp index 7fc99b7805..9a689a541d 100644 --- a/miniapps/shifted/sbm_solver.hpp +++ b/miniapps/shifted/sbm_solver.hpp @@ -93,7 +93,7 @@ protected: int NEproc; //Number of elements on the current MPI rank int par_shared_face_count; // - int cut_cell_marker_offset; + Array cut_marker; // these are not thread-safe! Vector shape, dshapedn, dshapephysdn, nor, nh, ni; @@ -105,16 +105,16 @@ public: const double a, VectorCoefficient &vD_, Array &elem_marker_, + Array &cut_marker_, bool include_cut_cell_ = false, - int nterms_ = 0, - int cut_cell_marker_offset_ = 0) + int nterms_ = 0) : alpha(a), vD(&vD_), elem_marker(&elem_marker_), include_cut_cell(include_cut_cell_), nterms(nterms_), NEproc(pmesh->GetNE()), par_shared_face_count(0), - cut_cell_marker_offset(cut_cell_marker_offset_) { } + cut_marker(cut_marker_) { } using BilinearFormIntegrator::AssembleFaceMatrix; virtual void AssembleFaceMatrix(const FiniteElement &el1, @@ -205,7 +205,7 @@ protected: int NEproc; //Number of elements on the current MPI rank int par_shared_face_count; // - int cut_cell_marker_offset; + Array cut_marker; // these are not thread-safe! @@ -219,16 +219,16 @@ public: VectorCoefficient &vD_, ShiftedVectorFunctionCoefficient &vN_, Array &elem_marker_, + Array &cut_marker_, bool include_cut_cell_ = false, - int nterms_ = 0, - int cut_cell_marker_offset_ = 0) + int nterms_ = 0) : alpha(alpha_), vD(&vD_), vN(&vN_), elem_marker(&elem_marker_), include_cut_cell(include_cut_cell_), nterms(nterms_), NEproc(pmesh->GetNE()), par_shared_face_count(0), - cut_cell_marker_offset(cut_cell_marker_offset_) { } + cut_marker(cut_marker_) { } using BilinearFormIntegrator::AssembleFaceMatrix; virtual void AssembleFaceMatrix(const FiniteElement &el1, From f89d359d06ec8652c25736f81cab9114d6283bef Mon Sep 17 00:00:00 2001 From: Tom Stitt Date: Thu, 22 Jul 2021 15:58:54 -0700 Subject: [PATCH 040/198] add MultAtB and MultT(vector) and const qualifier to Mult(vector) --- linalg/kernels.hpp | 57 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/linalg/kernels.hpp b/linalg/kernels.hpp index 68d4a73b50..21c67846b0 100644 --- a/linalg/kernels.hpp +++ b/linalg/kernels.hpp @@ -160,7 +160,7 @@ double Norml2(const int size, const T *data) data of the input and output vectors. */ template MFEM_HOST_DEVICE inline -void Mult(const int height, const int width, TA *data, const TX *x, TY *y) +void Mult(const int height, const int width, const TA *data, const TX *x, TY *y) { if (width == 0) { @@ -170,7 +170,7 @@ void Mult(const int height, const int width, TA *data, const TX *x, TY *y) } return; } - TA *d_col = data; + const TA *d_col = data; TX x_col = x[0]; for (int row = 0; row < height; row++) { @@ -188,6 +188,35 @@ void Mult(const int height, const int width, TA *data, const TX *x, TY *y) } } +/** @brief Matrix transpose vector multiplication: y = At x, where the matrix A + is of size @a height x @a width with given @a data, while @a x and @a y + specify the data of the input and output vectors. */ +template +MFEM_HOST_DEVICE inline +void MultT(const int height, const int width, const TA *data, const TX *x, + TY *y) +{ + if (width == 0) + { + for (int row = 0; row < height; row++) + { + y[row] = 0.0; + } + return; + } + TY *y_off = y; + for (int i = 0; i < width; ++i) + { + TY val = 0.0; + for (int j = 0; j < height; ++j) + { + val += x[j] * data[i * height + j]; + } + *y_off = val; + y_off++; + } +} + /// Symmetrize a square matrix with given @a size and @a data: A -> (A+A^T)/2. template MFEM_HOST_DEVICE inline @@ -353,6 +382,30 @@ void MultABt(const int Aheight, const int Awidth, const int Bheight, } } +/** @brief Multiply the transpose of a matrix of size @a Aheight x @a Awidth + and data @a Adata with a matrix of size @a Aheight x @a Bwidth and data @a + Bdata: At * B. Return the result in a matrix with data @a AtBdata. */ +template +MFEM_HOST_DEVICE inline +void MultAtB(const int Aheight, const int Awidth, const int Bwidth, + const TA *Adata, const TB *Bdata, TC *AtBdata) +{ + TC *c = AtBdata; + for (int i = 0; i < Bwidth; ++i) + { + for (int j = 0; j < Awidth; ++j) + { + TC val = 0.0; + for (int k = 0; k < Aheight; ++k) + { + val += Adata[j * Aheight + k] * Bdata[i * Aheight + k]; + } + *c = val; + c++; + } + } +} + /// Compute the spectrum of the matrix of size dim with given @a data, returning /// the eigenvalues in the array @a lambda and the eigenvectors in the array @a /// vec (listed consecutively). From 1fdfa6d39a1682f4d9aa3ce723c55fc403ee11a5 Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 23 Jul 2021 21:30:27 -0700 Subject: [PATCH 041/198] code compiles. Needs debugging --- examples/osc.cpp | 82 ++++++++++++++--------------------------- mesh/mesh_operators.cpp | 66 +++++++++++++++++++++++++++++++++ mesh/mesh_operators.hpp | 76 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 55 deletions(-) diff --git a/examples/osc.cpp b/examples/osc.cpp index 25841dbbc4..8ebedfe356 100644 --- a/examples/osc.cpp +++ b/examples/osc.cpp @@ -78,7 +78,7 @@ double wavefront_laplace(const Vector &p) double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0)); double num = - ( alpha - pow(alpha,3) * (pow(r,2) - pow(r0,2)) ); double denom = pow(r * ( pow(alpha,2) * pow(r0,2) + pow(alpha,2) * pow(r,2) \ - - 2 * pow(alpha,2) * r0 * r + 1.0 ),2); + - 2 * pow(alpha,2) * r0 * r + 1.0 ),2); denom = max(denom,1e-8); // return num / denom; if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1; } @@ -86,6 +86,20 @@ double wavefront_laplace(const Vector &p) return 0; } +double wavefront_laplace_alt(const Vector &p) +{ + double x = p(0), y = p(1); + double alpha = 1000.0; + double xc = -0.5, yc = -0.5; + double r0 = 0.7; + double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0)); + double num = - ( alpha - pow(alpha,3) * (pow(r,2) - pow(r0,2)) ); + double denom = pow(r * ( pow(alpha,2) * pow(r0,2) + pow(alpha,2) * pow(r,2) \ + - 2 * pow(alpha,2) * r0 * r + 1.0 ),2); + denom = max(denom,1e-8); + return num / denom; +} + int main(int argc, char *argv[]) { // 1. Parse command-line options. @@ -95,6 +109,7 @@ int main(int argc, char *argv[]) const char *device_config = "cpu"; int max_dofs = 50000; bool visualization = true; + int nc_limit = 1; OptionsParser args(argc, argv); args.AddOption(&mesh_file, "-m", "--mesh", @@ -209,65 +224,22 @@ int main(int argc, char *argv[]) // 11.5. Preprocess mesh to control osc - L2_FECollection l2fec(order, dim); - FiniteElementSpace l2fes(&mesh, &l2fec); - GridFunction load(&l2fes); - int order_quad = std::max(2, 2*order+1); - const IntegrationRule *irs[Geometry::NumGeom]; - for (int i=0; i < Geometry::NumGeom; ++i) - { - irs[i] = &(IntRules.Get(i, order_quad)); - } - double osc_tol = 1e-3; - while (true) - { - bool h_refined = false; - Array mesh_refinements; - // get L2-norm of load ( f ) - load.ProjectCoefficient(*rhs); - // load.ProjectDiscCoefficient(*rhs,mfem::GridFunction::AvgType::ARITHMETIC); - double norm_of_load = ComputeLpNorm(2.0,*rhs,mesh,irs); + CoefficientRefiner coeffrefiner(0); + coeffrefiner.SetCoefficient(*rhs); + coeffrefiner.SetThreshold(osc_tol); + coeffrefiner.SetNCLimit(0); + coeffrefiner.PreprocessMesh(mesh); - // construct h * (I - Pi) f - GridFunction osc_fun; - double NE = l2fes.GetNE(); - double av_norm_of_load = norm_of_load / sqrt(NE); - Vector norm_of_fine_scale(NE); - load.ComputeElementL2Errors(*rhs,norm_of_fine_scale); - // osc.Print(); - // break; + Coefficient * rhs2 = nullptr; + rhs2 = new FunctionCoefficient(wavefront_laplace_alt); + coeffrefiner.SetCoefficient(*rhs2); + coeffrefiner.PreprocessMesh(mesh); - for (int i = 0; i < NE; i++) - { - double h = mesh.GetElementSize(i); - double local_osc = h * norm_of_fine_scale(i); - if ( local_osc > osc_tol * av_norm_of_load ) - { - h_refined = true; - mesh_refinements.Append(i); - } - } - if (h_refined) - { - int nonconforming = -1; - int nc_limit = 1; - - mesh.GeneralRefinement(mesh_refinements, nonconforming, nc_limit); - l2fes.Update(false); - load.Update(); - } - else - { - break; - } - - sol_sock.precision(8); - sol_sock << "mesh\n" << mesh << flush; - } - + sol_sock.precision(8); + sol_sock << "mesh\n" << mesh << flush; cout << "press any key" << endl; cin.get(); diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index 403b5ceec8..e9b34e4971 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -157,6 +157,72 @@ int ThresholdDerefiner::ApplyImpl(Mesh &mesh) } +int CoefficientRefiner::ApplyImpl(Mesh &mesh) +{ + return PreprocessMesh(mesh, 1); +} + +int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) +{ + MFEM_VERIFY(max_it > 0, "max_it must be strictly positive") + MFEM_VERIFY(coeff, "Coefficient is not set for CoefficientRefiner object") + + int dim = mesh.Dimension(); + L2_FECollection l2fec(order, dim); + FiniteElementSpace l2fes(&mesh, &l2fec); + if (!irs.Size()) + { + irs.SetSize(Geometry::NumGeom); + int order_quad = 2*order + 3; + for (int i=0; i < Geometry::NumGeom; ++i) + { + irs[i] = &(IntRules.Get(i, order_quad)); + } + } + + for (int i = 0; i < max_it; i++) + { + // Get average L2-norm of f + double NE = mesh.GetNE(); + gf.SetSpace(&l2fes); + gf.ProjectCoefficient(*coeff); + double av_norm_of_gf = ComputeLpNorm(2.0,*coeff,mesh,irs.GetData()) / sqrt(NE); + + // Construct local L2-norms of (I - Pi) f + Vector norm_of_fine_scale(NE); + gf.ComputeElementL2Errors(*coeff,norm_of_fine_scale,irs.GetData()); + + // Define osc = h \cdot \| (I - Pi) f \| and select elements + // for refinement based on threshold + mesh_refinements.SetSize(0); + for (int j = 0; j < NE; j++) + { + double h = mesh.GetElementSize(j); + double local_osc = h * norm_of_fine_scale(j); + if ( local_osc > threshold * av_norm_of_gf ) + { + mesh_refinements.Append(j); + } + } + + // Refine elements + if (mesh_refinements.Size()) + { + mesh.GeneralRefinement(mesh_refinements, nonconforming, nc_limit); + l2fes.Update(false); + gf.Update(); + } + else + { + return STOP; + } + } + return CONTINUE + REFINED; +} + +void CoefficientRefiner::Reset() { coeff = nullptr; } + + int Rebalancer::ApplyImpl(Mesh &mesh) { #ifdef MFEM_USE_MPI diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index 6bc2d449c8..9960ba2b87 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -307,6 +307,82 @@ public: virtual void Reset() { estimator.Reset(); } }; +/** @brief Refinement operator to control data oscillation. + + This class uses the given computes osc_K(f) := \| h \cdot (I - \Pi) f \|_K at + each element K. Here, \Pi is the L2-projection and \| \cdot \|_K is the + L2-norm, restricted to the element K. All elements satisfying the inequality + \code + osc_K(f) > threshold \cdot \| f \| / sqrt(n_el) , + \endcode + are refined. Here, threshold is a postive parameter, \| \cdot \| is the + L2-norm over the entire \Omega, and n_el is the number of elements in the + mesh. + + Note that if osc(f) = threshold \cdot \| f \| / sqrt(n_el) for each K, + then + \code + osc(f) = sqrt( sum_K osc_K^2(f)) = threshold \cdot \| f \| . + \endcode + This is the reason for the 1/sqrt(n_el) factor. */ +class CoefficientRefiner : public MeshOperator +{ +protected: + Coefficient * coeff = NULL; + double threshold = 1.0e-3; + int nc_limit = 1; + int nonconforming = -1; + int order; + Array irs; +// const IntegrationRule *irs[Geometry::NumGeom] = NULL; + GridFunction gf; + Array mesh_refinements; + // TODO: Save oscillation error + + /** @brief Apply the operator to the mesh once. + @return STOP if a stopping criterion is satisfied or no elements were + marked for refinement; REFINED + CONTINUE otherwise. */ + virtual int ApplyImpl(Mesh &mesh); + +public: + /// Constructor + CoefficientRefiner(int order_) : order(order_) { } + + /** @brief Apply the operator to the mesh max_it times or until tolerance + * achieved. + @return STOP if a stopping criterion is satisfied or no elements were + marked for refinement; REFINED + CONTINUE otherwise. */ + virtual int PreprocessMesh(Mesh &mesh, int max_it); + + bool PreprocessMesh(Mesh &mesh) + { + int max_it = 100; + return PreprocessMesh(mesh, max_it); + } + + /// Set the de-refinement threshold. The default value is zero. + void SetThreshold(double threshold_) { threshold = threshold_; } + + /// Set the de-refinement threshold. The default value is zero. + void SetCoefficient(Coefficient &coeff_) { coeff = &coeff_; } + + /// Reset the oscillation order + void SetOrder(double order_) { order = order_; } + + /** @brief Set the maximum ratio of refinement levels of adjacent elements + (0 = unlimited). */ + void SetNCLimit(int nc_limit_) + { + MFEM_ASSERT(nc_limit_ >= 0, "Invalid NC limit"); + nc_limit = nc_limit_; + } + + // Set a custom integration rule + void SetIntRule(const IntegrationRule *irs_[]) { irs.Assign(&irs_); } + + /// Reset + virtual void Reset(); +}; /** @brief ParMesh rebalancing operator. From d2b8fa35bb784f5b42ccd01e33322f50c4f3133d Mon Sep 17 00:00:00 2001 From: psocratis Date: Wed, 28 Jul 2021 20:30:59 -0700 Subject: [PATCH 042/198] Fixing setting intrule. Adding parallel example --- examples/osc.cpp | 10 +- examples/oscp.cpp | 204 ++++++++++++++++++++++++++++++++++++++++ mesh/mesh_operators.cpp | 11 ++- mesh/mesh_operators.hpp | 15 ++- 4 files changed, 229 insertions(+), 11 deletions(-) create mode 100644 examples/oscp.cpp diff --git a/examples/osc.cpp b/examples/osc.cpp index 8ebedfe356..f1579242fe 100644 --- a/examples/osc.cpp +++ b/examples/osc.cpp @@ -221,12 +221,18 @@ int main(int argc, char *argv[]) ThresholdRefiner refiner(estimator); refiner.SetTotalErrorFraction(0.7); - + const IntegrationRule *irs[Geometry::NumGeom]; + int order_quad = 2*order + 5; + for (int i=0; i < Geometry::NumGeom; ++i) + { + irs[i] = &(IntRules.Get(i, order_quad)); + } // 11.5. Preprocess mesh to control osc - double osc_tol = 1e-3; + double osc_tol = 1e-2; CoefficientRefiner coeffrefiner(0); coeffrefiner.SetCoefficient(*rhs); + // coeffrefiner.SetIntRule(irs); coeffrefiner.SetThreshold(osc_tol); coeffrefiner.SetNCLimit(0); coeffrefiner.PreprocessMesh(mesh); diff --git a/examples/oscp.cpp b/examples/oscp.cpp new file mode 100644 index 0000000000..4dff22f2be --- /dev/null +++ b/examples/oscp.cpp @@ -0,0 +1,204 @@ +// MFEM Example 6 +// +// Compile with: make ex6 +// +// Sample runs: ex6 -m ../data/square-disc.mesh -o 1 +// ex6 -m ../data/square-disc.mesh -o 2 +// ex6 -m ../data/square-disc-nurbs.mesh -o 2 +// ex6 -m ../data/star.mesh -o 3 +// ex6 -m ../data/escher.mesh -o 2 +// ex6 -m ../data/fichera.mesh -o 2 +// ex6 -m ../data/disc-nurbs.mesh -o 2 +// ex6 -m ../data/ball-nurbs.mesh +// ex6 -m ../data/pipe-nurbs.mesh +// ex6 -m ../data/star-surf.mesh -o 2 +// ex6 -m ../data/square-disc-surf.mesh -o 2 +// ex6 -m ../data/amr-quad.mesh +// ex6 -m ../data/inline-segment.mesh -o 1 -md 100 +// +// Device sample runs: +// ex6 -pa -d cuda +// ex6 -pa -d occa-cuda +// ex6 -pa -d raja-omp +// ex6 -pa -d ceed-cpu +// * ex6 -pa -d ceed-cuda +// ex6 -pa -d ceed-cuda:/gpu/cuda/shared +// +// Description: This is a version of Example 1 with a simple adaptive mesh +// refinement loop. The problem being solved is again the Laplace +// equation -Delta u = 1 with homogeneous Dirichlet boundary +// conditions. The problem is solved on a sequence of meshes which +// are locally refined in a conforming (triangles, tetrahedrons) +// or non-conforming (quadrilaterals, hexahedra) manner according +// to a simple ZZ error estimator. +// +// The example demonstrates MFEM's capability to work with both +// conforming and nonconforming refinements, in 2D and 3D, on +// linear, curved and surface meshes. Interpolation of functions +// from coarse to fine meshes, as well as persistent GLVis +// visualization are also illustrated. +// +// We recommend viewing Example 1 before viewing this example. + +#include "mfem.hpp" +#include +#include + +using namespace std; +using namespace mfem; + +double wavefront_exsol(const Vector &p) +{ + double x = p(0), y = p(1); + double alpha = 1000.0; + // double xc = -0.05, yc = -0.05; + double xc = 0.0, yc = 0.0; + double r0 = 0.7; + double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0)); + return atan(alpha * (r - r0)); +} + +void wavefront_exgrad(const Vector &p, Vector &grad) +{ + double x = p(0), y = p(1); + double alpha = 1000.0; + double xc = -0.05, yc = -0.05; + double r0 = 0.7; + grad(0) = 0.0; + grad(1) = 0.0; +} + +double wavefront_laplace(const Vector &p) +{ + double x = p(0), y = p(1); + double alpha = 1000.0; + // double xc = -0.05, yc = -0.05; + double xc = 0.0, yc = 0.0; + double r0 = 0.7; + double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0)); + double num = - ( alpha - pow(alpha,3) * (pow(r,2) - pow(r0,2)) ); + double denom = pow(r * ( pow(alpha,2) * pow(r0,2) + pow(alpha,2) * pow(r,2) \ + - 2 * pow(alpha,2) * r0 * r + 1.0 ),2); + denom = max(denom,1e-8); + // return num / denom; + if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1; } + if (p.Normlp(2.0) < 0.4 || p.Normlp(2.0) > 0.6) { return 2; } + return 0; +} + +double wavefront_laplace_alt(const Vector &p) +{ + double x = p(0), y = p(1); + double alpha = 1000.0; + double xc = -0.5, yc = -0.5; + double r0 = 0.7; + double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0)); + double num = - ( alpha - pow(alpha,3) * (pow(r,2) - pow(r0,2)) ); + double denom = pow(r * ( pow(alpha,2) * pow(r0,2) + pow(alpha,2) * pow(r,2) \ + - 2 * pow(alpha,2) * r0 * r + 1.0 ),2); + denom = max(denom,1e-8); + return num / denom; +} + +int main(int argc, char *argv[]) +{ + // 0. Initialize MPI. + int num_procs, myid; + MPI_Init(&argc, &argv); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + MPI_Comm_rank(MPI_COMM_WORLD, &myid); + + // 1. Parse command-line options. + const char *mesh_file = "../data/star.mesh"; + int order = 1; + const char *device_config = "cpu"; + int max_dofs = 50000; + bool visualization = true; + int nc_limit = 1; + + OptionsParser args(argc, argv); + args.AddOption(&mesh_file, "-m", "--mesh", + "Mesh file to use."); + args.AddOption(&order, "-o", "--order", + "Finite element order (polynomial degree)."); + args.AddOption(&max_dofs, "-md", "--max-dofs", + "Stop after reaching this many degrees of freedom."); + args.AddOption(&visualization, "-vis", "--visualization", "-no-vis", + "--no-visualization", + "Enable or disable GLVis visualization."); + args.Parse(); + if (!args.Good()) + { + if (myid == 0) + { + args.PrintUsage(cout); + } + MPI_Finalize(); + return 1; + } + if (myid == 0) + { + args.PrintOptions(cout); + } + + Mesh mesh(mesh_file, 1, 1); + int dim = mesh.Dimension(); + int sdim = mesh.SpaceDimension(); + mesh.EnsureNCMesh(); + ParMesh pmesh(MPI_COMM_WORLD, mesh); + mesh.Clear(); + + + ConstantCoefficient one(1.0); + ConstantCoefficient zero(0.0); + Coefficient * exsol = nullptr; + Coefficient * rhs = nullptr; + exsol = new FunctionCoefficient(wavefront_exsol); + rhs = new FunctionCoefficient(wavefront_laplace); + + + // 8. All boundary attributes will be used for essential (Dirichlet) BC. + MFEM_VERIFY(pmesh.bdr_attributes.Size() > 0, + "Boundary attributes required in the mesh."); + Array ess_bdr(pmesh.bdr_attributes.Max()); + ess_bdr = 1; + + // 9. Connect to GLVis. + char vishost[] = "localhost"; + int visport = 19916; + socketstream sol_sock; + if (visualization) + { + sol_sock.open(vishost, visport); + } + + const IntegrationRule *irs[Geometry::NumGeom]; + int order_quad = 2*order + 5; + for (int i=0; i < Geometry::NumGeom; ++i) + { + irs[i] = &(IntRules.Get(i, order_quad)); + } + + // 11.5. Preprocess mesh to control osc + double osc_tol = 1e-2; + CoefficientRefiner coeffrefiner(0); + coeffrefiner.SetCoefficient(*rhs); + // coeffrefiner.SetIntRule(irs); + coeffrefiner.SetThreshold(osc_tol); + coeffrefiner.SetNCLimit(0); + coeffrefiner.PreprocessMesh(pmesh); + + // Coefficient * rhs2 = nullptr; + // rhs2 = new FunctionCoefficient(wavefront_laplace_alt); + // coeffrefiner.SetCoefficient(*rhs2); + // coeffrefiner.PreprocessMesh(pmesh); + + + sol_sock.precision(8); + sol_sock << "parallel " << num_procs << " " << myid << "\n"; + sol_sock << "mesh\n" << pmesh << flush; + + + MPI_Finalize(); + return 0; +} diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index e9b34e4971..557a179ce7 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -170,14 +170,15 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) int dim = mesh.Dimension(); L2_FECollection l2fec(order, dim); FiniteElementSpace l2fes(&mesh, &l2fec); - if (!irs.Size()) + + if (!irs) { - irs.SetSize(Geometry::NumGeom); int order_quad = 2*order + 3; for (int i=0; i < Geometry::NumGeom; ++i) { - irs[i] = &(IntRules.Get(i, order_quad)); + ir[i] = &(IntRules.Get(i, order_quad)); } + irs = ir; } for (int i = 0; i < max_it; i++) @@ -186,11 +187,11 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) double NE = mesh.GetNE(); gf.SetSpace(&l2fes); gf.ProjectCoefficient(*coeff); - double av_norm_of_gf = ComputeLpNorm(2.0,*coeff,mesh,irs.GetData()) / sqrt(NE); + double av_norm_of_gf = ComputeLpNorm(2.0,*coeff,mesh,irs) / sqrt(NE); // Construct local L2-norms of (I - Pi) f Vector norm_of_fine_scale(NE); - gf.ComputeElementL2Errors(*coeff,norm_of_fine_scale,irs.GetData()); + gf.ComputeElementL2Errors(*coeff,norm_of_fine_scale,irs); // Define osc = h \cdot \| (I - Pi) f \| and select elements // for refinement based on threshold diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index 9960ba2b87..15c40a661c 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -333,8 +333,9 @@ protected: int nc_limit = 1; int nonconforming = -1; int order; - Array irs; -// const IntegrationRule *irs[Geometry::NumGeom] = NULL; + const IntegrationRule *ir[Geometry::NumGeom]; + const IntegrationRule ** irs = NULL; + // const IntegrationRule *irs[Geometry::NumGeom] = NULL; GridFunction gf; Array mesh_refinements; // TODO: Save oscillation error @@ -346,7 +347,10 @@ protected: public: /// Constructor - CoefficientRefiner(int order_) : order(order_) { } + CoefficientRefiner(int order_) : order(order_) + { + + } /** @brief Apply the operator to the mesh max_it times or until tolerance * achieved. @@ -378,7 +382,10 @@ public: } // Set a custom integration rule - void SetIntRule(const IntegrationRule *irs_[]) { irs.Assign(&irs_); } + void SetIntRule(const IntegrationRule *irs_[]) + { + irs = irs_; + } /// Reset virtual void Reset(); From 2bc6f6146a3a55673fb20c3988221eb7021d6c48 Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 29 Jul 2021 11:26:24 -0700 Subject: [PATCH 043/198] cleaned up files --- mesh/mesh_operators.cpp | 36 +++++++++++++++++++++++++----------- mesh/mesh_operators.hpp | 30 +++++++++++++----------------- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index 557a179ce7..a5ec9aa40e 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -159,7 +159,8 @@ int ThresholdDerefiner::ApplyImpl(Mesh &mesh) int CoefficientRefiner::ApplyImpl(Mesh &mesh) { - return PreprocessMesh(mesh, 1); + int max_it = 1; + return PreprocessMesh(mesh, max_it); } int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) @@ -171,40 +172,48 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) L2_FECollection l2fec(order, dim); FiniteElementSpace l2fes(&mesh, &l2fec); + // If custom integration rule has not been set, + // then use the default integration rule if (!irs) { int order_quad = 2*order + 3; for (int i=0; i < Geometry::NumGeom; ++i) { - ir[i] = &(IntRules.Get(i, order_quad)); + ir_default[i] = &(IntRules.Get(i, order_quad)); } - irs = ir; + irs = ir_default; } for (int i = 0; i < max_it; i++) { - // Get average L2-norm of f double NE = mesh.GetNE(); + + // Compute L2-norm of f + double norm_of_gf = ComputeLpNorm(2.0,*coeff,mesh,irs); + double av_norm_of_gf = norm_of_gf / sqrt(NE); + + // Compute local L2-norms of (I - Pi) f + Vector local_norms_of_fine_scale(NE); gf.SetSpace(&l2fes); gf.ProjectCoefficient(*coeff); - double av_norm_of_gf = ComputeLpNorm(2.0,*coeff,mesh,irs) / sqrt(NE); - - // Construct local L2-norms of (I - Pi) f - Vector norm_of_fine_scale(NE); - gf.ComputeElementL2Errors(*coeff,norm_of_fine_scale,irs); + gf.ComputeElementL2Errors(*coeff,local_norms_of_fine_scale,irs); // Define osc = h \cdot \| (I - Pi) f \| and select elements // for refinement based on threshold mesh_refinements.SetSize(0); + relative_osc = 0.0; + norm_of_gf += 1e-10; // to avoid dividing by zero for (int j = 0; j < NE; j++) { double h = mesh.GetElementSize(j); - double local_osc = h * norm_of_fine_scale(j); + double local_osc = h * local_norms_of_fine_scale(j); + relative_osc += pow(local_osc/norm_of_gf,2.0); if ( local_osc > threshold * av_norm_of_gf ) { mesh_refinements.Append(j); } } + osc = sqrt(osc); // Refine elements if (mesh_refinements.Size()) @@ -221,7 +230,12 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) return CONTINUE + REFINED; } -void CoefficientRefiner::Reset() { coeff = nullptr; } +void CoefficientRefiner::Reset() +{ + osc = 0.0; + coeff = NULL; + *irs = NULL; +} int Rebalancer::ApplyImpl(Mesh &mesh) diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index 15c40a661c..d6baa86806 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -328,17 +328,16 @@ public: class CoefficientRefiner : public MeshOperator { protected: - Coefficient * coeff = NULL; - double threshold = 1.0e-3; int nc_limit = 1; int nonconforming = -1; int order; - const IntegrationRule *ir[Geometry::NumGeom]; - const IntegrationRule ** irs = NULL; - // const IntegrationRule *irs[Geometry::NumGeom] = NULL; - GridFunction gf; + double threshold = 1.0e-3; + double relative_osc; Array mesh_refinements; - // TODO: Save oscillation error + Coefficient *coeff = NULL; + GridFunction gf; + const IntegrationRule *ir_default[Geometry::NumGeom]; + const IntegrationRule **irs = NULL; /** @brief Apply the operator to the mesh once. @return STOP if a stopping criterion is satisfied or no elements were @@ -347,10 +346,7 @@ protected: public: /// Constructor - CoefficientRefiner(int order_) : order(order_) - { - - } + CoefficientRefiner(int order_) : order(order_) { } /** @brief Apply the operator to the mesh max_it times or until tolerance * achieved. @@ -358,9 +354,9 @@ public: marked for refinement; REFINED + CONTINUE otherwise. */ virtual int PreprocessMesh(Mesh &mesh, int max_it); - bool PreprocessMesh(Mesh &mesh) + int PreprocessMesh(Mesh &mesh) { - int max_it = 100; + int max_it = 10; return PreprocessMesh(mesh, max_it); } @@ -382,10 +378,10 @@ public: } // Set a custom integration rule - void SetIntRule(const IntegrationRule *irs_[]) - { - irs = irs_; - } + void SetIntRule(const IntegrationRule *irs_[]) { irs = irs_; } + + // Return data oscillation value + double GetOsc() { return osc; } /// Reset virtual void Reset(); From 2e6583650f01c0f382f3bb67fe53710a5acafa4d Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 29 Jul 2021 14:34:42 -0700 Subject: [PATCH 044/198] debugging parallel implementation --- examples/osc.cpp | 23 +++++++++++-- mesh/mesh_operators.cpp | 73 ++++++++++++++++++++++++++++++++--------- mesh/mesh_operators.hpp | 6 ++-- 3 files changed, 81 insertions(+), 21 deletions(-) diff --git a/examples/osc.cpp b/examples/osc.cpp index f1579242fe..991ee09a02 100644 --- a/examples/osc.cpp +++ b/examples/osc.cpp @@ -100,6 +100,13 @@ double wavefront_laplace_alt(const Vector &p) return num / denom; } +double load3(const Vector &p) +{ + double x = p(0), y = p(1); + double r = sqrt(x*x + y*y) + 1.0e-8; + return r * sin(1.0/r); +} + int main(int argc, char *argv[]) { // 1. Parse command-line options. @@ -229,19 +236,31 @@ int main(int argc, char *argv[]) } // 11.5. Preprocess mesh to control osc - double osc_tol = 1e-2; + double osc; + double osc_tol = 1e-3; CoefficientRefiner coeffrefiner(0); coeffrefiner.SetCoefficient(*rhs); // coeffrefiner.SetIntRule(irs); coeffrefiner.SetThreshold(osc_tol); - coeffrefiner.SetNCLimit(0); + coeffrefiner.SetNCLimit(nc_limit); coeffrefiner.PreprocessMesh(mesh); + osc = coeffrefiner.GetOsc(); + std::cout << " osc(f1) = " << osc << std::endl; + Coefficient * rhs2 = nullptr; rhs2 = new FunctionCoefficient(wavefront_laplace_alt); coeffrefiner.SetCoefficient(*rhs2); coeffrefiner.PreprocessMesh(mesh); + osc = coeffrefiner.GetOsc(); + std::cout << " osc(f2) = " << osc << std::endl; + + // Coefficient * rhs3 = nullptr; + // rhs3 = new FunctionCoefficient(load3); + // coeffrefiner.SetCoefficient(*rhs3); + // coeffrefiner.PreprocessMesh(mesh); + sol_sock.precision(8); diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index a5ec9aa40e..bdad88d4e3 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -170,7 +170,29 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) int dim = mesh.Dimension(); L2_FECollection l2fec(order, dim); - FiniteElementSpace l2fes(&mesh, &l2fec); + FiniteElementSpace* l2fes = NULL; + + std::cout << " tag 0 " << std::endl; + +#ifdef MFEM_USE_MPI + ParMesh* pmesh = dynamic_cast(&mesh); + if (pmesh && pmesh->Nonconforming()) + { + l2fes = new ParFiniteElementSpace(&mesh, &l2fec); + gf = new ParGridFunction(l2fes); + } + else + { + l2fes = new FiniteElementSpace(&mesh, &l2fec); + gf = new GridFunction(l2fes); + } + // MPI_Comm comm = pmesh->GetComm(); +#else + l2fes = new FiniteElementSpace(&mesh, &l2fec); + gf = new GridFunction(l2fes); +#endif + + std::cout << " tag 1 " << std::endl; // If custom integration rule has not been set, // then use the default integration rule @@ -189,38 +211,57 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) double NE = mesh.GetNE(); // Compute L2-norm of f - double norm_of_gf = ComputeLpNorm(2.0,*coeff,mesh,irs); - double av_norm_of_gf = norm_of_gf / sqrt(NE); + double norm_of_coeff; +#ifdef MFEM_USE_MPI + norm_of_coeff = ComputeGlobalLpNorm(2.0,*coeff,mesh,irs); +#else + norm_of_coeff = ComputeLpNorm(2.0,*coeff,mesh,irs); +#endif + double av_norm_of_coeff = norm_of_coeff / sqrt(NE); - // Compute local L2-norms of (I - Pi) f - Vector local_norms_of_fine_scale(NE); - gf.SetSpace(&l2fes); - gf.ProjectCoefficient(*coeff); - gf.ComputeElementL2Errors(*coeff,local_norms_of_fine_scale,irs); + // Compute element-wise L2-norms of (I - Pi) f + Vector element_norms_of_fine_scale(NE); + gf->SetSpace(l2fes); + gf->ProjectCoefficient(*coeff); + gf->ComputeElementL2Errors(*coeff,element_norms_of_fine_scale,irs); // Define osc = h \cdot \| (I - Pi) f \| and select elements // for refinement based on threshold mesh_refinements.SetSize(0); relative_osc = 0.0; - norm_of_gf += 1e-10; // to avoid dividing by zero + double my_relative_osc = 0.0; + norm_of_coeff += 1e-10; // to avoid dividing by zero for (int j = 0; j < NE; j++) { double h = mesh.GetElementSize(j); - double local_osc = h * local_norms_of_fine_scale(j); - relative_osc += pow(local_osc/norm_of_gf,2.0); - if ( local_osc > threshold * av_norm_of_gf ) + double element_osc = h * element_norms_of_fine_scale(j); + my_relative_osc += pow(element_osc/norm_of_coeff,2.0); + if ( element_osc > threshold * av_norm_of_coeff ) { mesh_refinements.Append(j); } } - osc = sqrt(osc); +#ifdef MFEM_USE_MPI + ParMesh* pmesh = dynamic_cast(&mesh); + if (pmesh) + { + MPI_Allreduce(&my_relative_osc, &relative_osc, 1, MPI_DOUBLE, MPI_SUM, pmesh->GetComm()); + relative_osc = sqrt(relative_osc); + } + else + { + relative_osc = sqrt(my_relative_osc); + } +#else + relative_osc = sqrt(my_relative_osc); +#endif // Refine elements if (mesh_refinements.Size()) { mesh.GeneralRefinement(mesh_refinements, nonconforming, nc_limit); - l2fes.Update(false); - gf.Update(); + l2fes->StealNURBSext(false); + gf->Update(); } else { @@ -232,7 +273,7 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) void CoefficientRefiner::Reset() { - osc = 0.0; + relative_osc = 0.0; coeff = NULL; *irs = NULL; } diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index d6baa86806..3553698bf3 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -332,10 +332,10 @@ protected: int nonconforming = -1; int order; double threshold = 1.0e-3; - double relative_osc; + double relative_osc = 0.0; Array mesh_refinements; Coefficient *coeff = NULL; - GridFunction gf; + GridFunction *gf; const IntegrationRule *ir_default[Geometry::NumGeom]; const IntegrationRule **irs = NULL; @@ -381,7 +381,7 @@ public: void SetIntRule(const IntegrationRule *irs_[]) { irs = irs_; } // Return data oscillation value - double GetOsc() { return osc; } + double GetOsc() { return relative_osc; } /// Reset virtual void Reset(); From f47244859a02da46a1a96cbcdb016eedfcab32e5 Mon Sep 17 00:00:00 2001 From: psocratis Date: Thu, 29 Jul 2021 15:01:31 -0700 Subject: [PATCH 045/198] fixing parallel implementation --- examples/oscp.cpp | 12 ++++++------ mesh/mesh_operators.cpp | 32 +++++++++++++++++--------------- mesh/mesh_operators.hpp | 2 +- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/examples/oscp.cpp b/examples/oscp.cpp index 4dff22f2be..c4a9efe0b4 100644 --- a/examples/oscp.cpp +++ b/examples/oscp.cpp @@ -180,18 +180,18 @@ int main(int argc, char *argv[]) } // 11.5. Preprocess mesh to control osc - double osc_tol = 1e-2; + double osc_tol = 1e-3; CoefficientRefiner coeffrefiner(0); coeffrefiner.SetCoefficient(*rhs); - // coeffrefiner.SetIntRule(irs); + coeffrefiner.SetIntRule(irs); coeffrefiner.SetThreshold(osc_tol); coeffrefiner.SetNCLimit(0); coeffrefiner.PreprocessMesh(pmesh); - // Coefficient * rhs2 = nullptr; - // rhs2 = new FunctionCoefficient(wavefront_laplace_alt); - // coeffrefiner.SetCoefficient(*rhs2); - // coeffrefiner.PreprocessMesh(pmesh); + Coefficient * rhs2 = nullptr; + rhs2 = new FunctionCoefficient(wavefront_laplace_alt); + coeffrefiner.SetCoefficient(*rhs2); + coeffrefiner.PreprocessMesh(pmesh); sol_sock.precision(8); diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index bdad88d4e3..46eed3ec57 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -172,14 +172,12 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) L2_FECollection l2fec(order, dim); FiniteElementSpace* l2fes = NULL; - std::cout << " tag 0 " << std::endl; - #ifdef MFEM_USE_MPI ParMesh* pmesh = dynamic_cast(&mesh); if (pmesh && pmesh->Nonconforming()) { - l2fes = new ParFiniteElementSpace(&mesh, &l2fec); - gf = new ParGridFunction(l2fes); + l2fes = new ParFiniteElementSpace(pmesh, &l2fec); + gf = new ParGridFunction(dynamic_cast(l2fes)); } else { @@ -192,8 +190,6 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) gf = new GridFunction(l2fes); #endif - std::cout << " tag 1 " << std::endl; - // If custom integration rule has not been set, // then use the default integration rule if (!irs) @@ -213,7 +209,8 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) // Compute L2-norm of f double norm_of_coeff; #ifdef MFEM_USE_MPI - norm_of_coeff = ComputeGlobalLpNorm(2.0,*coeff,mesh,irs); + norm_of_coeff = ComputeGlobalLpNorm(2.0,*coeff,*dynamic_cast(&mesh), + irs); #else norm_of_coeff = ComputeLpNorm(2.0,*coeff,mesh,irs); #endif @@ -245,7 +242,8 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) ParMesh* pmesh = dynamic_cast(&mesh); if (pmesh) { - MPI_Allreduce(&my_relative_osc, &relative_osc, 1, MPI_DOUBLE, MPI_SUM, pmesh->GetComm()); + MPI_Allreduce(&my_relative_osc, &relative_osc, 1, MPI_DOUBLE, MPI_SUM, + pmesh->GetComm()); relative_osc = sqrt(relative_osc); } else @@ -257,18 +255,22 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) #endif // Refine elements - if (mesh_refinements.Size()) - { - mesh.GeneralRefinement(mesh_refinements, nonconforming, nc_limit); - l2fes->StealNURBSext(false); - gf->Update(); - } - else + int num_marked_elements = mesh.ReduceInt(mesh_refinements.Size()); + if (num_marked_elements == 0) { + delete l2fes; + delete gf; return STOP; } + + mesh.GeneralRefinement(mesh_refinements, nonconforming, nc_limit); + l2fes->Update(false); + gf->Update(); } + delete l2fes; + delete gf; return CONTINUE + REFINED; + } void CoefficientRefiner::Reset() diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index 3553698bf3..ce12acf0ff 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -379,7 +379,7 @@ public: // Set a custom integration rule void SetIntRule(const IntegrationRule *irs_[]) { irs = irs_; } - + // Return data oscillation value double GetOsc() { return relative_osc; } From 54676a17c27aff1eec64c9ebd595ef33b71591cd Mon Sep 17 00:00:00 2001 From: psocratis Date: Thu, 29 Jul 2021 18:23:36 -0700 Subject: [PATCH 046/198] Fixed bug for serial builds. Fixed comments --- examples/oscp.cpp | 12 ++++++---- mesh/mesh_operators.cpp | 50 ++++++++++++++++++++--------------------- mesh/mesh_operators.hpp | 14 ++++++------ 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/examples/oscp.cpp b/examples/oscp.cpp index c4a9efe0b4..5f25d7381f 100644 --- a/examples/oscp.cpp +++ b/examples/oscp.cpp @@ -188,10 +188,14 @@ int main(int argc, char *argv[]) coeffrefiner.SetNCLimit(0); coeffrefiner.PreprocessMesh(pmesh); - Coefficient * rhs2 = nullptr; - rhs2 = new FunctionCoefficient(wavefront_laplace_alt); - coeffrefiner.SetCoefficient(*rhs2); - coeffrefiner.PreprocessMesh(pmesh); + // Coefficient * rhs2 = nullptr; + // rhs2 = new FunctionCoefficient(wavefront_laplace_alt); + // coeffrefiner.SetCoefficient(*rhs2); + // coeffrefiner.PreprocessMesh(pmesh); + + + cout << "Number of Elements " << pmesh.GetGlobalNE() << endl; + cout << "Osc error " << coeffrefiner.GetOsc() << endl; sol_sock.precision(8); diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index 46eed3ec57..c24158552b 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -172,23 +172,22 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) L2_FECollection l2fec(order, dim); FiniteElementSpace* l2fes = NULL; + bool par = false; + #ifdef MFEM_USE_MPI ParMesh* pmesh = dynamic_cast(&mesh); if (pmesh && pmesh->Nonconforming()) { + par = true; l2fes = new ParFiniteElementSpace(pmesh, &l2fec); - gf = new ParGridFunction(dynamic_cast(l2fes)); + gf = new ParGridFunction(static_cast(l2fes)); } - else +#endif + if (!par) { l2fes = new FiniteElementSpace(&mesh, &l2fec); gf = new GridFunction(l2fes); } - // MPI_Comm comm = pmesh->GetComm(); -#else - l2fes = new FiniteElementSpace(&mesh, &l2fec); - gf = new GridFunction(l2fes); -#endif // If custom integration rule has not been set, // then use the default integration rule @@ -204,17 +203,24 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) for (int i = 0; i < max_it; i++) { - double NE = mesh.GetNE(); + int NE = mesh.GetNE(); + int globalNE = NE; // Compute L2-norm of f - double norm_of_coeff; + double norm_of_coeff = 0.0; + if (par) + { #ifdef MFEM_USE_MPI - norm_of_coeff = ComputeGlobalLpNorm(2.0,*coeff,*dynamic_cast(&mesh), - irs); -#else - norm_of_coeff = ComputeLpNorm(2.0,*coeff,mesh,irs); + norm_of_coeff = ComputeGlobalLpNorm(2.0,*coeff,*pmesh,irs); + globalNE = pmesh->GetGlobalNE(); #endif - double av_norm_of_coeff = norm_of_coeff / sqrt(NE); + } + else + { + norm_of_coeff = ComputeLpNorm(2.0,*coeff,mesh,irs); + } + + double av_norm_of_coeff = norm_of_coeff / sqrt(globalNE); // Compute element-wise L2-norms of (I - Pi) f Vector element_norms_of_fine_scale(NE); @@ -226,33 +232,25 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) // for refinement based on threshold mesh_refinements.SetSize(0); relative_osc = 0.0; - double my_relative_osc = 0.0; norm_of_coeff += 1e-10; // to avoid dividing by zero for (int j = 0; j < NE; j++) { double h = mesh.GetElementSize(j); double element_osc = h * element_norms_of_fine_scale(j); - my_relative_osc += pow(element_osc/norm_of_coeff,2.0); + relative_osc += pow(element_osc/norm_of_coeff,2.0); if ( element_osc > threshold * av_norm_of_coeff ) { mesh_refinements.Append(j); } } #ifdef MFEM_USE_MPI - ParMesh* pmesh = dynamic_cast(&mesh); - if (pmesh) + if (par) { - MPI_Allreduce(&my_relative_osc, &relative_osc, 1, MPI_DOUBLE, MPI_SUM, + MPI_Allreduce(MPI_IN_PLACE, &relative_osc, 1, MPI_DOUBLE, MPI_SUM, pmesh->GetComm()); - relative_osc = sqrt(relative_osc); } - else - { - relative_osc = sqrt(my_relative_osc); - } -#else - relative_osc = sqrt(my_relative_osc); #endif + relative_osc = sqrt(relative_osc); // Refine elements int num_marked_elements = mesh.ReduceInt(mesh_refinements.Size()); diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index ce12acf0ff..01bc6c69cb 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -309,20 +309,20 @@ public: /** @brief Refinement operator to control data oscillation. - This class uses the given computes osc_K(f) := \| h \cdot (I - \Pi) f \|_K at - each element K. Here, \Pi is the L2-projection and \| \cdot \|_K is the + This class uses the given computes osc_K(f) := || h ⋅ (I - Π) f ||_K at + each element K. Here, Π is the L2-projection and ||⋅||_K is the L2-norm, restricted to the element K. All elements satisfying the inequality \code - osc_K(f) > threshold \cdot \| f \| / sqrt(n_el) , + osc_K(f) > threshold ⋅ ||f|| / sqrt(n_el), \endcode - are refined. Here, threshold is a postive parameter, \| \cdot \| is the - L2-norm over the entire \Omega, and n_el is the number of elements in the + are refined. Here, threshold is a postive parameter, ||⋅|| is the + L2-norm over the entire Ω, and n_el is the number of elements in the mesh. - Note that if osc(f) = threshold \cdot \| f \| / sqrt(n_el) for each K, + Note that if osc(f) = threshold ⋅ ||f|| / sqrt(n_el) for each K, then \code - osc(f) = sqrt( sum_K osc_K^2(f)) = threshold \cdot \| f \| . + osc(f) = sqrt( sum_K osc_K^2(f)) = threshold ⋅ ||f||. \endcode This is the reason for the 1/sqrt(n_el) factor. */ class CoefficientRefiner : public MeshOperator From 8a61b3e27bf622e0564644737ef6446c26d36fae Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 29 Jul 2021 20:56:01 -0700 Subject: [PATCH 047/198] adding extra features --- examples/osc.cpp | 2 +- examples/oscp.cpp | 179 ++++++++++++++++++---------------------- mesh/mesh_operators.cpp | 48 ++++++++--- mesh/mesh_operators.hpp | 23 ++++-- 4 files changed, 134 insertions(+), 118 deletions(-) diff --git a/examples/osc.cpp b/examples/osc.cpp index 991ee09a02..77e67eb92d 100644 --- a/examples/osc.cpp +++ b/examples/osc.cpp @@ -238,7 +238,7 @@ int main(int argc, char *argv[]) // 11.5. Preprocess mesh to control osc double osc; double osc_tol = 1e-3; - CoefficientRefiner coeffrefiner(0); + CoefficientRefiner coeffrefiner(order); coeffrefiner.SetCoefficient(*rhs); // coeffrefiner.SetIntRule(irs); coeffrefiner.SetThreshold(osc_tol); diff --git a/examples/oscp.cpp b/examples/oscp.cpp index 5f25d7381f..e0375caf92 100644 --- a/examples/oscp.cpp +++ b/examples/oscp.cpp @@ -1,44 +1,45 @@ -// MFEM Example 6 +// MFEM Example 30 - Parallel Version // -// Compile with: make ex6 +// Compile with: make ex30p // -// Sample runs: ex6 -m ../data/square-disc.mesh -o 1 -// ex6 -m ../data/square-disc.mesh -o 2 -// ex6 -m ../data/square-disc-nurbs.mesh -o 2 -// ex6 -m ../data/star.mesh -o 3 -// ex6 -m ../data/escher.mesh -o 2 -// ex6 -m ../data/fichera.mesh -o 2 -// ex6 -m ../data/disc-nurbs.mesh -o 2 -// ex6 -m ../data/ball-nurbs.mesh -// ex6 -m ../data/pipe-nurbs.mesh -// ex6 -m ../data/star-surf.mesh -o 2 -// ex6 -m ../data/square-disc-surf.mesh -o 2 -// ex6 -m ../data/amr-quad.mesh -// ex6 -m ../data/inline-segment.mesh -o 1 -md 100 +// Sample runs: mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 1 +// mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 2 +// mpirun -np 4 ex30p -m ../data/square-disc-nurbs.mesh -o 2 +// mpirun -np 4 ex30p -m ../data/star.mesh -o 3 +// mpirun -np 4 ex30p -m ../data/escher.mesh -o 2 +// mpirun -np 4 ex30p -m ../data/fichera.mesh -o 2 +// mpirun -np 4 ex30p -m ../data/disc-nurbs.mesh -o 2 +// mpirun -np 4 ex30p -m ../data/ball-nurbs.mesh +// mpirun -np 4 ex30p -m ../data/pipe-nurbs.mesh +// mpirun -np 4 ex30p -m ../data/star-surf.mesh -o 2 +// mpirun -np 4 ex30p -m ../data/square-disc-surf.mesh -o 2 +// mpirun -np 4 ex30p -m ../data/amr-quad.mesh +// mpirun -np 4 ex30p -m ../data/inline-segment.mesh -o 1 -md 100 // // Device sample runs: -// ex6 -pa -d cuda -// ex6 -pa -d occa-cuda -// ex6 -pa -d raja-omp -// ex6 -pa -d ceed-cpu -// * ex6 -pa -d ceed-cuda -// ex6 -pa -d ceed-cuda:/gpu/cuda/shared +// mpirun -np 4 ex30p -pa -d cuda +// mpirun -np 4 ex30p -pa -d occa-cuda +// mpirun -np 4 ex30p -pa -d raja-omp +// mpirun -np 4 ex30p -pa -d ceed-cpu +// * mpirun -np 4 ex30p -pa -d ceed-cuda +// mpirun -np 4 ex30p -pa -d ceed-cuda:/gpu/cuda/shared // -// Description: This is a version of Example 1 with a simple adaptive mesh -// refinement loop. The problem being solved is again the Laplace -// equation -Delta u = 1 with homogeneous Dirichlet boundary -// conditions. The problem is solved on a sequence of meshes which -// are locally refined in a conforming (triangles, tetrahedrons) -// or non-conforming (quadrilaterals, hexahedra) manner according -// to a simple ZZ error estimator. +// Description: This is an example of adaptive mesh refinement preprocessing +// which lowers the data oscillation [1] to a user-defined +// relative threshold. There is no PDE being solved. // -// The example demonstrates MFEM's capability to work with both -// conforming and nonconforming refinements, in 2D and 3D, on -// linear, curved and surface meshes. Interpolation of functions -// from coarse to fine meshes, as well as persistent GLVis -// visualization are also illustrated. +// MFEM's capability to work with both conforming and +// nonconforming meshes is demonstrated in example 6. In some +// problems, the material data or loading data is not sufficiently +// resolved on the initial mesh. This missing fine scale data +// reduces the accuracy of the solution as well as the accuracy +// of some local error estimators. By preprocessing the mesh +// before the solving the PDE, many issues can be avoided. // -// We recommend viewing Example 1 before viewing this example. +// [1] Morin, P., Nochetto, R. H., & Siebert, K. G. (2000). +// Data oscillation and convergence of adaptive FEM. SIAM +// Journal on Numerical Analysis, 38(2), 466-488. + #include "mfem.hpp" #include @@ -47,46 +48,21 @@ using namespace std; using namespace mfem; -double wavefront_exsol(const Vector &p) + +double function0(const Vector &p) { double x = p(0), y = p(1); - double alpha = 1000.0; - // double xc = -0.05, yc = -0.05; - double xc = 0.0, yc = 0.0; - double r0 = 0.7; - double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0)); - return atan(alpha * (r - r0)); + return 1.0 + x + y; } -void wavefront_exgrad(const Vector &p, Vector &grad) +double function1(const Vector &p) { - double x = p(0), y = p(1); - double alpha = 1000.0; - double xc = -0.05, yc = -0.05; - double r0 = 0.7; - grad(0) = 0.0; - grad(1) = 0.0; -} - -double wavefront_laplace(const Vector &p) -{ - double x = p(0), y = p(1); - double alpha = 1000.0; - // double xc = -0.05, yc = -0.05; - double xc = 0.0, yc = 0.0; - double r0 = 0.7; - double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0)); - double num = - ( alpha - pow(alpha,3) * (pow(r,2) - pow(r0,2)) ); - double denom = pow(r * ( pow(alpha,2) * pow(r0,2) + pow(alpha,2) * pow(r,2) \ - - 2 * pow(alpha,2) * r0 * r + 1.0 ),2); - denom = max(denom,1e-8); - // return num / denom; if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1; } if (p.Normlp(2.0) < 0.4 || p.Normlp(2.0) > 0.6) { return 2; } return 0; } -double wavefront_laplace_alt(const Vector &p) +double function2(const Vector &p) { double x = p(0), y = p(1); double alpha = 1000.0; @@ -111,21 +87,26 @@ int main(int argc, char *argv[]) // 1. Parse command-line options. const char *mesh_file = "../data/star.mesh"; int order = 1; - const char *device_config = "cpu"; - int max_dofs = 50000; - bool visualization = true; int nc_limit = 1; + int max_elems = 1e6; + bool visualization = true; + double osc_threshold = 1e-3; OptionsParser args(argc, argv); args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use."); args.AddOption(&order, "-o", "--order", "Finite element order (polynomial degree)."); - args.AddOption(&max_dofs, "-md", "--max-dofs", - "Stop after reaching this many degrees of freedom."); + args.AddOption(&nc_limit, "-l", "--nc-limit", + "Maximum level of hanging nodes."); + args.AddOption(&max_elems, "-me", "--max-elems", + "Stop after reaching this many elements."); args.AddOption(&visualization, "-vis", "--visualization", "-no-vis", "--no-visualization", "Enable or disable GLVis visualization."); + args.AddOption(&osc_threshold, "-e", "--error", + "relative data oscillation threshold"); + args.Parse(); if (!args.Good()) { @@ -142,28 +123,17 @@ int main(int argc, char *argv[]) } Mesh mesh(mesh_file, 1, 1); - int dim = mesh.Dimension(); - int sdim = mesh.SpaceDimension(); mesh.EnsureNCMesh(); ParMesh pmesh(MPI_COMM_WORLD, mesh); mesh.Clear(); + // 2. Define functions and refiner. + FunctionCoefficient coeff0(function0); + FunctionCoefficient coeff1(function1); + FunctionCoefficient coeff2(function2); + CoefficientRefiner coeffrefiner(order); - ConstantCoefficient one(1.0); - ConstantCoefficient zero(0.0); - Coefficient * exsol = nullptr; - Coefficient * rhs = nullptr; - exsol = new FunctionCoefficient(wavefront_exsol); - rhs = new FunctionCoefficient(wavefront_laplace); - - - // 8. All boundary attributes will be used for essential (Dirichlet) BC. - MFEM_VERIFY(pmesh.bdr_attributes.Size() > 0, - "Boundary attributes required in the mesh."); - Array ess_bdr(pmesh.bdr_attributes.Max()); - ess_bdr = 1; - - // 9. Connect to GLVis. + // 2. Connect to GLVis. char vishost[] = "localhost"; int visport = 19916; socketstream sol_sock; @@ -179,24 +149,35 @@ int main(int argc, char *argv[]) irs[i] = &(IntRules.Get(i, order_quad)); } - // 11.5. Preprocess mesh to control osc - double osc_tol = 1e-3; - CoefficientRefiner coeffrefiner(0); - coeffrefiner.SetCoefficient(*rhs); - coeffrefiner.SetIntRule(irs); - coeffrefiner.SetThreshold(osc_tol); - coeffrefiner.SetNCLimit(0); + // 3. Preprocess mesh to control osc + coeffrefiner.SetCoefficient(coeff0); coeffrefiner.PreprocessMesh(pmesh); - // Coefficient * rhs2 = nullptr; - // rhs2 = new FunctionCoefficient(wavefront_laplace_alt); - // coeffrefiner.SetCoefficient(*rhs2); - // coeffrefiner.PreprocessMesh(pmesh); + mfem::out << "\n"; + mfem::out << "Function 0 (affine) \n"; + mfem::out << "Number of Elements " << pmesh.GetGlobalNE() << "\n"; + mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n"; + mfem::out << "\n"; + // coeffrefiner.SetIntRule(irs); + coeffrefiner.SetMaxElements( (long) max_elems); + coeffrefiner.SetThreshold(osc_threshold); + coeffrefiner.SetNCLimit(nc_limit); - cout << "Number of Elements " << pmesh.GetGlobalNE() << endl; - cout << "Osc error " << coeffrefiner.GetOsc() << endl; + coeffrefiner.SetCoefficient(coeff1); + coeffrefiner.PreprocessMesh(pmesh); + mfem::out << "Function 1 (discontinuous) \n"; + mfem::out << "Number of Elements " << pmesh.GetGlobalNE() << "\n"; + mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n"; + mfem::out << "\n"; + + coeffrefiner.SetCoefficient(coeff2); + coeffrefiner.PreprocessMesh(pmesh); + + mfem::out << "Function 2 (singular) \n"; + mfem::out << "Number of Elements " << pmesh.GetGlobalNE() << "\n"; + mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n"; sol_sock.precision(8); sol_sock << "parallel " << num_procs << " " << myid << "\n"; diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index c24158552b..47409e9da0 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -203,8 +203,28 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) for (int i = 0; i < max_it; i++) { + + // Compute number of elements. int NE = mesh.GetNE(); - int globalNE = NE; + int globalNE; + if (par) + { +#ifdef MFEM_USE_MPI + globalNE = pmesh->GetGlobalNE(); +#endif + } + else + { + globalNE = NE; + } + + // Exit if the maximum number of elements has been reached. + if (globalNE > max_elements) + { + delete l2fes; + delete gf; + return STOP; + } // Compute L2-norm of f double norm_of_coeff = 0.0; @@ -212,7 +232,6 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) { #ifdef MFEM_USE_MPI norm_of_coeff = ComputeGlobalLpNorm(2.0,*coeff,*pmesh,irs); - globalNE = pmesh->GetGlobalNE(); #endif } else @@ -220,24 +239,24 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) norm_of_coeff = ComputeLpNorm(2.0,*coeff,mesh,irs); } + // Compute average L2-norm of f double av_norm_of_coeff = norm_of_coeff / sqrt(globalNE); - // Compute element-wise L2-norms of (I - Pi) f + // Compute element-wise L2-norms of (I - Π) f Vector element_norms_of_fine_scale(NE); gf->SetSpace(l2fes); gf->ProjectCoefficient(*coeff); gf->ComputeElementL2Errors(*coeff,element_norms_of_fine_scale,irs); - // Define osc = h \cdot \| (I - Pi) f \| and select elements - // for refinement based on threshold - mesh_refinements.SetSize(0); + // Define osc_K(f) := || h ⋅ (I - Π) f ||_K and select elements + // for refinement based on threshold. Also record relative osc(f). relative_osc = 0.0; - norm_of_coeff += 1e-10; // to avoid dividing by zero + mesh_refinements.SetSize(0); for (int j = 0; j < NE; j++) { double h = mesh.GetElementSize(j); double element_osc = h * element_norms_of_fine_scale(j); - relative_osc += pow(element_osc/norm_of_coeff,2.0); + relative_osc += element_osc*element_osc; if ( element_osc > threshold * av_norm_of_coeff ) { mesh_refinements.Append(j); @@ -250,9 +269,9 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) pmesh->GetComm()); } #endif - relative_osc = sqrt(relative_osc); + relative_osc = sqrt(relative_osc)/(norm_of_coeff + 1e-10); - // Refine elements + // Exit if there are no elements to refine. int num_marked_elements = mesh.ReduceInt(mesh_refinements.Size()); if (num_marked_elements == 0) { @@ -261,9 +280,18 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) return STOP; } + // Refine elements mesh.GeneralRefinement(mesh_refinements, nonconforming, nc_limit); l2fes->Update(false); gf->Update(); + + // Exit if the global threshold has been reached. + if (relative_osc < threshold) + { + delete l2fes; + delete gf; + return STOP; + } } delete l2fes; delete gf; diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index 01bc6c69cb..928498750f 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -316,11 +316,10 @@ public: osc_K(f) > threshold ⋅ ||f|| / sqrt(n_el), \endcode are refined. Here, threshold is a postive parameter, ||⋅|| is the - L2-norm over the entire Ω, and n_el is the number of elements in the + L2-norm over the entire domain Ω, and n_el is the number of elements in the mesh. - Note that if osc(f) = threshold ⋅ ||f|| / sqrt(n_el) for each K, - then + Note that if osc(f) = threshold ⋅ ||f|| / sqrt(n_el) for each K, then \code osc(f) = sqrt( sum_K osc_K^2(f)) = threshold ⋅ ||f||. \endcode @@ -331,7 +330,8 @@ protected: int nc_limit = 1; int nonconforming = -1; int order; - double threshold = 1.0e-3; + long max_elements = std::numeric_limits::max(); + double threshold = 1.0e-2; double relative_osc = 0.0; Array mesh_refinements; Coefficient *coeff = NULL; @@ -360,17 +360,24 @@ public: return PreprocessMesh(mesh, max_it); } - /// Set the de-refinement threshold. The default value is zero. + /// Set the refinement threshold. The default value is 1.0e-3. void SetThreshold(double threshold_) { threshold = threshold_; } - /// Set the de-refinement threshold. The default value is zero. + /** @brief Set the maximum number of elements stopping criterion: stop when + the input mesh has num_elements >= max_elem. The default value is + LONG_MAX. */ + void SetMaxElements(int max_elements_) { max_elements = max_elements_; } + + /// Set the function f void SetCoefficient(Coefficient &coeff_) { coeff = &coeff_; } /// Reset the oscillation order void SetOrder(double order_) { order = order_; } /** @brief Set the maximum ratio of refinement levels of adjacent elements - (0 = unlimited). */ + (0 = unlimited). The default value is 1, which helps ensure appropriate + refinements in pathological situations where the default quadrature + order is too low. */ void SetNCLimit(int nc_limit_) { MFEM_ASSERT(nc_limit_ >= 0, "Invalid NC limit"); @@ -380,7 +387,7 @@ public: // Set a custom integration rule void SetIntRule(const IntegrationRule *irs_[]) { irs = irs_; } - // Return data oscillation value + // Return the value of the global relative data oscillation double GetOsc() { return relative_osc; } /// Reset From 8877f99da86db2b435c66b65297a346bed8c5c9c Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 30 Jul 2021 13:03:48 -0700 Subject: [PATCH 048/198] Fixed Mesh::GetElementSize(), which did not work for embedded meshes --- mesh/mesh.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesh/mesh.cpp b/mesh/mesh.cpp index b2c14a44c0..54714e5e5a 100644 --- a/mesh/mesh.cpp +++ b/mesh/mesh.cpp @@ -75,7 +75,7 @@ void Mesh::GetElementCenter(int i, Vector ¢er) double Mesh::GetElementSize(ElementTransformation *T, int type) { - DenseMatrix J(Dim); + DenseMatrix J(spaceDim,Dim); Geometry::Type geom = T->GetGeometryType(); T->SetIntPoint(&Geometries.GetCenter(geom)); @@ -83,7 +83,7 @@ double Mesh::GetElementSize(ElementTransformation *T, int type) if (type == 0) { - return pow(fabs(J.Det()), 1./Dim); + return pow(fabs(J.Weight()), 1./double(Dim)); } else if (type == 1) { From 865b2facc277482fe75654e352eb829df4c9454f Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 30 Jul 2021 13:04:43 -0700 Subject: [PATCH 049/198] verification with embedded meshes --- examples/osc.cpp | 365 +++++++++------------------------------- examples/oscp.cpp | 35 ++-- mesh/mesh_operators.cpp | 6 +- mesh/mesh_operators.hpp | 2 +- 4 files changed, 105 insertions(+), 303 deletions(-) diff --git a/examples/osc.cpp b/examples/osc.cpp index 77e67eb92d..f8f95e01fe 100644 --- a/examples/osc.cpp +++ b/examples/osc.cpp @@ -1,44 +1,36 @@ -// MFEM Example 6 +// MFEM Example 30 // -// Compile with: make ex6 +// Compile with: make ex30 // -// Sample runs: ex6 -m ../data/square-disc.mesh -o 1 -// ex6 -m ../data/square-disc.mesh -o 2 -// ex6 -m ../data/square-disc-nurbs.mesh -o 2 -// ex6 -m ../data/star.mesh -o 3 -// ex6 -m ../data/escher.mesh -o 2 -// ex6 -m ../data/fichera.mesh -o 2 -// ex6 -m ../data/disc-nurbs.mesh -o 2 -// ex6 -m ../data/ball-nurbs.mesh -// ex6 -m ../data/pipe-nurbs.mesh -// ex6 -m ../data/star-surf.mesh -o 2 -// ex6 -m ../data/square-disc-surf.mesh -o 2 -// ex6 -m ../data/amr-quad.mesh -// ex6 -m ../data/inline-segment.mesh -o 1 -md 100 +// Sample runs: ex30 -m ../data/square-disc.mesh -o 1 +// ex30 -m ../data/square-disc.mesh -o 2 +// ex30 -m ../data/square-disc-nurbs.mesh -o 2 ??? +// ex30 -m ../data/star.mesh -o 3 +// ex30 -m ../data/escher.mesh -o 2 ??? +// ex30 -m ../data/fichera.mesh -o 2 !!! +// ex30 -m ../data/disc-nurbs.mesh -o 2 +// ex30 -m ../data/ball-nurbs.mesh +// ex30 -m ../data/pipe-nurbs.mesh +// ex30 -m ../data/star-surf.mesh -o 2 ??? +// ex30 -m ../data/square-disc-surf.mesh -o 2 ??? +// ex30 -m ../data/amr-quad.mesh // -// Device sample runs: -// ex6 -pa -d cuda -// ex6 -pa -d occa-cuda -// ex6 -pa -d raja-omp -// ex6 -pa -d ceed-cpu -// * ex6 -pa -d ceed-cuda -// ex6 -pa -d ceed-cuda:/gpu/cuda/shared +// Description: This is an example of adaptive mesh refinement preprocessing +// which lowers the data oscillation [1] to a user-defined +// relative threshold. There is no PDE being solved. // -// Description: This is a version of Example 1 with a simple adaptive mesh -// refinement loop. The problem being solved is again the Laplace -// equation -Delta u = 1 with homogeneous Dirichlet boundary -// conditions. The problem is solved on a sequence of meshes which -// are locally refined in a conforming (triangles, tetrahedrons) -// or non-conforming (quadrilaterals, hexahedra) manner according -// to a simple ZZ error estimator. +// MFEM's capability to work with both conforming and +// nonconforming meshes is demonstrated in example 6. In some +// problems, the material data or loading data is not sufficiently +// resolved on the initial mesh. This missing fine scale data +// reduces the accuracy of the solution as well as the accuracy +// of some local error estimators. By preprocessing the mesh +// before the solving the PDE, many issues can be avoided. // -// The example demonstrates MFEM's capability to work with both -// conforming and nonconforming refinements, in 2D and 3D, on -// linear, curved and surface meshes. Interpolation of functions -// from coarse to fine meshes, as well as persistent GLVis -// visualization are also illustrated. -// -// We recommend viewing Example 1 before viewing this example. +// [1] Morin, P., Nochetto, R. H., & Siebert, K. G. (2000). +// Data oscillation and convergence of adaptive FEM. SIAM +// Journal on Numerical Analysis, 38(2), 466-488. + #include "mfem.hpp" #include @@ -47,50 +39,25 @@ using namespace std; using namespace mfem; -double wavefront_exsol(const Vector &p) + +double function0(const Vector &p) { double x = p(0), y = p(1); - double alpha = 1000.0; - // double xc = -0.05, yc = -0.05; - double xc = 0.0, yc = 0.0; - double r0 = 0.7; - double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0)); - return atan(alpha * (r - r0)); + return 1.0 + x + y; } -void wavefront_exgrad(const Vector &p, Vector &grad) +double function1(const Vector &p) { - double x = p(0), y = p(1); - double alpha = 1000.0; - double xc = -0.05, yc = -0.05; - double r0 = 0.7; - grad(0) = 0.0; - grad(1) = 0.0; -} - -double wavefront_laplace(const Vector &p) -{ - double x = p(0), y = p(1); - double alpha = 1000.0; - // double xc = -0.05, yc = -0.05; - double xc = 0.0, yc = 0.0; - double r0 = 0.7; - double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0)); - double num = - ( alpha - pow(alpha,3) * (pow(r,2) - pow(r0,2)) ); - double denom = pow(r * ( pow(alpha,2) * pow(r0,2) + pow(alpha,2) * pow(r,2) \ - - 2 * pow(alpha,2) * r0 * r + 1.0 ),2); - denom = max(denom,1e-8); - // return num / denom; if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1; } if (p.Normlp(2.0) < 0.4 || p.Normlp(2.0) > 0.6) { return 2; } return 0; } -double wavefront_laplace_alt(const Vector &p) +double function2(const Vector &p) { double x = p(0), y = p(1); double alpha = 1000.0; - double xc = -0.5, yc = -0.5; + double xc = 0.75, yc = 0.5; double r0 = 0.7; double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0)); double num = - ( alpha - pow(alpha,3) * (pow(r,2) - pow(r0,2)) ); @@ -100,38 +67,31 @@ double wavefront_laplace_alt(const Vector &p) return num / denom; } -double load3(const Vector &p) -{ - double x = p(0), y = p(1); - double r = sqrt(x*x + y*y) + 1.0e-8; - return r * sin(1.0/r); -} - int main(int argc, char *argv[]) { // 1. Parse command-line options. const char *mesh_file = "../data/star.mesh"; int order = 1; - bool pa = false; - const char *device_config = "cpu"; - int max_dofs = 50000; - bool visualization = true; int nc_limit = 1; + int max_elems = 1e5; + bool visualization = true; + double osc_threshold = 1e-3; OptionsParser args(argc, argv); args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use."); args.AddOption(&order, "-o", "--order", "Finite element order (polynomial degree)."); - args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa", - "--no-partial-assembly", "Enable Partial Assembly."); - args.AddOption(&device_config, "-d", "--device", - "Device configuration string, see Device::Configure()."); - args.AddOption(&max_dofs, "-md", "--max-dofs", - "Stop after reaching this many degrees of freedom."); + args.AddOption(&nc_limit, "-l", "--nc-limit", + "Maximum level of hanging nodes."); + args.AddOption(&max_elems, "-me", "--max-elems", + "Stop after reaching this many elements."); args.AddOption(&visualization, "-vis", "--visualization", "-no-vis", "--no-visualization", "Enable or disable GLVis visualization."); + args.AddOption(&osc_threshold, "-e", "--error", + "relative data oscillation threshold"); + args.Parse(); if (!args.Good()) { @@ -140,70 +100,28 @@ int main(int argc, char *argv[]) } args.PrintOptions(cout); - // 2. Enable hardware devices such as GPUs, and programming models such as - // CUDA, OCCA, RAJA and OpenMP based on command line options. - Device device(device_config); - device.Print(); - - // 3. Read the mesh from the given mesh file. We can handle triangular, - // quadrilateral, tetrahedral, hexahedral, surface and volume meshes with - // the same code. Mesh mesh(mesh_file, 1, 1); - int dim = mesh.Dimension(); - int sdim = mesh.SpaceDimension(); + mesh.EnsureNCMesh(); - // 4. Since a NURBS mesh can currently only be refined uniformly, we need to - // convert it to a piecewise-polynomial curved mesh. First we refine the - // NURBS mesh a bit more and then project the curvature to quadratic Nodes. - if (mesh.NURBSext) - { - for (int i = 0; i < 2; i++) - { - mesh.UniformRefinement(); - } - mesh.SetCurvature(2); - } + // // 2. Since a NURBS mesh can currently only be refined uniformly, we need to + // // convert it to a piecewise-polynomial curved mesh. First we refine the + // // NURBS mesh a bit more and then project the curvature to quadratic Nodes. + // if (mesh.NURBSext) + // { + // for (int i = 0; i < 2; i++) + // { + // mesh.UniformRefinement(); + // } + // mesh.SetCurvature(2); + // } - // 5. Define a finite element space on the mesh. The polynomial order is - // one (linear) by default, but this can be changed on the command line. - H1_FECollection fec(order, dim); - FiniteElementSpace fespace(&mesh, &fec); + // 2. Define functions and refiner. + FunctionCoefficient coeff0(function0); + FunctionCoefficient coeff1(function1); + FunctionCoefficient coeff2(function2); + CoefficientRefiner coeffrefiner(order); - // 6. As in Example 1, we set up bilinear and linear forms corresponding to - // the Laplace problem -\Delta u = 1. We don't assemble the discrete - // problem yet, this will be done in the main loop. - BilinearForm a(&fespace); - if (pa) - { - a.SetAssemblyLevel(AssemblyLevel::PARTIAL); - a.SetDiagonalPolicy(Operator::DIAG_ONE); - } - LinearForm b(&fespace); - - ConstantCoefficient one(1.0); - ConstantCoefficient zero(0.0); - Coefficient * exsol = nullptr; - Coefficient * rhs = nullptr; - exsol = new FunctionCoefficient(wavefront_exsol); - rhs = new FunctionCoefficient(wavefront_laplace); - - BilinearFormIntegrator *integ = new DiffusionIntegrator(one); - a.AddDomainIntegrator(integ); - // b.AddDomainIntegrator(new DomainLFIntegrator(one)); - b.AddDomainIntegrator(new DomainLFIntegrator(*rhs)); - - // 7. The solution vector x and the associated finite element grid function - // will be maintained over the AMR iterations. We initialize it to zero. - GridFunction x(&fespace); - x = 0.0; - - // 8. All boundary attributes will be used for essential (Dirichlet) BC. - MFEM_VERIFY(mesh.bdr_attributes.Size() > 0, - "Boundary attributes required in the mesh."); - Array ess_bdr(mesh.bdr_attributes.Max()); - ess_bdr = 1; - - // 9. Connect to GLVis. + // 2. Connect to GLVis. char vishost[] = "localhost"; int visport = 19916; socketstream sol_sock; @@ -212,22 +130,6 @@ int main(int argc, char *argv[]) sol_sock.open(vishost, visport); } - // 10. Set up an error estimator. Here we use the Zienkiewicz-Zhu estimator - // that uses the ComputeElementFlux method of the DiffusionIntegrator to - // recover a smoothed flux (gradient) that is subtracted from the element - // flux to get an error indicator. We need to supply the space for the - // smoothed flux: an (H1)^sdim (i.e., vector-valued) space is used here. - FiniteElementSpace flux_fespace(&mesh, &fec, sdim); - ZienkiewiczZhuEstimator estimator(*integ, x, flux_fespace); - estimator.SetAnisotropic(); - - // 11. A refiner selects and refines elements based on a refinement strategy. - // The strategy here is to refine elements with errors larger than a - // fraction of the maximum element error. Other strategies are possible. - // The refiner will call the given error estimator. - ThresholdRefiner refiner(estimator); - refiner.SetTotalErrorFraction(0.7); - const IntegrationRule *irs[Geometry::NumGeom]; int order_quad = 2*order + 5; for (int i=0; i < Geometry::NumGeom; ++i) @@ -235,138 +137,39 @@ int main(int argc, char *argv[]) irs[i] = &(IntRules.Get(i, order_quad)); } - // 11.5. Preprocess mesh to control osc - double osc; - double osc_tol = 1e-3; - CoefficientRefiner coeffrefiner(order); - coeffrefiner.SetCoefficient(*rhs); + // 3. Preprocess mesh to control osc + coeffrefiner.SetCoefficient(coeff0); + coeffrefiner.PreprocessMesh(mesh); + + mfem::out << "\n"; + mfem::out << "Function 0 (affine) \n"; + mfem::out << "Number of Elements " << mesh.GetNE() << "\n"; + mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n"; + mfem::out << "\n"; + // coeffrefiner.SetIntRule(irs); - coeffrefiner.SetThreshold(osc_tol); + coeffrefiner.SetMaxElements( (long) max_elems); + coeffrefiner.SetThreshold(osc_threshold); coeffrefiner.SetNCLimit(nc_limit); + + coeffrefiner.SetCoefficient(coeff1); coeffrefiner.PreprocessMesh(mesh); - osc = coeffrefiner.GetOsc(); - std::cout << " osc(f1) = " << osc << std::endl; + mfem::out << "Function 1 (discontinuous) \n"; + mfem::out << "Number of Elements " << mesh.GetNE() << "\n"; + mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n"; + mfem::out << "\n"; - Coefficient * rhs2 = nullptr; - rhs2 = new FunctionCoefficient(wavefront_laplace_alt); - coeffrefiner.SetCoefficient(*rhs2); + coeffrefiner.SetCoefficient(coeff2); coeffrefiner.PreprocessMesh(mesh); - osc = coeffrefiner.GetOsc(); - std::cout << " osc(f2) = " << osc << std::endl; - - // Coefficient * rhs3 = nullptr; - // rhs3 = new FunctionCoefficient(load3); - // coeffrefiner.SetCoefficient(*rhs3); - // coeffrefiner.PreprocessMesh(mesh); - - + mfem::out << "Function 2 (singular) \n"; + mfem::out << "Number of Elements " << mesh.GetNE() << "\n"; + mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n"; sol_sock.precision(8); sol_sock << "mesh\n" << mesh << flush; - cout << "press any key" << endl; - cin.get(); - fespace.Update(false); - b.Update(); - a.Update(); - x.Update(); - - // 12. The main AMR loop. In each iteration we solve the problem on the - // current mesh, visualize the solution, and refine the mesh. - for (int it = 0; ; it++) - { - int cdofs = fespace.GetTrueVSize(); - cout << "\nAMR iteration " << it << endl; - cout << "Number of unknowns: " << cdofs << endl; - - // 13. Assemble the right-hand side. - b.Assemble(); - - // 14. Set Dirichlet boundary values in the GridFunction x. - // Determine the list of Dirichlet true DOFs in the linear system. - Array ess_tdof_list; - x.ProjectBdrCoefficient(*exsol, ess_bdr); - // x.ProjectBdrCoefficient(zero, ess_bdr); - fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list); - - // 15. Assemble the stiffness matrix. - a.Assemble(); - - // 16. Create the linear system: eliminate boundary conditions, constrain - // hanging nodes and possibly apply other transformations. The system - // will be solved for true (unconstrained) DOFs only. - OperatorPtr A; - Vector B, X; - - const int copy_interior = 1; - a.FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior); - - // 17. Solve the linear system A X = B. - if (!pa) - { -#ifndef MFEM_USE_SUITESPARSE - // Use a simple symmetric Gauss-Seidel preconditioner with PCG. - GSSmoother M((SparseMatrix&)(*A)); - PCG(*A, M, B, X, 3, 200, 1e-12, 0.0); -#else - // If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system. - UMFPackSolver umf_solver; - umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS; - umf_solver.SetOperator(*A); - umf_solver.Mult(B, X); -#endif - } - else // Diagonal preconditioning in partial assembly mode. - { - OperatorJacobiSmoother M(a, ess_tdof_list); - PCG(*A, M, B, X, 3, 2000, 1e-12, 0.0); - } - - // 18. After solving the linear system, reconstruct the solution as a - // finite element GridFunction. Constrained nodes are interpolated - // from true DOFs (it may therefore happen that x.Size() >= X.Size()). - a.RecoverFEMSolution(X, b, x); - - // 19. Send solution by socket to the GLVis server. - if (visualization && sol_sock.good()) - { - sol_sock.precision(8); - sol_sock << "solution\n" << mesh << x << flush; - } - - if (cdofs > max_dofs) - { - cout << "Reached the maximum number of dofs. Stop." << endl; - break; - } - - // 20. Call the refiner to modify the mesh. The refiner calls the error - // estimator to obtain element errors, then it selects elements to be - // refined and finally it modifies the mesh. The Stop() method can be - // used to determine if a stopping criterion was met. - refiner.Apply(mesh); - if (refiner.Stop()) - { - cout << "Stopping criterion satisfied. Stop." << endl; - break; - } - - // 21. Update the space to reflect the new state of the mesh. Also, - // interpolate the solution x so that it lies in the new space but - // represents the same function. This saves solver iterations later - // since we'll have a good initial guess of x in the next step. - // Internally, FiniteElementSpace::Update() calculates an - // interpolation matrix which is then used by GridFunction::Update(). - fespace.Update(); - x.Update(); - - // 22. Inform also the bilinear and linear forms that the space has - // changed. - a.Update(); - b.Update(); - } return 0; } diff --git a/examples/oscp.cpp b/examples/oscp.cpp index e0375caf92..131bd2a3f7 100644 --- a/examples/oscp.cpp +++ b/examples/oscp.cpp @@ -4,25 +4,16 @@ // // Sample runs: mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 1 // mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 2 -// mpirun -np 4 ex30p -m ../data/square-disc-nurbs.mesh -o 2 +// mpirun -np 4 ex30p -m ../data/square-disc-nurbs.mesh -o 2 ??? // mpirun -np 4 ex30p -m ../data/star.mesh -o 3 -// mpirun -np 4 ex30p -m ../data/escher.mesh -o 2 -// mpirun -np 4 ex30p -m ../data/fichera.mesh -o 2 +// mpirun -np 4 ex30p -m ../data/escher.mesh -o 2 ??? +// mpirun -np 4 ex30p -m ../data/fichera.mesh -o 2 !!! // mpirun -np 4 ex30p -m ../data/disc-nurbs.mesh -o 2 // mpirun -np 4 ex30p -m ../data/ball-nurbs.mesh // mpirun -np 4 ex30p -m ../data/pipe-nurbs.mesh -// mpirun -np 4 ex30p -m ../data/star-surf.mesh -o 2 -// mpirun -np 4 ex30p -m ../data/square-disc-surf.mesh -o 2 +// mpirun -np 4 ex30p -m ../data/star-surf.mesh -o 2 ??? +// mpirun -np 4 ex30p -m ../data/square-disc-surf.mesh -o 2 ??? // mpirun -np 4 ex30p -m ../data/amr-quad.mesh -// mpirun -np 4 ex30p -m ../data/inline-segment.mesh -o 1 -md 100 -// -// Device sample runs: -// mpirun -np 4 ex30p -pa -d cuda -// mpirun -np 4 ex30p -pa -d occa-cuda -// mpirun -np 4 ex30p -pa -d raja-omp -// mpirun -np 4 ex30p -pa -d ceed-cpu -// * mpirun -np 4 ex30p -pa -d ceed-cuda -// mpirun -np 4 ex30p -pa -d ceed-cuda:/gpu/cuda/shared // // Description: This is an example of adaptive mesh refinement preprocessing // which lowers the data oscillation [1] to a user-defined @@ -66,7 +57,7 @@ double function2(const Vector &p) { double x = p(0), y = p(1); double alpha = 1000.0; - double xc = -0.5, yc = -0.5; + double xc = 0.75, yc = 0.5; double r0 = 0.7; double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0)); double num = - ( alpha - pow(alpha,3) * (pow(r,2) - pow(r0,2)) ); @@ -88,7 +79,7 @@ int main(int argc, char *argv[]) const char *mesh_file = "../data/star.mesh"; int order = 1; int nc_limit = 1; - int max_elems = 1e6; + int max_elems = 1e5; bool visualization = true; double osc_threshold = 1e-3; @@ -127,6 +118,18 @@ int main(int argc, char *argv[]) ParMesh pmesh(MPI_COMM_WORLD, mesh); mesh.Clear(); + // // 2. Since a NURBS mesh can currently only be refined uniformly, we need to + // // convert it to a piecewise-polynomial curved mesh. First we refine the + // // NURBS mesh a bit more and then project the curvature to quadratic Nodes. + // if (mesh.NURBSext) + // { + // for (int i = 0; i < 2; i++) + // { + // mesh.UniformRefinement(); + // } + // mesh.SetCurvature(2); + // } + // 2. Define functions and refiner. FunctionCoefficient coeff0(function0); FunctionCoefficient coeff1(function1); diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index 47409e9da0..5693e9d50e 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -206,17 +206,13 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) // Compute number of elements. int NE = mesh.GetNE(); - int globalNE; + int globalNE = NE; if (par) { #ifdef MFEM_USE_MPI globalNE = pmesh->GetGlobalNE(); #endif } - else - { - globalNE = NE; - } // Exit if the maximum number of elements has been reached. if (globalNE > max_elements) diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index 928498750f..e0f17728e3 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -366,7 +366,7 @@ public: /** @brief Set the maximum number of elements stopping criterion: stop when the input mesh has num_elements >= max_elem. The default value is LONG_MAX. */ - void SetMaxElements(int max_elements_) { max_elements = max_elements_; } + void SetMaxElements(long max_elements_) { max_elements = max_elements_; } /// Set the function f void SetCoefficient(Coefficient &coeff_) { coeff = &coeff_; } From 96a10dfa2ef847bf6ffa2588441181202c7150e4 Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 30 Jul 2021 19:44:54 -0700 Subject: [PATCH 050/198] finalize serial examples --- examples/osc.cpp | 110 ++++++++++++++++++++++++---------------- examples/oscp.cpp | 35 ++++++++----- mesh/mesh_operators.cpp | 49 ++++++++++-------- mesh/mesh_operators.hpp | 5 +- 4 files changed, 122 insertions(+), 77 deletions(-) diff --git a/examples/osc.cpp b/examples/osc.cpp index f8f95e01fe..29897d7f2b 100644 --- a/examples/osc.cpp +++ b/examples/osc.cpp @@ -4,16 +4,15 @@ // // Sample runs: ex30 -m ../data/square-disc.mesh -o 1 // ex30 -m ../data/square-disc.mesh -o 2 -// ex30 -m ../data/square-disc-nurbs.mesh -o 2 ??? -// ex30 -m ../data/star.mesh -o 3 -// ex30 -m ../data/escher.mesh -o 2 ??? -// ex30 -m ../data/fichera.mesh -o 2 !!! +// ex30 -m ../data/square-disc.mesh -o 2 -me 1e3 +// ex30 -m ../data/square-disc-nurbs.mesh -o 2 +// ex30 -m ../data/star.mesh -o 3 -eo 4 +// ex30 -m ../data/fichera.mesh -o 2 -me 1e4 // ex30 -m ../data/disc-nurbs.mesh -o 2 -// ex30 -m ../data/ball-nurbs.mesh -// ex30 -m ../data/pipe-nurbs.mesh -// ex30 -m ../data/star-surf.mesh -o 2 ??? -// ex30 -m ../data/square-disc-surf.mesh -o 2 ??? -// ex30 -m ../data/amr-quad.mesh +// ex30 -m ../data/ball-nurbs.mesh -o 2 -eo 3 -e 1e-2 -me 1e4 +// ex30 -m ../data/star-surf.mesh -o 2 +// ex30 -m ../data/square-disc-surf.mesh -o 2 +// ex30 -m ../data/amr-quad.mesh -l 2 // // Description: This is an example of adaptive mesh refinement preprocessing // which lowers the data oscillation [1] to a user-defined @@ -30,6 +29,11 @@ // [1] Morin, P., Nochetto, R. H., & Siebert, K. G. (2000). // Data oscillation and convergence of adaptive FEM. SIAM // Journal on Numerical Analysis, 38(2), 466-488. +// +// [2] Mitchell, W. F. (2013). A collection of 2D elliptic +// problems for testing adaptive grid refinement algorithms. +// Applied mathematics and computation, 220, 350-364. + #include "mfem.hpp" @@ -39,21 +43,31 @@ using namespace std; using namespace mfem; - -double function0(const Vector &p) +// Piecewise-affine function which is sometimes mesh-conforming +double affine_function(const Vector &p) { double x = p(0), y = p(1); - return 1.0 + x + y; + if (x < 0.0) + { + return 1.0 + x + y; + } + else + { + return 0.0; + } } -double function1(const Vector &p) +// Piecewise-constant function which is never mesh-conforming +double jump_function(const Vector &p) { if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1; } if (p.Normlp(2.0) < 0.4 || p.Normlp(2.0) > 0.6) { return 2; } return 0; } -double function2(const Vector &p) +// Singular function derived from the Laplacian of the "steep wavefront" +// problem in [2]. +double singular_function(const Vector &p) { double x = p(0), y = p(1); double alpha = 1000.0; @@ -74,8 +88,10 @@ int main(int argc, char *argv[]) int order = 1; int nc_limit = 1; int max_elems = 1e5; + double double_max_elems = double(max_elems); bool visualization = true; double osc_threshold = 1e-3; + int enriched_order = 5; OptionsParser args(argc, argv); args.AddOption(&mesh_file, "-m", "--mesh", @@ -84,13 +100,15 @@ int main(int argc, char *argv[]) "Finite element order (polynomial degree)."); args.AddOption(&nc_limit, "-l", "--nc-limit", "Maximum level of hanging nodes."); - args.AddOption(&max_elems, "-me", "--max-elems", + args.AddOption(&double_max_elems, "-me", "--max-elems", "Stop after reaching this many elements."); args.AddOption(&visualization, "-vis", "--visualization", "-no-vis", "--no-visualization", "Enable or disable GLVis visualization."); args.AddOption(&osc_threshold, "-e", "--error", "relative data oscillation threshold"); + args.AddOption(&enriched_order, "-eo", "--enriched_order", + "Enriched quadrature order."); args.Parse(); if (!args.Good()) @@ -99,29 +117,29 @@ int main(int argc, char *argv[]) return 1; } args.PrintOptions(cout); - + + max_elems = int(double_max_elems); Mesh mesh(mesh_file, 1, 1); - mesh.EnsureNCMesh(); - // // 2. Since a NURBS mesh can currently only be refined uniformly, we need to - // // convert it to a piecewise-polynomial curved mesh. First we refine the - // // NURBS mesh a bit more and then project the curvature to quadratic Nodes. - // if (mesh.NURBSext) - // { - // for (int i = 0; i < 2; i++) - // { - // mesh.UniformRefinement(); - // } - // mesh.SetCurvature(2); - // } + // 2. Since a NURBS mesh can currently only be refined uniformly, we need to + // convert it to a piecewise-polynomial curved mesh. First we refine the + // NURBS mesh a bit more and then project the curvature to quadratic Nodes. + if (mesh.NURBSext) + { + for (int i = 0; i < 2; i++) + { + mesh.UniformRefinement(); + } + mesh.SetCurvature(2); + } - // 2. Define functions and refiner. - FunctionCoefficient coeff0(function0); - FunctionCoefficient coeff1(function1); - FunctionCoefficient coeff2(function2); + // 3. Define functions and refiner. + FunctionCoefficient affine_coeff(affine_function); + FunctionCoefficient jump_coeff(jump_function); + FunctionCoefficient singular_coeff(singular_function); CoefficientRefiner coeffrefiner(order); - // 2. Connect to GLVis. + // 4. Connect to GLVis. char vishost[] = "localhost"; int visport = 19916; socketstream sol_sock; @@ -130,15 +148,24 @@ int main(int argc, char *argv[]) sol_sock.open(vishost, visport); } + // 5. Define custom integration rule (optional). const IntegrationRule *irs[Geometry::NumGeom]; - int order_quad = 2*order + 5; + int order_quad = 2*order + enriched_order; for (int i=0; i < Geometry::NumGeom; ++i) { irs[i] = &(IntRules.Get(i, order_quad)); } - // 3. Preprocess mesh to control osc - coeffrefiner.SetCoefficient(coeff0); + // 6. Apply custom refiner settings. + coeffrefiner.SetIntRule(irs); + coeffrefiner.SetMaxElements( (long) max_elems); + coeffrefiner.SetThreshold(osc_threshold); + coeffrefiner.SetNCLimit(nc_limit); + + // 7. Preprocess mesh to control osc (piecewise-affine function). + // This is mostly just a verification check. The oscillation should + // be zero if the function is mesh-conforming and order > 0. + coeffrefiner.SetCoefficient(affine_coeff); coeffrefiner.PreprocessMesh(mesh); mfem::out << "\n"; @@ -147,12 +174,8 @@ int main(int argc, char *argv[]) mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n"; mfem::out << "\n"; - // coeffrefiner.SetIntRule(irs); - coeffrefiner.SetMaxElements( (long) max_elems); - coeffrefiner.SetThreshold(osc_threshold); - coeffrefiner.SetNCLimit(nc_limit); - - coeffrefiner.SetCoefficient(coeff1); + // 8. Preprocess mesh to control osc (jump function). + coeffrefiner.SetCoefficient(jump_coeff); coeffrefiner.PreprocessMesh(mesh); mfem::out << "Function 1 (discontinuous) \n"; @@ -160,7 +183,8 @@ int main(int argc, char *argv[]) mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n"; mfem::out << "\n"; - coeffrefiner.SetCoefficient(coeff2); + // 9. Preprocess mesh to control osc (singular function). + coeffrefiner.SetCoefficient(singular_coeff); coeffrefiner.PreprocessMesh(mesh); mfem::out << "Function 2 (singular) \n"; diff --git a/examples/oscp.cpp b/examples/oscp.cpp index 131bd2a3f7..a33c46fa36 100644 --- a/examples/oscp.cpp +++ b/examples/oscp.cpp @@ -81,6 +81,7 @@ int main(int argc, char *argv[]) int nc_limit = 1; int max_elems = 1e5; bool visualization = true; + bool nc_simplices = true; double osc_threshold = 1e-3; OptionsParser args(argc, argv); @@ -97,6 +98,10 @@ int main(int argc, char *argv[]) "Enable or disable GLVis visualization."); args.AddOption(&osc_threshold, "-e", "--error", "relative data oscillation threshold"); + args.AddOption(&nc_simplices, "-ns", "--nonconforming-simplices", + "-cs", "--conforming-simplices", + "For simplicial meshes, enable/disable nonconforming" + " refinement"); args.Parse(); if (!args.Good()) @@ -114,21 +119,27 @@ int main(int argc, char *argv[]) } Mesh mesh(mesh_file, 1, 1); - mesh.EnsureNCMesh(); ParMesh pmesh(MPI_COMM_WORLD, mesh); mesh.Clear(); - // // 2. Since a NURBS mesh can currently only be refined uniformly, we need to - // // convert it to a piecewise-polynomial curved mesh. First we refine the - // // NURBS mesh a bit more and then project the curvature to quadratic Nodes. - // if (mesh.NURBSext) - // { - // for (int i = 0; i < 2; i++) - // { - // mesh.UniformRefinement(); - // } - // mesh.SetCurvature(2); - // } + // 2. Since a NURBS mesh can currently only be refined uniformly, we need to + // convert it to a piecewise-polynomial curved mesh. First we refine the + // NURBS mesh a bit more and then project the curvature to quadratic Nodes. + if (mesh.NURBSext) + { + for (int i = 0; i < 2; i++) + { + mesh.UniformRefinement(); + } + mesh.SetCurvature(2); + } + + // 7. Make sure the mesh is in the non-conforming mode to enable local + // refinement of quadrilaterals/hexahedra, and the above partitioning + // algorithm. Simplices can be refined either in conforming or in non- + // conforming mode. The conforming mode however does not support + // dynamic partitioning. + mesh.EnsureNCMesh(nc_simplices); // 2. Define functions and refiner. FunctionCoefficient coeff0(function0); diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index 5693e9d50e..5c1c4badf9 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -213,14 +213,6 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) globalNE = pmesh->GetGlobalNE(); #endif } - - // Exit if the maximum number of elements has been reached. - if (globalNE > max_elements) - { - delete l2fes; - delete gf; - return STOP; - } // Compute L2-norm of f double norm_of_coeff = 0.0; @@ -267,28 +259,43 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) #endif relative_osc = sqrt(relative_osc)/(norm_of_coeff + 1e-10); - // Exit if there are no elements to refine. - int num_marked_elements = mesh.ReduceInt(mesh_refinements.Size()); - if (num_marked_elements == 0) + // // Exit if the maximum number of elements has been reached. + // if (globalNE > max_elements) + // { + // MFEM_WARNING("Reached maximum number of elements."); + // delete l2fes; + // delete gf; + // return STOP; + // } + + // Exit if the global threshold or maximum number of elements is reached. + if (relative_osc < threshold || globalNE > max_elements) { + if (relative_osc > threshold && globalNE > max_elements) + { + MFEM_WARNING("Reached maximum number of elements " + "before resolving data to tolerance."); + } delete l2fes; delete gf; return STOP; } - // Refine elements + // // Exit if there are no elements left to refine. + // int num_marked_elements = mesh.ReduceInt(mesh_refinements.Size()); + // if (num_marked_elements == 0) + // { + // delete l2fes; + // delete gf; + // return STOP; + // } + + // Refine elements. mesh.GeneralRefinement(mesh_refinements, nonconforming, nc_limit); l2fes->Update(false); gf->Update(); - // Exit if the global threshold has been reached. - if (relative_osc < threshold) - { - delete l2fes; - delete gf; - return STOP; - } - } +} delete l2fes; delete gf; return CONTINUE + REFINED; @@ -299,7 +306,7 @@ void CoefficientRefiner::Reset() { relative_osc = 0.0; coeff = NULL; - *irs = NULL; + irs = NULL; } diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index e0f17728e3..7fecdf4a9d 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -369,7 +369,10 @@ public: void SetMaxElements(long max_elements_) { max_elements = max_elements_; } /// Set the function f - void SetCoefficient(Coefficient &coeff_) { coeff = &coeff_; } + void SetCoefficient(Coefficient &coeff_) { + relative_osc = 0.0; + coeff = &coeff_; + } /// Reset the oscillation order void SetOrder(double order_) { order = order_; } From b9eaa8c309f91c8ba9a2fa38c30b09a15b794ac3 Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 30 Jul 2021 19:45:52 -0700 Subject: [PATCH 051/198] fix style --- examples/osc.cpp | 8 ++++---- examples/oscp.cpp | 12 ++++++------ mesh/mesh_operators.cpp | 4 ++-- mesh/mesh_operators.hpp | 9 +++++---- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/examples/osc.cpp b/examples/osc.cpp index 29897d7f2b..dc43f62abe 100644 --- a/examples/osc.cpp +++ b/examples/osc.cpp @@ -18,15 +18,15 @@ // which lowers the data oscillation [1] to a user-defined // relative threshold. There is no PDE being solved. // -// MFEM's capability to work with both conforming and +// MFEM's capability to work with both conforming and // nonconforming meshes is demonstrated in example 6. In some // problems, the material data or loading data is not sufficiently -// resolved on the initial mesh. This missing fine scale data +// resolved on the initial mesh. This missing fine scale data // reduces the accuracy of the solution as well as the accuracy // of some local error estimators. By preprocessing the mesh // before the solving the PDE, many issues can be avoided. // -// [1] Morin, P., Nochetto, R. H., & Siebert, K. G. (2000). +// [1] Morin, P., Nochetto, R. H., & Siebert, K. G. (2000). // Data oscillation and convergence of adaptive FEM. SIAM // Journal on Numerical Analysis, 38(2), 466-488. // @@ -117,7 +117,7 @@ int main(int argc, char *argv[]) return 1; } args.PrintOptions(cout); - + max_elems = int(double_max_elems); Mesh mesh(mesh_file, 1, 1); diff --git a/examples/oscp.cpp b/examples/oscp.cpp index a33c46fa36..6f825e93b3 100644 --- a/examples/oscp.cpp +++ b/examples/oscp.cpp @@ -19,15 +19,15 @@ // which lowers the data oscillation [1] to a user-defined // relative threshold. There is no PDE being solved. // -// MFEM's capability to work with both conforming and +// MFEM's capability to work with both conforming and // nonconforming meshes is demonstrated in example 6. In some // problems, the material data or loading data is not sufficiently -// resolved on the initial mesh. This missing fine scale data +// resolved on the initial mesh. This missing fine scale data // reduces the accuracy of the solution as well as the accuracy // of some local error estimators. By preprocessing the mesh // before the solving the PDE, many issues can be avoided. // -// [1] Morin, P., Nochetto, R. H., & Siebert, K. G. (2000). +// [1] Morin, P., Nochetto, R. H., & Siebert, K. G. (2000). // Data oscillation and convergence of adaptive FEM. SIAM // Journal on Numerical Analysis, 38(2), 466-488. @@ -99,9 +99,9 @@ int main(int argc, char *argv[]) args.AddOption(&osc_threshold, "-e", "--error", "relative data oscillation threshold"); args.AddOption(&nc_simplices, "-ns", "--nonconforming-simplices", - "-cs", "--conforming-simplices", - "For simplicial meshes, enable/disable nonconforming" - " refinement"); + "-cs", "--conforming-simplices", + "For simplicial meshes, enable/disable nonconforming" + " refinement"); args.Parse(); if (!args.Good()) diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index 5c1c4badf9..e4a49f5cc6 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -203,7 +203,7 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) for (int i = 0; i < max_it; i++) { - + // Compute number of elements. int NE = mesh.GetNE(); int globalNE = NE; @@ -295,7 +295,7 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) l2fes->Update(false); gf->Update(); -} + } delete l2fes; delete gf; return CONTINUE + REFINED; diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index 7fecdf4a9d..859c24eed6 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -369,10 +369,11 @@ public: void SetMaxElements(long max_elements_) { max_elements = max_elements_; } /// Set the function f - void SetCoefficient(Coefficient &coeff_) { - relative_osc = 0.0; - coeff = &coeff_; - } + void SetCoefficient(Coefficient &coeff_) + { + relative_osc = 0.0; + coeff = &coeff_; + } /// Reset the oscillation order void SetOrder(double order_) { order = order_; } From bdcbfafb4208da265214c773027aa4a2311707d1 Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 30 Jul 2021 20:39:02 -0700 Subject: [PATCH 052/198] cleaning code --- examples/oscp.cpp | 135 ++++++++++++++++++++++++++-------------- mesh/mesh_operators.cpp | 32 ++-------- 2 files changed, 93 insertions(+), 74 deletions(-) diff --git a/examples/oscp.cpp b/examples/oscp.cpp index 6f825e93b3..8cf3a5e4ad 100644 --- a/examples/oscp.cpp +++ b/examples/oscp.cpp @@ -2,18 +2,17 @@ // // Compile with: make ex30p // -// Sample runs: mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 1 -// mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 2 -// mpirun -np 4 ex30p -m ../data/square-disc-nurbs.mesh -o 2 ??? -// mpirun -np 4 ex30p -m ../data/star.mesh -o 3 -// mpirun -np 4 ex30p -m ../data/escher.mesh -o 2 ??? -// mpirun -np 4 ex30p -m ../data/fichera.mesh -o 2 !!! -// mpirun -np 4 ex30p -m ../data/disc-nurbs.mesh -o 2 -// mpirun -np 4 ex30p -m ../data/ball-nurbs.mesh -// mpirun -np 4 ex30p -m ../data/pipe-nurbs.mesh -// mpirun -np 4 ex30p -m ../data/star-surf.mesh -o 2 ??? -// mpirun -np 4 ex30p -m ../data/square-disc-surf.mesh -o 2 ??? -// mpirun -np 4 ex30p -m ../data/amr-quad.mesh +// Sample runs: mpirun -np 4 ex30 -m ../data/square-disc.mesh -o 1 +// mpirun -np 4 ex30 -m ../data/square-disc.mesh -o 2 +// mpirun -np 4 ex30 -m ../data/square-disc.mesh -o 2 -me 1e3 +// mpirun -np 4 ex30 -m ../data/square-disc-nurbs.mesh -o 2 +// mpirun -np 4 ex30 -m ../data/star.mesh -o 3 -eo 4 +// mpirun -np 4 ex30 -m ../data/fichera.mesh -o 2 -me 1e4 +// mpirun -np 4 ex30 -m ../data/disc-nurbs.mesh -o 2 +// mpirun -np 4 ex30 -m ../data/ball-nurbs.mesh -o 2 -eo 3 -e 1e-2 -me 1e4 +// mpirun -np 4 ex30 -m ../data/star-surf.mesh -o 2 +// mpirun -np 4 ex30 -m ../data/square-disc-surf.mesh -o 2 +// mpirun -np 4 ex30 -m ../data/amr-quad.mesh -l 2 // // Description: This is an example of adaptive mesh refinement preprocessing // which lowers the data oscillation [1] to a user-defined @@ -30,6 +29,10 @@ // [1] Morin, P., Nochetto, R. H., & Siebert, K. G. (2000). // Data oscillation and convergence of adaptive FEM. SIAM // Journal on Numerical Analysis, 38(2), 466-488. +// +// [2] Mitchell, W. F. (2013). A collection of 2D elliptic +// problems for testing adaptive grid refinement algorithms. +// Applied mathematics and computation, 220, 350-364. #include "mfem.hpp" @@ -40,20 +43,31 @@ using namespace std; using namespace mfem; -double function0(const Vector &p) +// Piecewise-affine function which is sometimes mesh-conforming +double affine_function(const Vector &p) { double x = p(0), y = p(1); - return 1.0 + x + y; + if (x < 0.0) + { + return 1.0 + x + y; + } + else + { + return 0.0; + } } -double function1(const Vector &p) +// Piecewise-constant function which is never mesh-conforming +double jump_function(const Vector &p) { if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1; } if (p.Normlp(2.0) < 0.4 || p.Normlp(2.0) > 0.6) { return 2; } return 0; } -double function2(const Vector &p) +// Singular function derived from the Laplacian of the "steep wavefront" +// problem in [2]. +double singular_function(const Vector &p) { double x = p(0), y = p(1); double alpha = 1000.0; @@ -80,9 +94,11 @@ int main(int argc, char *argv[]) int order = 1; int nc_limit = 1; int max_elems = 1e5; + double double_max_elems = double(max_elems); bool visualization = true; bool nc_simplices = true; double osc_threshold = 1e-3; + int enriched_order = 5; OptionsParser args(argc, argv); args.AddOption(&mesh_file, "-m", "--mesh", @@ -91,13 +107,15 @@ int main(int argc, char *argv[]) "Finite element order (polynomial degree)."); args.AddOption(&nc_limit, "-l", "--nc-limit", "Maximum level of hanging nodes."); - args.AddOption(&max_elems, "-me", "--max-elems", + args.AddOption(&double_max_elems, "-me", "--max-elems", "Stop after reaching this many elements."); args.AddOption(&visualization, "-vis", "--visualization", "-no-vis", "--no-visualization", "Enable or disable GLVis visualization."); args.AddOption(&osc_threshold, "-e", "--error", "relative data oscillation threshold"); + args.AddOption(&enriched_order, "-eo", "--enriched_order", + "Enriched quadrature order."); args.AddOption(&nc_simplices, "-ns", "--nonconforming-simplices", "-cs", "--conforming-simplices", "For simplicial meshes, enable/disable nonconforming" @@ -118,9 +136,8 @@ int main(int argc, char *argv[]) args.PrintOptions(cout); } + max_elems = int(double_max_elems); Mesh mesh(mesh_file, 1, 1); - ParMesh pmesh(MPI_COMM_WORLD, mesh); - mesh.Clear(); // 2. Since a NURBS mesh can currently only be refined uniformly, we need to // convert it to a piecewise-polynomial curved mesh. First we refine the @@ -134,20 +151,25 @@ int main(int argc, char *argv[]) mesh.SetCurvature(2); } - // 7. Make sure the mesh is in the non-conforming mode to enable local + // 3. Make sure the mesh is in the non-conforming mode to enable local // refinement of quadrilaterals/hexahedra, and the above partitioning // algorithm. Simplices can be refined either in conforming or in non- // conforming mode. The conforming mode however does not support // dynamic partitioning. mesh.EnsureNCMesh(nc_simplices); - // 2. Define functions and refiner. - FunctionCoefficient coeff0(function0); - FunctionCoefficient coeff1(function1); - FunctionCoefficient coeff2(function2); + // 4. Define a parallel mesh by partitioning the serial mesh. + // Once the parallel mesh is defined, the serial mesh can be deleted. + ParMesh pmesh(MPI_COMM_WORLD, mesh); + mesh.Clear(); + + // 5. Define functions and refiner. + FunctionCoefficient affine_coeff(affine_function); + FunctionCoefficient jump_coeff(jump_function); + FunctionCoefficient singular_coeff(singular_function); CoefficientRefiner coeffrefiner(order); - // 2. Connect to GLVis. + // 6. Connect to GLVis. char vishost[] = "localhost"; int visport = 19916; socketstream sol_sock; @@ -156,42 +178,63 @@ int main(int argc, char *argv[]) sol_sock.open(vishost, visport); } + // 7. Define custom integration rule (optional). const IntegrationRule *irs[Geometry::NumGeom]; - int order_quad = 2*order + 5; + int order_quad = 2*order + enriched_order; for (int i=0; i < Geometry::NumGeom; ++i) { irs[i] = &(IntRules.Get(i, order_quad)); } - // 3. Preprocess mesh to control osc - coeffrefiner.SetCoefficient(coeff0); - coeffrefiner.PreprocessMesh(pmesh); - - mfem::out << "\n"; - mfem::out << "Function 0 (affine) \n"; - mfem::out << "Number of Elements " << pmesh.GetGlobalNE() << "\n"; - mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n"; - mfem::out << "\n"; - - // coeffrefiner.SetIntRule(irs); + // 8. Apply custom refiner settings. + coeffrefiner.SetIntRule(irs); coeffrefiner.SetMaxElements( (long) max_elems); coeffrefiner.SetThreshold(osc_threshold); coeffrefiner.SetNCLimit(nc_limit); - coeffrefiner.SetCoefficient(coeff1); + // 9. Preprocess mesh to control osc (piecewise-affine function). + // This is mostly just a verification check. The oscillation should + // be zero if the function is mesh-conforming and order > 0. + coeffrefiner.SetCoefficient(affine_coeff); coeffrefiner.PreprocessMesh(pmesh); - mfem::out << "Function 1 (discontinuous) \n"; - mfem::out << "Number of Elements " << pmesh.GetGlobalNE() << "\n"; - mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n"; - mfem::out << "\n"; + int globalNE = pmesh.GetGlobalNE(); + double osc = coeffrefiner.GetOsc(); + if (myid == 0) + { + mfem::out << "\n"; + mfem::out << "Function 0 (affine) \n"; + mfem::out << "Number of Elements " << globalNE << "\n"; + mfem::out << "Osc error " << osc << "\n"; + mfem::out << "\n"; + } - coeffrefiner.SetCoefficient(coeff2); + // 10. Preprocess mesh to control osc (jump function). + coeffrefiner.SetCoefficient(jump_coeff); coeffrefiner.PreprocessMesh(pmesh); - mfem::out << "Function 2 (singular) \n"; - mfem::out << "Number of Elements " << pmesh.GetGlobalNE() << "\n"; - mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n"; + globalNE = pmesh.GetGlobalNE(); + osc = coeffrefiner.GetOsc(); + if (myid == 0) + { + mfem::out << "Function 1 (discontinuous) \n"; + mfem::out << "Number of Elements " << globalNE << "\n"; + mfem::out << "Osc error " << osc << "\n"; + mfem::out << "\n"; + } + + // 11. Preprocess mesh to control osc (singular function). + coeffrefiner.SetCoefficient(singular_coeff); + coeffrefiner.PreprocessMesh(pmesh); + + globalNE = pmesh.GetGlobalNE(); + osc = coeffrefiner.GetOsc(); + if (myid == 0) + { + mfem::out << "Function 2 (singular) \n"; + mfem::out << "Number of Elements " << globalNE << "\n"; + mfem::out << "Osc error " << osc << "\n"; + } sol_sock.precision(8); sol_sock << "parallel " << num_procs << " " << myid << "\n"; diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index e4a49f5cc6..bf3f79f5b0 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -204,26 +204,20 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) for (int i = 0; i < max_it; i++) { - // Compute number of elements. + // Compute number of elements and L2-norm of f. int NE = mesh.GetNE(); - int globalNE = NE; - if (par) - { -#ifdef MFEM_USE_MPI - globalNE = pmesh->GetGlobalNE(); -#endif - } - - // Compute L2-norm of f + int globalNE = 0; double norm_of_coeff = 0.0; if (par) { #ifdef MFEM_USE_MPI + globalNE = pmesh->GetGlobalNE(); norm_of_coeff = ComputeGlobalLpNorm(2.0,*coeff,*pmesh,irs); #endif } else { + globalNE = NE; norm_of_coeff = ComputeLpNorm(2.0,*coeff,mesh,irs); } @@ -259,15 +253,6 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) #endif relative_osc = sqrt(relative_osc)/(norm_of_coeff + 1e-10); - // // Exit if the maximum number of elements has been reached. - // if (globalNE > max_elements) - // { - // MFEM_WARNING("Reached maximum number of elements."); - // delete l2fes; - // delete gf; - // return STOP; - // } - // Exit if the global threshold or maximum number of elements is reached. if (relative_osc < threshold || globalNE > max_elements) { @@ -281,15 +266,6 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) return STOP; } - // // Exit if there are no elements left to refine. - // int num_marked_elements = mesh.ReduceInt(mesh_refinements.Size()); - // if (num_marked_elements == 0) - // { - // delete l2fes; - // delete gf; - // return STOP; - // } - // Refine elements. mesh.GeneralRefinement(mesh_refinements, nonconforming, nc_limit); l2fes->Update(false); From e93a3542c0865692d772f82eceee40b925bbe11e Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 30 Jul 2021 21:14:04 -0700 Subject: [PATCH 053/198] example files written --- examples/osc.cpp | 30 +++++++++++++++--------------- examples/oscp.cpp | 32 ++++++++++++++++---------------- mesh/mesh_operators.cpp | 8 +++++--- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/examples/osc.cpp b/examples/osc.cpp index dc43f62abe..c4b767bfff 100644 --- a/examples/osc.cpp +++ b/examples/osc.cpp @@ -2,17 +2,17 @@ // // Compile with: make ex30 // -// Sample runs: ex30 -m ../data/square-disc.mesh -o 1 -// ex30 -m ../data/square-disc.mesh -o 2 -// ex30 -m ../data/square-disc.mesh -o 2 -me 1e3 -// ex30 -m ../data/square-disc-nurbs.mesh -o 2 -// ex30 -m ../data/star.mesh -o 3 -eo 4 -// ex30 -m ../data/fichera.mesh -o 2 -me 1e4 -// ex30 -m ../data/disc-nurbs.mesh -o 2 -// ex30 -m ../data/ball-nurbs.mesh -o 2 -eo 3 -e 1e-2 -me 1e4 -// ex30 -m ../data/star-surf.mesh -o 2 -// ex30 -m ../data/square-disc-surf.mesh -o 2 -// ex30 -m ../data/amr-quad.mesh -l 2 +// Sample runs: osc -m ../data/square-disc.mesh -o 1 +// osc -m ../data/square-disc.mesh -o 2 +// osc -m ../data/square-disc.mesh -o 2 -me 1e3 +// osc -m ../data/square-disc-nurbs.mesh -o 2 +// osc -m ../data/star.mesh -o 2 -eo 4 +// osc -m ../data/fichera.mesh -o 2 -me 1e4 +// osc -m ../data/disc-nurbs.mesh -o 2 +// osc -m ../data/ball-nurbs.mesh -o 2 -eo 3 -e 1e-2 -me 1e4 +// osc -m ../data/star-surf.mesh -o 2 +// osc -m ../data/square-disc-surf.mesh -o 2 +// osc -m ../data/amr-quad.mesh -l 2 // // Description: This is an example of adaptive mesh refinement preprocessing // which lowers the data oscillation [1] to a user-defined @@ -53,7 +53,7 @@ double affine_function(const Vector &p) } else { - return 0.0; + return 1.0; } } @@ -61,7 +61,7 @@ double affine_function(const Vector &p) double jump_function(const Vector &p) { if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1; } - if (p.Normlp(2.0) < 0.4 || p.Normlp(2.0) > 0.6) { return 2; } + if (p.Normlp(2.0) < 0.4 || p.Normlp(2.0) > 0.6) { return 5; } return 0; } @@ -172,21 +172,21 @@ int main(int argc, char *argv[]) mfem::out << "Function 0 (affine) \n"; mfem::out << "Number of Elements " << mesh.GetNE() << "\n"; mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n"; - mfem::out << "\n"; // 8. Preprocess mesh to control osc (jump function). coeffrefiner.SetCoefficient(jump_coeff); coeffrefiner.PreprocessMesh(mesh); + mfem::out << "\n"; mfem::out << "Function 1 (discontinuous) \n"; mfem::out << "Number of Elements " << mesh.GetNE() << "\n"; mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n"; - mfem::out << "\n"; // 9. Preprocess mesh to control osc (singular function). coeffrefiner.SetCoefficient(singular_coeff); coeffrefiner.PreprocessMesh(mesh); + mfem::out << "\n"; mfem::out << "Function 2 (singular) \n"; mfem::out << "Number of Elements " << mesh.GetNE() << "\n"; mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n"; diff --git a/examples/oscp.cpp b/examples/oscp.cpp index 8cf3a5e4ad..222aa61444 100644 --- a/examples/oscp.cpp +++ b/examples/oscp.cpp @@ -1,18 +1,18 @@ // MFEM Example 30 - Parallel Version // -// Compile with: make ex30p +// Compile with: make oscp // -// Sample runs: mpirun -np 4 ex30 -m ../data/square-disc.mesh -o 1 -// mpirun -np 4 ex30 -m ../data/square-disc.mesh -o 2 -// mpirun -np 4 ex30 -m ../data/square-disc.mesh -o 2 -me 1e3 -// mpirun -np 4 ex30 -m ../data/square-disc-nurbs.mesh -o 2 -// mpirun -np 4 ex30 -m ../data/star.mesh -o 3 -eo 4 -// mpirun -np 4 ex30 -m ../data/fichera.mesh -o 2 -me 1e4 -// mpirun -np 4 ex30 -m ../data/disc-nurbs.mesh -o 2 -// mpirun -np 4 ex30 -m ../data/ball-nurbs.mesh -o 2 -eo 3 -e 1e-2 -me 1e4 -// mpirun -np 4 ex30 -m ../data/star-surf.mesh -o 2 -// mpirun -np 4 ex30 -m ../data/square-disc-surf.mesh -o 2 -// mpirun -np 4 ex30 -m ../data/amr-quad.mesh -l 2 +// Sample runs: mpirun -np 4 oscp -m ../data/square-disc.mesh -o 1 +// mpirun -np 4 oscp -m ../data/square-disc.mesh -o 2 +// mpirun -np 4 oscp -m ../data/square-disc.mesh -o 2 -me 1e3 +// mpirun -np 4 oscp -m ../data/square-disc-nurbs.mesh -o 2 +// mpirun -np 4 oscp -m ../data/star.mesh -o 2 -eo 4 +// mpirun -np 4 oscp -m ../data/fichera.mesh -o 2 -me 1e4 +// mpirun -np 4 oscp -m ../data/disc-nurbs.mesh -o 2 +// mpirun -np 4 oscp -m ../data/ball-nurbs.mesh -o 2 -eo 3 -e 1e-2 +// mpirun -np 4 oscp -m ../data/star-surf.mesh -o 2 +// mpirun -np 4 oscp -m ../data/square-disc-surf.mesh -o 2 +// mpirun -np 4 oscp -m ../data/amr-quad.mesh -l 2 // // Description: This is an example of adaptive mesh refinement preprocessing // which lowers the data oscillation [1] to a user-defined @@ -53,7 +53,7 @@ double affine_function(const Vector &p) } else { - return 0.0; + return 1.0; } } @@ -61,7 +61,7 @@ double affine_function(const Vector &p) double jump_function(const Vector &p) { if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1; } - if (p.Normlp(2.0) < 0.4 || p.Normlp(2.0) > 0.6) { return 2; } + if (p.Normlp(2.0) < 0.4 || p.Normlp(2.0) > 0.6) { return 5; } return 0; } @@ -206,7 +206,6 @@ int main(int argc, char *argv[]) mfem::out << "Function 0 (affine) \n"; mfem::out << "Number of Elements " << globalNE << "\n"; mfem::out << "Osc error " << osc << "\n"; - mfem::out << "\n"; } // 10. Preprocess mesh to control osc (jump function). @@ -217,10 +216,10 @@ int main(int argc, char *argv[]) osc = coeffrefiner.GetOsc(); if (myid == 0) { + mfem::out << "\n"; mfem::out << "Function 1 (discontinuous) \n"; mfem::out << "Number of Elements " << globalNE << "\n"; mfem::out << "Osc error " << osc << "\n"; - mfem::out << "\n"; } // 11. Preprocess mesh to control osc (singular function). @@ -231,6 +230,7 @@ int main(int argc, char *argv[]) osc = coeffrefiner.GetOsc(); if (myid == 0) { + mfem::out << "\n"; mfem::out << "Function 2 (singular) \n"; mfem::out << "Number of Elements " << globalNE << "\n"; mfem::out << "Osc error " << osc << "\n"; diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index bf3f79f5b0..9ef41b3819 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -165,6 +165,7 @@ int CoefficientRefiner::ApplyImpl(Mesh &mesh) int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) { + int rank = 0; MFEM_VERIFY(max_it > 0, "max_it must be strictly positive") MFEM_VERIFY(coeff, "Coefficient is not set for CoefficientRefiner object") @@ -247,8 +248,9 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) #ifdef MFEM_USE_MPI if (par) { - MPI_Allreduce(MPI_IN_PLACE, &relative_osc, 1, MPI_DOUBLE, MPI_SUM, - pmesh->GetComm()); + MPI_Comm comm = pmesh->GetComm(); + MPI_Allreduce(MPI_IN_PLACE, &relative_osc, 1, MPI_DOUBLE, MPI_SUM, comm); + MPI_Comm_rank(comm, &rank); } #endif relative_osc = sqrt(relative_osc)/(norm_of_coeff + 1e-10); @@ -256,7 +258,7 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) // Exit if the global threshold or maximum number of elements is reached. if (relative_osc < threshold || globalNE > max_elements) { - if (relative_osc > threshold && globalNE > max_elements) + if (relative_osc > threshold && globalNE > max_elements && rank == 0) { MFEM_WARNING("Reached maximum number of elements " "before resolving data to tolerance."); From 2a09088fb072e094ff8b51e19a1122f81c57b89a Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 30 Jul 2021 23:12:56 -0700 Subject: [PATCH 054/198] added some simple unit tests --- examples/osc.cpp | 4 +- examples/oscp.cpp | 2 +- tests/unit/fem/test_oscillation.cpp | 333 ++++++++++++++++++++++++++++ 3 files changed, 336 insertions(+), 3 deletions(-) create mode 100644 tests/unit/fem/test_oscillation.cpp diff --git a/examples/osc.cpp b/examples/osc.cpp index c4b767bfff..4a077a4fb3 100644 --- a/examples/osc.cpp +++ b/examples/osc.cpp @@ -1,6 +1,6 @@ -// MFEM Example 30 +// MFEM Example 30+ // -// Compile with: make ex30 +// Compile with: make osc // // Sample runs: osc -m ../data/square-disc.mesh -o 1 // osc -m ../data/square-disc.mesh -o 2 diff --git a/examples/oscp.cpp b/examples/oscp.cpp index 222aa61444..6df32f06fe 100644 --- a/examples/oscp.cpp +++ b/examples/oscp.cpp @@ -1,4 +1,4 @@ -// MFEM Example 30 - Parallel Version +// MFEM Example 30+ - Parallel Version // // Compile with: make oscp // diff --git a/tests/unit/fem/test_oscillation.cpp b/tests/unit/fem/test_oscillation.cpp new file mode 100644 index 0000000000..9ea9703983 --- /dev/null +++ b/tests/unit/fem/test_oscillation.cpp @@ -0,0 +1,333 @@ +// Copyright (c) 2010-2021, 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 "mfem.hpp" +#include "unit_tests.hpp" + +#include +#include + +using namespace mfem; + +#if defined(MFEM_USE_MPI) + +namespace testhelper_osc +{ +double SmoothSolutionX(const mfem::Vector& x) +{ + return x(0); +} + +double SmoothSolutionY(const mfem::Vector& x) +{ + return x(1); +} + +double SmoothSolutionZ(const mfem::Vector& x) +{ + return x(2); +} + +double NonsmoothSolutionX(const mfem::Vector& x) +{ + return std::abs(x(0)-0.5); +} + +double NonsmoothSolutionY(const mfem::Vector& x) +{ + return std::abs(x(1)-0.5); +} + +double NonsmoothSolutionZ(const mfem::Vector& x) +{ + return std::abs(x(2)-0.5); +} +} + +TEST_CASE("Data Oscillation on 2D NCMesh", + "[NCMesh], [Parallel]") +{ + // Setup + const auto order = GENERATE(1, 3, 5); + Mesh mesh = Mesh::MakeCartesian2D(2, 2, Element::QUADRILATERAL); + + // Make the mesh NC + mesh.EnsureNCMesh(); + { + Array elements_to_refine(1); + elements_to_refine[0] = 1; + mesh.GeneralRefinement(elements_to_refine, 1, 0); + } + + auto pmesh = new ParMesh(MPI_COMM_WORLD, mesh); + mesh.Clear(); + + SECTION("Perfect Approximation X") + { + FunctionCoefficient u_analytic(testhelper_osc::SmoothSolutionX); + + CoefficientRefiner coeffrefiner(order); + coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.PreprocessMesh(*pmesh); + double osc = coeffrefiner.GetOsc(); + + REQUIRE(osc == MFEM_Approx(0.0)); + } + + SECTION("Perfect Approximation Y") + { + FunctionCoefficient u_analytic(testhelper_osc::SmoothSolutionY); + + CoefficientRefiner coeffrefiner(order); + coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.PreprocessMesh(*pmesh); + double osc = coeffrefiner.GetOsc(); + + REQUIRE(osc == MFEM_Approx(0.0)); + } + + SECTION("Nonsmooth Approximation X") + { + FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionX); + + CoefficientRefiner coeffrefiner(order); + coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.PreprocessMesh(*pmesh); + double osc = coeffrefiner.GetOsc(); + + REQUIRE(osc > 0.0); + } + + SECTION("Nonsmooth Approximation Y") + { + FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionY); + + CoefficientRefiner coeffrefiner(order); + coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.PreprocessMesh(*pmesh); + double osc = coeffrefiner.GetOsc(); + + REQUIRE(osc > 0.0); + } + + delete pmesh; +} + +TEST_CASE("Data Oscillation on 2D NCMesh embedded in 3D", + "[NCMesh], [Parallel]") +{ + // Setup + const auto order = GENERATE(1, 3, 5); + const auto max_it = GENERATE(1, 2, 4); + + // Manually construct embedded mesh + std::array vertices = + { + 0.0,0.0,0.0, + 0.0,1.0,0.0, + 1.0,1.0,0.0, + 1.0,0.0,0.0 + }; + + std::array element_indices = + { + 0,1,2,3 + }; + + std::array element_attributes = + { + 1 + }; + + std::array boundary_indices = + { + 0,1, + 1,2, + 2,3, + 3,0 + }; + + std::array boundary_attributes = + { + 1, + 1, + 1, + 1 + }; + + auto mesh = new Mesh( + vertices.data(), 4, + element_indices.data(), Geometry::SQUARE, + element_attributes.data(), 1, + boundary_indices.data(), Geometry::SEGMENT, + boundary_attributes.data(), 4, + 2, 3 + ); + mesh->UniformRefinement(); + mesh->Finalize(); + + // Make the mesh NC + mesh->EnsureNCMesh(); + { + Array elements_to_refine(1); + elements_to_refine[0] = 1; + mesh->GeneralRefinement(elements_to_refine, 1, 0); + } + + auto pmesh = new ParMesh(MPI_COMM_WORLD, *mesh); + delete mesh; + + SECTION("Perfect Approximation X") + { + FunctionCoefficient u_analytic(testhelper_osc::SmoothSolutionX); + + CoefficientRefiner coeffrefiner(order); + coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.PreprocessMesh(*pmesh, max_it); + double osc = coeffrefiner.GetOsc(); + + REQUIRE(osc == MFEM_Approx(0.0)); + } + + SECTION("Perfect Approximation Y") + { + FunctionCoefficient u_analytic(testhelper_osc::SmoothSolutionY); + + CoefficientRefiner coeffrefiner(order); + coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.PreprocessMesh(*pmesh, max_it); + double osc = coeffrefiner.GetOsc(); + + REQUIRE(osc == MFEM_Approx(0.0)); + } + + SECTION("Nonsmooth Approximation X") + { + FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionX); + + CoefficientRefiner coeffrefiner(order); + coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.PreprocessMesh(*pmesh, max_it); + double osc = coeffrefiner.GetOsc(); + + REQUIRE(osc > 0.0); + } + + SECTION("Nonsmooth Approximation Y") + { + FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionY); + + CoefficientRefiner coeffrefiner(order); + coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.PreprocessMesh(*pmesh, max_it); + double osc = coeffrefiner.GetOsc(); + + REQUIRE(osc > 0.0); + } + + delete pmesh; +} + +TEST_CASE("Data Oscillation on 3D NCMesh", + "[NCMesh], [Parallel]") +{ + // Setup + const auto order = GENERATE(1, 3, 5); + int max_it = 2; + Mesh mesh = Mesh::MakeCartesian3D(2, 2, 2, Element::HEXAHEDRON); + + // Make the mesh NC + mesh.EnsureNCMesh(); + { + Array elements_to_refine(1); + elements_to_refine[0] = 1; + mesh.GeneralRefinement(elements_to_refine, 1, 0); + } + + auto pmesh = new ParMesh(MPI_COMM_WORLD, mesh); + mesh.Clear(); + + SECTION("Perfect Approximation X") + { + FunctionCoefficient u_analytic(testhelper_osc::SmoothSolutionX); + + CoefficientRefiner coeffrefiner(order); + coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.PreprocessMesh(*pmesh, max_it); + double osc = coeffrefiner.GetOsc(); + + REQUIRE(osc == MFEM_Approx(0.0)); + } + + SECTION("Perfect Approximation Y") + { + FunctionCoefficient u_analytic(testhelper_osc::SmoothSolutionY); + + CoefficientRefiner coeffrefiner(order); + coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.PreprocessMesh(*pmesh, max_it); + double osc = coeffrefiner.GetOsc(); + + REQUIRE(osc == MFEM_Approx(0.0)); + } + + SECTION("Perfect Approximation Z") + { + FunctionCoefficient u_analytic(testhelper_osc::SmoothSolutionZ); + + CoefficientRefiner coeffrefiner(order); + coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.PreprocessMesh(*pmesh, max_it); + double osc = coeffrefiner.GetOsc(); + + REQUIRE(osc == MFEM_Approx(0.0)); + } + + SECTION("Nonsmooth Approximation X") + { + FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionX); + + CoefficientRefiner coeffrefiner(order); + coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.PreprocessMesh(*pmesh, max_it); + double osc = coeffrefiner.GetOsc(); + + REQUIRE(osc > 0.0); + } + + SECTION("Nonsmooth Approximation Y") + { + FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionY); + + CoefficientRefiner coeffrefiner(order); + coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.PreprocessMesh(*pmesh, max_it); + double osc = coeffrefiner.GetOsc(); + + REQUIRE(osc > 0.0); + } + + SECTION("Nonsmooth Approximation Z") + { + FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionZ); + + CoefficientRefiner coeffrefiner(order); + coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.PreprocessMesh(*pmesh, max_it); + double osc = coeffrefiner.GetOsc(); + + REQUIRE(osc > 0.0); + } + + delete pmesh; +} + +#endif From ec09fe54e43ba6cae5d948d63a4f318068eab365 Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 30 Jul 2021 23:13:23 -0700 Subject: [PATCH 055/198] style --- tests/unit/fem/test_oscillation.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/unit/fem/test_oscillation.cpp b/tests/unit/fem/test_oscillation.cpp index 9ea9703983..20114db450 100644 --- a/tests/unit/fem/test_oscillation.cpp +++ b/tests/unit/fem/test_oscillation.cpp @@ -73,7 +73,7 @@ TEST_CASE("Data Oscillation on 2D NCMesh", SECTION("Perfect Approximation X") { FunctionCoefficient u_analytic(testhelper_osc::SmoothSolutionX); - + CoefficientRefiner coeffrefiner(order); coeffrefiner.SetCoefficient(u_analytic); coeffrefiner.PreprocessMesh(*pmesh); @@ -97,7 +97,7 @@ TEST_CASE("Data Oscillation on 2D NCMesh", SECTION("Nonsmooth Approximation X") { FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionX); - + CoefficientRefiner coeffrefiner(order); coeffrefiner.SetCoefficient(u_analytic); coeffrefiner.PreprocessMesh(*pmesh); @@ -109,7 +109,7 @@ TEST_CASE("Data Oscillation on 2D NCMesh", SECTION("Nonsmooth Approximation Y") { FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionY); - + CoefficientRefiner coeffrefiner(order); coeffrefiner.SetCoefficient(u_analytic); coeffrefiner.PreprocessMesh(*pmesh); @@ -188,7 +188,7 @@ TEST_CASE("Data Oscillation on 2D NCMesh embedded in 3D", SECTION("Perfect Approximation X") { FunctionCoefficient u_analytic(testhelper_osc::SmoothSolutionX); - + CoefficientRefiner coeffrefiner(order); coeffrefiner.SetCoefficient(u_analytic); coeffrefiner.PreprocessMesh(*pmesh, max_it); @@ -200,7 +200,7 @@ TEST_CASE("Data Oscillation on 2D NCMesh embedded in 3D", SECTION("Perfect Approximation Y") { FunctionCoefficient u_analytic(testhelper_osc::SmoothSolutionY); - + CoefficientRefiner coeffrefiner(order); coeffrefiner.SetCoefficient(u_analytic); coeffrefiner.PreprocessMesh(*pmesh, max_it); @@ -212,7 +212,7 @@ TEST_CASE("Data Oscillation on 2D NCMesh embedded in 3D", SECTION("Nonsmooth Approximation X") { FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionX); - + CoefficientRefiner coeffrefiner(order); coeffrefiner.SetCoefficient(u_analytic); coeffrefiner.PreprocessMesh(*pmesh, max_it); @@ -224,7 +224,7 @@ TEST_CASE("Data Oscillation on 2D NCMesh embedded in 3D", SECTION("Nonsmooth Approximation Y") { FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionY); - + CoefficientRefiner coeffrefiner(order); coeffrefiner.SetCoefficient(u_analytic); coeffrefiner.PreprocessMesh(*pmesh, max_it); From 5ffd03a2da3ae6bc10b8643952d914cbee9a22e8 Mon Sep 17 00:00:00 2001 From: Keith Date: Mon, 2 Aug 2021 12:13:56 -0700 Subject: [PATCH 056/198] updated unit tests --- tests/unit/fem/test_oscillation.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/tests/unit/fem/test_oscillation.cpp b/tests/unit/fem/test_oscillation.cpp index 20114db450..d6717958d4 100644 --- a/tests/unit/fem/test_oscillation.cpp +++ b/tests/unit/fem/test_oscillation.cpp @@ -100,10 +100,11 @@ TEST_CASE("Data Oscillation on 2D NCMesh", CoefficientRefiner coeffrefiner(order); coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.SetThreshold(1e-3); coeffrefiner.PreprocessMesh(*pmesh); double osc = coeffrefiner.GetOsc(); - REQUIRE(osc > 0.0); + REQUIRE(osc <= 1e-3); } SECTION("Nonsmooth Approximation Y") @@ -112,10 +113,11 @@ TEST_CASE("Data Oscillation on 2D NCMesh", CoefficientRefiner coeffrefiner(order); coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.SetThreshold(1e-3); coeffrefiner.PreprocessMesh(*pmesh); double osc = coeffrefiner.GetOsc(); - REQUIRE(osc > 0.0); + REQUIRE(osc <= 1e-3); } delete pmesh; @@ -215,10 +217,11 @@ TEST_CASE("Data Oscillation on 2D NCMesh embedded in 3D", CoefficientRefiner coeffrefiner(order); coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.SetThreshold(1e-3); coeffrefiner.PreprocessMesh(*pmesh, max_it); double osc = coeffrefiner.GetOsc(); - REQUIRE(osc > 0.0); + REQUIRE(osc <= 1e-3); } SECTION("Nonsmooth Approximation Y") @@ -227,10 +230,11 @@ TEST_CASE("Data Oscillation on 2D NCMesh embedded in 3D", CoefficientRefiner coeffrefiner(order); coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.SetThreshold(1e-3); coeffrefiner.PreprocessMesh(*pmesh, max_it); double osc = coeffrefiner.GetOsc(); - REQUIRE(osc > 0.0); + REQUIRE(osc <= 1e-3); } delete pmesh; @@ -297,10 +301,11 @@ TEST_CASE("Data Oscillation on 3D NCMesh", CoefficientRefiner coeffrefiner(order); coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.SetThreshold(1e-3); coeffrefiner.PreprocessMesh(*pmesh, max_it); double osc = coeffrefiner.GetOsc(); - REQUIRE(osc > 0.0); + REQUIRE(osc <= 1e-3); } SECTION("Nonsmooth Approximation Y") @@ -309,10 +314,11 @@ TEST_CASE("Data Oscillation on 3D NCMesh", CoefficientRefiner coeffrefiner(order); coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.SetThreshold(1e-3); coeffrefiner.PreprocessMesh(*pmesh, max_it); double osc = coeffrefiner.GetOsc(); - REQUIRE(osc > 0.0); + REQUIRE(osc <= 1e-3); } SECTION("Nonsmooth Approximation Z") @@ -321,10 +327,11 @@ TEST_CASE("Data Oscillation on 3D NCMesh", CoefficientRefiner coeffrefiner(order); coeffrefiner.SetCoefficient(u_analytic); + coeffrefiner.SetThreshold(1e-3); coeffrefiner.PreprocessMesh(*pmesh, max_it); double osc = coeffrefiner.GetOsc(); - REQUIRE(osc > 0.0); + REQUIRE(osc <= 1e-3); } delete pmesh; From c0912fe75e39bdbf1101db68764b6fddfcbd29ff Mon Sep 17 00:00:00 2001 From: Keith Date: Mon, 2 Aug 2021 12:53:29 -0700 Subject: [PATCH 057/198] added support to return local oscs --- mesh/mesh_operators.cpp | 18 +++++++++++------- mesh/mesh_operators.hpp | 14 +++++++++++--- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index 9ef41b3819..7034c8f6bb 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -233,32 +233,36 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) // Define osc_K(f) := || h ⋅ (I - Π) f ||_K and select elements // for refinement based on threshold. Also record relative osc(f). - relative_osc = 0.0; + global_osc = 0.0; mesh_refinements.SetSize(0); + element_oscs.Destroy(); + element_oscs.SetSize(NE); + element_oscs = 0.0; for (int j = 0; j < NE; j++) { double h = mesh.GetElementSize(j); double element_osc = h * element_norms_of_fine_scale(j); - relative_osc += element_osc*element_osc; if ( element_osc > threshold * av_norm_of_coeff ) { mesh_refinements.Append(j); } + element_oscs(j) = element_osc/(norm_of_coeff + 1e-10); + global_osc += element_osc*element_osc; } #ifdef MFEM_USE_MPI if (par) { MPI_Comm comm = pmesh->GetComm(); - MPI_Allreduce(MPI_IN_PLACE, &relative_osc, 1, MPI_DOUBLE, MPI_SUM, comm); + MPI_Allreduce(MPI_IN_PLACE, &global_osc, 1, MPI_DOUBLE, MPI_SUM, comm); MPI_Comm_rank(comm, &rank); } #endif - relative_osc = sqrt(relative_osc)/(norm_of_coeff + 1e-10); + global_osc = sqrt(global_osc)/(norm_of_coeff + 1e-10); // Exit if the global threshold or maximum number of elements is reached. - if (relative_osc < threshold || globalNE > max_elements) + if (global_osc < threshold || globalNE > max_elements) { - if (relative_osc > threshold && globalNE > max_elements && rank == 0) + if (global_osc > threshold && globalNE > max_elements && rank == 0) { MFEM_WARNING("Reached maximum number of elements " "before resolving data to tolerance."); @@ -282,7 +286,7 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) void CoefficientRefiner::Reset() { - relative_osc = 0.0; + global_osc = 0.0; coeff = NULL; irs = NULL; } diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index 859c24eed6..d8b2fe9066 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -332,8 +332,9 @@ protected: int order; long max_elements = std::numeric_limits::max(); double threshold = 1.0e-2; - double relative_osc = 0.0; + double global_osc = 0.0; Array mesh_refinements; + Vector element_oscs; Coefficient *coeff = NULL; GridFunction *gf; const IntegrationRule *ir_default[Geometry::NumGeom]; @@ -371,7 +372,7 @@ public: /// Set the function f void SetCoefficient(Coefficient &coeff_) { - relative_osc = 0.0; + global_osc = 0.0; coeff = &coeff_; } @@ -392,7 +393,14 @@ public: void SetIntRule(const IntegrationRule *irs_[]) { irs = irs_; } // Return the value of the global relative data oscillation - double GetOsc() { return relative_osc; } + double GetOsc() { return global_osc; } + + // Return the local relative data oscillation errors + Vector GetLocalOscs() + { + MFEM_ASSERT(element_oscs.Size() > 0, "Local oscillations have not been computed yet") + return element_oscs; + } /// Reset virtual void Reset(); From 6f2c580774d904ad649ef463ac782706ec672d2f Mon Sep 17 00:00:00 2001 From: Keith Date: Mon, 2 Aug 2021 12:54:54 -0700 Subject: [PATCH 058/198] fixed style --- mesh/mesh_operators.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index d8b2fe9066..cbc4c99236 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -398,8 +398,9 @@ public: // Return the local relative data oscillation errors Vector GetLocalOscs() { - MFEM_ASSERT(element_oscs.Size() > 0, "Local oscillations have not been computed yet") - return element_oscs; + MFEM_ASSERT(element_oscs.Size() > 0, + "Local oscillations have not been computed yet") + return element_oscs; } /// Reset From 2cb619d9cf8693d12af4fb7062816cab3506eb94 Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Mon, 2 Aug 2021 14:22:19 -0700 Subject: [PATCH 059/198] minor change to how level sets are indicated --- miniapps/shifted/diffusion.cpp | 71 +++++++++++++-------------------- miniapps/shifted/marking.cpp | 4 +- miniapps/shifted/sbm_aux.hpp | 15 +++---- miniapps/shifted/sbm_solver.cpp | 26 +++++------- miniapps/shifted/sbm_solver.hpp | 60 ++++++++++++++++------------ 5 files changed, 80 insertions(+), 96 deletions(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index 56cbd90ef7..88f9a8d51c 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -65,6 +65,7 @@ // inhomogeneous Dirichlet, and square hole with homogeneous Dirichlet // boundary condition. // mpirun -np 1 diffusion -m ../../data/inline-quad.mesh -rs 3 -o 1 -vis -lst 5 -ho 1 -nlst 7 -alpha 10.0 -dlstc 6 + #include "mfem.hpp" #include "../common/mfem-common.hpp" #include "sbm_aux.hpp" @@ -406,7 +407,7 @@ int main(int argc, char *argv[]) ShiftedFunctionCoefficient *dbcCoefCombo = NULL; if (dirichlet_level_set_type_combo == 6) { - dbcCoefCombo = new ShiftedFunctionCoefficient(unity); + dbcCoefCombo = new ShiftedFunctionCoefficient(ConstantCoefficient(0.015)); } // Homogeneous Neumann boundary condition coefficient @@ -414,21 +415,26 @@ int main(int argc, char *argv[]) ShiftedVectorFunctionCoefficient *normalbcCoef = NULL; if (neumann_level_set_type == 1) { - normalbcCoef = new ShiftedVectorFunctionCoefficient(dim, normal_vector); + normalbcCoef = new ShiftedVectorFunctionCoefficient(dim, normal_vector_1); } else if (neumann_level_set_type == 7) { - normalbcCoef = new ShiftedVectorFunctionCoefficient(dim, normal_vector2); + normalbcCoef = new ShiftedVectorFunctionCoefficient(dim, normal_vector_2); } else if (neumann_level_set_type > 0) { MFEM_ABORT(" Normal vector coefficient not implemented for level set."); } + // Add integrators corresponding to the shifted boundary method (SBM) // for Dirichlet boundaries. - int cut_marker_offset = 0; - Array bilinear_dirichlet_marker(0), - bilinear_neumann_marker(0); + // For each LinearFormIntegrator, we indicate the marker that we have used + // for the cut-cell corresponding to the level-set. + int ls_cut_marker = ShiftedFaceMarker::SBElementType::CUT; + // For each BilinearFormIntegrators, we make a list of the markers + // corresponding to the cut-cell whose faces they will be applied to. + Array bf_dirichlet_marker(0), + bf_neumann_marker(0); if (dirichlet_level_set_type > 0) { @@ -437,50 +443,43 @@ int main(int argc, char *argv[]) elem_marker, include_cut_cell, ho_terms, - cut_marker_offset)); + ls_cut_marker)); b.AddBdrFaceIntegrator(new SBM2DirichletLFIntegrator(&pmesh, *dbcCoef, alpha, *dist_vec, elem_marker, include_cut_cell, ho_terms, - cut_marker_offset), ess_shift_bdr); - bilinear_dirichlet_marker.Append(ShiftedFaceMarker::SBElementType::CUT - +cut_marker_offset); - cut_marker_offset += 1; + ls_cut_marker), + ess_shift_bdr); + bf_dirichlet_marker.Append(ls_cut_marker); } if (dirichlet_level_set_type_combo == 6) { + + ls_cut_marker += 1; b.AddInteriorFaceIntegrator(new SBM2DirichletLFIntegrator(&pmesh, *dbcCoefCombo, alpha, *dist_vec, elem_marker, include_cut_cell, ho_terms, - cut_marker_offset)); - bilinear_dirichlet_marker.Append(ShiftedFaceMarker::SBElementType::CUT - +cut_marker_offset); - cut_marker_offset += 1; + ls_cut_marker)); + bf_dirichlet_marker.Append(ls_cut_marker); } // Add integrators corresponding to the shifted boundary method (SBM) // for Neumann boundaries. if (neumann_level_set_type > 0) { + ls_cut_marker += 1; b.AddInteriorFaceIntegrator(new SBM2NeumannLFIntegrator( &pmesh, nbcCoef, alpha, *dist_vec, *normalbcCoef, - elem_marker, include_cut_cell, ho_terms, cut_marker_offset)); - bilinear_neumann_marker.Append(ShiftedFaceMarker::SBElementType::CUT - +cut_marker_offset); - cut_marker_offset += 1; + elem_marker, include_cut_cell, ho_terms, ls_cut_marker)); + bf_neumann_marker.Append(ls_cut_marker); } b.Assemble(); - // elem_marker.Print(); - // bilinear_dirichlet_marker.Print(); - // bilinear_neumann_marker.Print(); - // MFEM_ABORT(" "); - // Set up the bilinear form a(.,.) on the finite element space corresponding // to the Laplacian operator -Delta, by adding the Diffusion domain // integrator and SBM integrator. @@ -492,12 +491,12 @@ int main(int argc, char *argv[]) a.AddInteriorFaceIntegrator(new SBM2DirichletIntegrator(&pmesh, alpha, *dist_vec, elem_marker, - bilinear_dirichlet_marker, + bf_dirichlet_marker, include_cut_cell, ho_terms)); a.AddBdrFaceIntegrator(new SBM2DirichletIntegrator(&pmesh, alpha, *dist_vec, elem_marker, - bilinear_dirichlet_marker, + bf_dirichlet_marker, include_cut_cell, ho_terms), ess_shift_bdr); } @@ -509,7 +508,7 @@ int main(int argc, char *argv[]) *dist_vec, *normalbcCoef, elem_marker, - bilinear_neumann_marker, + bf_neumann_marker, include_cut_cell, ho_terms)); } @@ -520,25 +519,11 @@ int main(int argc, char *argv[]) // Project the exact solution as an initial condition for Dirichlet boundary. x = 0.0; - if (dirichlet_level_set_type > 0) + if (dirichlet_level_set_type > 0 && dirichlet_level_set_type_combo != 6) { - //x.ProjectCoefficient(*dbcCoef); - if (dirichlet_level_set_type_combo == 6) - { - x = 0.0; - } + x.ProjectCoefficient(*dbcCoef); } - if (visualization) - { - char vishost[] = "localhost"; - int visport = 19916, s = 350; - socketstream sol_sock; - common::VisualizeField(sol_sock, vishost, visport, x, - "Solution", s, 0, s, s, "Rj"); - } - - // Form the linear system and solve it. OperatorPtr A; Vector B, X; diff --git a/miniapps/shifted/marking.cpp b/miniapps/shifted/marking.cpp index f659e316a9..f15c01a080 100644 --- a/miniapps/shifted/marking.cpp +++ b/miniapps/shifted/marking.cpp @@ -75,12 +75,14 @@ void ShiftedFaceMarker::MarkElements(Array &elem_marker) if (count == ir.GetNPoints()) // completely outside { + MFEM_VERIFY(elem_marker[i] != SBElementType::OUTSIDE, + "An element cannot be excluded by more than 1 level-set."); elem_marker[i] = SBElementType::OUTSIDE; } else if (count > 0) // partially outside { MFEM_VERIFY(elem_marker[i] <= SBElementType::OUTSIDE, - " One element cut by multiple level-sets."); + "An element cannot be cut by multiple level-sets."); elem_marker[i] = SBElementType::CUT + level_set_index; } } diff --git a/miniapps/shifted/sbm_aux.hpp b/miniapps/shifted/sbm_aux.hpp index 2260d32686..2d58acc6b9 100644 --- a/miniapps/shifted/sbm_aux.hpp +++ b/miniapps/shifted/sbm_aux.hpp @@ -208,11 +208,6 @@ double unity(const Vector &x) return 0.015; } -double zero(const Vector &x) -{ - return 0.0; -} - double dirichlet_velocity_xy_exponent(const Vector &x) { double xy_p = 2.; // exponent for level set 2 where u = x^p+y^p; @@ -231,21 +226,21 @@ double neumann_velocity_circle(const Vector &x) } /// Normal vector for level_set_type = 1. Circle centered at [0.5 , 0.5] -void normal_vector(const Vector &x, Vector &p) +void normal_vector_1(const Vector &x, Vector &p) { p.SetSize(x.Size()); p(0) = x(0)-0.5; - p(1) = x(1)-0.5; //center of circle at [0.5, 0.5] + p(1) = x(1)-0.5; // center of circle at [0.5, 0.5] p /= p.Norml2(); p *= -1; } -/// Normal vector for level_set_type = 6. Circle centered at [0.75 , 0.25] -void normal_vector2(const Vector &x, Vector &p) +/// Normal vector for level_set_type = 7. Circle centered at [0.75 , 0.25] +void normal_vector_2(const Vector &x, Vector &p) { p.SetSize(x.Size()); p(0) = x(0)-0.5; - p(1) = x(1)-0.6; //center of circle at [0.5, 0.6] + p(1) = x(1)-0.6; // center of circle at [0.5, 0.6] p /= p.Norml2(); p *= -1; } diff --git a/miniapps/shifted/sbm_solver.cpp b/miniapps/shifted/sbm_solver.cpp index a5edc25ffc..8d0b26521f 100644 --- a/miniapps/shifted/sbm_solver.cpp +++ b/miniapps/shifted/sbm_solver.cpp @@ -19,6 +19,8 @@ double ShiftedFunctionCoefficient::Eval(ElementTransformation & T, const IntegrationPoint & ip, const Vector &D) { + if (constantcoefficient) { return constant; } + Vector transip; T.Transform(ip, transip); for (int i = 0; i < D.Size(); i++) @@ -395,16 +397,14 @@ void SBM2DirichletLFIntegrator::AssembleRHSElementVect( { // 1 is inside and 2 is cut or 1 is a boundary element. if ( marker1 == ShiftedFaceMarker::SBElementType::INSIDE && - (marker2 == ShiftedFaceMarker::SBElementType::CUT - + cut_cell_marker_offset || + (marker2 == ls_cut_marker || Tr.ElementType == ElementTransformation::BDR_FACE)) { elem1f = true; ndof = ndof1; } // 1 is cut, 2 is inside - else if (marker1 == ShiftedFaceMarker::SBElementType::CUT - + cut_cell_marker_offset && + else if (marker1 == ls_cut_marker && marker2 == ShiftedFaceMarker::SBElementType::INSIDE) { if (Tr.Elem2No >= NEproc) { return; } @@ -419,8 +419,7 @@ void SBM2DirichletLFIntegrator::AssembleRHSElementVect( else { // 1 is cut and 2 is outside or 1 is a boundary element. - if (marker1 == ShiftedFaceMarker::SBElementType::CUT - + cut_cell_marker_offset && + if (marker1 == ls_cut_marker && (marker2 == ShiftedFaceMarker::SBElementType::OUTSIDE || Tr.ElementType == ElementTransformation::BDR_FACE)) { @@ -429,8 +428,7 @@ void SBM2DirichletLFIntegrator::AssembleRHSElementVect( } // 1 is outside, 2 is cut else if (marker1 == ShiftedFaceMarker::SBElementType::OUTSIDE && - marker2 == ShiftedFaceMarker::SBElementType::CUT - + cut_cell_marker_offset) + marker2 == ls_cut_marker) { if (Tr.Elem2No >= NEproc) { return; } elem1f = false; @@ -1010,16 +1008,14 @@ void SBM2NeumannLFIntegrator::AssembleRHSElementVect( { // 1 is inside and 2 is cut or 1 is a boundary element. if ( marker1 == ShiftedFaceMarker::SBElementType::INSIDE && - (marker2 == ShiftedFaceMarker::SBElementType::CUT - + cut_cell_marker_offset || + (marker2 == ls_cut_marker || Tr.ElementType == ElementTransformation::BDR_FACE)) { elem1f = true; ndof = ndof1; } // 1 is cut, 2 is inside - else if (marker1 == ShiftedFaceMarker::SBElementType::CUT - + cut_cell_marker_offset && + else if (marker1 == ls_cut_marker && marker2 == ShiftedFaceMarker::SBElementType::INSIDE) { if (Tr.Elem2No >= NEproc) { return; } @@ -1034,8 +1030,7 @@ void SBM2NeumannLFIntegrator::AssembleRHSElementVect( else { // 1 is cut and 2 is outside or 1 is a boundary element. - if (marker1 == ShiftedFaceMarker::SBElementType::CUT - + cut_cell_marker_offset && + if (marker1 == ls_cut_marker && (marker2 == ShiftedFaceMarker::SBElementType::OUTSIDE || Tr.ElementType == ElementTransformation::BDR_FACE)) { @@ -1044,8 +1039,7 @@ void SBM2NeumannLFIntegrator::AssembleRHSElementVect( } // 1 is outside, 2 is cut else if (marker1 == ShiftedFaceMarker::SBElementType::OUTSIDE && - marker2 == ShiftedFaceMarker::SBElementType::CUT - + cut_cell_marker_offset) + marker2 == ls_cut_marker) { if (Tr.Elem2No >= NEproc) { return; } elem1f = false; diff --git a/miniapps/shifted/sbm_solver.hpp b/miniapps/shifted/sbm_solver.hpp index 9a689a541d..1c459efbda 100644 --- a/miniapps/shifted/sbm_solver.hpp +++ b/miniapps/shifted/sbm_solver.hpp @@ -24,15 +24,22 @@ class ShiftedFunctionCoefficient : public Coefficient { protected: std::function Function; + double constant = 0.0; + bool constantcoefficient; public: ShiftedFunctionCoefficient(std::function F) - : Function(std::move(F)) { } + : Function(std::move(F)), constantcoefficient(false) { } + ShiftedFunctionCoefficient(ConstantCoefficient C) + : constant(C.constant), constantcoefficient(true) { } virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip) { - Vector D(1); + if (constantcoefficient) { return constant; } + Vector transip; + T.Transform(ip, transip); + Vector D = transip; D = 0.; return (this)->Eval(T, ip, D); } @@ -90,10 +97,11 @@ protected: bool include_cut_cell; // include element cut by true boundary int nterms; // Number of terms in addition to the gradient // term from Taylor expansion that should be included. (0 by default). - int NEproc; //Number of elements on the current MPI rank + int NEproc; // Number of elements on the current MPI rank int par_shared_face_count; // - Array cut_marker; + Array cut_marker; // Array with marker values for cut-cell + // corresponding to the level set that BilinearForm applies to. // these are not thread-safe! Vector shape, dshapedn, dshapephysdn, nor, nh, ni; @@ -146,15 +154,15 @@ protected: ShiftedFunctionCoefficient *uD; double alpha; // Nitsche parameter VectorCoefficient *vD; // Distance function coefficient - Array *elem_marker; //marker indicating whether element is inside, + Array *elem_marker; // marker indicating whether element is inside, //cut, or outside the domain. bool include_cut_cell; // include element cut by true boundary int nterms; // Number of terms in addition to the gradient // term from Taylor expansion that should be included. (0 by default). - int NEproc; //Number of elements on the current MPI rank + int NEproc; // Number of elements on the current MPI rank int par_shared_face_count; // - - int cut_cell_marker_offset; + int ls_cut_marker; // Flag used for the cut-cell corresponding to the + // level set. // these are not thread-safe! Vector shape, dshape_dd, dshape_dn, nor, nh, ni; @@ -168,14 +176,14 @@ public: Array &elem_marker_, bool include_cut_cell_ = false, int nterms_ = 0, - int cut_cell_marker_offset_ = 0) + int ls_cut_marker_ = ShiftedFaceMarker::SBElementType::CUT) : uD(&u), alpha(alpha_), vD(&vD_), elem_marker(&elem_marker_), include_cut_cell(include_cut_cell_), nterms(nterms_), NEproc(pmesh->GetNE()), par_shared_face_count(0), - cut_cell_marker_offset(cut_cell_marker_offset_) { } + ls_cut_marker(ls_cut_marker_) { } virtual void AssembleRHSElementVect(const FiniteElement &el, ElementTransformation &Tr, @@ -195,14 +203,14 @@ class SBM2NeumannIntegrator : public BilinearFormIntegrator { protected: double alpha; - VectorCoefficient *vD; // Distance function coefficient ShiftedVectorFunctionCoefficient *vN; // Normal function coefficient - Array *elem_marker; //marker indicating whether element is inside, + VectorCoefficient *vD; // Distance function coefficient + Array *elem_marker; // Marker indicating whether element is inside, //cut, or outside the domain. bool include_cut_cell; int nterms; // Number of terms in addition to the gradient // term from Taylor expansion that should be included. (0 by default). - int NEproc; //Number of elements on the current MPI rank + int NEproc; // Number of elements on the current MPI rank int par_shared_face_count; // Array cut_marker; @@ -222,7 +230,7 @@ public: Array &cut_marker_, bool include_cut_cell_ = false, int nterms_ = 0) - : alpha(alpha_), vD(&vD_), vN(&vN_), + : alpha(alpha_), vN(&vN_), vD(&vD_), elem_marker(&elem_marker_), include_cut_cell(include_cut_cell_), nterms(nterms_), @@ -244,17 +252,17 @@ public: class SBM2NeumannLFIntegrator : public LinearFormIntegrator { protected: - ShiftedFunctionCoefficient *uN; //Neumann condition on true boundary - VectorCoefficient *vD; // Distance function coefficient - ShiftedVectorFunctionCoefficient *vN; // Normal function coefficient - Array *elem_marker; //marker indicating whether element is inside, - double alpha; // Nitsche parameter - int nterms; //Number of terms in addition to the gradient term from Taylor - //expansion that should be included. (0 by default). + ShiftedVectorFunctionCoefficient *vN; // Normal function coefficient + ShiftedFunctionCoefficient *uN; // Neumann condition on true boundary + VectorCoefficient *vD; // Distance function coefficient + Array *elem_marker; // Marker indicating whether element is inside, + double alpha; // Nitsche parameter + int nterms; // Number of terms in addition to the gradient + // term from Taylor expansion that should be included. (0 by default). bool include_cut_cell; - int NEproc; //Number of elements on the current MPI rank + int NEproc; // Number of elements on the current MPI rank int par_shared_face_count; // - int cut_cell_marker_offset; + int ls_cut_marker; // these are not thread-safe! Vector shape, dshape_dd, dshape_dn, nor, nh, ni; @@ -269,14 +277,14 @@ public: Array &elem_marker_, int nterms_ = 0, bool include_cut_cell_ = true, - int cut_cell_marker_offset_ = 0) - : uN(&u), vD(&vD_), vN(&vN_), + int ls_cut_marker_ = ShiftedFaceMarker::SBElementType::CUT) + : vN(&vN_), uN(&u), vD(&vD_), elem_marker(&elem_marker_), alpha(alpha_), nterms(nterms_), include_cut_cell(include_cut_cell_), NEproc(pmesh->GetNE()), par_shared_face_count(0), - cut_cell_marker_offset(cut_cell_marker_offset_) { } + ls_cut_marker(ls_cut_marker_) { } virtual void AssembleRHSElementVect(const FiniteElement &el, ElementTransformation &Tr, From 93be96fcc048f358d8f09d5e8c9703a6df51e633 Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Mon, 2 Aug 2021 14:25:31 -0700 Subject: [PATCH 060/198] make style --- miniapps/shifted/diffusion.cpp | 2 +- miniapps/shifted/sbm_solver.hpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index 88f9a8d51c..553b160922 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -434,7 +434,7 @@ int main(int argc, char *argv[]) // For each BilinearFormIntegrators, we make a list of the markers // corresponding to the cut-cell whose faces they will be applied to. Array bf_dirichlet_marker(0), - bf_neumann_marker(0); + bf_neumann_marker(0); if (dirichlet_level_set_type > 0) { diff --git a/miniapps/shifted/sbm_solver.hpp b/miniapps/shifted/sbm_solver.hpp index 1c459efbda..03097da4bd 100644 --- a/miniapps/shifted/sbm_solver.hpp +++ b/miniapps/shifted/sbm_solver.hpp @@ -252,8 +252,8 @@ public: class SBM2NeumannLFIntegrator : public LinearFormIntegrator { protected: - ShiftedVectorFunctionCoefficient *vN; // Normal function coefficient - ShiftedFunctionCoefficient *uN; // Neumann condition on true boundary + ShiftedVectorFunctionCoefficient *vN; // Normal function coefficient + ShiftedFunctionCoefficient *uN; // Neumann condition on true boundary VectorCoefficient *vD; // Distance function coefficient Array *elem_marker; // Marker indicating whether element is inside, double alpha; // Nitsche parameter @@ -279,12 +279,12 @@ public: bool include_cut_cell_ = true, int ls_cut_marker_ = ShiftedFaceMarker::SBElementType::CUT) : vN(&vN_), uN(&u), vD(&vD_), - elem_marker(&elem_marker_), - alpha(alpha_), nterms(nterms_), - include_cut_cell(include_cut_cell_), - NEproc(pmesh->GetNE()), - par_shared_face_count(0), - ls_cut_marker(ls_cut_marker_) { } + elem_marker(&elem_marker_), + alpha(alpha_), nterms(nterms_), + include_cut_cell(include_cut_cell_), + NEproc(pmesh->GetNE()), + par_shared_face_count(0), + ls_cut_marker(ls_cut_marker_) { } virtual void AssembleRHSElementVect(const FiniteElement &el, ElementTransformation &Tr, From 81aacd2c9efaa61a335ed5d04887f5a422eb9641 Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Mon, 2 Aug 2021 15:28:13 -0700 Subject: [PATCH 061/198] documentation --- miniapps/shifted/diffusion.cpp | 5 +++++ miniapps/shifted/sbm_aux.hpp | 7 +----- miniapps/shifted/sbm_solver.cpp | 5 ----- miniapps/shifted/sbm_solver.hpp | 39 ++++++++++++++++++++++++--------- 4 files changed, 35 insertions(+), 21 deletions(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index 07bfcd7b98..388b3b9e37 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -619,9 +619,14 @@ int main(int argc, char *argv[]) // Free the used memory. delete prec; delete bicg; + delete normalbcCoef; + delete dbcCoefCombo; delete dbcCoef; delete rhs_f; delete dist_vec; + delete neumann_dist_coef; + delete dirichlet_dist_coef; + delete dirichlet_dist_coef_2; MPI_Finalize(); diff --git a/miniapps/shifted/sbm_aux.hpp b/miniapps/shifted/sbm_aux.hpp index 8cc71078a3..c4b1ab894f 100644 --- a/miniapps/shifted/sbm_aux.hpp +++ b/miniapps/shifted/sbm_aux.hpp @@ -9,7 +9,7 @@ // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. -#include "../../mfem.hpp" +#include "mfem.hpp" #include #include @@ -205,11 +205,6 @@ double dirichlet_velocity_circle(const Vector &x) return 0.; } -double unity(const Vector &x) -{ - return 0.015; -} - double dirichlet_velocity_xy_exponent(const Vector &x) { double xy_p = 2.; // exponent for level set 2 where u = x^p+y^p; diff --git a/miniapps/shifted/sbm_solver.cpp b/miniapps/shifted/sbm_solver.cpp index 8d0b26521f..a57c940b8b 100644 --- a/miniapps/shifted/sbm_solver.cpp +++ b/miniapps/shifted/sbm_solver.cpp @@ -752,7 +752,6 @@ void SBM2NeumannIntegrator::AssembleFaceMatrix( shape.SetSize(ndof1); dshape.SetSize(ndof1, dim); - dshapephys.SetSize(ndof1, dim); dshapedn.SetSize(ndof1); Vector wrk = shape; @@ -1056,10 +1055,6 @@ void SBM2NeumannLFIntegrator::AssembleRHSElementVect( nor.SetSize(dim); - nh.SetSize(dim); - ni.SetSize(dim); - adjJ.SetSize(dim); - shape.SetSize(ndof); const IntegrationRule *ir = IntRule; diff --git a/miniapps/shifted/sbm_solver.hpp b/miniapps/shifted/sbm_solver.hpp index 03097da4bd..3d6c555df4 100644 --- a/miniapps/shifted/sbm_solver.hpp +++ b/miniapps/shifted/sbm_solver.hpp @@ -99,13 +99,12 @@ protected: // term from Taylor expansion that should be included. (0 by default). int NEproc; // Number of elements on the current MPI rank int par_shared_face_count; // - Array cut_marker; // Array with marker values for cut-cell // corresponding to the level set that BilinearForm applies to. // these are not thread-safe! - Vector shape, dshapedn, dshapephysdn, nor, nh, ni; - DenseMatrix jmat, dshape, dshapephys, adjJ; + Vector shape, dshapedn, nor, nh, ni; + DenseMatrix dshape, dshapephys, adjJ; public: @@ -166,7 +165,7 @@ protected: // these are not thread-safe! Vector shape, dshape_dd, dshape_dn, nor, nh, ni; - DenseMatrix dshape, mq, adjJ; + DenseMatrix dshape, adjJ; public: SBM2DirichletLFIntegrator(const ParMesh *pmesh, @@ -198,7 +197,14 @@ public: }; -// +/// BilinearFormIntegrator for Neumann boundaries using the shifted boundary +/// method. +/// A(u, w) = +/// Since this interior face integrator is applied to the surrogate boundary +/// (see marking.hpp for notes on how the surrogate faces are determined and +/// elements are marked), this integrator adds contribution to only the element +/// that is adjacent to that face (Trans.Elem1 or Trans.Elem2) and is part of +/// the surrogate domain. class SBM2NeumannIntegrator : public BilinearFormIntegrator { protected: @@ -212,13 +218,12 @@ protected: // term from Taylor expansion that should be included. (0 by default). int NEproc; // Number of elements on the current MPI rank int par_shared_face_count; // - Array cut_marker; // these are not thread-safe! - Vector shape, dshapedn, dshapephysdn, nor, nh, ni; - DenseMatrix jmat, dshape, dshapephys, adjJ; + Vector shape, dshapedn, nor, nh, ni; + DenseMatrix dshape, adjJ; public: @@ -249,6 +254,21 @@ public: virtual ~SBM2NeumannIntegrator() { } }; +/// LinearFormIntegrator for Neumann boundaries using the shifted boundary +/// method. +/// (u, w) = +/// where nhat is the normal vector at the true boundary, n is the normal vector +/// at the surrogate boundary, and t_n is the traction boundary condition. +/// Since this interior face integrator is applied to the surrogate boundary +/// (see marking.hpp for notes on how the surrogate faces are determined and +/// elements are marked), this integrator adds contribution to only the element +/// that is adjacent to that face (Trans.Elem1 or Trans.Elem2) and is part of +/// the surrogate domain. +/// Note that t_N is evaluated at the true boundary using the distance function +/// and ShiftedFunctionCoefficient, i.e. t_N(x_true) = t_N(x_surrogate + D), +/// where x_surrogate is the location of the integration point on the surrogate +/// boundary and D is the distance vector from the surrogate boundary to the +/// true boundary. class SBM2NeumannLFIntegrator : public LinearFormIntegrator { protected: @@ -265,8 +285,7 @@ protected: int ls_cut_marker; // these are not thread-safe! - Vector shape, dshape_dd, dshape_dn, nor, nh, ni; - DenseMatrix dshape, mq, adjJ; + Vector shape, nor; public: SBM2NeumannLFIntegrator(const ParMesh *pmesh, From 6a63b46bd4f9fb4d171293302b5d806cb7253e8a Mon Sep 17 00:00:00 2001 From: Keith Date: Mon, 2 Aug 2021 23:34:33 -0700 Subject: [PATCH 062/198] typo in comment --- mesh/mesh_operators.hpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index cbc4c99236..6518fd3795 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -309,15 +309,14 @@ public: /** @brief Refinement operator to control data oscillation. - This class uses the given computes osc_K(f) := || h ⋅ (I - Π) f ||_K at - each element K. Here, Π is the L2-projection and ||⋅||_K is the - L2-norm, restricted to the element K. All elements satisfying the inequality + This class computes osc_K(f) := || h ⋅ (I - Π) f ||_K at each element K. + Here, Π is the L2-projection and ||⋅||_K is the L2-norm, restricted to the + element K. All elements satisfying the inequality \code osc_K(f) > threshold ⋅ ||f|| / sqrt(n_el), \endcode - are refined. Here, threshold is a postive parameter, ||⋅|| is the - L2-norm over the entire domain Ω, and n_el is the number of elements in the - mesh. + are refined. Here, threshold is a postive parameter, ||⋅|| is the L2-norm + over the entire domain Ω, and n_el is the number of elements in the mesh. Note that if osc(f) = threshold ⋅ ||f|| / sqrt(n_el) for each K, then \code From 1cd7bfb28d4e52ffbb5af070799dee26cce548b0 Mon Sep 17 00:00:00 2001 From: Keith Date: Tue, 3 Aug 2021 09:12:50 -0700 Subject: [PATCH 063/198] remove unused variables --- mesh/mesh_operators.cpp | 1 + mesh/mesh_operators.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index 7034c8f6bb..5fb10134dd 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -286,6 +286,7 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) void CoefficientRefiner::Reset() { + element_oscs.Destroy(); global_osc = 0.0; coeff = NULL; irs = NULL; diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index 6518fd3795..b63869b5c2 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -336,7 +336,6 @@ protected: Vector element_oscs; Coefficient *coeff = NULL; GridFunction *gf; - const IntegrationRule *ir_default[Geometry::NumGeom]; const IntegrationRule **irs = NULL; /** @brief Apply the operator to the mesh once. @@ -371,6 +370,7 @@ public: /// Set the function f void SetCoefficient(Coefficient &coeff_) { + element_oscs.Destroy(); global_osc = 0.0; coeff = &coeff_; } From 7ec56ca61fbe0f50d4d7ae0e830d248a205b7cdf Mon Sep 17 00:00:00 2001 From: Keith Date: Tue, 3 Aug 2021 09:17:55 -0700 Subject: [PATCH 064/198] FIX: Removed too much in last commit --- mesh/mesh_operators.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index b63869b5c2..be8d8f9043 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -336,6 +336,7 @@ protected: Vector element_oscs; Coefficient *coeff = NULL; GridFunction *gf; + const IntegrationRule *ir_default[Geometry::NumGeom]; const IntegrationRule **irs = NULL; /** @brief Apply the operator to the mesh once. From 838ac0cf36848d21ecff2b1a5ef91ef0a2141216 Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Tue, 3 Aug 2021 14:13:34 -0700 Subject: [PATCH 065/198] improved documentation and other misc changes --- miniapps/shifted/diffusion.cpp | 55 ++++++++++++++------------- miniapps/shifted/marking.cpp | 2 + miniapps/shifted/marking.hpp | 16 +++++--- miniapps/shifted/quad.mesh | 7 ---- miniapps/shifted/sbm_aux.hpp | 2 +- miniapps/shifted/sbm_solver.cpp | 67 ++++++++++++++------------------- miniapps/shifted/sbm_solver.hpp | 21 ++++++----- 7 files changed, 81 insertions(+), 89 deletions(-) delete mode 100644 miniapps/shifted/quad.mesh diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index 388b3b9e37..795b34bd2a 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -57,14 +57,14 @@ // Solves -nabla^2 u = 1 with homogeneous boundary conditions. // mpirun -np 4 diffusion -rs 5 -lst 4 -alpha 2 -// Problem 5: Circular hole of radius 0.2 at [0.5, 0.5] and [1.5, 0.5] -// Solves -nabla^2 u = 1 with homogeneous Neumann boundary conditions. -// mpirun -np 1 diffusion -m ../../data/inline-quad.mesh -rs 3 -o 1 -vis -lst -1 -ho 1 -nlst 1 +// Problem 5: Circular hole of radius 0.2 at [0.5, 0.5]. +// Solves -nabla^2 u = 1 with homogeneous Neumann boundary conditions. +// mpirun -np 1 diffusion -m ../../data/inline-quad.mesh -rs 3 -o 1 -vis -lst -1 -ho 1 -nlst 1 // Problem 6: Circular hole with homogeneous Neumann, triangular hole with // inhomogeneous Dirichlet, and square hole with homogeneous Dirichlet // boundary condition. -// mpirun -np 1 diffusion -m ../../data/inline-quad.mesh -rs 3 -o 1 -vis -lst 5 -ho 1 -nlst 7 -alpha 10.0 -dlstc 6 +// mpirun -np 1 diffusion -m ../../data/inline-quad.mesh -rs 3 -o 1 -vis -lst 5 -ho 1 -nlst 7 -alpha 10.0 -dlstc 6 #include "mfem.hpp" #include "../common/mfem-common.hpp" @@ -193,6 +193,7 @@ int main(int argc, char *argv[]) // Determine if each element in the ParMesh is inside the actual domain, // partially cut by its boundary, or completely outside the domain. + // Setup the level-set coefficients, and mark the elements. Dist_Level_Set_Coefficient *dirichlet_dist_coef = NULL; Dist_Level_Set_Coefficient *dirichlet_dist_coef_2 = NULL; Dist_Level_Set_Coefficient *neumann_dist_coef = NULL; @@ -202,6 +203,7 @@ int main(int argc, char *argv[]) ShiftedFaceMarker marker(pmesh, pfespace, include_cut_cell); Array elem_marker; + // Dirichlet level-set. if (dirichlet_level_set_type > 0) { // ParGridFunction for level_set_value. @@ -217,10 +219,11 @@ int main(int argc, char *argv[]) combo_dist_coef.Add_Level_Set_Coefficient(*dirichlet_dist_coef); } + // Second Dirichlet level-set. if (dirichlet_level_set_type_combo == 6) { MFEM_VERIFY(dirichlet_level_set_type == 5, - " The combo level set example has been only set for" + "The combo level set example has been only set for" " dirichlet_level_set_type == 5."); ParGridFunction dirichlet_level_set_val(&pfespace); dirichlet_dist_coef_2 = new Dist_Level_Set_Coefficient( @@ -231,7 +234,7 @@ int main(int argc, char *argv[]) combo_dist_coef.Add_Level_Set_Coefficient(*dirichlet_dist_coef_2); } - // Setup the Neumann level set grid function + // Neumann level-set. if (neumann_level_set_type > 0) { ParGridFunction neumann_level_set_val(&pfespace); @@ -293,8 +296,7 @@ int main(int argc, char *argv[]) ParFiniteElementSpace distance_vec_space(&pmesh, &fec, dim); ParGridFunction distance(&distance_vec_space); VectorCoefficient *dist_vec = NULL; - // Compute the distance field using the HeatDistanceSolver for - // level_set_type == 4 or analytically for all other level set types. + // Compute the distance field analytically or using the HeatDistanceSolver. if (dirichlet_level_set_type == 1 || dirichlet_level_set_type == 2 || dirichlet_level_set_type == 3) { @@ -308,14 +310,7 @@ int main(int argc, char *argv[]) double dx = AvgElementSize(pmesh); ParGridFunction filt_gf(&pfespace); PDEFilter *filter = new PDEFilter(pmesh, 2.0 * dx); - if (dirichlet_level_set_type == 4) - { - filter->Filter(*dirichlet_dist_coef, filt_gf); - } - else - { - filter->Filter(combo_dist_coef, filt_gf); - } + filter->Filter(combo_dist_coef, filt_gf); delete filter; GridFunctionCoefficient ls_filt_coeff(&filt_gf); @@ -345,7 +340,6 @@ int main(int argc, char *argv[]) "Distance Vector", s, s, s, s, "Rjmmpcvv", 1); } - // Set up a list to indicate element attributes to be included in assembly, // so that inactive elements are excluded. const int max_elem_attr = pmesh.attributes.Max(); @@ -399,7 +393,7 @@ int main(int argc, char *argv[]) ShiftedFunctionCoefficient *dbcCoef = NULL; if (dirichlet_level_set_type == 1 || dirichlet_level_set_type >= 4) { - dbcCoef = new ShiftedFunctionCoefficient(dirichlet_velocity_circle); + dbcCoef = new ShiftedFunctionCoefficient(0.0); } else if (dirichlet_level_set_type == 2) { @@ -413,7 +407,7 @@ int main(int argc, char *argv[]) ShiftedFunctionCoefficient *dbcCoefCombo = NULL; if (dirichlet_level_set_type_combo == 6) { - dbcCoefCombo = new ShiftedFunctionCoefficient(ConstantCoefficient(0.015)); + dbcCoefCombo = new ShiftedFunctionCoefficient(0.015); } // Homogeneous Neumann boundary condition coefficient @@ -439,8 +433,7 @@ int main(int argc, char *argv[]) int ls_cut_marker = ShiftedFaceMarker::SBElementType::CUT; // For each BilinearFormIntegrators, we make a list of the markers // corresponding to the cut-cell whose faces they will be applied to. - Array bf_dirichlet_marker(0), - bf_neumann_marker(0); + Array bf_dirichlet_marker(0), bf_neumann_marker(0); if (dirichlet_level_set_type > 0) { @@ -458,12 +451,12 @@ int main(int argc, char *argv[]) ls_cut_marker), ess_shift_bdr); bf_dirichlet_marker.Append(ls_cut_marker); + ls_cut_marker += 1; } if (dirichlet_level_set_type_combo == 6) { - ls_cut_marker += 1; b.AddInteriorFaceIntegrator(new SBM2DirichletLFIntegrator(&pmesh, *dbcCoefCombo, alpha, *dist_vec, elem_marker, @@ -471,17 +464,25 @@ int main(int argc, char *argv[]) ho_terms, ls_cut_marker)); bf_dirichlet_marker.Append(ls_cut_marker); + ls_cut_marker += 1; } // Add integrators corresponding to the shifted boundary method (SBM) // for Neumann boundaries. + // High-order extension is not available for Neumann boundary condition. + // Number of terms used for Taylor expansion must be set to 1. + const int neumann_ho_terms = 1; if (neumann_level_set_type > 0) { - ls_cut_marker += 1; + MFEM_VERIFY(!include_cut_cell, "include_cut_cell option must be set to" + " false for Neumann boundary conditions."); b.AddInteriorFaceIntegrator(new SBM2NeumannLFIntegrator( - &pmesh, nbcCoef, alpha, *dist_vec, *normalbcCoef, - elem_marker, include_cut_cell, ho_terms, ls_cut_marker)); + &pmesh, nbcCoef, alpha, *dist_vec, + *normalbcCoef, elem_marker, + include_cut_cell, neumann_ho_terms, + ls_cut_marker)); bf_neumann_marker.Append(ls_cut_marker); + ls_cut_marker += 1; } b.Assemble(); @@ -507,7 +508,7 @@ int main(int argc, char *argv[]) ho_terms), ess_shift_bdr); } - // Add neumann bilinearform integrator + // Add neumann bilinearform integrator. if (neumann_level_set_type > 0) { a.AddInteriorFaceIntegrator(new SBM2NeumannIntegrator(&pmesh, alpha, @@ -516,7 +517,7 @@ int main(int argc, char *argv[]) elem_marker, bf_neumann_marker, include_cut_cell, - ho_terms)); + neumann_ho_terms)); } // Assemble the bilinear form and the corresponding linear system, diff --git a/miniapps/shifted/marking.cpp b/miniapps/shifted/marking.cpp index f15c01a080..71f586b68e 100644 --- a/miniapps/shifted/marking.cpp +++ b/miniapps/shifted/marking.cpp @@ -16,6 +16,8 @@ namespace mfem void ShiftedFaceMarker::MarkElements(Array &elem_marker) { + MFEM_VERIFY(ls_func, "Level-set function to be used for marking has not " + "been specified. Check ShiftedFaceMarker constructor."); elem_marker.SetSize(pmesh.GetNE() + pmesh.GetNSharedFaces()); if (!initial_marking_done) { elem_marker = SBElementType::INSIDE; } else { level_set_index += 1; } diff --git a/miniapps/shifted/marking.hpp b/miniapps/shifted/marking.hpp index 18f2b13340..751eb46469 100644 --- a/miniapps/shifted/marking.hpp +++ b/miniapps/shifted/marking.hpp @@ -12,7 +12,7 @@ #ifndef MFEM_MARKING_HPP #define MFEM_MARKING_HPP -#include "../../mfem.hpp" +#include "mfem.hpp" namespace mfem { @@ -22,11 +22,13 @@ namespace mfem class ShiftedFaceMarker { protected: - ParMesh &pmesh; - ParGridFunction *ls_func; - ParFiniteElementSpace *pfes_sltn; - bool include_cut_cell; - bool initial_marking_done; + ParMesh &pmesh; // Mesh whose elements have to be marked. + ParGridFunction *ls_func; // Gridfunction to be used for marking. + ParFiniteElementSpace *pfes_sltn; // FESpace associated with the solution. + bool include_cut_cell; // Flag indicating wether cut-cells + // will be included in assembly. + bool initial_marking_done; // Flag indicating wether all the elements + // have been marked at-least once. // Marking of face dofs by using an averaged continuous GridFunction. const bool func_dof_marking = false; @@ -40,6 +42,8 @@ private: public: /// Element type related to shifted boundaries (not interfaces). + /// For more than 1 level-set, we set the marker to CUT+level_set_index + /// to discern between different level-sets. enum SBElementType {INSIDE = 0, OUTSIDE = 1, CUT = 2}; ShiftedFaceMarker(ParMesh &pm, ParGridFunction &ls, diff --git a/miniapps/shifted/quad.mesh b/miniapps/shifted/quad.mesh deleted file mode 100644 index 5194ead47f..0000000000 --- a/miniapps/shifted/quad.mesh +++ /dev/null @@ -1,7 +0,0 @@ -MFEM INLINE mesh v1.0 - -type = quad -nx = 8 -ny = 4 -sx = 2.0 -sy = 1.0 diff --git a/miniapps/shifted/sbm_aux.hpp b/miniapps/shifted/sbm_aux.hpp index c4b1ab894f..6564de3b71 100644 --- a/miniapps/shifted/sbm_aux.hpp +++ b/miniapps/shifted/sbm_aux.hpp @@ -232,7 +232,7 @@ void normal_vector_1(const Vector &x, Vector &p) p *= -1; } -/// Normal vector for level_set_type = 7. Circle centered at [0.75 , 0.25] +/// Normal vector for level_set_type = 7. Circle centered at [0.5 , 0.6] void normal_vector_2(const Vector &x, Vector &p) { p.SetSize(x.Size()); diff --git a/miniapps/shifted/sbm_solver.cpp b/miniapps/shifted/sbm_solver.cpp index a57c940b8b..ba3a0d918f 100644 --- a/miniapps/shifted/sbm_solver.cpp +++ b/miniapps/shifted/sbm_solver.cpp @@ -750,9 +750,9 @@ void SBM2NeumannIntegrator::AssembleFaceMatrix( ni.SetSize(dim); adjJ.SetSize(dim); - shape.SetSize(ndof1); - dshape.SetSize(ndof1, dim); - dshapedn.SetSize(ndof1); + shape.SetSize(ndof); + dshape.SetSize(ndof, dim); + dshapedn.SetSize(ndof); Vector wrk = shape; const IntegrationRule *ir = IntRule; @@ -762,11 +762,11 @@ void SBM2NeumannIntegrator::AssembleFaceMatrix( ir = &IntRules.Get(Trans.GetGeometryType(), order); } - MFEM_VERIFY(nterms == 1, " nterms must be 1 for Neumann.\n"); + MFEM_VERIFY(nterms == 1, " High-order extension is not available for Neumann" + " boundary condition. Set nterms=1.\n"); Array dkphi_dxk; DenseMatrix grad_phys; - Vector Factorial; Array grad_phys_dir; if (nterms > 0) @@ -831,25 +831,16 @@ void SBM2NeumannIntegrator::AssembleFaceMatrix( { delete grad_phys_dir[i]; } - - Factorial.SetSize(nterms); - Factorial(0) = 2; - for (int i = 1; i < nterms; i++) - { - Factorial(i) = Factorial(i-1)*(i+2); - } } - DenseMatrix q_hess_dn(dim, ndof1); - Vector q_hess_dn_work(q_hess_dn.GetData(), ndof1*dim); - Vector q_hess_dot_d(ndof1); + DenseMatrix q_hess_dn(dim, ndof); + Vector q_hess_dn_work(q_hess_dn.GetData(), ndof*dim); + Vector q_hess_dot_d_nhat(ndof); Vector D(vD->GetVDim()); - Vector N(vN->GetVDim()); - // assemble: -< \nabla u.n, w > - // -< u + \nabla u.d + h.o.t, \nabla w.n> - // - + Vector Nhat(vN->GetVDim()); + // Assemble: for (int p = 0; p < ir->GetNPoints(); p++) { const IntegrationPoint &ip = ir->IntPoint(p); @@ -873,7 +864,7 @@ void SBM2NeumannIntegrator::AssembleFaceMatrix( CalcOrtho(Trans.Jacobian(), nor); } vD->Eval(D, Trans, ip); - vN->Eval(N, Trans, ip, D); + vN->Eval(Nhat, Trans, ip, D); double nor_dot_d = nor*D; // If we are clipping inside the domain, ntilde and d vector should be @@ -896,29 +887,29 @@ void SBM2NeumannIntegrator::AssembleFaceMatrix( CalcAdjugate(Trans.Elem2->Jacobian(), adjJ); } - ni.Set(w, nor); // alpha_k*nor/det(J) + ni.Set(w, nor); // nor/det(J) adjJ.Mult(ni, nh); - dshape.Mult(nh, dshapedn); //dphi/dn * Jinv * alpha_k * nor + dshape.Mult(nh, dshapedn); //dphi/dn * Jinv * nor // - - Term 2 AddMult_a_VWt(-1., shape, dshapedn, temp_elmat); - // -MultTranspose(shape, T1_wrk); DenseMatrix T2; @@ -926,17 +917,17 @@ void SBM2NeumannIntegrator::AssembleFaceMatrix( for (int j = 0; j < i+1; j++) { int sz2 = pow(dim, i-j); - T2.SetSize(dim, ndof1*sz2); - T2_wrk.SetDataAndSize(T2.GetData(), dim*ndof1*sz2); + T2.SetSize(dim, ndof*sz2); + T2_wrk.SetDataAndSize(T2.GetData(), dim*ndof*sz2); T1.MultTranspose(D, T2_wrk); T1 = T2; } - Vector q_hess_dot_d_work(ndof1); - T1.MultTranspose(N, q_hess_dot_d_work); - q_hess_dot_d += q_hess_dot_d_work; + Vector q_hess_dot_d_work(ndof); + T1.MultTranspose(Nhat, q_hess_dot_d_work); + q_hess_dot_d_nhat += q_hess_dot_d_work; } - wrk = q_hess_dot_d; + wrk = q_hess_dot_d_nhat; wrk *= ip.weight * n_dot_ntilde; AddMult_a_VWt(1., shape, wrk, temp_elmat); @@ -1066,7 +1057,7 @@ void SBM2NeumannLFIntegrator::AssembleRHSElementVect( } Vector D(vD->GetVDim()); - Vector N(vN->GetVDim()); + Vector Nhat(vN->GetVDim()); Vector wrk = shape; for (int p = 0; p < ir->GetNPoints(); p++) { @@ -1089,7 +1080,7 @@ void SBM2NeumannLFIntegrator::AssembleRHSElementVect( CalcOrtho(Tr.Jacobian(), nor); } vD->Eval(D, Tr, ip); - vN->Eval(N, Tr, ip, D); + vN->Eval(Nhat, Tr, ip, D); double nor_dot_d = nor*D; if (!include_cut_cell && nor_dot_d < 0) { nor *= -1; } @@ -1108,7 +1099,7 @@ void SBM2NeumannLFIntegrator::AssembleRHSElementVect( w = ip.weight * uN->Eval(Tr, ip, D); } - double n_dot_ntilde = (nor*N); //nor and N are pointing in opposite direction + double n_dot_ntilde = nor*Nhat; wrk.Set(n_dot_ntilde*w, shape); // F) : Function(std::move(F)), constantcoefficient(false) { } - ShiftedFunctionCoefficient(ConstantCoefficient C) - : constant(C.constant), constantcoefficient(true) { } + ShiftedFunctionCoefficient(double constant_) + : constant(constant_), constantcoefficient(true) { } virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip) @@ -199,12 +199,13 @@ public: /// BilinearFormIntegrator for Neumann boundaries using the shifted boundary /// method. -/// A(u, w) = -/// Since this interior face integrator is applied to the surrogate boundary -/// (see marking.hpp for notes on how the surrogate faces are determined and -/// elements are marked), this integrator adds contribution to only the element -/// that is adjacent to that face (Trans.Elem1 or Trans.Elem2) and is part of -/// the surrogate domain. +/// A(u, w) = <[nabla u + nabla(nabla u).d].nhat (n.nhat), w> - , +/// where nhat is the normal vector at the true boundary, n is the normal vector +/// at the surrogate boundary. Since this interior face integrator is applied to +/// the surrogate boundary (see marking.hpp for notes on how the surrogate faces +/// are determined and elements are marked), this integrator adds contribution +/// to only the element that is adjacent to that face (Trans.Elem1 or +/// Trans.Elem2) and is part of the surrogate domain. class SBM2NeumannIntegrator : public BilinearFormIntegrator { protected: @@ -234,7 +235,7 @@ public: Array &elem_marker_, Array &cut_marker_, bool include_cut_cell_ = false, - int nterms_ = 0) + int nterms_ = 1) : alpha(alpha_), vN(&vN_), vD(&vD_), elem_marker(&elem_marker_), include_cut_cell(include_cut_cell_), @@ -295,7 +296,7 @@ public: ShiftedVectorFunctionCoefficient &vN_, Array &elem_marker_, int nterms_ = 0, - bool include_cut_cell_ = true, + bool include_cut_cell_ = false, int ls_cut_marker_ = ShiftedFaceMarker::SBElementType::CUT) : vN(&vN_), uN(&u), vD(&vD_), elem_marker(&elem_marker_), From 9bf2e813b02b220c5c1810883d0fc34fb5e3b8d8 Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Tue, 3 Aug 2021 14:13:58 -0700 Subject: [PATCH 066/198] make style --- miniapps/shifted/diffusion.cpp | 10 +++++----- miniapps/shifted/marking.cpp | 2 +- miniapps/shifted/marking.hpp | 4 ++-- miniapps/shifted/sbm_solver.cpp | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index 795b34bd2a..b6e552fbb4 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -475,12 +475,12 @@ int main(int argc, char *argv[]) if (neumann_level_set_type > 0) { MFEM_VERIFY(!include_cut_cell, "include_cut_cell option must be set to" - " false for Neumann boundary conditions."); + " false for Neumann boundary conditions."); b.AddInteriorFaceIntegrator(new SBM2NeumannLFIntegrator( - &pmesh, nbcCoef, alpha, *dist_vec, - *normalbcCoef, elem_marker, - include_cut_cell, neumann_ho_terms, - ls_cut_marker)); + &pmesh, nbcCoef, alpha, *dist_vec, + *normalbcCoef, elem_marker, + include_cut_cell, neumann_ho_terms, + ls_cut_marker)); bf_neumann_marker.Append(ls_cut_marker); ls_cut_marker += 1; } diff --git a/miniapps/shifted/marking.cpp b/miniapps/shifted/marking.cpp index 71f586b68e..f286a4df23 100644 --- a/miniapps/shifted/marking.cpp +++ b/miniapps/shifted/marking.cpp @@ -17,7 +17,7 @@ namespace mfem void ShiftedFaceMarker::MarkElements(Array &elem_marker) { MFEM_VERIFY(ls_func, "Level-set function to be used for marking has not " - "been specified. Check ShiftedFaceMarker constructor."); + "been specified. Check ShiftedFaceMarker constructor."); elem_marker.SetSize(pmesh.GetNE() + pmesh.GetNSharedFaces()); if (!initial_marking_done) { elem_marker = SBElementType::INSIDE; } else { level_set_index += 1; } diff --git a/miniapps/shifted/marking.hpp b/miniapps/shifted/marking.hpp index 751eb46469..2f19f00a5b 100644 --- a/miniapps/shifted/marking.hpp +++ b/miniapps/shifted/marking.hpp @@ -26,9 +26,9 @@ protected: ParGridFunction *ls_func; // Gridfunction to be used for marking. ParFiniteElementSpace *pfes_sltn; // FESpace associated with the solution. bool include_cut_cell; // Flag indicating wether cut-cells - // will be included in assembly. + // will be included in assembly. bool initial_marking_done; // Flag indicating wether all the elements - // have been marked at-least once. + // have been marked at-least once. // Marking of face dofs by using an averaged continuous GridFunction. const bool func_dof_marking = false; diff --git a/miniapps/shifted/sbm_solver.cpp b/miniapps/shifted/sbm_solver.cpp index ba3a0d918f..43dc72fe01 100644 --- a/miniapps/shifted/sbm_solver.cpp +++ b/miniapps/shifted/sbm_solver.cpp @@ -763,7 +763,7 @@ void SBM2NeumannIntegrator::AssembleFaceMatrix( } MFEM_VERIFY(nterms == 1, " High-order extension is not available for Neumann" - " boundary condition. Set nterms=1.\n"); + " boundary condition. Set nterms=1.\n"); Array dkphi_dxk; DenseMatrix grad_phys; From bbba4d863f9fea2735fd13f3af044748f3bb7c35 Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Wed, 4 Aug 2021 10:05:49 -0700 Subject: [PATCH 067/198] minor fix for inhomogeneous Neumann --- miniapps/shifted/diffusion.cpp | 53 ++++++++++++++++++++++----------- miniapps/shifted/sbm_aux.hpp | 20 ++++++++----- miniapps/shifted/sbm_solver.cpp | 2 -- 3 files changed, 49 insertions(+), 26 deletions(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index b6e552fbb4..0bb8a751f5 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -33,14 +33,20 @@ // // Problem 1: Circular hole of radius 0.2 at the center of the domain. // Solves -nabla^2 u = 1 with homogeneous boundary conditions. +// Dirichlet boundary condition // mpirun -np 4 diffusion -m ../../data/inline-quad.mesh -rs 3 -o 1 -vis -lst 1 // mpirun -np 4 diffusion -m ../../data/inline-hex.mesh -rs 2 -o 2 -vis -lst 1 -ho 1 -alpha 10 +// Neumann boundary condition +// mpirun -np 1 diffusion -m ../../data/inline-quad.mesh -rs 3 -o 1 -vis -lst -1 -ho 1 -nlst 1 // // Problem 2: Circular hole of radius 0.2 at the center of the domain. // Solves -nabla^2 u = f with inhomogeneous boundary conditions, and // f is setup such that u = x^p + y^p, where p = 2 by default. // This is a 2D convergence test. +// Dirichlet boundary condition // mpirun -np 4 diffusion -rs 2 -o 2 -vis -lst 2 +// Neumann boundary condition (inhomogeneous condition derived using exact solution) +// mpirun -np 1 diffusion -m ../../data/inline-quad.mesh -rs 2 -o 1 -vis -lst -1 -ho 1 -nlst 2 // // Problem 3: Domain is y = [0, 1] but mesh is shifted to [-1.e-4, 1]. // Solves -nabla^2 u = f with inhomogeneous boundary conditions, @@ -56,12 +62,8 @@ // Problem 4: Complex 2D shape: // Solves -nabla^2 u = 1 with homogeneous boundary conditions. // mpirun -np 4 diffusion -rs 5 -lst 4 -alpha 2 - -// Problem 5: Circular hole of radius 0.2 at [0.5, 0.5]. -// Solves -nabla^2 u = 1 with homogeneous Neumann boundary conditions. -// mpirun -np 1 diffusion -m ../../data/inline-quad.mesh -rs 3 -o 1 -vis -lst -1 -ho 1 -nlst 1 - -// Problem 6: Circular hole with homogeneous Neumann, triangular hole with +// +// Problem 5: Circular hole with homogeneous Neumann, triangular hole with // inhomogeneous Dirichlet, and square hole with homogeneous Dirichlet // boundary condition. // mpirun -np 1 diffusion -m ../../data/inline-quad.mesh -rs 3 -o 1 -vis -lst 5 -ho 1 -nlst 7 -alpha 10.0 -dlstc 6 @@ -378,7 +380,7 @@ int main(int argc, char *argv[]) { rhs_f = new FunctionCoefficient(rhs_fun_circle); } - else if (dirichlet_level_set_type == 2) + else if (dirichlet_level_set_type == 2 || neumann_level_set_type == 2) { rhs_f = new FunctionCoefficient(rhs_fun_xy_exponent); } @@ -389,19 +391,24 @@ int main(int argc, char *argv[]) else { MFEM_ABORT("RHS function not set for level set type.\n"); } b.AddDomainIntegrator(new DomainLFIntegrator(*rhs_f), ess_elem); + // Exact solution to project for Dirichlet boundaries + FunctionCoefficient *exactCoef = NULL; // Dirichlet BC that must be imposed on the true boundary. ShiftedFunctionCoefficient *dbcCoef = NULL; if (dirichlet_level_set_type == 1 || dirichlet_level_set_type >= 4) { - dbcCoef = new ShiftedFunctionCoefficient(0.0); + dbcCoef = new ShiftedFunctionCoefficient(homogeneous); + exactCoef = new FunctionCoefficient(homogeneous); } else if (dirichlet_level_set_type == 2) { dbcCoef = new ShiftedFunctionCoefficient(dirichlet_velocity_xy_exponent); + exactCoef = new FunctionCoefficient(dirichlet_velocity_xy_exponent); } else if (dirichlet_level_set_type == 3) { dbcCoef = new ShiftedFunctionCoefficient(dirichlet_velocity_xy_sinusoidal); + exactCoef = new FunctionCoefficient(dirichlet_velocity_xy_sinusoidal); } ShiftedFunctionCoefficient *dbcCoefCombo = NULL; @@ -411,14 +418,22 @@ int main(int argc, char *argv[]) } // Homogeneous Neumann boundary condition coefficient - ShiftedFunctionCoefficient nbcCoef(neumann_velocity_circle); + ShiftedFunctionCoefficient *nbcCoef = NULL; ShiftedVectorFunctionCoefficient *normalbcCoef = NULL; if (neumann_level_set_type == 1) { + nbcCoef = new ShiftedFunctionCoefficient(homogeneous); normalbcCoef = new ShiftedVectorFunctionCoefficient(dim, normal_vector_1); } + else if (neumann_level_set_type == 2) + { + nbcCoef = new ShiftedFunctionCoefficient(traction_xy_exponent); + normalbcCoef = new ShiftedVectorFunctionCoefficient(dim, normal_vector_1); + exactCoef = new FunctionCoefficient(dirichlet_velocity_xy_exponent); + } else if (neumann_level_set_type == 7) { + nbcCoef = new ShiftedFunctionCoefficient(homogeneous); normalbcCoef = new ShiftedVectorFunctionCoefficient(dim, normal_vector_2); } else if (neumann_level_set_type > 0) @@ -476,10 +491,11 @@ int main(int argc, char *argv[]) { MFEM_VERIFY(!include_cut_cell, "include_cut_cell option must be set to" " false for Neumann boundary conditions."); + std::cout << include_cut_cell << " k10inc\n"; b.AddInteriorFaceIntegrator(new SBM2NeumannLFIntegrator( - &pmesh, nbcCoef, alpha, *dist_vec, + &pmesh, *nbcCoef, alpha, *dist_vec, *normalbcCoef, elem_marker, - include_cut_cell, neumann_ho_terms, + neumann_ho_terms, include_cut_cell, ls_cut_marker)); bf_neumann_marker.Append(ls_cut_marker); ls_cut_marker += 1; @@ -525,11 +541,11 @@ int main(int argc, char *argv[]) a.Assemble(); // Project the exact solution as an initial condition for Dirichlet boundary. - x = 0.0; - if (dirichlet_level_set_type > 0 && dirichlet_level_set_type_combo != 6) + if (!exactCoef) { - x.ProjectCoefficient(*dbcCoef); + exactCoef = new FunctionCoefficient(homogeneous); } + x.ProjectCoefficient(*exactCoef); // Form the linear system and solve it. OperatorPtr A; @@ -580,7 +596,8 @@ int main(int argc, char *argv[]) } // Construct an error grid function if the exact solution is known. - if (dirichlet_level_set_type == 2 || dirichlet_level_set_type == 3) + if (dirichlet_level_set_type == 2 || dirichlet_level_set_type == 3 || + (dirichlet_level_set_type == -1 && neumann_level_set_type == 2)) { ParGridFunction err(x); Vector pxyz(dim); @@ -590,7 +607,7 @@ int main(int argc, char *argv[]) pxyz(0) = vxyz(i); pxyz(1) = vxyz(i+nodes_cnt); double exact_val = 0.; - if (dirichlet_level_set_type == 2) + if (dirichlet_level_set_type == 2 || neumann_level_set_type == 2) { exact_val = dirichlet_velocity_xy_exponent(pxyz); } @@ -610,7 +627,7 @@ int main(int argc, char *argv[]) "Error", 2*s, 0, s, s, "Rj"); } - const double global_error = x.ComputeL2Error(*dbcCoef); + const double global_error = x.ComputeL2Error(*exactCoef); if (myid == 0) { std::cout << "Global L2 error: " << global_error << endl; @@ -621,8 +638,10 @@ int main(int argc, char *argv[]) delete prec; delete bicg; delete normalbcCoef; + delete nbcCoef; delete dbcCoefCombo; delete dbcCoef; + delete exactCoef; delete rhs_f; delete dist_vec; delete neumann_dist_coef; diff --git a/miniapps/shifted/sbm_aux.hpp b/miniapps/shifted/sbm_aux.hpp index 6564de3b71..5174e1c4c5 100644 --- a/miniapps/shifted/sbm_aux.hpp +++ b/miniapps/shifted/sbm_aux.hpp @@ -200,9 +200,9 @@ public: }; /// Boundary conditions - Dirichlet -double dirichlet_velocity_circle(const Vector &x) +double homogeneous(const Vector &x) { - return 0.; + return 0.0; } double dirichlet_velocity_xy_exponent(const Vector &x) @@ -217,11 +217,6 @@ double dirichlet_velocity_xy_sinusoidal(const Vector &x) } /// Boundary conditions - Neumann -double neumann_velocity_circle(const Vector &x) -{ - return 0.; -} - /// Normal vector for level_set_type = 1. Circle centered at [0.5 , 0.5] void normal_vector_1(const Vector &x, Vector &p) { @@ -242,6 +237,17 @@ void normal_vector_2(const Vector &x, Vector &p) p *= -1; } +/// Neumann condition for exponent based solution +double traction_xy_exponent(const Vector &x) +{ + double xy_p = 2; + Vector gradient(2); + gradient(0) = xy_p*x(0); + gradient(1) = xy_p*x(1); + Vector normal(2); + normal_vector_1(x, normal); + return 1.0*(gradient*normal); +} /// `f` for the Poisson problem (-nabla^2 u = f). double rhs_fun_circle(const Vector &x) diff --git a/miniapps/shifted/sbm_solver.cpp b/miniapps/shifted/sbm_solver.cpp index 43dc72fe01..563d969688 100644 --- a/miniapps/shifted/sbm_solver.cpp +++ b/miniapps/shifted/sbm_solver.cpp @@ -957,7 +957,6 @@ void SBM2NeumannLFIntegrator::AssembleRHSElementVect( const FiniteElement &el1, const FiniteElement &el2, FaceElementTransformations &Tr, Vector &elvect) { - int dim, ndof1, ndof2, ndof, ndoftotal; double w; Vector temp_elvect; @@ -1044,7 +1043,6 @@ void SBM2NeumannLFIntegrator::AssembleRHSElementVect( temp_elvect.SetSize(ndof); temp_elvect = 0.0; - nor.SetSize(dim); shape.SetSize(ndof); From d4c102b94ce7465916963b234d43a2f329ed375f Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Thu, 5 Aug 2021 17:02:11 -0700 Subject: [PATCH 068/198] enable high-order terms for Neumann --- miniapps/shifted/diffusion.cpp | 12 ++++-------- miniapps/shifted/sbm_solver.cpp | 12 +++++++++--- miniapps/shifted/sbm_solver.hpp | 13 +++++-------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index 0bb8a751f5..899c21dcdc 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -484,18 +484,14 @@ int main(int argc, char *argv[]) // Add integrators corresponding to the shifted boundary method (SBM) // for Neumann boundaries. - // High-order extension is not available for Neumann boundary condition. - // Number of terms used for Taylor expansion must be set to 1. - const int neumann_ho_terms = 1; if (neumann_level_set_type > 0) { MFEM_VERIFY(!include_cut_cell, "include_cut_cell option must be set to" " false for Neumann boundary conditions."); - std::cout << include_cut_cell << " k10inc\n"; b.AddInteriorFaceIntegrator(new SBM2NeumannLFIntegrator( - &pmesh, *nbcCoef, alpha, *dist_vec, + &pmesh, *nbcCoef, *dist_vec, *normalbcCoef, elem_marker, - neumann_ho_terms, include_cut_cell, + ho_terms, include_cut_cell, ls_cut_marker)); bf_neumann_marker.Append(ls_cut_marker); ls_cut_marker += 1; @@ -527,13 +523,13 @@ int main(int argc, char *argv[]) // Add neumann bilinearform integrator. if (neumann_level_set_type > 0) { - a.AddInteriorFaceIntegrator(new SBM2NeumannIntegrator(&pmesh, alpha, + a.AddInteriorFaceIntegrator(new SBM2NeumannIntegrator(&pmesh, *dist_vec, *normalbcCoef, elem_marker, bf_neumann_marker, include_cut_cell, - neumann_ho_terms)); + ho_terms)); } // Assemble the bilinear form and the corresponding linear system, diff --git a/miniapps/shifted/sbm_solver.cpp b/miniapps/shifted/sbm_solver.cpp index 563d969688..e2d21778ee 100644 --- a/miniapps/shifted/sbm_solver.cpp +++ b/miniapps/shifted/sbm_solver.cpp @@ -762,11 +762,9 @@ void SBM2NeumannIntegrator::AssembleFaceMatrix( ir = &IntRules.Get(Trans.GetGeometryType(), order); } - MFEM_VERIFY(nterms == 1, " High-order extension is not available for Neumann" - " boundary condition. Set nterms=1.\n"); - Array dkphi_dxk; DenseMatrix grad_phys; + Vector Factorial; Array grad_phys_dir; if (nterms > 0) @@ -831,6 +829,13 @@ void SBM2NeumannIntegrator::AssembleFaceMatrix( { delete grad_phys_dir[i]; } + + Factorial.SetSize(nterms); + Factorial(0) = 1; + for (int i = 1; i < nterms; i++) + { + Factorial(i) = Factorial(i-1)*(i+1); + } } @@ -924,6 +929,7 @@ void SBM2NeumannIntegrator::AssembleFaceMatrix( } Vector q_hess_dot_d_work(ndof); T1.MultTranspose(Nhat, q_hess_dot_d_work); + q_hess_dot_d_work *= 1./Factorial(i); q_hess_dot_d_nhat += q_hess_dot_d_work; } diff --git a/miniapps/shifted/sbm_solver.hpp b/miniapps/shifted/sbm_solver.hpp index 87ff611fc6..118cd470a2 100644 --- a/miniapps/shifted/sbm_solver.hpp +++ b/miniapps/shifted/sbm_solver.hpp @@ -199,9 +199,10 @@ public: /// BilinearFormIntegrator for Neumann boundaries using the shifted boundary /// method. -/// A(u, w) = <[nabla u + nabla(nabla u).d].nhat (n.nhat), w> - , -/// where nhat is the normal vector at the true boundary, n is the normal vector -/// at the surrogate boundary. Since this interior face integrator is applied to +/// A(u,w) = <[nabla u + nabla(nabla u).d + h.o.t.].nhat(n.nhat),w>- +/// where h.o.t are the high-order terms due to Taylor expansion for nabla u, +/// nhat is the normal vector at the true boundary, n is the normal vector at +/// the surrogate boundary. Since this interior face integrator is applied to /// the surrogate boundary (see marking.hpp for notes on how the surrogate faces /// are determined and elements are marked), this integrator adds contribution /// to only the element that is adjacent to that face (Trans.Elem1 or @@ -209,7 +210,6 @@ public: class SBM2NeumannIntegrator : public BilinearFormIntegrator { protected: - double alpha; ShiftedVectorFunctionCoefficient *vN; // Normal function coefficient VectorCoefficient *vD; // Distance function coefficient Array *elem_marker; // Marker indicating whether element is inside, @@ -229,14 +229,13 @@ protected: public: SBM2NeumannIntegrator(const ParMesh *pmesh, - const double alpha_, VectorCoefficient &vD_, ShiftedVectorFunctionCoefficient &vN_, Array &elem_marker_, Array &cut_marker_, bool include_cut_cell_ = false, int nterms_ = 1) - : alpha(alpha_), vN(&vN_), vD(&vD_), + : vN(&vN_), vD(&vD_), elem_marker(&elem_marker_), include_cut_cell(include_cut_cell_), nterms(nterms_), @@ -277,7 +276,6 @@ protected: ShiftedFunctionCoefficient *uN; // Neumann condition on true boundary VectorCoefficient *vD; // Distance function coefficient Array *elem_marker; // Marker indicating whether element is inside, - double alpha; // Nitsche parameter int nterms; // Number of terms in addition to the gradient // term from Taylor expansion that should be included. (0 by default). bool include_cut_cell; @@ -291,7 +289,6 @@ protected: public: SBM2NeumannLFIntegrator(const ParMesh *pmesh, ShiftedFunctionCoefficient &u, - const double alpha_, VectorCoefficient &vD_, ShiftedVectorFunctionCoefficient &vN_, Array &elem_marker_, From b133ec754eca3c147ab542e56c9c3d3e3e30f881 Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Fri, 6 Aug 2021 14:31:48 -0700 Subject: [PATCH 069/198] minor --- miniapps/shifted/sbm_solver.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miniapps/shifted/sbm_solver.hpp b/miniapps/shifted/sbm_solver.hpp index 118cd470a2..d6ecd40fc5 100644 --- a/miniapps/shifted/sbm_solver.hpp +++ b/miniapps/shifted/sbm_solver.hpp @@ -297,7 +297,7 @@ public: int ls_cut_marker_ = ShiftedFaceMarker::SBElementType::CUT) : vN(&vN_), uN(&u), vD(&vD_), elem_marker(&elem_marker_), - alpha(alpha_), nterms(nterms_), + nterms(nterms_), include_cut_cell(include_cut_cell_), NEproc(pmesh->GetNE()), par_shared_face_count(0), From 246f9bcec1a8c77c7d5624ad1570e73717bd03db Mon Sep 17 00:00:00 2001 From: "Stowell, Mark L" Date: Tue, 17 Aug 2021 09:51:47 -0700 Subject: [PATCH 070/198] Making *Coefficient::SetTime virtual --- fem/coefficient.cpp | 184 ++++++++++++++++++++++++++++++++++++++++++++ fem/coefficient.hpp | 89 ++++++++++++++++++++- 2 files changed, 269 insertions(+), 4 deletions(-) diff --git a/fem/coefficient.cpp b/fem/coefficient.cpp index abe479a488..bfa9544f08 100644 --- a/fem/coefficient.cpp +++ b/fem/coefficient.cpp @@ -52,6 +52,13 @@ double GridFunctionCoefficient::Eval (ElementTransformation &T, return GridF -> GetValue (T, ip, Component); } +void TransformedCoefficient::SetTime(double t) +{ + if (Q1) { Q1->SetTime(t); } + if (Q2) { Q2->SetTime(t); } + this->Coefficient::SetTime(t); +} + double TransformedCoefficient::Eval(ElementTransformation &T, const IntegrationPoint &ip) { @@ -66,6 +73,12 @@ double TransformedCoefficient::Eval(ElementTransformation &T, } } +void DeltaCoefficient::SetTime(double t) +{ + if (weight) { weight->SetTime(t); } + this->Coefficient::SetTime(t); +} + void DeltaCoefficient::SetDeltaCenter(const Vector& vcenter) { MFEM_VERIFY(vcenter.Size() <= 3, @@ -87,6 +100,12 @@ double DeltaCoefficient::EvalDelta(ElementTransformation &T, return weight ? weight->Eval(T, ip, GetTime())*w : w; } +void RestrictedCoefficient::SetTime(double t) +{ + if (c) { c->SetTime(t); } + this->Coefficient::SetTime(t); +} + void VectorCoefficient::Eval(DenseMatrix &M, ElementTransformation &T, const IntegrationRule &ir) { @@ -134,6 +153,15 @@ VectorArrayCoefficient::VectorArrayCoefficient (int dim) } } +void VectorArrayCoefficient::SetTime(double t) +{ + for (int i = 0; i < vdim; i++) + { + if (Coeff[i]) { Coeff[i]->SetTime(t); } + } + this->VectorCoefficient::SetTime(t); +} + void VectorArrayCoefficient::Set(int i, Coefficient *c, bool own) { if (ownCoeff[i]) { delete Coeff[i]; } @@ -247,6 +275,12 @@ double DivergenceGridFunctionCoefficient::Eval(ElementTransformation &T, return GridFunc->GetDivergence(T); } +void VectorDeltaCoefficient::SetTime(double t) +{ + d.SetTime(t); + this->VectorCoefficient::SetTime(t); +} + void VectorDeltaCoefficient::SetDirection(const Vector &d_) { dir = d_; @@ -261,6 +295,12 @@ void VectorDeltaCoefficient::EvalDelta( V *= d.EvalDelta(T, ip); } +void VectorRestrictedCoefficient::SetTime(double t) +{ + if (c) { c->SetTime(t); } + this->VectorCoefficient::SetTime(t); +} + void VectorRestrictedCoefficient::Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip) { @@ -371,6 +411,12 @@ void MatrixFunctionCoefficient::EvalSymmetric(Vector &K, } } +void SymmetricMatrixFunctionCoefficient::SetTime(double t) +{ + if (Q) { Q->SetTime(t); } + this->SymmetricMatrixCoefficient::SetTime(t); +} + void SymmetricMatrixFunctionCoefficient::Eval(DenseSymmetricMatrix &K, ElementTransformation &T, const IntegrationPoint &ip) @@ -413,6 +459,15 @@ MatrixArrayCoefficient::MatrixArrayCoefficient (int dim) } } +void MatrixArrayCoefficient::SetTime(double t) +{ + for (int i=0; i < height*width; i++) + { + if (Coeff[i]) { Coeff[i]->SetTime(t); } + } + this->MatrixCoefficient::SetTime(t); +} + void MatrixArrayCoefficient::Set(int i, int j, Coefficient * c, bool own) { if (ownCoeff[i*width+j]) { delete Coeff[i*width+j]; } @@ -440,6 +495,12 @@ void MatrixArrayCoefficient::Eval(DenseMatrix &K, ElementTransformation &T, } } +void MatrixRestrictedCoefficient::SetTime(double t) +{ + if (c) { c->SetTime(t); } + this->MatrixCoefficient::SetTime(t); +} + void MatrixRestrictedCoefficient::Eval(DenseMatrix &K, ElementTransformation &T, const IntegrationPoint &ip) { @@ -455,6 +516,33 @@ void MatrixRestrictedCoefficient::Eval(DenseMatrix &K, ElementTransformation &T, } } +void SumCoefficient::SetTime(double t) +{ + if (a) { a->SetTime(t); } + if (b) { b->SetTime(t); } + this->Coefficient::SetTime(t); +} + +void ProductCoefficient::SetTime(double t) +{ + if (a) { a->SetTime(t); } + if (b) { b->SetTime(t); } + this->Coefficient::SetTime(t); +} + +void RatioCoefficient::SetTime(double t) +{ + if (a) { a->SetTime(t); } + if (b) { b->SetTime(t); } + this->Coefficient::SetTime(t); +} + +void PowerCoefficient::SetTime(double t) +{ + if (a) { a->SetTime(t); } + this->Coefficient::SetTime(t); +} + InnerProductCoefficient::InnerProductCoefficient(VectorCoefficient &A, VectorCoefficient &B) : a(&A), b(&B) @@ -464,6 +552,13 @@ InnerProductCoefficient::InnerProductCoefficient(VectorCoefficient &A, "Arguments have incompatible dimensions."); } +void InnerProductCoefficient::SetTime(double t) +{ + if (a) { a->SetTime(t); } + if (b) { b->SetTime(t); } + this->Coefficient::SetTime(t); +} + double InnerProductCoefficient::Eval(ElementTransformation &T, const IntegrationPoint &ip) { @@ -481,6 +576,13 @@ VectorRotProductCoefficient::VectorRotProductCoefficient(VectorCoefficient &A, "Arguments must have dimension equal to two."); } +void VectorRotProductCoefficient::SetTime(double t) +{ + if (a) { a->SetTime(t); } + if (b) { b->SetTime(t); } + this->Coefficient::SetTime(t); +} + double VectorRotProductCoefficient::Eval(ElementTransformation &T, const IntegrationPoint &ip) { @@ -497,6 +599,12 @@ DeterminantCoefficient::DeterminantCoefficient(MatrixCoefficient &A) "Argument must be a square matrix."); } +void DeterminantCoefficient::SetTime(double t) +{ + if (a) { a->SetTime(t); } + this->Coefficient::SetTime(t); +} + double DeterminantCoefficient::Eval(ElementTransformation &T, const IntegrationPoint &ip) { @@ -545,6 +653,15 @@ VectorSumCoefficient::VectorSumCoefficient(VectorCoefficient &A_, "Arguments must have the same dimension."); } +void VectorSumCoefficient::SetTime(double t) +{ + if (ACoef) { ACoef->SetTime(t); } + if (BCoef) { BCoef->SetTime(t); } + if (alphaCoef) { alphaCoef->SetTime(t); } + if (betaCoef) { betaCoef->SetTime(t); } + this->VectorCoefficient::SetTime(t); +} + void VectorSumCoefficient::Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip) { @@ -568,6 +685,13 @@ ScalarVectorProductCoefficient::ScalarVectorProductCoefficient( : VectorCoefficient(B.GetVDim()), aConst(0.0), a(&A), b(&B) {} +void ScalarVectorProductCoefficient::SetTime(double t) +{ + if (a) { a->SetTime(t); } + if (b) { b->SetTime(t); } + this->VectorCoefficient::SetTime(t); +} + void ScalarVectorProductCoefficient::Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip) { @@ -581,6 +705,12 @@ NormalizedVectorCoefficient::NormalizedVectorCoefficient(VectorCoefficient &A, : VectorCoefficient(A.GetVDim()), a(&A), tol(tol_) {} +void NormalizedVectorCoefficient::SetTime(double t) +{ + if (a) { a->SetTime(t); } + this->VectorCoefficient::SetTime(t); +} + void NormalizedVectorCoefficient::Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip) { @@ -599,6 +729,13 @@ VectorCrossProductCoefficient::VectorCrossProductCoefficient( "Arguments must have dimension equal to three."); } +void VectorCrossProductCoefficient::SetTime(double t) +{ + if (a) { a->SetTime(t); } + if (b) { b->SetTime(t); } + this->VectorCoefficient::SetTime(t); +} + void VectorCrossProductCoefficient::Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip) { @@ -620,6 +757,13 @@ MatrixVectorProductCoefficient::MatrixVectorProductCoefficient( "Arguments have incompatible dimensions."); } +void MatrixVectorProductCoefficient::SetTime(double t) +{ + if (a) { a->SetTime(t); } + if (b) { b->SetTime(t); } + this->VectorCoefficient::SetTime(t); +} + void MatrixVectorProductCoefficient::Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip) { @@ -649,6 +793,13 @@ MatrixSumCoefficient::MatrixSumCoefficient(MatrixCoefficient &A, "Arguments must have the same dimensions."); } +void MatrixSumCoefficient::SetTime(double t) +{ + if (a) { a->SetTime(t); } + if (b) { b->SetTime(t); } + this->MatrixCoefficient::SetTime(t); +} + void MatrixSumCoefficient::Eval(DenseMatrix &M, ElementTransformation &T, const IntegrationPoint &ip) { @@ -670,6 +821,13 @@ ScalarMatrixProductCoefficient::ScalarMatrixProductCoefficient( : MatrixCoefficient(B.GetHeight(), B.GetWidth()), aConst(0.0), a(&A), b(&B) {} +void ScalarMatrixProductCoefficient::SetTime(double t) +{ + if (a) { a->SetTime(t); } + if (b) { b->SetTime(t); } + this->MatrixCoefficient::SetTime(t); +} + void ScalarMatrixProductCoefficient::Eval(DenseMatrix &M, ElementTransformation &T, const IntegrationPoint &ip) @@ -683,6 +841,12 @@ TransposeMatrixCoefficient::TransposeMatrixCoefficient(MatrixCoefficient &A) : MatrixCoefficient(A.GetWidth(), A.GetHeight()), a(&A) {} +void TransposeMatrixCoefficient::SetTime(double t) +{ + if (a) { a->SetTime(t); } + this->MatrixCoefficient::SetTime(t); +} + void TransposeMatrixCoefficient::Eval(DenseMatrix &M, ElementTransformation &T, const IntegrationPoint &ip) @@ -699,6 +863,12 @@ InverseMatrixCoefficient::InverseMatrixCoefficient(MatrixCoefficient &A) "Argument must be a square matrix."); } +void InverseMatrixCoefficient::SetTime(double t) +{ + if (a) { a->SetTime(t); } + this->MatrixCoefficient::SetTime(t); +} + void InverseMatrixCoefficient::Eval(DenseMatrix &M, ElementTransformation &T, const IntegrationPoint &ip) @@ -713,6 +883,13 @@ OuterProductCoefficient::OuterProductCoefficient(VectorCoefficient &A, va(A.GetVDim()), vb(B.GetVDim()) {} +void OuterProductCoefficient::SetTime(double t) +{ + if (a) { a->SetTime(t); } + if (b) { b->SetTime(t); } + this->MatrixCoefficient::SetTime(t); +} + void OuterProductCoefficient::Eval(DenseMatrix &M, ElementTransformation &T, const IntegrationPoint &ip) { @@ -739,6 +916,13 @@ CrossCrossCoefficient::CrossCrossCoefficient(Coefficient &A, vk(K.GetVDim()) {} +void CrossCrossCoefficient::SetTime(double t) +{ + if (a) { a->SetTime(t); } + if (k) { k->SetTime(t); } + this->MatrixCoefficient::SetTime(t); +} + void CrossCrossCoefficient::Eval(DenseMatrix &M, ElementTransformation &T, const IntegrationPoint &ip) { diff --git a/fem/coefficient.hpp b/fem/coefficient.hpp index 4363a1d484..acbe9bb1c2 100644 --- a/fem/coefficient.hpp +++ b/fem/coefficient.hpp @@ -45,7 +45,7 @@ public: Coefficient() { time = 0.; } /// Set the time for time dependent coefficients - void SetTime(double t) { time = t; } + virtual void SetTime(double t) { time = t; } /// Get the time for time dependent coefficients double GetTime() { return time; } @@ -217,6 +217,9 @@ public: double (*F)(double,double)) : Q1(q1), Q2(q2), Transform2(F) { Transform1 = 0; } + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Evaluate the coefficient at @a ip. virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip); }; @@ -269,6 +272,9 @@ public: weight = NULL; sdim = 3; tdf = NULL; } + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Set the center location of the delta function. void SetDeltaCenter(const Vector& center); @@ -333,6 +339,9 @@ public: RestrictedCoefficient(Coefficient &c_, Array &attr) { c = &c_; attr.Copy(active_attr); } + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Evaluate the coefficient at @a ip. virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip) { return active_attr[T.Attribute-1] ? c->Eval(T, ip, GetTime()) : 0.0; } @@ -350,7 +359,7 @@ public: VectorCoefficient(int vd) { vdim = vd; time = 0.; } /// Set the time for time dependent coefficients - void SetTime(double t) { time = t; } + virtual void SetTime(double t) { time = t; } /// Get the time for time dependent coefficients double GetTime() { return time; } @@ -456,6 +465,9 @@ public: still need to be added with Set(). */ explicit VectorArrayCoefficient(int dim); + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Returns i'th coefficient. Coefficient* GetCoeff(int i) { return Coeff[i]; } @@ -632,6 +644,9 @@ public: double s) : VectorCoefficient(dir_.Size()), dir(dir_), d(x,y,z,s) { } + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Replace the associated DeltaCoefficient with a new DeltaCoefficient. /** The new DeltaCoefficient cannot have a specified weight Coefficient, i.e. DeltaCoefficient::Weight() should return NULL. */ @@ -677,6 +692,9 @@ public: : VectorCoefficient(vc.GetVDim()) { c = &vc; attr.Copy(active_attr); } + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Evaluate the vector coefficient at @a ip. virtual void Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip); @@ -708,7 +726,7 @@ public: height(h), width(w), time(0.), symmetric(symm) { } /// Set the time for time dependent coefficients - void SetTime(double t) { time = t; } + virtual void SetTime(double t) { time = t; } /// Get the time for time dependent coefficients double GetTime() { return time; } @@ -844,6 +862,9 @@ public: actual coefficients still need to be added with Set(). */ explicit MatrixArrayCoefficient (int dim); + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Get the coefficient located at (i,j) in the matrix. Coefficient* GetCoeff (int i, int j) { return Coeff[i*width+j]; } @@ -881,6 +902,9 @@ public: : MatrixCoefficient(mc.GetHeight(), mc.GetWidth()) { c = &mc; attr.Copy(active_attr); } + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Evaluate the matrix coefficient at @a ip. virtual void Eval(DenseMatrix &K, ElementTransformation &T, const IntegrationPoint &ip); @@ -911,6 +935,9 @@ public: double alpha_ = 1.0, double beta_ = 1.0) : aConst(0.0), a(&A), b(&B), alpha(alpha_), beta(beta_) { } + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the first term in the linear combination as a constant void SetAConst(double A) { a = NULL; aConst = A; } /// Return the first term in the linear combination @@ -959,7 +986,7 @@ public: { dim = dimension; time = 0.; } /// Set the time for time dependent coefficients - void SetTime(double t) { time = t; } + virtual void SetTime(double t) { time = t; } /// Get the time for time dependent coefficients double GetTime() { return time; } @@ -1037,6 +1064,9 @@ public: : SymmetricMatrixCoefficient(dim), TDFunction(std::move(TDF)), Q(q) { } + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Evaluate the matrix coefficient at @a ip. virtual void Eval(DenseSymmetricMatrix &K, ElementTransformation &T, const IntegrationPoint &ip); @@ -1063,6 +1093,9 @@ public: ProductCoefficient(Coefficient &A, Coefficient &B) : aConst(0.0), a(&A), b(&B) { } + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the first term in the product as a constant void SetAConst(double A) { a = NULL; aConst = A; } /// Return the first term in the product @@ -1108,6 +1141,9 @@ public: RatioCoefficient(Coefficient &A, double B) : aConst(0.0), bConst(B), a(&A), b(NULL) { } + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the numerator in the ratio as a constant void SetAConst(double A) { a = NULL; aConst = A; } /// Return the numerator of the ratio @@ -1151,6 +1187,9 @@ public: PowerCoefficient(Coefficient &A, double p_) : a(&A), p(p_) { } + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the base coefficient void SetACoef(Coefficient &A) { a = &A; } /// Return the base coefficient @@ -1181,6 +1220,9 @@ public: /// Construct with the two vector coefficients. Result is \f$ A \cdot B \f$. InnerProductCoefficient(VectorCoefficient &A, VectorCoefficient &B); + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the first vector in the inner product void SetACoef(VectorCoefficient &A) { a = &A; } /// Return the first vector coefficient in the inner product @@ -1210,6 +1252,9 @@ public: /// Constructor with two vector coefficients. Result is \f$ A_x B_y - A_y * B_x; \f$. VectorRotProductCoefficient(VectorCoefficient &A, VectorCoefficient &B); + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the first vector in the product void SetACoef(VectorCoefficient &A) { a = &A; } /// Return the first vector of the product @@ -1237,6 +1282,9 @@ public: /// Construct with the matrix. DeterminantCoefficient(MatrixCoefficient &A); + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the matrix coefficient void SetACoef(MatrixCoefficient &A) { a = &A; } /// Return the matrix coefficient @@ -1280,6 +1328,9 @@ public: VectorSumCoefficient(VectorCoefficient &A_, VectorCoefficient &B_, Coefficient &alpha_, Coefficient &beta_); + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the first vector coefficient void SetACoef(VectorCoefficient &A) { ACoef = &A; } /// Return the first vector coefficient @@ -1341,6 +1392,9 @@ public: /// Constructor with two coefficients. Result is A * B. ScalarVectorProductCoefficient(Coefficient &A, VectorCoefficient &B); + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the scalar factor as a constant void SetAConst(double A) { a = NULL; aConst = A; } /// Return the scalar factor @@ -1379,6 +1433,9 @@ public: */ NormalizedVectorCoefficient(VectorCoefficient &A, double tol = 1e-6); + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the vector coefficient void SetACoef(VectorCoefficient &A) { a = &A; } /// Return the vector coefficient @@ -1404,6 +1461,9 @@ public: /// Construct with the two coefficients. Result is A x B. VectorCrossProductCoefficient(VectorCoefficient &A, VectorCoefficient &B); + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the first term in the product void SetACoef(VectorCoefficient &A) { a = &A; } /// Return the first term in the product @@ -1435,6 +1495,9 @@ public: /// Constructor with two coefficients. Result is A*B. MatrixVectorProductCoefficient(MatrixCoefficient &A, VectorCoefficient &B); + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the matrix coefficient void SetACoef(MatrixCoefficient &A) { a = &A; } /// Return the matrix coefficient @@ -1487,6 +1550,9 @@ public: MatrixSumCoefficient(MatrixCoefficient &A, MatrixCoefficient &B, double alpha_ = 1.0, double beta_ = 1.0); + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the first matrix coefficient void SetACoef(MatrixCoefficient &A) { a = &A; } /// Return the first matrix coefficient @@ -1528,6 +1594,9 @@ public: /// Constructor with two coefficients. Result is A*B. ScalarMatrixProductCoefficient(Coefficient &A, MatrixCoefficient &B); + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the scalar factor as a constant void SetAConst(double A) { a = NULL; aConst = A; } /// Return the scalar factor @@ -1558,6 +1627,9 @@ public: /// Construct with the matrix coefficient. Result is \f$ A^T \f$. TransposeMatrixCoefficient(MatrixCoefficient &A); + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the matrix coefficient void SetACoef(MatrixCoefficient &A) { a = &A; } /// Return the matrix coefficient @@ -1578,6 +1650,9 @@ public: /// Construct with the matrix coefficient. Result is \f$ A^{-1} \f$. InverseMatrixCoefficient(MatrixCoefficient &A); + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the matrix coefficient void SetACoef(MatrixCoefficient &A) { a = &A; } /// Return the matrix coefficient @@ -1602,6 +1677,9 @@ public: /// Construct with two vector coefficients. Result is \f$ A B^T \f$. OuterProductCoefficient(VectorCoefficient &A, VectorCoefficient &B); + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the first vector in the outer product void SetACoef(VectorCoefficient &A) { a = &A; } /// Return the first vector coefficient in the outer product @@ -1637,6 +1715,9 @@ public: CrossCrossCoefficient(double A, VectorCoefficient &K); CrossCrossCoefficient(Coefficient &A, VectorCoefficient &K); + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Reset the scalar factor as a constant void SetAConst(double A) { a = NULL; aConst = A; } /// Return the scalar factor From 4d4cf2726435f6a18d92a03a135e357b3bff6d51 Mon Sep 17 00:00:00 2001 From: "Stowell, Mark L" Date: Tue, 17 Aug 2021 10:45:37 -0700 Subject: [PATCH 071/198] Adding CHANGELOG entry --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 342c0d16cc..9306a540a9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -281,6 +281,9 @@ Miscellaneous - Various other simplifications, extensions, and bugfixes in the code. +- Coefficient::SetTime now propagates the new time into internally stored + Coefficient objects. + API changes ----------- - Added an abstract interface `mfem::FaceRestriction` for `H1FaceRestriction` From 9dee99fbf1934cbd749b1daf0ca295e941638c11 Mon Sep 17 00:00:00 2001 From: "Stowell, Mark L" Date: Thu, 19 Aug 2021 12:45:30 -0700 Subject: [PATCH 072/198] Moving CHANGELOG entry (Oops) --- CHANGELOG | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9306a540a9..c2aa3f39ff 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,9 @@ Version 4.3.1 (development) =========================== - Added support for hr-adaptivity using TMOP-based error estimator. +- Coefficient::SetTime now propagates the new time into internally stored + Coefficient objects. + Version 4.3, released on July 29, 2021 ====================================== @@ -281,9 +284,6 @@ Miscellaneous - Various other simplifications, extensions, and bugfixes in the code. -- Coefficient::SetTime now propagates the new time into internally stored - Coefficient objects. - API changes ----------- - Added an abstract interface `mfem::FaceRestriction` for `H1FaceRestriction` From a5be9f36ed40736b7edffcde6e1c186ae3e55849 Mon Sep 17 00:00:00 2001 From: Brendan Keith Date: Thu, 26 Aug 2021 09:09:47 -0700 Subject: [PATCH 073/198] Addressing comments: Added period for conformity. --- examples/osc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/osc.cpp b/examples/osc.cpp index 4a077a4fb3..768595bea6 100644 --- a/examples/osc.cpp +++ b/examples/osc.cpp @@ -106,7 +106,7 @@ int main(int argc, char *argv[]) "--no-visualization", "Enable or disable GLVis visualization."); args.AddOption(&osc_threshold, "-e", "--error", - "relative data oscillation threshold"); + "relative data oscillation threshold."); args.AddOption(&enriched_order, "-eo", "--enriched_order", "Enriched quadrature order."); From ebddfa7114789a452324c637eebdddfa7f3019c0 Mon Sep 17 00:00:00 2001 From: Brendan Keith Date: Thu, 26 Aug 2021 09:12:50 -0700 Subject: [PATCH 074/198] Addressing comments: Added period for conformity. --- examples/oscp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/oscp.cpp b/examples/oscp.cpp index 6df32f06fe..3699917f6f 100644 --- a/examples/oscp.cpp +++ b/examples/oscp.cpp @@ -113,7 +113,7 @@ int main(int argc, char *argv[]) "--no-visualization", "Enable or disable GLVis visualization."); args.AddOption(&osc_threshold, "-e", "--error", - "relative data oscillation threshold"); + "relative data oscillation threshold."); args.AddOption(&enriched_order, "-eo", "--enriched_order", "Enriched quadrature order."); args.AddOption(&nc_simplices, "-ns", "--nonconforming-simplices", From eb195d1cb366464d1da36cc902942021d114a0ba Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 26 Aug 2021 09:30:52 -0700 Subject: [PATCH 075/198] Addressing comments: Change default value --- mesh/mesh_operators.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index be8d8f9043..59abdb08a1 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -360,7 +360,7 @@ public: return PreprocessMesh(mesh, max_it); } - /// Set the refinement threshold. The default value is 1.0e-3. + /// Set the refinement threshold. The default value is 1.0e-2. void SetThreshold(double threshold_) { threshold = threshold_; } /** @brief Set the maximum number of elements stopping criterion: stop when From 4d40b7536f75283c1e7d1e110cd278e5e8435ed6 Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 26 Aug 2021 11:47:40 -0700 Subject: [PATCH 076/198] make GetOsc at const member function --- mesh/mesh_operators.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index 59abdb08a1..5d58c66793 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -393,7 +393,7 @@ public: void SetIntRule(const IntegrationRule *irs_[]) { irs = irs_; } // Return the value of the global relative data oscillation - double GetOsc() { return global_osc; } + const double GetOsc() { return global_osc; } // Return the local relative data oscillation errors Vector GetLocalOscs() From 7246251b52e5b2035a86532fd26cd6dfe78c9c18 Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 26 Aug 2021 12:24:34 -0700 Subject: [PATCH 077/198] const Vector & GetLocalOscs() const --- mesh/mesh_operators.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index 5d58c66793..01d9d4b1ef 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -396,7 +396,7 @@ public: const double GetOsc() { return global_osc; } // Return the local relative data oscillation errors - Vector GetLocalOscs() + const Vector & GetLocalOscs() const { MFEM_ASSERT(element_oscs.Size() > 0, "Local oscillations have not been computed yet") From 7d309b3d9e60437e61a2a276835d592367ebbc15 Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 26 Aug 2021 12:30:58 -0700 Subject: [PATCH 078/198] initialize gf --- mesh/mesh_operators.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index 01d9d4b1ef..9d487fa6cc 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -335,7 +335,7 @@ protected: Array mesh_refinements; Vector element_oscs; Coefficient *coeff = NULL; - GridFunction *gf; + GridFunction *gf = NULL; const IntegrationRule *ir_default[Geometry::NumGeom]; const IntegrationRule **irs = NULL; From 165f4301d8545772c58747ea352acb5145488239 Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 26 Aug 2021 13:09:40 -0700 Subject: [PATCH 079/198] make gf local --- mesh/mesh_operators.cpp | 1 + mesh/mesh_operators.hpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index 5fb10134dd..3bcb520517 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -174,6 +174,7 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) FiniteElementSpace* l2fes = NULL; bool par = false; + GridFunction *gf = NULL; #ifdef MFEM_USE_MPI ParMesh* pmesh = dynamic_cast(&mesh); diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index 9d487fa6cc..5df3dc3bf7 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -335,7 +335,6 @@ protected: Array mesh_refinements; Vector element_oscs; Coefficient *coeff = NULL; - GridFunction *gf = NULL; const IntegrationRule *ir_default[Geometry::NumGeom]; const IntegrationRule **irs = NULL; From 71c4967e806fa21201f82df6f4fc1946f3ee94a3 Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 26 Aug 2021 13:34:08 -0700 Subject: [PATCH 080/198] added Coefficient & as argument in constructor --- examples/osc.cpp | 7 +++---- examples/oscp.cpp | 7 +++---- mesh/mesh_operators.cpp | 1 - mesh/mesh_operators.hpp | 17 ++++++++++++----- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/examples/osc.cpp b/examples/osc.cpp index 768595bea6..1ffc3c4f35 100644 --- a/examples/osc.cpp +++ b/examples/osc.cpp @@ -137,7 +137,7 @@ int main(int argc, char *argv[]) FunctionCoefficient affine_coeff(affine_function); FunctionCoefficient jump_coeff(jump_function); FunctionCoefficient singular_coeff(singular_function); - CoefficientRefiner coeffrefiner(order); + CoefficientRefiner coeffrefiner(affine_coeff,order); // 4. Connect to GLVis. char vishost[] = "localhost"; @@ -165,7 +165,6 @@ int main(int argc, char *argv[]) // 7. Preprocess mesh to control osc (piecewise-affine function). // This is mostly just a verification check. The oscillation should // be zero if the function is mesh-conforming and order > 0. - coeffrefiner.SetCoefficient(affine_coeff); coeffrefiner.PreprocessMesh(mesh); mfem::out << "\n"; @@ -174,7 +173,7 @@ int main(int argc, char *argv[]) mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n"; // 8. Preprocess mesh to control osc (jump function). - coeffrefiner.SetCoefficient(jump_coeff); + coeffrefiner.ResetCoefficient(jump_coeff); coeffrefiner.PreprocessMesh(mesh); mfem::out << "\n"; @@ -183,7 +182,7 @@ int main(int argc, char *argv[]) mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n"; // 9. Preprocess mesh to control osc (singular function). - coeffrefiner.SetCoefficient(singular_coeff); + coeffrefiner.ResetCoefficient(singular_coeff); coeffrefiner.PreprocessMesh(mesh); mfem::out << "\n"; diff --git a/examples/oscp.cpp b/examples/oscp.cpp index 3699917f6f..7ebc5e0549 100644 --- a/examples/oscp.cpp +++ b/examples/oscp.cpp @@ -167,7 +167,7 @@ int main(int argc, char *argv[]) FunctionCoefficient affine_coeff(affine_function); FunctionCoefficient jump_coeff(jump_function); FunctionCoefficient singular_coeff(singular_function); - CoefficientRefiner coeffrefiner(order); + CoefficientRefiner coeffrefiner(affine_coeff,order); // 6. Connect to GLVis. char vishost[] = "localhost"; @@ -195,7 +195,6 @@ int main(int argc, char *argv[]) // 9. Preprocess mesh to control osc (piecewise-affine function). // This is mostly just a verification check. The oscillation should // be zero if the function is mesh-conforming and order > 0. - coeffrefiner.SetCoefficient(affine_coeff); coeffrefiner.PreprocessMesh(pmesh); int globalNE = pmesh.GetGlobalNE(); @@ -209,7 +208,7 @@ int main(int argc, char *argv[]) } // 10. Preprocess mesh to control osc (jump function). - coeffrefiner.SetCoefficient(jump_coeff); + coeffrefiner.ResetCoefficient(jump_coeff); coeffrefiner.PreprocessMesh(pmesh); globalNE = pmesh.GetGlobalNE(); @@ -223,7 +222,7 @@ int main(int argc, char *argv[]) } // 11. Preprocess mesh to control osc (singular function). - coeffrefiner.SetCoefficient(singular_coeff); + coeffrefiner.ResetCoefficient(singular_coeff); coeffrefiner.PreprocessMesh(pmesh); globalNE = pmesh.GetGlobalNE(); diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index 3bcb520517..52019db520 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -167,7 +167,6 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) { int rank = 0; MFEM_VERIFY(max_it > 0, "max_it must be strictly positive") - MFEM_VERIFY(coeff, "Coefficient is not set for CoefficientRefiner object") int dim = mesh.Dimension(); L2_FECollection l2fec(order, dim); diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index 5df3dc3bf7..528db4c24c 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -331,7 +331,7 @@ protected: int order; long max_elements = std::numeric_limits::max(); double threshold = 1.0e-2; - double global_osc = 0.0; + double global_osc = NAN; Array mesh_refinements; Vector element_oscs; Coefficient *coeff = NULL; @@ -345,7 +345,14 @@ protected: public: /// Constructor - CoefficientRefiner(int order_) : order(order_) { } + CoefficientRefiner(Coefficient &coeff_, int order_) + { + // function f + coeff = &coeff_; + + // order of the projection Π + order = order_; + } /** @brief Apply the operator to the mesh max_it times or until tolerance * achieved. @@ -367,11 +374,11 @@ public: LONG_MAX. */ void SetMaxElements(long max_elements_) { max_elements = max_elements_; } - /// Set the function f - void SetCoefficient(Coefficient &coeff_) + /// Reset the function f + void ResetCoefficient(Coefficient &coeff_) { element_oscs.Destroy(); - global_osc = 0.0; + global_osc = NAN; coeff = &coeff_; } From 6bdd079b40a50e0fd69ff4e20d14c7a84f22c45a Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 26 Aug 2021 13:37:07 -0700 Subject: [PATCH 081/198] fix style --- mesh/mesh_operators.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index 528db4c24c..7a4fe46458 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -349,7 +349,7 @@ public: { // function f coeff = &coeff_; - + // order of the projection Π order = order_; } From 4d48ddb56641240018470a4e3f489d22e42215a1 Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 26 Aug 2021 13:53:47 -0700 Subject: [PATCH 082/198] fix unit tests after constructor redefinition --- tests/unit/fem/test_oscillation.cpp | 42 ++++++++++------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/tests/unit/fem/test_oscillation.cpp b/tests/unit/fem/test_oscillation.cpp index d6717958d4..2c52f94b2e 100644 --- a/tests/unit/fem/test_oscillation.cpp +++ b/tests/unit/fem/test_oscillation.cpp @@ -74,8 +74,7 @@ TEST_CASE("Data Oscillation on 2D NCMesh", { FunctionCoefficient u_analytic(testhelper_osc::SmoothSolutionX); - CoefficientRefiner coeffrefiner(order); - coeffrefiner.SetCoefficient(u_analytic); + CoefficientRefiner coeffrefiner(u_analytic,order); coeffrefiner.PreprocessMesh(*pmesh); double osc = coeffrefiner.GetOsc(); @@ -86,8 +85,7 @@ TEST_CASE("Data Oscillation on 2D NCMesh", { FunctionCoefficient u_analytic(testhelper_osc::SmoothSolutionY); - CoefficientRefiner coeffrefiner(order); - coeffrefiner.SetCoefficient(u_analytic); + CoefficientRefiner coeffrefiner(u_analytic,order); coeffrefiner.PreprocessMesh(*pmesh); double osc = coeffrefiner.GetOsc(); @@ -98,8 +96,7 @@ TEST_CASE("Data Oscillation on 2D NCMesh", { FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionX); - CoefficientRefiner coeffrefiner(order); - coeffrefiner.SetCoefficient(u_analytic); + CoefficientRefiner coeffrefiner(u_analytic,order); coeffrefiner.SetThreshold(1e-3); coeffrefiner.PreprocessMesh(*pmesh); double osc = coeffrefiner.GetOsc(); @@ -111,8 +108,7 @@ TEST_CASE("Data Oscillation on 2D NCMesh", { FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionY); - CoefficientRefiner coeffrefiner(order); - coeffrefiner.SetCoefficient(u_analytic); + CoefficientRefiner coeffrefiner(u_analytic,order); coeffrefiner.SetThreshold(1e-3); coeffrefiner.PreprocessMesh(*pmesh); double osc = coeffrefiner.GetOsc(); @@ -191,8 +187,7 @@ TEST_CASE("Data Oscillation on 2D NCMesh embedded in 3D", { FunctionCoefficient u_analytic(testhelper_osc::SmoothSolutionX); - CoefficientRefiner coeffrefiner(order); - coeffrefiner.SetCoefficient(u_analytic); + CoefficientRefiner coeffrefiner(u_analytic,order); coeffrefiner.PreprocessMesh(*pmesh, max_it); double osc = coeffrefiner.GetOsc(); @@ -203,8 +198,7 @@ TEST_CASE("Data Oscillation on 2D NCMesh embedded in 3D", { FunctionCoefficient u_analytic(testhelper_osc::SmoothSolutionY); - CoefficientRefiner coeffrefiner(order); - coeffrefiner.SetCoefficient(u_analytic); + CoefficientRefiner coeffrefiner(u_analytic,order); coeffrefiner.PreprocessMesh(*pmesh, max_it); double osc = coeffrefiner.GetOsc(); @@ -215,8 +209,7 @@ TEST_CASE("Data Oscillation on 2D NCMesh embedded in 3D", { FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionX); - CoefficientRefiner coeffrefiner(order); - coeffrefiner.SetCoefficient(u_analytic); + CoefficientRefiner coeffrefiner(u_analytic,order); coeffrefiner.SetThreshold(1e-3); coeffrefiner.PreprocessMesh(*pmesh, max_it); double osc = coeffrefiner.GetOsc(); @@ -228,8 +221,7 @@ TEST_CASE("Data Oscillation on 2D NCMesh embedded in 3D", { FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionY); - CoefficientRefiner coeffrefiner(order); - coeffrefiner.SetCoefficient(u_analytic); + CoefficientRefiner coeffrefiner(u_analytic,order); coeffrefiner.SetThreshold(1e-3); coeffrefiner.PreprocessMesh(*pmesh, max_it); double osc = coeffrefiner.GetOsc(); @@ -263,8 +255,7 @@ TEST_CASE("Data Oscillation on 3D NCMesh", { FunctionCoefficient u_analytic(testhelper_osc::SmoothSolutionX); - CoefficientRefiner coeffrefiner(order); - coeffrefiner.SetCoefficient(u_analytic); + CoefficientRefiner coeffrefiner(u_analytic,order); coeffrefiner.PreprocessMesh(*pmesh, max_it); double osc = coeffrefiner.GetOsc(); @@ -275,8 +266,7 @@ TEST_CASE("Data Oscillation on 3D NCMesh", { FunctionCoefficient u_analytic(testhelper_osc::SmoothSolutionY); - CoefficientRefiner coeffrefiner(order); - coeffrefiner.SetCoefficient(u_analytic); + CoefficientRefiner coeffrefiner(u_analytic,order); coeffrefiner.PreprocessMesh(*pmesh, max_it); double osc = coeffrefiner.GetOsc(); @@ -287,8 +277,7 @@ TEST_CASE("Data Oscillation on 3D NCMesh", { FunctionCoefficient u_analytic(testhelper_osc::SmoothSolutionZ); - CoefficientRefiner coeffrefiner(order); - coeffrefiner.SetCoefficient(u_analytic); + CoefficientRefiner coeffrefiner(u_analytic,order); coeffrefiner.PreprocessMesh(*pmesh, max_it); double osc = coeffrefiner.GetOsc(); @@ -299,8 +288,7 @@ TEST_CASE("Data Oscillation on 3D NCMesh", { FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionX); - CoefficientRefiner coeffrefiner(order); - coeffrefiner.SetCoefficient(u_analytic); + CoefficientRefiner coeffrefiner(u_analytic,order); coeffrefiner.SetThreshold(1e-3); coeffrefiner.PreprocessMesh(*pmesh, max_it); double osc = coeffrefiner.GetOsc(); @@ -312,8 +300,7 @@ TEST_CASE("Data Oscillation on 3D NCMesh", { FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionY); - CoefficientRefiner coeffrefiner(order); - coeffrefiner.SetCoefficient(u_analytic); + CoefficientRefiner coeffrefiner(u_analytic,order); coeffrefiner.SetThreshold(1e-3); coeffrefiner.PreprocessMesh(*pmesh, max_it); double osc = coeffrefiner.GetOsc(); @@ -325,8 +312,7 @@ TEST_CASE("Data Oscillation on 3D NCMesh", { FunctionCoefficient u_analytic(testhelper_osc::NonsmoothSolutionZ); - CoefficientRefiner coeffrefiner(order); - coeffrefiner.SetCoefficient(u_analytic); + CoefficientRefiner coeffrefiner(u_analytic,order); coeffrefiner.SetThreshold(1e-3); coeffrefiner.PreprocessMesh(*pmesh, max_it); double osc = coeffrefiner.GetOsc(); From bba6e27f858194563e049a4238bc50c4b686223a Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Fri, 27 Aug 2021 14:57:23 -0700 Subject: [PATCH 083/198] Minor edits. --- miniapps/shifted/diffusion.cpp | 9 +++++---- miniapps/shifted/marking.cpp | 19 +++++-------------- miniapps/shifted/marking.hpp | 14 ++------------ miniapps/shifted/sbm_aux.hpp | 33 ++++++++++++--------------------- miniapps/shifted/sbm_solver.hpp | 3 +-- 5 files changed, 25 insertions(+), 53 deletions(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index 899c21dcdc..0493b53e61 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -164,8 +164,8 @@ int main(int argc, char *argv[]) Vector vxyz; // Set the nodal grid function for the mesh, and modify the nodal positions - // for dirichlet_level_set_type = 3 such that some of the mesh elements are intersected - // by the true boundary (y = 0). + // for dirichlet_level_set_type = 3 such that some of the mesh elements are + // intersected by the true boundary (y = 0). ParFiniteElementSpace pfespace_mesh(&pmesh, &fec, dim); pmesh.SetNodalFESpace(&pfespace_mesh); ParGridFunction x_mesh(&pfespace_mesh); @@ -191,7 +191,6 @@ int main(int argc, char *argv[]) // Define the solution vector x as a finite element grid function // corresponding to pfespace. ParGridFunction x(&pfespace); - ParGridFunction combo_level_set_val(&pfespace); // Determine if each element in the ParMesh is inside the actual domain, // partially cut by its boundary, or completely outside the domain. @@ -471,7 +470,6 @@ int main(int argc, char *argv[]) if (dirichlet_level_set_type_combo == 6) { - b.AddInteriorFaceIntegrator(new SBM2DirichletLFIntegrator(&pmesh, *dbcCoefCombo, alpha, *dist_vec, elem_marker, @@ -630,6 +628,9 @@ int main(int argc, char *argv[]) } } + const double norm = x.ComputeL1Error(one); + if (myid == 0) { std::cout << setprecision(8) << norm << std::endl; } + // Free the used memory. delete prec; delete bicg; diff --git a/miniapps/shifted/marking.cpp b/miniapps/shifted/marking.cpp index f286a4df23..a826077142 100644 --- a/miniapps/shifted/marking.cpp +++ b/miniapps/shifted/marking.cpp @@ -14,10 +14,9 @@ namespace mfem { -void ShiftedFaceMarker::MarkElements(Array &elem_marker) +void ShiftedFaceMarker::MarkElements(const ParGridFunction &ls_func, + Array &elem_marker) { - MFEM_VERIFY(ls_func, "Level-set function to be used for marking has not " - "been specified. Check ShiftedFaceMarker constructor."); elem_marker.SetSize(pmesh.GetNE() + pmesh.GetNSharedFaces()); if (!initial_marking_done) { elem_marker = SBElementType::INSIDE; } else { level_set_index += 1; } @@ -31,7 +30,7 @@ void ShiftedFaceMarker::MarkElements(Array &elem_marker) ElementTransformation *Tr = pmesh.GetElementTransformation(i); const IntegrationRule &ir = IntRulesLo.Get(pmesh.GetElementBaseGeometry(i), 4*Tr->OrderJ()); - ls_func->GetValues(i, ir, vals); + ls_func.GetValues(i, ir, vals); int count = 0; for (int j = 0; j < ir.GetNPoints(); j++) @@ -62,8 +61,7 @@ void ShiftedFaceMarker::MarkElements(Array &elem_marker) ElementTransformation *eltr = pmesh.GetFaceNbrElementTransformation(Elem2NbrNo); const IntegrationRule &ir = - IntRulesLo.Get(pmesh.GetElementBaseGeometry(0), - 4*eltr->OrderJ()); + IntRulesLo.Get(pmesh.GetElementBaseGeometry(0), 4*eltr->OrderJ()); const int nip = ir.GetNPoints(); vals.SetSize(nip); @@ -71,7 +69,7 @@ void ShiftedFaceMarker::MarkElements(Array &elem_marker) for (int j = 0; j < nip; j++) { const IntegrationPoint &ip = ir.IntPoint(j); - vals[j] = ls_func->GetValue(tr->Elem2No, ip); + vals[j] = ls_func.GetValue(tr->Elem2No, ip); if (vals[j] <= 0.) { count++; } } @@ -91,13 +89,6 @@ void ShiftedFaceMarker::MarkElements(Array &elem_marker) initial_marking_done = true; } -void ShiftedFaceMarker::MarkElements(ParGridFunction &ls, - Array &elem_marker) -{ - SetLevelSetFunction(ls); - MarkElements(elem_marker); -} - void ShiftedFaceMarker::ListShiftedFaceDofs(const Array &elem_marker, Array &sface_dof_list) const { diff --git a/miniapps/shifted/marking.hpp b/miniapps/shifted/marking.hpp index 2f19f00a5b..c9663cf572 100644 --- a/miniapps/shifted/marking.hpp +++ b/miniapps/shifted/marking.hpp @@ -23,7 +23,6 @@ class ShiftedFaceMarker { protected: ParMesh &pmesh; // Mesh whose elements have to be marked. - ParGridFunction *ls_func; // Gridfunction to be used for marking. ParFiniteElementSpace *pfes_sltn; // FESpace associated with the solution. bool include_cut_cell; // Flag indicating wether cut-cells // will be included in assembly. @@ -46,21 +45,14 @@ public: /// to discern between different level-sets. enum SBElementType {INSIDE = 0, OUTSIDE = 1, CUT = 2}; - ShiftedFaceMarker(ParMesh &pm, ParGridFunction &ls, - ParFiniteElementSpace &pfes, bool include_cut_cell_) - : pmesh(pm), ls_func(&ls), pfes_sltn(&pfes), - include_cut_cell(include_cut_cell_), initial_marking_done(false), - level_set_index(0) { } - ShiftedFaceMarker(ParMesh &pm, ParFiniteElementSpace &pfes, bool include_cut_cell_) - : pmesh(pm), ls_func(NULL), pfes_sltn(&pfes), + : pmesh(pm), pfes_sltn(&pfes), include_cut_cell(include_cut_cell_), initial_marking_done(false), level_set_index(0) { } /// Mark all the elements in the mesh using the @a SBElementType - void MarkElements(Array &elem_marker); - void MarkElements(ParGridFunction &ls, Array &elem_marker); + void MarkElements(const ParGridFunction &ls_func, Array &elem_marker); /// List dofs associated with the surrogate boundary. /// If @a include_cut_cell = false, the surrogate boundary includes faces @@ -81,8 +73,6 @@ public: const Array &sface_dof_list, Array &ess_tdof_list, Array &ess_shift_bdr) const; - - void SetLevelSetFunction(ParGridFunction &ls) { ls_func = &ls; } }; } // namespace mfem diff --git a/miniapps/shifted/sbm_aux.hpp b/miniapps/shifted/sbm_aux.hpp index 5174e1c4c5..19dc368e3e 100644 --- a/miniapps/shifted/sbm_aux.hpp +++ b/miniapps/shifted/sbm_aux.hpp @@ -10,8 +10,6 @@ // CONTRIBUTING.md for details. #include "mfem.hpp" -#include -#include using namespace std; using namespace mfem; @@ -22,26 +20,21 @@ double point_inside_trigon(const Vector px, Vector p1, Vector p2, Vector p3) Vector v1 = p2; v1 -=p1; Vector v2 = p3; v2 -=p1; double p, q; - p = ((px(0)*v2(1)-px(1)*v2(0))-(v0(0)*v2(1)-v0(1)*v2(0)))/(v1(0)*v2(1)-v1(1)*v2( - 0)); - q = -((px(0)*v1(1)-px(1)*v1(0))-(v0(0)*v1(1)-v0(1)*v1(0)))/(v1(0)*v2(1)-v1( - 1)*v2(0)); + p = ((px(0)*v2(1)-px(1)*v2(0))-(v0(0)*v2(1)-v0(1)*v2(0))) / + (v1(0)*v2(1)-v1(1)*v2(0)); + q = -((px(0)*v1(1)-px(1)*v1(0))-(v0(0)*v1(1)-v0(1)*v1(0))) / + (v1(0)*v2(1)-v1(1)*v2(0)); - if (p > 0 && q > 0 && 1-p-q > 0) - { - return -1.0; - } - return 1.0; + return (p > 0 && q > 0 && 1-p-q > 0) ? -1.0 : 1.0; } /// Analytic distance to the 0 level set. Positive value if the point is inside /// the domain, and negative value if outside. double dist_value(const Vector &x, const int type) { - - double ring_radius = 0.2; if (type == 1 || type == 2) // circle of radius 0.2 - centered at 0.5, 0.5 { + const double ring_radius = 0.2; Vector xc(x.Size()); xc = 0.5; xc -= x; @@ -116,7 +109,7 @@ double dist_value(const Vector &x, const int type) return 0.; } -/// Level set coefficient - +1 inside the domain, -1 outside, 0 at the boundary. +/// Level set coefficient: +1 inside the true domain, -1 outside. class Dist_Level_Set_Coefficient : public Coefficient { private: @@ -131,12 +124,11 @@ public: Vector x(3); T.Transform(ip, x); double dist = dist_value(x, type); - if (dist >= 0.) { return 1.; } - else { return -1.; } + return (dist >= 0.0) ? 1.0 : -1.0; } }; -/// Level set coefficient - +1 inside the domain, -1 outside, 0 at the boundary. +/// Combination of level sets: +1 inside the true domain, -1 outside. class Combo_Level_Set_Coefficient : public Coefficient { private: @@ -152,15 +144,14 @@ public: virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip) { - MFEM_VERIFY(dls.Size() > 0, "Add at-least 1 Dist_level_Set_Coefficient to" - " the Combo."); + MFEM_VERIFY(dls.Size() > 0, + "Add at least 1 Dist_level_Set_Coefficient to the Combo."); double dist = dls[0]->Eval(T, ip); for (int j = 1; j < dls.Size(); j++) { dist = min(dist, dls[j]->Eval(T, ip)); } - if (dist >= 0.) { return 1.; } - else { return -1.; } + return (dist >= 0.0) ? 1.0 : -1.0; } }; diff --git a/miniapps/shifted/sbm_solver.hpp b/miniapps/shifted/sbm_solver.hpp index d6ecd40fc5..73d057783c 100644 --- a/miniapps/shifted/sbm_solver.hpp +++ b/miniapps/shifted/sbm_solver.hpp @@ -33,8 +33,7 @@ public: ShiftedFunctionCoefficient(double constant_) : constant(constant_), constantcoefficient(true) { } - virtual double Eval(ElementTransformation &T, - const IntegrationPoint &ip) + virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip) { if (constantcoefficient) { return constant; } Vector transip; From 4aaa441d70614f310f795592de8d6e91a639db25 Mon Sep 17 00:00:00 2001 From: Dylan Copeland Date: Fri, 27 Aug 2021 15:40:21 -0700 Subject: [PATCH 084/198] Fixing a bug in DiffusionIntegrator in the case of a vector (diagonal matrix) coefficient. --- fem/bilininteg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fem/bilininteg.cpp b/fem/bilininteg.cpp index 7289058e63..5a7f35ec4d 100644 --- a/fem/bilininteg.cpp +++ b/fem/bilininteg.cpp @@ -996,7 +996,7 @@ void DiffusionIntegrator::ComputeElementFlux #endif vec.SetSize(dim); vecdxt.SetSize(spaceDim); - pointflux.SetSize(MQ ? spaceDim : 0); + pointflux.SetSize(MQ || VQ ? spaceDim : 0); const IntegrationRule &ir = fluxelem.GetNodes(); fnd = ir.GetNPoints(); From 08715a83cfceb86111890ebf8a38310f00a76f17 Mon Sep 17 00:00:00 2001 From: Dylan Copeland Date: Sat, 28 Aug 2021 12:54:47 -0700 Subject: [PATCH 085/198] Fixing with_coef logic in flux calculation. --- fem/bilininteg.cpp | 49 +++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/fem/bilininteg.cpp b/fem/bilininteg.cpp index 5a7f35ec4d..cffabdae91 100644 --- a/fem/bilininteg.cpp +++ b/fem/bilininteg.cpp @@ -1012,36 +1012,45 @@ void DiffusionIntegrator::ComputeElementFlux CalcInverse(Trans.Jacobian(), invdfdx); invdfdx.MultTranspose(vec, vecdxt); - if (!MQ && !VQ) + if (with_coef) { - if (Q && with_coef) + if (!MQ && !VQ) { - vecdxt *= Q->Eval(Trans,ip); + if (Q) + { + vecdxt *= Q->Eval(Trans,ip); + } + for (j = 0; j < spaceDim; j++) + { + flux(fnd*j+i) = vecdxt(j); + } } - for (j = 0; j < spaceDim; j++) + else { - flux(fnd*j+i) = vecdxt(j); + if (MQ) + { + MQ->Eval(M, Trans, ip); + M.Mult(vecdxt, pointflux); + } + else + { + VQ->Eval(D, Trans, ip); + for (int j=0; jEval(M, Trans, ip); - M.Mult(vecdxt, pointflux); - } - else - { - VQ->Eval(D, Trans, ip); - for (int j=0; j Date: Tue, 31 Aug 2021 16:55:27 +0200 Subject: [PATCH 086/198] Minor whitespace and typos. --- examples/osc.cpp | 4 ++-- mesh/mesh.cpp | 4 ++-- mesh/mesh_operators.hpp | 9 ++++++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/examples/osc.cpp b/examples/osc.cpp index 1ffc3c4f35..866fdc8066 100644 --- a/examples/osc.cpp +++ b/examples/osc.cpp @@ -137,7 +137,7 @@ int main(int argc, char *argv[]) FunctionCoefficient affine_coeff(affine_function); FunctionCoefficient jump_coeff(jump_function); FunctionCoefficient singular_coeff(singular_function); - CoefficientRefiner coeffrefiner(affine_coeff,order); + CoefficientRefiner coeffrefiner(affine_coeff, order); // 4. Connect to GLVis. char vishost[] = "localhost"; @@ -151,7 +151,7 @@ int main(int argc, char *argv[]) // 5. Define custom integration rule (optional). const IntegrationRule *irs[Geometry::NumGeom]; int order_quad = 2*order + enriched_order; - for (int i=0; i < Geometry::NumGeom; ++i) + for (int i = 0; i < Geometry::NumGeom; ++i) { irs[i] = &(IntRules.Get(i, order_quad)); } diff --git a/mesh/mesh.cpp b/mesh/mesh.cpp index 099a62196f..2b6e7c5a48 100644 --- a/mesh/mesh.cpp +++ b/mesh/mesh.cpp @@ -75,7 +75,7 @@ void Mesh::GetElementCenter(int i, Vector ¢er) double Mesh::GetElementSize(ElementTransformation *T, int type) { - DenseMatrix J(spaceDim,Dim); + DenseMatrix J(spaceDim, Dim); Geometry::Type geom = T->GetGeometryType(); T->SetIntPoint(&Geometries.GetCenter(geom)); @@ -102,7 +102,7 @@ double Mesh::GetElementSize(int i, int type) double Mesh::GetElementSize(int i, const Vector &dir) { - DenseMatrix J(spaceDim,Dim); + DenseMatrix J(spaceDim, Dim); Vector d_hat(Dim); GetElementJacobian(i, J); J.MultTranspose(dir, d_hat); diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index 7a4fe46458..e9ba3fe3f4 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -307,6 +307,7 @@ public: virtual void Reset() { estimator.Reset(); } }; + /** @brief Refinement operator to control data oscillation. This class computes osc_K(f) := || h ⋅ (I - Π) f ||_K at each element K. @@ -315,14 +316,15 @@ public: \code osc_K(f) > threshold ⋅ ||f|| / sqrt(n_el), \endcode - are refined. Here, threshold is a postive parameter, ||⋅|| is the L2-norm + are refined. Here, threshold is a positive parameter, ||⋅|| is the L2-norm over the entire domain Ω, and n_el is the number of elements in the mesh. Note that if osc(f) = threshold ⋅ ||f|| / sqrt(n_el) for each K, then \code - osc(f) = sqrt( sum_K osc_K^2(f)) = threshold ⋅ ||f||. + osc(f) = sqrt(sum_K osc_K^2(f)) = threshold ⋅ ||f||. \endcode - This is the reason for the 1/sqrt(n_el) factor. */ + This is the reason for the 1/sqrt(n_el) factor. +*/ class CoefficientRefiner : public MeshOperator { protected: @@ -413,6 +415,7 @@ public: virtual void Reset(); }; + /** @brief ParMesh rebalancing operator. If the mesh is a parallel mesh, perform rebalancing; otherwise, do nothing. From d0831c2f11f91aaec8b17eb39c5bb11850d48c57 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Tue, 31 Aug 2021 10:46:21 -0700 Subject: [PATCH 087/198] Minor edits in the miniapp. --- miniapps/shifted/diffusion.cpp | 41 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index 0493b53e61..ac83c172a8 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -34,10 +34,10 @@ // Problem 1: Circular hole of radius 0.2 at the center of the domain. // Solves -nabla^2 u = 1 with homogeneous boundary conditions. // Dirichlet boundary condition -// mpirun -np 4 diffusion -m ../../data/inline-quad.mesh -rs 3 -o 1 -vis -lst 1 +// mpirun -np 4 diffusion -rs 3 -o 1 -vis -lst 1 // mpirun -np 4 diffusion -m ../../data/inline-hex.mesh -rs 2 -o 2 -vis -lst 1 -ho 1 -alpha 10 // Neumann boundary condition -// mpirun -np 1 diffusion -m ../../data/inline-quad.mesh -rs 3 -o 1 -vis -lst -1 -ho 1 -nlst 1 +// mpirun -np 4 diffusion -rs 3 -o 1 -vis -nlst 1 -ho 1 // // Problem 2: Circular hole of radius 0.2 at the center of the domain. // Solves -nabla^2 u = f with inhomogeneous boundary conditions, and @@ -46,7 +46,7 @@ // Dirichlet boundary condition // mpirun -np 4 diffusion -rs 2 -o 2 -vis -lst 2 // Neumann boundary condition (inhomogeneous condition derived using exact solution) -// mpirun -np 1 diffusion -m ../../data/inline-quad.mesh -rs 2 -o 1 -vis -lst -1 -ho 1 -nlst 2 +// mpirun -np 4 diffusion -rs 2 -o 2 -vis -nlst 2 -ho 1 // // Problem 3: Domain is y = [0, 1] but mesh is shifted to [-1.e-4, 1]. // Solves -nabla^2 u = f with inhomogeneous boundary conditions, @@ -66,7 +66,7 @@ // Problem 5: Circular hole with homogeneous Neumann, triangular hole with // inhomogeneous Dirichlet, and square hole with homogeneous Dirichlet // boundary condition. -// mpirun -np 1 diffusion -m ../../data/inline-quad.mesh -rs 3 -o 1 -vis -lst 5 -ho 1 -nlst 7 -alpha 10.0 -dlstc 6 +// mpirun -np 4 diffusion -rs 3 -o 1 -vis -lst 5 -ho 1 -nlst 7 -alpha 10.0 -dc #include "mfem.hpp" #include "../common/mfem-common.hpp" @@ -97,9 +97,9 @@ int main(int argc, char *argv[]) int order = 2; bool visualization = true; int ser_ref_levels = 0; - int dirichlet_level_set_type = 1; - int dirichlet_level_set_type_combo = -1; + int dirichlet_level_set_type = -1; int neumann_level_set_type = -1; + bool dirichlet_combo = false; int ho_terms = 0; double alpha = 1; bool include_cut_cell = false; @@ -119,6 +119,9 @@ int main(int argc, char *argv[]) "level-set-type."); args.AddOption(&neumann_level_set_type, "-nlst", "--neumann-level-set-type", "neumann-level-set-type."); + args.AddOption(&dirichlet_combo, "-dc", "--dcombo", + "no-dc", "--no-dcombo", + "Combination of two Dirichlet level sets."); args.AddOption(&ho_terms, "-ho", "--high-order", "Additional high-order terms to include"); args.AddOption(&alpha, "-alpha", "--alpha", @@ -126,9 +129,6 @@ int main(int argc, char *argv[]) args.AddOption(&include_cut_cell, "-cut", "--cut", "-no-cut-cell", "--no-cut-cell", "Include or not include elements cut by true boundary."); - args.AddOption(&dirichlet_level_set_type_combo, "-dlstc", - "--level-set-type-combo", "level-set-type-combo."); - args.Parse(); if (!args.Good()) { @@ -141,6 +141,11 @@ int main(int argc, char *argv[]) } if (myid == 0) { args.PrintOptions(cout); } + MFEM_VERIFY(dirichlet_level_set_type >= 0 || neumann_level_set_type >= 0, + "The level set and type of BC are not specified."); + MFEM_VERIFY((neumann_level_set_type >= 0 && ho_terms < 1) == false, + "Shifted Neumann BC requires extra terms, i.e., -ho >= 1."); + // Enable hardware devices such as GPUs, and programming models such as CUDA, // OCCA, RAJA and OpenMP based on command line options. Device device("cpu"); @@ -198,7 +203,6 @@ int main(int argc, char *argv[]) Dist_Level_Set_Coefficient *dirichlet_dist_coef = NULL; Dist_Level_Set_Coefficient *dirichlet_dist_coef_2 = NULL; Dist_Level_Set_Coefficient *neumann_dist_coef = NULL; - // Create a Combo level set coefficient Combo_Level_Set_Coefficient combo_dist_coef; ShiftedFaceMarker marker(pmesh, pfespace, include_cut_cell); @@ -221,14 +225,13 @@ int main(int argc, char *argv[]) } // Second Dirichlet level-set. - if (dirichlet_level_set_type_combo == 6) + if (dirichlet_combo) { MFEM_VERIFY(dirichlet_level_set_type == 5, "The combo level set example has been only set for" " dirichlet_level_set_type == 5."); ParGridFunction dirichlet_level_set_val(&pfespace); - dirichlet_dist_coef_2 = new Dist_Level_Set_Coefficient( - dirichlet_level_set_type_combo); + dirichlet_dist_coef_2 = new Dist_Level_Set_Coefficient(6); dirichlet_level_set_val.ProjectCoefficient(*dirichlet_dist_coef_2); dirichlet_level_set_val.ExchangeFaceNbrData(); marker.MarkElements(dirichlet_level_set_val, elem_marker); @@ -411,7 +414,7 @@ int main(int argc, char *argv[]) } ShiftedFunctionCoefficient *dbcCoefCombo = NULL; - if (dirichlet_level_set_type_combo == 6) + if (dirichlet_combo) { dbcCoefCombo = new ShiftedFunctionCoefficient(0.015); } @@ -468,7 +471,7 @@ int main(int argc, char *argv[]) ls_cut_marker += 1; } - if (dirichlet_level_set_type_combo == 6) + if (dirichlet_combo) { b.AddInteriorFaceIntegrator(new SBM2DirichletLFIntegrator(&pmesh, *dbcCoefCombo, alpha, *dist_vec, @@ -566,9 +569,9 @@ int main(int argc, char *argv[]) sol_ofs.precision(8); x.SaveAsOne(sol_ofs); - // Save the solution in ParaView format if (visualization) { + // Save the solution in ParaView format. ParaViewDataCollection dacol("ParaViewDiffusion", &pmesh); dacol.SetLevelsOfDetail(order); dacol.RegisterField("distance", &distance); @@ -576,12 +579,8 @@ int main(int argc, char *argv[]) dacol.SetTime(1.0); dacol.SetCycle(1); dacol.Save(); - } - - // Send the solution by socket to a GLVis server. - if (visualization) - { + // Send the solution by socket to a GLVis server. char vishost[] = "localhost"; int visport = 19916, s = 350; socketstream sol_sock; From 5f6fda1bf354fc06a9a8e3dd30dac9c2efd04e33 Mon Sep 17 00:00:00 2001 From: Keith Date: Tue, 31 Aug 2021 11:55:22 -0700 Subject: [PATCH 088/198] Jakub's changes --- examples/osc.cpp | 2 +- examples/oscp.cpp | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/osc.cpp b/examples/osc.cpp index 866fdc8066..0dd445de68 100644 --- a/examples/osc.cpp +++ b/examples/osc.cpp @@ -62,7 +62,7 @@ double jump_function(const Vector &p) { if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1; } if (p.Normlp(2.0) < 0.4 || p.Normlp(2.0) > 0.6) { return 5; } - return 0; + return 5; } // Singular function derived from the Laplacian of the "steep wavefront" diff --git a/examples/oscp.cpp b/examples/oscp.cpp index 7ebc5e0549..adf02e0e3e 100644 --- a/examples/oscp.cpp +++ b/examples/oscp.cpp @@ -62,7 +62,7 @@ double jump_function(const Vector &p) { if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1; } if (p.Normlp(2.0) < 0.4 || p.Normlp(2.0) > 0.6) { return 5; } - return 0; + return 5; } // Singular function derived from the Laplacian of the "steep wavefront" @@ -152,10 +152,9 @@ int main(int argc, char *argv[]) } // 3. Make sure the mesh is in the non-conforming mode to enable local - // refinement of quadrilaterals/hexahedra, and the above partitioning - // algorithm. Simplices can be refined either in conforming or in non- - // conforming mode. The conforming mode however does not support - // dynamic partitioning. + // refinement of quadrilaterals/hexahedra. Simplices can be refined + // either in conforming or in non-conforming mode. The conforming + // mode however does not support dynamic partitioning. mesh.EnsureNCMesh(nc_simplices); // 4. Define a parallel mesh by partitioning the serial mesh. From 6b33ad4abfdfe0bfdbd952d40977aab97a76dd61 Mon Sep 17 00:00:00 2001 From: Keith Date: Tue, 31 Aug 2021 12:58:44 -0700 Subject: [PATCH 089/198] Jakub's changes --- examples/osc.cpp | 1 - examples/oscp.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/examples/osc.cpp b/examples/osc.cpp index 0dd445de68..ca14096e68 100644 --- a/examples/osc.cpp +++ b/examples/osc.cpp @@ -61,7 +61,6 @@ double affine_function(const Vector &p) double jump_function(const Vector &p) { if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1; } - if (p.Normlp(2.0) < 0.4 || p.Normlp(2.0) > 0.6) { return 5; } return 5; } diff --git a/examples/oscp.cpp b/examples/oscp.cpp index adf02e0e3e..f3832495d4 100644 --- a/examples/oscp.cpp +++ b/examples/oscp.cpp @@ -61,7 +61,6 @@ double affine_function(const Vector &p) double jump_function(const Vector &p) { if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1; } - if (p.Normlp(2.0) < 0.4 || p.Normlp(2.0) > 0.6) { return 5; } return 5; } From 1fce1b630668723ee49165ade85a6ac7b9dddea0 Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Tue, 31 Aug 2021 14:44:20 -0700 Subject: [PATCH 090/198] Add device configuration to plor_solvers --- miniapps/solvers/plor_solvers.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/miniapps/solvers/plor_solvers.cpp b/miniapps/solvers/plor_solvers.cpp index cc31f36dad..0df6715d8e 100644 --- a/miniapps/solvers/plor_solvers.cpp +++ b/miniapps/solvers/plor_solvers.cpp @@ -76,6 +76,7 @@ int main(int argc, char *argv[]) int ser_ref_levels = 1, par_ref_levels = 1; int order = 3; const char *fe = "h"; + const char *device_config = "cpu"; bool visualization = true; OptionsParser args(argc, argv); @@ -90,8 +91,13 @@ int main(int argc, char *argv[]) args.AddOption(&visualization, "-vis", "--visualization", "-no-vis", "--no-visualization", "Enable or disable GLVis visualization."); + args.AddOption(&device_config, "-d", "--device", + "Device configuration string, see Device::Configure()."); args.ParseCheck(); + Device device(device_config); + device.Print(); + bool H1 = false, ND = false, RT = false, L2 = false; if (string(fe) == "h") { H1 = true; } else if (string(fe) == "n") { ND = true; } From 1459c4f5a62a4480d17163db9aaadd9e10f0e820 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Wed, 1 Sep 2021 18:14:25 -0700 Subject: [PATCH 091/198] Minor. --- miniapps/shifted/sbm_solver.cpp | 1 - miniapps/shifted/sbm_solver.hpp | 13 ++++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/miniapps/shifted/sbm_solver.cpp b/miniapps/shifted/sbm_solver.cpp index e2d21778ee..d7c8cde63a 100644 --- a/miniapps/shifted/sbm_solver.cpp +++ b/miniapps/shifted/sbm_solver.cpp @@ -10,7 +10,6 @@ // CONTRIBUTING.md for details. #include "sbm_solver.hpp" -#include "mfem.hpp" namespace mfem { diff --git a/miniapps/shifted/sbm_solver.hpp b/miniapps/shifted/sbm_solver.hpp index 73d057783c..8848f1b8dd 100644 --- a/miniapps/shifted/sbm_solver.hpp +++ b/miniapps/shifted/sbm_solver.hpp @@ -12,8 +12,8 @@ #ifndef MFEM_SBM_SOLVER_HPP #define MFEM_SBM_SOLVER_HPP -#include "marking.hpp" #include "mfem.hpp" +#include "marking.hpp" namespace mfem { @@ -36,9 +36,8 @@ public: virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip) { if (constantcoefficient) { return constant; } - Vector transip; - T.Transform(ip, transip); - Vector D = transip; + + Vector D(T.GetSpaceDim()); D = 0.; return (this)->Eval(T, ip, D); } @@ -248,7 +247,7 @@ public: FaceElementTransformations &Trans, DenseMatrix &elmat); - bool GetTrimFlag() { return include_cut_cell; } + bool GetTrimFlag() const { return include_cut_cell; } virtual ~SBM2NeumannIntegrator() { } }; @@ -279,7 +278,7 @@ protected: // term from Taylor expansion that should be included. (0 by default). bool include_cut_cell; int NEproc; // Number of elements on the current MPI rank - int par_shared_face_count; // + int par_shared_face_count; int ls_cut_marker; // these are not thread-safe! @@ -312,7 +311,7 @@ public: const FiniteElement &el2, FaceElementTransformations &Tr, Vector &elvect); - bool GetTrimFlag() { return include_cut_cell; } + bool GetTrimFlag() const { return include_cut_cell; } }; } // namespace mfem From c8f6d1ad4f7ada24549f52642164015db71fcafe Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 2 Sep 2021 12:40:36 -0700 Subject: [PATCH 092/198] Socratis's comments --- examples/osc.cpp | 6 +++--- examples/oscp.cpp | 6 +++--- mesh/mesh_operators.hpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/osc.cpp b/examples/osc.cpp index ca14096e68..732a348e4f 100644 --- a/examples/osc.cpp +++ b/examples/osc.cpp @@ -60,8 +60,8 @@ double affine_function(const Vector &p) // Piecewise-constant function which is never mesh-conforming double jump_function(const Vector &p) { - if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1; } - return 5; + if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1.0; } + return 5.0; } // Singular function derived from the Laplacian of the "steep wavefront" @@ -157,7 +157,7 @@ int main(int argc, char *argv[]) // 6. Apply custom refiner settings. coeffrefiner.SetIntRule(irs); - coeffrefiner.SetMaxElements( (long) max_elems); + coeffrefiner.SetMaxElements( max_elems); coeffrefiner.SetThreshold(osc_threshold); coeffrefiner.SetNCLimit(nc_limit); diff --git a/examples/oscp.cpp b/examples/oscp.cpp index f3832495d4..dbba9b03fb 100644 --- a/examples/oscp.cpp +++ b/examples/oscp.cpp @@ -60,8 +60,8 @@ double affine_function(const Vector &p) // Piecewise-constant function which is never mesh-conforming double jump_function(const Vector &p) { - if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1; } - return 5; + if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1.0; } + return 5.0; } // Singular function derived from the Laplacian of the "steep wavefront" @@ -186,7 +186,7 @@ int main(int argc, char *argv[]) // 8. Apply custom refiner settings. coeffrefiner.SetIntRule(irs); - coeffrefiner.SetMaxElements( (long) max_elems); + coeffrefiner.SetMaxElements( max_elems); coeffrefiner.SetThreshold(osc_threshold); coeffrefiner.SetNCLimit(nc_limit); diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index e9ba3fe3f4..f671ba8ecb 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -401,7 +401,7 @@ public: void SetIntRule(const IntegrationRule *irs_[]) { irs = irs_; } // Return the value of the global relative data oscillation - const double GetOsc() { return global_osc; } + double GetOsc() { return global_osc; } // Return the local relative data oscillation errors const Vector & GetLocalOscs() const From 3388160761f151058e0cb8587cc6ba7385d03d8f Mon Sep 17 00:00:00 2001 From: Dylan Copeland Date: Thu, 2 Sep 2021 21:25:49 -0700 Subject: [PATCH 093/198] Fixing some unsupported coefficients in ComputeFluxEnergy. --- fem/bilininteg.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/fem/bilininteg.cpp b/fem/bilininteg.cpp index cffabdae91..ded6e4491d 100644 --- a/fem/bilininteg.cpp +++ b/fem/bilininteg.cpp @@ -984,6 +984,8 @@ void DiffusionIntegrator::ComputeElementFlux "Unexpected height for MatrixCoefficient"); } + MFEM_VERIFY(!SMQ, "SymmetricMatrixCoefficient not supported here"); + #ifdef MFEM_THREAD_SAFE DenseMatrix dshape(nd,dim), invdfdx(dim, spaceDim); DenseMatrix M(MQ ? spaceDim : 0); @@ -1066,8 +1068,13 @@ double DiffusionIntegrator::ComputeFluxEnergy #ifdef MFEM_THREAD_SAFE DenseMatrix M; + Vector D(VQ ? VQ->GetVDim() : 0); +#else + D.SetSize(VQ ? VQ->GetVDim() : 0); #endif + MFEM_VERIFY(!SMQ, "SymmetricMatrixCoefficient not supported here"); + shape.SetSize(nd); pointflux.SetSize(spaceDim); if (d_energy) { vec.SetSize(spaceDim); } @@ -1096,17 +1103,23 @@ double DiffusionIntegrator::ComputeFluxEnergy Trans.SetIntPoint(&ip); double w = Trans.Weight() * ip.weight; - if (!MQ) + if (MQ) + { + MQ->Eval(M, Trans, ip); + energy += w * M.InnerProduct(pointflux, pointflux); + } + else if (VQ) + { + VQ->Eval(D, Trans, ip); + D *= pointflux; + energy += w * (D * pointflux); + } + else { double e = (pointflux * pointflux); if (Q) { e *= Q->Eval(Trans, ip); } energy += w * e; } - else - { - MQ->Eval(M, Trans, ip); - energy += w * M.InnerProduct(pointflux, pointflux); - } if (d_energy) { From 3c82571b6892e433311c78bc75bd0479e636caaf Mon Sep 17 00:00:00 2001 From: Dylan Copeland Date: Fri, 3 Sep 2021 09:55:32 -0700 Subject: [PATCH 094/198] Small comment. --- fem/bilininteg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fem/bilininteg.cpp b/fem/bilininteg.cpp index ded6e4491d..7dd64b9bc9 100644 --- a/fem/bilininteg.cpp +++ b/fem/bilininteg.cpp @@ -1129,7 +1129,7 @@ double DiffusionIntegrator::ComputeFluxEnergy { (*d_energy)[k] += w * vec[k] * vec[k]; } - // TODO: Q, MQ + // TODO: Q, VQ, MQ } } From 68fabe1cfaf8c2b23d5e1b8297e06af1be85c602 Mon Sep 17 00:00:00 2001 From: "Stowell, Mark L" Date: Fri, 3 Sep 2021 15:00:55 -0700 Subject: [PATCH 095/198] Adding GetVectorFieldValues check to GetVectorValue unit test --- tests/unit/fem/test_get_value.cpp | 47 +++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/unit/fem/test_get_value.cpp b/tests/unit/fem/test_get_value.cpp index 7b3da161bf..19a78ac11c 100644 --- a/tests/unit/fem/test_get_value.cpp +++ b/tests/unit/fem/test_get_value.cpp @@ -2495,6 +2495,12 @@ TEST_CASE("3D GetVectorValue", Vector dgv_gvv_val(dim); dgv_gvv_val = 0.0; Vector dgi_gvv_val(dim); dgi_gvv_val = 0.0; + Vector nd_gvf_val(dim); nd_gvf_val = 0.0; + Vector rt_gvf_val(dim); rt_gvf_val = 0.0; + DenseMatrix nd_gvf_vals; + DenseMatrix rt_gvf_vals; + DenseMatrix tr; + SECTION("Domain Evaluation 3D") { std::cout << "Domain Evaluation 3D" << std::endl; @@ -2519,6 +2525,12 @@ TEST_CASE("3D GetVectorValue", double dgv_gvv_err = 0.0; double dgi_gvv_err = 0.0; + double nd_gvf_err = 0.0; + double rt_gvf_err = 0.0; + + nd_x.GetVectorFieldValues(e, ir, nd_gvf_vals, tr); + rt_x.GetVectorFieldValues(e, ir, rt_gvf_vals, tr); + for (int j=0; j 0 && h1_gfc_dist > tol) { std::cout << e << ":" << j << " h1 gfc (" @@ -2681,6 +2702,26 @@ TEST_CASE("3D GetVectorValue", << dgi_gvv_val[2] << ") " << dgi_gvv_dist << std::endl; } + if (log > 0 && nd_gvf_dist > tol) + { + std::cout << e << ":" << j << " nd gvf (" + << f_val[0] << "," << f_val[1] << "," + << f_val[2] << ") vs. (" + << nd_gvf_val[0] << "," + << nd_gvf_val[1] << "," + << nd_gvf_val[2] << ") " + << nd_gvf_dist << std::endl; + } + if (log > 0 && rt_gvf_dist > tol) + { + std::cout << e << ":" << j << " rt gvf (" + << f_val[0] << "," << f_val[1] << "," + << f_val[2] << ") vs. (" + << rt_gvf_val[0] << "," + << rt_gvf_val[1] << "," + << rt_gvf_val[2] << ") " + << rt_gvf_dist << std::endl; + } } h1_gfc_err /= ir.GetNPoints(); @@ -2697,6 +2738,9 @@ TEST_CASE("3D GetVectorValue", dgv_gvv_err /= ir.GetNPoints(); dgi_gvv_err /= ir.GetNPoints(); + nd_gvf_err /= ir.GetNPoints(); + rt_gvf_err /= ir.GetNPoints(); + REQUIRE( h1_gfc_err == MFEM_Approx(0.0)); REQUIRE( nd_gfc_err == MFEM_Approx(0.0)); REQUIRE( rt_gfc_err == MFEM_Approx(0.0)); @@ -2710,6 +2754,9 @@ TEST_CASE("3D GetVectorValue", REQUIRE( l2_gvv_err == MFEM_Approx(0.0)); REQUIRE(dgv_gvv_err == MFEM_Approx(0.0)); REQUIRE(dgi_gvv_err == MFEM_Approx(0.0)); + + REQUIRE( nd_gvf_err == MFEM_Approx(0.0)); + REQUIRE( rt_gvf_err == MFEM_Approx(0.0)); } } From 1d5df46cdbc60a3f898325ef57a7ffd68ff65be2 Mon Sep 17 00:00:00 2001 From: "Stowell, Mark L" Date: Fri, 3 Sep 2021 15:01:25 -0700 Subject: [PATCH 096/198] Adding rescaling to special case at apex of RT pyramid --- fem/fe.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fem/fe.cpp b/fem/fe.cpp index f27c190be1..8ff7aeceb1 100644 --- a/fem/fe.cpp +++ b/fem/fe.cpp @@ -8034,6 +8034,15 @@ void RT0PyrFiniteElement::CalcVShape(const IntegrationPoint &ip, shape(4,1) = - 0.5; shape(4,2) = 1.0; + if (!rt0) + { + for (int i=1; i<5; i++) + for (int j=0; j<3; j++) + { + shape(i, j) *= 0.5; + } + } + return; } From 583024eaa70f5c6873412a22a4d5611a62dd6bae Mon Sep 17 00:00:00 2001 From: "Robert W. Anderson" Date: Fri, 3 Sep 2021 17:12:18 -0700 Subject: [PATCH 097/198] keep track of # of nonghost elements in derefinement, use this to fix GetCoarseFineMap in the case of derefinement --- mesh/ncmesh.cpp | 21 +++++++++++++++------ mesh/ncmesh.hpp | 6 +++++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/mesh/ncmesh.cpp b/mesh/ncmesh.cpp index 133f3b1f4b..151d5f90b3 100644 --- a/mesh/ncmesh.cpp +++ b/mesh/ncmesh.cpp @@ -1868,6 +1868,8 @@ void NCMesh::InitDerefTransforms() transforms.embeddings[i].parent = -1; transforms.embeddings[i].matrix = 0; } + + transforms.orig_elements = NElements; } void NCMesh::SetDerefMatrixCodes(int parent, Array &fine_coarse) @@ -4452,12 +4454,18 @@ struct RefType } // namespace internal void CoarseFineTransformations::GetCoarseToFineMap( - const mfem::Mesh &fine_mesh, Table &coarse_to_fine, + const mfem::Mesh &new_mesh, Table &coarse_to_fine, Array &coarse_to_ref_type, Table &ref_type_to_matrix, Array &ref_type_to_geom, bool get_coarse_to_fine_only) const { - const int fine_ne = embeddings.Size(); + int fine_ne = embeddings.Size(); + + // In the case of derefinement, we want to process only nonghost elements. + if (new_mesh.GetLastOperation() == Mesh::Operation::DEREFINE) + { + fine_ne = orig_elements; + } int coarse_ne = -1; for (int i = 0; i < fine_ne; i++) { @@ -4496,7 +4504,7 @@ void CoarseFineTransformations::GetCoarseToFineMap( } if (get_coarse_to_fine_only) { return; } - MFEM_VERIFY(fine_mesh.GetLastOperation() != Mesh::Operation::DEREFINE, + MFEM_VERIFY(new_mesh.GetLastOperation() != Mesh::Operation::DEREFINE, "GetCoarseToFineMap is not fully supported for derefined meshes." " Set 'get_coarse_to_fine_only=true'.") @@ -4511,7 +4519,7 @@ void CoarseFineTransformations::GetCoarseToFineMap( MFEM_ASSERT(num_children > 0, ""); const int fine_el = cf_j[cf_i[i]].two; // Assuming the coarse and the fine elements have the same geometry: - const Geometry::Type geom = fine_mesh.GetElementBaseGeometry(fine_el); + const Geometry::Type geom = new_mesh.GetElementBaseGeometry(fine_el); const RefType ref_type(geom, num_children, &cf_j[cf_i[i]]); pair::iterator,bool> res = ref_type_map.insert( @@ -4541,14 +4549,14 @@ void CoarseFineTransformations::GetCoarseToFineMap( ref_type_to_matrix.ShiftUpI(); } -void CoarseFineTransformations::GetCoarseToFineMap(const Mesh &fine_mesh, +void CoarseFineTransformations::GetCoarseToFineMap(const Mesh &new_mesh, Table &coarse_to_fine) const { Array coarse_to_ref_type; Table ref_type_to_matrix; Array ref_type_to_geom; bool get_coarse_to_fine_only = true; - GetCoarseToFineMap(fine_mesh, coarse_to_fine, coarse_to_ref_type, + GetCoarseToFineMap(new_mesh, coarse_to_fine, coarse_to_ref_type, ref_type_to_matrix, ref_type_to_geom, get_coarse_to_fine_only); } @@ -4566,6 +4574,7 @@ void CoarseFineTransformations::Clear() point_matrices[i].SetSize(0, 0, 0); } embeddings.DeleteAll(); + orig_elements = -1; } bool CoarseFineTransformations::IsInitialized() const diff --git a/mesh/ncmesh.hpp b/mesh/ncmesh.hpp index fd426b4964..bc38e277e9 100644 --- a/mesh/ncmesh.hpp +++ b/mesh/ncmesh.hpp @@ -64,7 +64,11 @@ struct CoarseFineTransformations /// Fine element positions in their parents. Array embeddings; - void GetCoarseToFineMap(const Mesh &fine_mesh, + /// The number of nonghost elements on the original mesh. This is only + /// needed in the case of derefinement. + int orig_elements; + + void GetCoarseToFineMap(const Mesh &adapted_mesh, Table &coarse_to_fine, Array &coarse_to_ref_type, Table &ref_type_to_matrix, From 091ff0c47583a751a19dcff72060c344fa165c5e Mon Sep 17 00:00:00 2001 From: Tucker Babcock Date: Fri, 3 Sep 2021 18:13:19 -0600 Subject: [PATCH 098/198] adding DofTransformation objects to NonlinearForm Mult, GetGridFunctionEnergy, and GetGradient methods similar to those used in BilinearForm::Assemble and LinearForm::Assemble --- fem/nonlinearform.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/fem/nonlinearform.cpp b/fem/nonlinearform.cpp index a207a91688..7e7976516f 100644 --- a/fem/nonlinearform.cpp +++ b/fem/nonlinearform.cpp @@ -96,6 +96,8 @@ double NonlinearForm::GetGridFunctionEnergy(const Vector &x) const Vector el_x; const FiniteElement *fe; ElementTransformation *T; + DofTransformation *doftrans; + double energy = 0.0; if (dnfi.Size()) @@ -103,9 +105,10 @@ double NonlinearForm::GetGridFunctionEnergy(const Vector &x) const for (int i = 0; i < fes->GetNE(); i++) { fe = fes->GetFE(i); - fes->GetElementVDofs(i, vdofs); + doftrans = fes->GetElementVDofs(i, vdofs); T = fes->GetElementTransformation(i); x.GetSubVector(vdofs, el_x); + if (doftrans) {doftrans->InvTransformPrimal(el_x); } for (int k = 0; k < dnfi.Size(); k++) { energy += dnfi[k]->GetElementEnergy(*fe, *T, el_x); @@ -166,6 +169,7 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const Vector el_x, el_y; const FiniteElement *fe; ElementTransformation *T; + DofTransformation *doftrans; Mesh *mesh = fes->GetMesh(); py = 0.0; @@ -175,12 +179,14 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const for (int i = 0; i < fes->GetNE(); i++) { fe = fes->GetFE(i); - fes->GetElementVDofs(i, vdofs); + doftrans = fes->GetElementVDofs(i, vdofs); T = fes->GetElementTransformation(i); px.GetSubVector(vdofs, el_x); + if (doftrans) {doftrans->InvTransformPrimal(el_x); } for (int k = 0; k < dnfi.Size(); k++) { dnfi[k]->AssembleElementVector(*fe, *T, el_x, el_y); + if (doftrans) {doftrans->TransformDual(el_y); } py.AddElementVector(vdofs, el_y); } } @@ -302,6 +308,7 @@ Operator &NonlinearForm::GetGradient(const Vector &x) const DenseMatrix elmat; const FiniteElement *fe; ElementTransformation *T; + DofTransformation *doftrans; Mesh *mesh = fes->GetMesh(); const Vector &px = Prolongate(x); @@ -319,12 +326,14 @@ Operator &NonlinearForm::GetGradient(const Vector &x) const for (int i = 0; i < fes->GetNE(); i++) { fe = fes->GetFE(i); - fes->GetElementVDofs(i, vdofs); + doftrans = fes->GetElementVDofs(i, vdofs); T = fes->GetElementTransformation(i); px.GetSubVector(vdofs, el_x); + if (doftrans) {doftrans->InvTransformPrimal(el_x); } for (int k = 0; k < dnfi.Size(); k++) { dnfi[k]->AssembleElementGrad(*fe, *T, el_x, elmat); + if (doftrans) { doftrans->TransformDual(elmat); } Grad->AddSubMatrix(vdofs, vdofs, elmat, skip_zeros); // Grad->AddSubMatrix(vdofs, vdofs, elmat, 1); } From 70bb02a0c74e95cf5653afc505d124a998ab448b Mon Sep 17 00:00:00 2001 From: "Stowell, Mark L" Date: Fri, 3 Sep 2021 17:37:03 -0700 Subject: [PATCH 099/198] Proposed changes to documentation requirements --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 49a4f5f362..06ba0a5918 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -323,8 +323,8 @@ Before you can start, you need a GitHub account, here are a few suggestions: change the code by default. - Code specifics - - All significant new classes, methods and functions have Doxygen-style - documentation in source comments. + - All new classes, methods and functions have Doxygen-style documentation in + source comments. - Consistent code styling is enforced with `make style` in the top-level directory. This requires [Artistic Style](http://astyle.sourceforge.net) (we specifically use version 2.05.1). See also the file `config/mfem.astylerc`. @@ -450,7 +450,7 @@ Before a PR can be merged, it should satisfy the following: - [ ] The miniapps go at the end of the page, and are usually listed only under a specific "Application (PDE)" category. - [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`. - [ ] New capability: - - [ ] All significant new classes, methods and functions have Doxygen-style documentation in source comments. + - [ ] All new classes, methods and functions have full Doxygen-style documentation in source comments. Documentation should include descriptions of member data, function arguments and return values, and prerequisites for calling new functions. - [ ] Consider adding new sample runs in existing examples to highlight the new capability. - [ ] Consider saving cool simulation pictures with the new capability in the Confluence gallery (LLNL only) or submitting them, via pull request, to the gallery section of the `mfem/web` repo. - [ ] If this is a major new feature, consider mentioning it in the short summary inside `README` *(rare)*. From f53e14016dc8536cc4d63ccda7d6796aacee2384 Mon Sep 17 00:00:00 2001 From: "Stowell, Mark L" Date: Fri, 3 Sep 2021 19:20:07 -0700 Subject: [PATCH 100/198] Separate statements for public and private entities. --- CONTRIBUTING.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 06ba0a5918..703df5ac11 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -323,8 +323,10 @@ Before you can start, you need a GitHub account, here are a few suggestions: change the code by default. - Code specifics - - All new classes, methods and functions have Doxygen-style documentation in - source comments. + - All new public classes, methods and functions have Doxygen-style + documentation in source comments. + - All new private classes, methods and functions have documentation in source + comments (Doxygen-style is optional). - Consistent code styling is enforced with `make style` in the top-level directory. This requires [Artistic Style](http://astyle.sourceforge.net) (we specifically use version 2.05.1). See also the file `config/mfem.astylerc`. @@ -450,7 +452,8 @@ Before a PR can be merged, it should satisfy the following: - [ ] The miniapps go at the end of the page, and are usually listed only under a specific "Application (PDE)" category. - [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`. - [ ] New capability: - - [ ] All new classes, methods and functions have full Doxygen-style documentation in source comments. Documentation should include descriptions of member data, function arguments and return values, and prerequisites for calling new functions. + - [ ] All new public classes, methods and functions have full Doxygen-style documentation in source comments. Documentation should include descriptions of member data, function arguments and return values, and prerequisites for calling new functions. + - [ ] All new private classes, methods and functions have similar documentation to their public counterparts though not necessarily Doxygen-style. - [ ] Consider adding new sample runs in existing examples to highlight the new capability. - [ ] Consider saving cool simulation pictures with the new capability in the Confluence gallery (LLNL only) or submitting them, via pull request, to the gallery section of the `mfem/web` repo. - [ ] If this is a major new feature, consider mentioning it in the short summary inside `README` *(rare)*. From 3a8908e6bfe182b3b135a733c4b83f87c593b3c7 Mon Sep 17 00:00:00 2001 From: Tucker Babcock Date: Tue, 7 Sep 2021 14:16:08 -0600 Subject: [PATCH 101/198] add DofTransformation to BlockNonlinearForm methods --- fem/nonlinearform.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/fem/nonlinearform.cpp b/fem/nonlinearform.cpp index 7e7976516f..d130c68e62 100644 --- a/fem/nonlinearform.cpp +++ b/fem/nonlinearform.cpp @@ -97,7 +97,6 @@ double NonlinearForm::GetGridFunctionEnergy(const Vector &x) const const FiniteElement *fe; ElementTransformation *T; DofTransformation *doftrans; - double energy = 0.0; if (dnfi.Size()) @@ -592,6 +591,7 @@ double BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const Array el_x_const(fes.Size()); Array fe(fes.Size()); ElementTransformation *T; + DofTransformation *doftrans; double energy = 0.0; for (int i=0; iGetFE(i); - fes[s]->GetElementVDofs(i, *vdofs[s]); + doftrans = fes[s]->GetElementVDofs(i, *vdofs[s]); bx.GetBlock(s).GetSubVector(*vdofs[s], *el_x[s]); + if (doftrans) {doftrans->InvTransformPrimal(*el_x[s]); } } for (int k = 0; k < dnfi.Size(); ++k) @@ -654,6 +655,7 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx, Array fe(fes.Size()); Array fe2(fes.Size()); ElementTransformation *T; + DofTransformation *doftrans; by.UseDevice(true); by = 0.0; @@ -673,9 +675,10 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx, T = fes[0]->GetElementTransformation(i); for (int s = 0; s < fes.Size(); ++s) { - fes[s]->GetElementVDofs(i, *(vdofs[s])); + doftrans = fes[s]->GetElementVDofs(i, *(vdofs[s])); fe[s] = fes[s]->GetFE(i); bx.GetBlock(s).GetSubVector(*(vdofs[s]), *el_x[s]); + if (doftrans) {doftrans->InvTransformPrimal(*el_x[s]); } } for (int k = 0; k < dnfi.Size(); ++k) @@ -686,6 +689,7 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx, for (int s=0; sSize() == 0) { continue; } + if (doftrans) {doftrans->TransformDual(*el_y[s]); } by.GetBlock(s).AddElementVector(*(vdofs[s]), *el_y[s]); } } @@ -853,6 +857,7 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const Arrayfe(fes.Size()); Arrayfe2(fes.Size()); ElementTransformation * T; + DofTransformation *doftrans; for (int i=0; iGetFE(i); - fes[s]->GetElementVDofs(i, *vdofs[s]); + doftrans = fes[s]->GetElementVDofs(i, *vdofs[s]); bx.GetBlock(s).GetSubVector(*vdofs[s], *el_x[s]); + if (doftrans) {doftrans->InvTransformPrimal(*el_x[s]); } } for (int k = 0; k < dnfi.Size(); ++k) @@ -902,6 +908,7 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const for (int l=0; lHeight() == 0) { continue; } + if (doftrans) { doftrans->TransformDual(*elmats(j,l)); } Grads(j,l)->AddSubMatrix(*vdofs[j], *vdofs[l], *elmats(j,l), skip_zeros); } From dd5f50bb40ed9c9671a10948b086a94bd824e336 Mon Sep 17 00:00:00 2001 From: Tucker Babcock Date: Tue, 7 Sep 2021 14:24:33 -0600 Subject: [PATCH 102/198] store DofTransformations in Array for BlockNonlinearForm::MultBlocked and ::ComputeGradientBlocked since they need to be reused --- fem/nonlinearform.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fem/nonlinearform.cpp b/fem/nonlinearform.cpp index d130c68e62..3339bc1b33 100644 --- a/fem/nonlinearform.cpp +++ b/fem/nonlinearform.cpp @@ -655,7 +655,7 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx, Array fe(fes.Size()); Array fe2(fes.Size()); ElementTransformation *T; - DofTransformation *doftrans; + Array doftrans(fes.Size()); doftrans = nullptr; by.UseDevice(true); by = 0.0; @@ -675,10 +675,10 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx, T = fes[0]->GetElementTransformation(i); for (int s = 0; s < fes.Size(); ++s) { - doftrans = fes[s]->GetElementVDofs(i, *(vdofs[s])); + doftrans[s] = fes[s]->GetElementVDofs(i, *(vdofs[s])); fe[s] = fes[s]->GetFE(i); bx.GetBlock(s).GetSubVector(*(vdofs[s]), *el_x[s]); - if (doftrans) {doftrans->InvTransformPrimal(*el_x[s]); } + if (doftrans[s]) {doftrans[s]->InvTransformPrimal(*el_x[s]); } } for (int k = 0; k < dnfi.Size(); ++k) @@ -689,7 +689,7 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx, for (int s=0; sSize() == 0) { continue; } - if (doftrans) {doftrans->TransformDual(*el_y[s]); } + if (doftrans[s]) {doftrans[s]->TransformDual(*el_y[s]); } by.GetBlock(s).AddElementVector(*(vdofs[s]), *el_y[s]); } } @@ -857,7 +857,7 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const Arrayfe(fes.Size()); Arrayfe2(fes.Size()); ElementTransformation * T; - DofTransformation *doftrans; + Array doftrans(fes.Size()); doftrans = nullptr; for (int i=0; iGetFE(i); - doftrans = fes[s]->GetElementVDofs(i, *vdofs[s]); + doftrans[s] = fes[s]->GetElementVDofs(i, *vdofs[s]); bx.GetBlock(s).GetSubVector(*vdofs[s], *el_x[s]); - if (doftrans) {doftrans->InvTransformPrimal(*el_x[s]); } + if (doftrans[s]) {doftrans[s]->InvTransformPrimal(*el_x[s]); } } for (int k = 0; k < dnfi.Size(); ++k) @@ -908,7 +908,7 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const for (int l=0; lHeight() == 0) { continue; } - if (doftrans) { doftrans->TransformDual(*elmats(j,l)); } + if (doftrans[s]) { doftrans[s]->TransformDual(*elmats(j,l)); } Grads(j,l)->AddSubMatrix(*vdofs[j], *vdofs[l], *elmats(j,l), skip_zeros); } From 5ad64b058b632b4fd53d9ce8efcd3d9c7b4ab435 Mon Sep 17 00:00:00 2001 From: Tucker Babcock Date: Tue, 7 Sep 2021 14:35:00 -0600 Subject: [PATCH 103/198] fix compile error, changed ComputeGradientBlocked doftrans usage to be similar to MixedBilinearForm::Assemble. --- fem/nonlinearform.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fem/nonlinearform.cpp b/fem/nonlinearform.cpp index 3339bc1b33..c95e1cd3e2 100644 --- a/fem/nonlinearform.cpp +++ b/fem/nonlinearform.cpp @@ -908,7 +908,10 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const for (int l=0; lHeight() == 0) { continue; } - if (doftrans[s]) { doftrans[s]->TransformDual(*elmats(j,l)); } + if (doftrans[j] || doftrans[l]) + { + TransformDual(doftrans[j], doftrans[l], *elmats(j,l)); + } Grads(j,l)->AddSubMatrix(*vdofs[j], *vdofs[l], *elmats(j,l), skip_zeros); } From 4806441a03cdbcbccf1ce796bda616d16c0cb28a Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Wed, 8 Sep 2021 13:58:20 -0700 Subject: [PATCH 104/198] make sure normal vector is pointing outside the domain --- miniapps/shifted/sbm_solver.cpp | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/miniapps/shifted/sbm_solver.cpp b/miniapps/shifted/sbm_solver.cpp index d7c8cde63a..fce2ba2d11 100644 --- a/miniapps/shifted/sbm_solver.cpp +++ b/miniapps/shifted/sbm_solver.cpp @@ -258,11 +258,8 @@ void SBM2DirichletIntegrator::AssembleFaceMatrix( } vD->Eval(D, Trans, ip); - double nor_dot_d = nor*D; - // If we are clipping inside the domain, ntilde and d vector should be - // aligned. - if (!include_cut_cell && nor_dot_d < 0) { nor *= -1; } - if (include_cut_cell && nor_dot_d > 0) { nor *= -1; } + // Make sure the normal vector is pointing outside the domain. + if (!elem1f) { nor *= -1; } if (elem1f) { @@ -568,11 +565,8 @@ void SBM2DirichletLFIntegrator::AssembleRHSElementVect( } vD->Eval(D, Tr, ip); - double nor_dot_d = nor*D; - if (!include_cut_cell && nor_dot_d < 0) { nor *= -1; } - if (include_cut_cell && nor_dot_d > 0) { nor *= -1; } - // note here that if we are clipping outside the domain, we will have to - // flip the sign if nor_dot_d is positive. + // Make sure the normal vector is pointing outside the domain. + if (!elem1f) { nor *= -1; } double hinvdx; @@ -870,11 +864,8 @@ void SBM2NeumannIntegrator::AssembleFaceMatrix( vD->Eval(D, Trans, ip); vN->Eval(Nhat, Trans, ip, D); - double nor_dot_d = nor*D; - // If we are clipping inside the domain, ntilde and d vector should be - // aligned. - if (!include_cut_cell && nor_dot_d < 0) { nor *= -1; } - if (include_cut_cell && nor_dot_d > 0) { nor *= -1; } + // Make sure the normal vector is pointing outside the domain. + if (!elem1f) { nor *= -1; } if (elem1f) { @@ -1085,11 +1076,8 @@ void SBM2NeumannLFIntegrator::AssembleRHSElementVect( vD->Eval(D, Tr, ip); vN->Eval(Nhat, Tr, ip, D); - double nor_dot_d = nor*D; - if (!include_cut_cell && nor_dot_d < 0) { nor *= -1; } - if (include_cut_cell && nor_dot_d > 0) { nor *= -1; } - // note here that if we are clipping outside the domain, we will have to - // flip the sign if nor_dot_d is +ve. + // Make sure the normal vector is pointing outside the domain. + if (!elem1f) { nor *= -1; } if (elem1f) { From 36e2e4d37fc9dde9030df34a8317ace1b5609ed4 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Wed, 8 Sep 2021 14:38:39 -0700 Subject: [PATCH 105/198] Some additional tests. --- miniapps/shifted/diffusion.cpp | 46 ++++++++++++++++++---------------- miniapps/shifted/distance.cpp | 13 ++++++++-- miniapps/shifted/sbm_aux.hpp | 24 ++++++++++++++++++ 3 files changed, 59 insertions(+), 24 deletions(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index ac83c172a8..a47803ee66 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -59,9 +59,10 @@ // the boundary conditions. // mpirun -np 4 diffusion -rs 2 -o 1 -vis -lst 3 // -// Problem 4: Complex 2D shape: +// Problem 4: Complex 2D / 3D shapes: // Solves -nabla^2 u = 1 with homogeneous boundary conditions. // mpirun -np 4 diffusion -rs 5 -lst 4 -alpha 2 +// mpirun -np 4 diffusion -m ../../data/inline-hex.mesh -rs 3 -lst 8 -alpha 10 // // Problem 5: Circular hole with homogeneous Neumann, triangular hole with // inhomogeneous Dirichlet, and square hole with homogeneous Dirichlet @@ -205,22 +206,24 @@ int main(int argc, char *argv[]) Dist_Level_Set_Coefficient *neumann_dist_coef = NULL; Combo_Level_Set_Coefficient combo_dist_coef; + ParGridFunction level_set_gf(&pfespace); ShiftedFaceMarker marker(pmesh, pfespace, include_cut_cell); Array elem_marker; // Dirichlet level-set. if (dirichlet_level_set_type > 0) { - // ParGridFunction for level_set_value. - ParGridFunction dirichlet_level_set_val(&pfespace); dirichlet_dist_coef = new Dist_Level_Set_Coefficient(dirichlet_level_set_type); - dirichlet_level_set_val.ProjectCoefficient(*dirichlet_dist_coef); + const double dx = AvgElementSize(pmesh); + PDEFilter filter(pmesh, dx); + filter.Filter(*dirichlet_dist_coef, level_set_gf); + //level_set_gf.ProjectCoefficient(*dirichlet_dist_coef); // Exchange information for ghost elements i.e. elements that share a face // with element on the current processor, but belong to another processor. - dirichlet_level_set_val.ExchangeFaceNbrData(); + level_set_gf.ExchangeFaceNbrData(); // Setup the class to mark all elements based on whether they are located // inside or outside the true domain, or intersected by the true boundary. - marker.MarkElements(dirichlet_level_set_val, elem_marker); + marker.MarkElements(level_set_gf, elem_marker); combo_dist_coef.Add_Level_Set_Coefficient(*dirichlet_dist_coef); } @@ -230,22 +233,20 @@ int main(int argc, char *argv[]) MFEM_VERIFY(dirichlet_level_set_type == 5, "The combo level set example has been only set for" " dirichlet_level_set_type == 5."); - ParGridFunction dirichlet_level_set_val(&pfespace); dirichlet_dist_coef_2 = new Dist_Level_Set_Coefficient(6); - dirichlet_level_set_val.ProjectCoefficient(*dirichlet_dist_coef_2); - dirichlet_level_set_val.ExchangeFaceNbrData(); - marker.MarkElements(dirichlet_level_set_val, elem_marker); + level_set_gf.ProjectCoefficient(*dirichlet_dist_coef_2); + level_set_gf.ExchangeFaceNbrData(); + marker.MarkElements(level_set_gf, elem_marker); combo_dist_coef.Add_Level_Set_Coefficient(*dirichlet_dist_coef_2); } // Neumann level-set. if (neumann_level_set_type > 0) { - ParGridFunction neumann_level_set_val(&pfespace); neumann_dist_coef = new Dist_Level_Set_Coefficient(neumann_level_set_type); - neumann_level_set_val.ProjectCoefficient(*neumann_dist_coef); - neumann_level_set_val.ExchangeFaceNbrData(); - marker.MarkElements(neumann_level_set_val, elem_marker); + level_set_gf.ProjectCoefficient(*neumann_dist_coef); + level_set_gf.ExchangeFaceNbrData(); + marker.MarkElements(level_set_gf, elem_marker); combo_dist_coef.Add_Level_Set_Coefficient(*neumann_dist_coef); } @@ -378,6 +379,7 @@ int main(int argc, char *argv[]) FunctionCoefficient *rhs_f = NULL; if (dirichlet_level_set_type == 1 || dirichlet_level_set_type == 4 || dirichlet_level_set_type == 5 || dirichlet_level_set_type == 6 || + dirichlet_level_set_type == 8 || neumann_level_set_type == 1 || neumann_level_set_type == 7) { rhs_f = new FunctionCoefficient(rhs_fun_circle); @@ -550,13 +552,13 @@ int main(int argc, char *argv[]) a.FormLinearSystem(ess_tdof_list, x, b, A, X, B); Solver *prec = new HypreBoomerAMG; - BiCGSTABSolver *bicg = new BiCGSTABSolver(MPI_COMM_WORLD); - bicg->SetRelTol(1e-12); - bicg->SetMaxIter(2000); - bicg->SetPrintLevel(1); - bicg->SetPreconditioner(*prec); - bicg->SetOperator(*A); - bicg->Mult(B, X); + BiCGSTABSolver bicg(MPI_COMM_WORLD); + bicg.SetRelTol(1e-12); + bicg.SetMaxIter(500); + bicg.SetPrintLevel(1); + bicg.SetPreconditioner(*prec); + bicg.SetOperator(*A); + bicg.Mult(B, X); // Recover the solution as a finite element grid function. a.RecoverFEMSolution(X, b, x); @@ -575,6 +577,7 @@ int main(int argc, char *argv[]) ParaViewDataCollection dacol("ParaViewDiffusion", &pmesh); dacol.SetLevelsOfDetail(order); dacol.RegisterField("distance", &distance); + dacol.RegisterField("level_set", &level_set_gf); dacol.RegisterField("solution", &x); dacol.SetTime(1.0); dacol.SetCycle(1); @@ -632,7 +635,6 @@ int main(int argc, char *argv[]) // Free the used memory. delete prec; - delete bicg; delete normalbcCoef; delete nbcCoef; delete dbcCoefCombo; diff --git a/miniapps/shifted/distance.cpp b/miniapps/shifted/distance.cpp index 69f1507a33..ccc73d552a 100644 --- a/miniapps/shifted/distance.cpp +++ b/miniapps/shifted/distance.cpp @@ -76,11 +76,15 @@ // Problem 3: level set: Gyroid // mpirun -np 4 distance -m ../../data/periodic-square.mesh -rs 5 -o 2 -t 1.0 -p 3 // mpirun -np 4 distance -m ../../data/periodic-cube.mesh -rs 3 -o 2 -t 1.0 -p 3 +// +// Problem 4: level set: Union of doughnut and swiss cheese shapes +// mpirun -np 4 distance -m ../../data/inline-hex.mesh -rs 3 -o 2 -t 1.0 -p 4 #include #include -#include "dist_solver.hpp" #include "../common/mfem-common.hpp" +#include "dist_solver.hpp" +#include "sbm_aux.hpp" using namespace std; using namespace mfem; @@ -251,11 +255,16 @@ int main(int argc, char *argv[]) ls_coeff = new FunctionCoefficient(sine_ls); smooth_steps = 0; } - else + else if (problem == 3) { ls_coeff = new FunctionCoefficient(Gyroid); smooth_steps = 0; } + else if (problem == 4) + { + ls_coeff = new FunctionCoefficient(doughnut_cheese); + smooth_steps = 0; + } const double dx = AvgElementSize(pmesh); DistanceSolver *dist_solver = NULL; diff --git a/miniapps/shifted/sbm_aux.hpp b/miniapps/shifted/sbm_aux.hpp index 19dc368e3e..acae9e0f0f 100644 --- a/miniapps/shifted/sbm_aux.hpp +++ b/miniapps/shifted/sbm_aux.hpp @@ -28,6 +28,29 @@ double point_inside_trigon(const Vector px, Vector p1, Vector p2, Vector p3) return (p > 0 && q > 0 && 1-p-q > 0) ? -1.0 : 1.0; } +// 1 is inside the doughnut, -1 is outside. +double doughnut_cheese(const Vector &coord) +{ + // map [0,1] to [-1,1]. + double x = 2*coord(0)-1.0, y = 2*coord(1)-1.0, z = 2*coord(2)-1.0; + + bool doughnut; + const double R = 0.8, r = 0.15; + const double t = R - std::sqrt(x*x + y*y); + doughnut = t*t + z*z - r*r <= 0; + + bool cheese; + x = 3.0*x, y = 3.0*y, z = 3.0*z; + cheese = (x*x + y*y - 4.0) * (x*x + y*y - 4.0) + + (z*z - 1.0) * (z*z - 1.0) + + (y*y + z*z - 4.0) * (y*y + z*z - 4.0) + + (x*x - 1.0) * (x*x - 1.0) + + (z*z + x*x - 4.0) * (z*z + x*x - 4.0) + + (y*y - 1.0) * (y*y - 1.0) - 15.0 <= 0.0; + + return (doughnut || cheese) ? 1.0 : -1.0; +} + /// Analytic distance to the 0 level set. Positive value if the point is inside /// the domain, and negative value if outside. double dist_value(const Vector &x, const int type) @@ -102,6 +125,7 @@ double dist_value(const Vector &x, const int type) xc -= x; return xc.Norml2() - 0.2; } + else if (type == 8) { return doughnut_cheese(x); } else { MFEM_ABORT(" Function type not implement yet."); From 7b64d8739a77fc02c80ab66fd6335b249ff0b2ca Mon Sep 17 00:00:00 2001 From: "Stowell, Mark L" Date: Thu, 9 Sep 2021 14:29:17 -0700 Subject: [PATCH 106/198] Adjusting statements based on input obtained during MFEM developer meeting --- CONTRIBUTING.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 703df5ac11..5acae9cfcf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -323,10 +323,8 @@ Before you can start, you need a GitHub account, here are a few suggestions: change the code by default. - Code specifics - - All new public classes, methods and functions have Doxygen-style - documentation in source comments. - - All new private classes, methods and functions have documentation in source - comments (Doxygen-style is optional). + - All new public, protected, and private classes, methods, data members, and + functions have Doxygen-style documentation in source comments. - Consistent code styling is enforced with `make style` in the top-level directory. This requires [Artistic Style](http://astyle.sourceforge.net) (we specifically use version 2.05.1). See also the file `config/mfem.astylerc`. @@ -452,8 +450,7 @@ Before a PR can be merged, it should satisfy the following: - [ ] The miniapps go at the end of the page, and are usually listed only under a specific "Application (PDE)" category. - [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`. - [ ] New capability: - - [ ] All new public classes, methods and functions have full Doxygen-style documentation in source comments. Documentation should include descriptions of member data, function arguments and return values, and prerequisites for calling new functions. - - [ ] All new private classes, methods and functions have similar documentation to their public counterparts though not necessarily Doxygen-style. + - [ ] All new public, protected, and private classes, methods, data members, and functions have full Doxygen-style documentation in source comments. Documentation should include descriptions of member data, function arguments and return values, template parameters, and prerequisites for calling new functions. - [ ] Consider adding new sample runs in existing examples to highlight the new capability. - [ ] Consider saving cool simulation pictures with the new capability in the Confluence gallery (LLNL only) or submitting them, via pull request, to the gallery section of the `mfem/web` repo. - [ ] If this is a major new feature, consider mentioning it in the short summary inside `README` *(rare)*. From 09b5b1f184285eac4d040b96aefedf4aa29e5fc6 Mon Sep 17 00:00:00 2001 From: "Stowell, Mark L" Date: Thu, 9 Sep 2021 14:48:58 -0700 Subject: [PATCH 107/198] Mention of new function usage --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5acae9cfcf..4b2943e37d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -451,6 +451,7 @@ Before a PR can be merged, it should satisfy the following: - [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`. - [ ] New capability: - [ ] All new public, protected, and private classes, methods, data members, and functions have full Doxygen-style documentation in source comments. Documentation should include descriptions of member data, function arguments and return values, template parameters, and prerequisites for calling new functions. + - [ ] Any new functions should include descriptions of their intended use e.g. for internal use only, user-facing, etc., along with references to example code whenever possible/appropriate. - [ ] Consider adding new sample runs in existing examples to highlight the new capability. - [ ] Consider saving cool simulation pictures with the new capability in the Confluence gallery (LLNL only) or submitting them, via pull request, to the gallery section of the `mfem/web` repo. - [ ] If this is a major new feature, consider mentioning it in the short summary inside `README` *(rare)*. From 83c11c4ea5adf5fd476e99bbf08a6bf0d8043938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C4=8Cerven=C3=BD?= Date: Mon, 13 Sep 2021 16:01:36 +0200 Subject: [PATCH 108/198] Refactoring coarse/fine: added MakeCoarseToFineTable, Embedding::geom/ghost. --- fem/fespace.cpp | 1 + mesh/ncmesh.cpp | 58 +++++++++++++++++++++++++++++-------------------- mesh/ncmesh.hpp | 41 +++++++++++++++++----------------- 3 files changed, 56 insertions(+), 44 deletions(-) diff --git a/fem/fespace.cpp b/fem/fespace.cpp index b24928d0d4..e9d3e25a28 100644 --- a/fem/fespace.cpp +++ b/fem/fespace.cpp @@ -1782,6 +1782,7 @@ FiniteElementSpace::DerefinementOperator::DerefinementOperator( num_ref_types[g]++; num_fine_elems[g] += ref_type_to_matrix.RowSize(i); } + DenseTensor localPtMP[Geometry::NumGeom]; for (int g = 0; g < Geometry::NumGeom; g++) { diff --git a/mesh/ncmesh.cpp b/mesh/ncmesh.cpp index 151d5f90b3..fefc8a3128 100644 --- a/mesh/ncmesh.cpp +++ b/mesh/ncmesh.cpp @@ -1868,8 +1868,6 @@ void NCMesh::InitDerefTransforms() transforms.embeddings[i].parent = -1; transforms.embeddings[i].matrix = 0; } - - transforms.orig_elements = NElements; } void NCMesh::SetDerefMatrixCodes(int parent, Array &fine_coarse) @@ -4388,7 +4386,9 @@ const CoarseFineTransformations& NCMesh::GetDerefinementTransforms() int &matrix = mat_no[geom][ref_type_child]; if (!matrix) { matrix = mat_no[geom].size(); } + transforms.embeddings[i].matrix = matrix - 1; + transforms.embeddings[i].geom = geom; // TODO } } @@ -4453,19 +4453,25 @@ struct RefType } // namespace internal -void CoarseFineTransformations::GetCoarseToFineMap( - const mfem::Mesh &new_mesh, Table &coarse_to_fine, - Array &coarse_to_ref_type, Table &ref_type_to_matrix, - Array &ref_type_to_geom, - bool get_coarse_to_fine_only) const +void CoarseFineTransformations::MakeCoarseToFineTable(Table &coarse_to_fine, + bool want_ghosts) const { - int fine_ne = embeddings.Size(); - - // In the case of derefinement, we want to process only nonghost elements. - if (new_mesh.GetLastOperation() == Mesh::Operation::DEREFINE) + // count fine elements + int fine_ne; + if (want_ghosts) { - fine_ne = orig_elements; + fine_ne = embeddings.Size(); } + else + { + fine_ne = 0; + for (int i = 0; i < embeddings.Size(); i++) + { + if (!embeddings[i].ghost) { fine_ne++; } + } + } + + // count coarse elements int coarse_ne = -1; for (int i = 0; i < fine_ne; i++) { @@ -4473,21 +4479,26 @@ void CoarseFineTransformations::GetCoarseToFineMap( } coarse_ne++; - coarse_to_ref_type.SetSize(coarse_ne); coarse_to_fine.SetDims(coarse_ne, fine_ne); + // count table row sizes Array cf_i(coarse_to_fine.GetI(), coarse_ne+1); - Array > cf_j(fine_ne); cf_i = 0; for (int i = 0; i < fine_ne; i++) { - cf_i[embeddings[i].parent+1]++; + const Embedding &e = embeddings[i]; + if (!want_ghosts && e.ghost) { continue; } + if (e.parent >= 0) { cf_i[e.parent + 1]++; } } cf_i.PartialSum(); - MFEM_ASSERT(cf_i.Last() == cf_j.Size(), "internal error"); + MFEM_ASSERT(cf_i.Last() == fine_ne, "internal error"); + + // fill and sort rows + Array > cf_j(fine_ne); for (int i = 0; i < fine_ne; i++) { const Embedding &e = embeddings[i]; + if (!want_ghosts && e.ghost) { continue; } cf_j[cf_i[e.parent]].one = e.matrix; // used as sort key below cf_j[cf_i[e.parent]].two = i; cf_i[e.parent]++; @@ -4502,11 +4513,11 @@ void CoarseFineTransformations::GetCoarseToFineMap( { coarse_to_fine.GetJ()[i] = cf_j[i].two; } +} - if (get_coarse_to_fine_only) { return; } - MFEM_VERIFY(new_mesh.GetLastOperation() != Mesh::Operation::DEREFINE, - "GetCoarseToFineMap is not fully supported for derefined meshes." - " Set 'get_coarse_to_fine_only=true'.") + +/*{ + coarse_to_ref_type.SetSize(coarse_ne); using internal::RefType; using std::map; @@ -4547,9 +4558,9 @@ void CoarseFineTransformations::GetCoarseToFineMap( } } ref_type_to_matrix.ShiftUpI(); -} +}*/ -void CoarseFineTransformations::GetCoarseToFineMap(const Mesh &new_mesh, +/*void CoarseFineTransformations::GetCoarseToFineMap(const Mesh &new_mesh, Table &coarse_to_fine) const { Array coarse_to_ref_type; @@ -4559,7 +4570,7 @@ void CoarseFineTransformations::GetCoarseToFineMap(const Mesh &new_mesh, GetCoarseToFineMap(new_mesh, coarse_to_fine, coarse_to_ref_type, ref_type_to_matrix, ref_type_to_geom, get_coarse_to_fine_only); -} +}*/ void NCMesh::ClearTransforms() { @@ -4574,7 +4585,6 @@ void CoarseFineTransformations::Clear() point_matrices[i].SetSize(0, 0, 0); } embeddings.DeleteAll(); - orig_elements = -1; } bool CoarseFineTransformations::IsInitialized() const diff --git a/mesh/ncmesh.hpp b/mesh/ncmesh.hpp index bc38e277e9..97e528f2ae 100644 --- a/mesh/ncmesh.hpp +++ b/mesh/ncmesh.hpp @@ -38,22 +38,27 @@ struct Refinement char ref_type; ///< refinement XYZ bit mask (7 = full isotropic) Refinement() = default; - Refinement(int index, int type = 7) : index(index), ref_type(type) {} }; /// Defines the position of a fine element within a coarse element. struct Embedding { - /// %Element index in the coarse mesh. + /// Coarse %Element index in the coarse mesh. int parent; - /** @brief Index into the DenseTensor corresponding to the parent - Geometry::Type stored in CoarseFineTransformations::point_matrices. */ - int matrix; + + /** The (geom, matrix) pair determines the sub-element transformation for the + fine element: CoarseFineTransformations::point_matrices[geom](matrix) is + the point matrix of the region within the coarse element reference domain.*/ + unsigned geom : 4; + unsigned matrix : 27; + + /// For internal use: 0 if regular fine element, 1 if parallel ghost element. + unsigned ghost : 1; Embedding() = default; - - Embedding(int elem, int matrix = 0) : parent(elem), matrix(matrix) {} + Embedding(int elem, Geometry::Type geom, int matrix = 0, bool ghost = false) + : parent(elem), geom(geom), matrix(matrix), ghost(ghost) {} }; /// Defines the coarse-fine transformations of all fine elements. @@ -64,23 +69,19 @@ struct CoarseFineTransformations /// Fine element positions in their parents. Array embeddings; - /// The number of nonghost elements on the original mesh. This is only - /// needed in the case of derefinement. - int orig_elements; - - void GetCoarseToFineMap(const Mesh &adapted_mesh, - Table &coarse_to_fine, - Array &coarse_to_ref_type, - Table &ref_type_to_matrix, - Array &ref_type_to_geom, - bool get_coarse_to_fine_only = false) const; - - void GetCoarseToFineMap(const Mesh &fine_mesh, - Table &coarse_to_fine) const; + /** Invert the 'embeddings' array: create a Table with coarse elements as + rows and fine elements as columns. If 'want_ghosts' is false, parallel + ghost fine elements are not included in the table. */ + void MakeCoarseToFineTable(Table &coarse_to_fine, + bool want_ghosts = false) const; void Clear(); bool IsInitialized() const; long MemoryUsage() const; + + MFEM_DEPRECATED + void GetCoarseToFineMap(const Mesh &fine_mesh, Table &coarse_to_fine) const + { MakeCoarseToFineTable(coarse_to_fine, true); (void) fine_mesh; } }; void Swap(CoarseFineTransformations &a, CoarseFineTransformations &b); From 32cc4c3198abd74b31723c6fed3debc45fe994df Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Mon, 13 Sep 2021 11:58:27 -0700 Subject: [PATCH 109/198] Resources->Grid --- general/forall.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/general/forall.hpp b/general/forall.hpp index 364971aebd..755e60c230 100644 --- a/general/forall.hpp +++ b/general/forall.hpp @@ -139,7 +139,7 @@ void RajaCuWrap2D(const int N, DBODY &&d_body, using RAJA::RangeSegment; launch - (DEVICE, Resources(Teams(G), Threads(X, Y, BZ)), + (DEVICE, Grid(Teams(G), Threads(X, Y, BZ)), [=] RAJA_DEVICE (LaunchContext ctx) { @@ -172,7 +172,7 @@ void RajaCuWrap3D(const int N, DBODY &&d_body, using RAJA::RangeSegment; launch - (DEVICE, Resources(Teams(GRID), Threads(X, Y, Z)), + (DEVICE, Grid(Teams(GRID), Threads(X, Y, Z)), [=] RAJA_DEVICE (LaunchContext ctx) { @@ -205,7 +205,7 @@ void RajaHipWrap2D(const int N, DBODY &&d_body, using RAJA::RangeSegment; launch - (DEVICE, Resources(Teams(G), Threads(X, Y, BZ)), + (DEVICE, Grid(Teams(G), Threads(X, Y, BZ)), [=] RAJA_DEVICE (LaunchContext ctx) { @@ -238,7 +238,7 @@ void RajaHipWrap3D(const int N, DBODY &&d_body, using RAJA::RangeSegment; launch - (DEVICE, Resources(Teams(GRID), Threads(X, Y, Z)), + (DEVICE, Grid(Teams(GRID), Threads(X, Y, Z)), [=] RAJA_DEVICE (LaunchContext ctx) { From 895293f55e36fb646807c0631c2cc06f70c461cb Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Mon, 13 Sep 2021 11:59:38 -0700 Subject: [PATCH 110/198] changelog --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 3c98fcb26a..701553e558 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -293,7 +293,7 @@ Miscellaneous * HYPRE >= 2.22.0 for CUDA support * libCEED >= 0.8 * PETSc >= 3.15.0 for CUDA support - * RAJA >= 0.13.0 + * RAJA >= 0.14.0 see INSTALL for more details. - Added a "scaled Jacobian" visualization option in the Mesh Explorer miniapp to From 27dfa26f0d878cd6cc306acebf422d26d3e0bb21 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Mon, 13 Sep 2021 14:12:21 -0700 Subject: [PATCH 111/198] Improved the marking algorithm. --- miniapps/shifted/diffusion.cpp | 8 +++++--- miniapps/shifted/marking.cpp | 26 ++++++++++++++++++++------ miniapps/shifted/marking.hpp | 12 ++++++------ 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index a47803ee66..42458bfc18 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -61,8 +61,10 @@ // // Problem 4: Complex 2D / 3D shapes: // Solves -nabla^2 u = 1 with homogeneous boundary conditions. -// mpirun -np 4 diffusion -rs 5 -lst 4 -alpha 2 -// mpirun -np 4 diffusion -m ../../data/inline-hex.mesh -rs 3 -lst 8 -alpha 10 +// mpirun -np 4 diffusion -m ../../data/inline-quad.mesh -rs 4 -lst 4 -alpha 10 +// mpirun -np 4 diffusion -m ../../data/inline-tri.mesh -rs 4 -lst 4 -alpha 10 +// mpirun -np 4 diffusion -m ../../data/inline-hex.mesh -rs 3 -lst 8 -alpha 10 +// mpirun -np 4 diffusion -m ../../data/inline-tet.mesh -rs 3 -lst 8 -alpha 10 // // Problem 5: Circular hole with homogeneous Neumann, triangular hole with // inhomogeneous Dirichlet, and square hole with homogeneous Dirichlet @@ -284,7 +286,7 @@ int main(int argc, char *argv[]) int visport = 19916, s = 350; socketstream sol_sock; common::VisualizeField(sol_sock, vishost, visport, face_dofs, - "Shifted Face Dofs", 0, s, s, s, "Rjmp"); + "Shifted Face Dofs", 0, s, s, s, "Rjmplo"); } // Make a list of inactive tdofs that will be eliminated from the system. diff --git a/miniapps/shifted/marking.cpp b/miniapps/shifted/marking.cpp index a826077142..b59bede325 100644 --- a/miniapps/shifted/marking.cpp +++ b/miniapps/shifted/marking.cpp @@ -23,19 +23,33 @@ void ShiftedFaceMarker::MarkElements(const ParGridFunction &ls_func, IntegrationRules IntRulesLo(0, Quadrature1D::GaussLobatto); + // This tolerance is relevant for points that are exactly on the zero LS. + const double eps = 1e-10; + auto outside_of_domain = [&](double value) + { + if (include_cut_cell) + { + // Points on the zero LS are considered outside the domain. + return (value - eps < 0.0); + } + else + { + // Points on the zero LS are considered inside the domain. + return (value + eps < 0.0); + } + }; + Vector vals; // Check elements on the current MPI rank for (int i = 0; i < pmesh.GetNE(); i++) { - ElementTransformation *Tr = pmesh.GetElementTransformation(i); - const IntegrationRule &ir = - IntRulesLo.Get(pmesh.GetElementBaseGeometry(i), 4*Tr->OrderJ()); + const IntegrationRule &ir = pfes_sltn->GetFE(i)->GetNodes(); ls_func.GetValues(i, ir, vals); int count = 0; for (int j = 0; j < ir.GetNPoints(); j++) { - if (vals(j) <= 0.) { count++; } + if (outside_of_domain(vals(j))) { count++; } } if (count == ir.GetNPoints()) // completely outside @@ -69,8 +83,8 @@ void ShiftedFaceMarker::MarkElements(const ParGridFunction &ls_func, for (int j = 0; j < nip; j++) { const IntegrationPoint &ip = ir.IntPoint(j); - vals[j] = ls_func.GetValue(tr->Elem2No, ip); - if (vals[j] <= 0.) { count++; } + vals(j) = ls_func.GetValue(tr->Elem2No, ip); + if (outside_of_domain(vals(j))) { count++; } } if (count == ir.GetNPoints()) // completely outside diff --git a/miniapps/shifted/marking.hpp b/miniapps/shifted/marking.hpp index c9663cf572..e1cd24f7ee 100644 --- a/miniapps/shifted/marking.hpp +++ b/miniapps/shifted/marking.hpp @@ -24,14 +24,14 @@ class ShiftedFaceMarker protected: ParMesh &pmesh; // Mesh whose elements have to be marked. ParFiniteElementSpace *pfes_sltn; // FESpace associated with the solution. - bool include_cut_cell; // Flag indicating wether cut-cells - // will be included in assembly. - bool initial_marking_done; // Flag indicating wether all the elements - // have been marked at-least once. + + // Indicates whether cut-cells will be included in assembly. + const bool include_cut_cell; + // Indicates whether all the elements have been marked at-least once. + bool initial_marking_done; // Marking of face dofs by using an averaged continuous GridFunction. - const bool func_dof_marking = false; - + const bool func_dof_marking = true; // Alternative implementation of ListShiftedFaceDofs(). void ListShiftedFaceDofs2(const Array &elem_marker, Array &sface_dof_list) const; From 9b05700cb5aa7bb1703bedada9b420690068f180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C4=8Cerven=C3=BD?= Date: Tue, 14 Sep 2021 11:23:49 +0200 Subject: [PATCH 112/198] Initialization of Embedding::geom and ::ghost in NCMesh and Mesh. --- fem/fespace.cpp | 75 +++++++++++++++++++++++++++++++ mesh/mesh.cpp | 18 ++++---- mesh/ncmesh.cpp | 114 +++++++----------------------------------------- mesh/ncmesh.hpp | 5 ++- mesh/pmesh.cpp | 4 +- 5 files changed, 105 insertions(+), 111 deletions(-) diff --git a/fem/fespace.cpp b/fem/fespace.cpp index e9d3e25a28..21fda41aba 100644 --- a/fem/fespace.cpp +++ b/fem/fespace.cpp @@ -1723,6 +1723,36 @@ void FiniteElementSpace::RefinementOperator } } +namespace internal +{ + +// Used in CoarseFineTransformations::GetCoarseToFineMap() below. +struct RefType +{ + Geometry::Type geom; + int num_children; + const Pair *children; + + RefType(Geometry::Type g, int n, const Pair *c) + : geom(g), num_children(n), children(c) { } + + bool operator<(const RefType &other) const + { + if (geom < other.geom) { return true; } + if (geom > other.geom) { return false; } + if (num_children < other.num_children) { return true; } + if (num_children > other.num_children) { return false; } + for (int i = 0; i < num_children; i++) + { + if (children[i].one < other.children[i].one) { return true; } + if (children[i].one > other.children[i].one) { return false; } + } + return false; // everything is equal + } +}; + +} // namespace internal + /// TODO: Implement DofTransformation support FiniteElementSpace::DerefinementOperator::DerefinementOperator( const FiniteElementSpace *f_fes, const FiniteElementSpace *c_fes, @@ -1768,6 +1798,51 @@ FiniteElementSpace::DerefinementOperator::DerefinementOperator( ref_type_to_matrix, ref_type_to_geom); MFEM_ASSERT(coarse_to_fine.Size() == c_fes->GetNE(), ""); + + /*{ + coarse_to_ref_type.SetSize(coarse_ne); + + using internal::RefType; + using std::map; + using std::pair; + + map ref_type_map; + for (int i = 0; i < coarse_ne; i++) + { + const int num_children = cf_i[i+1]-cf_i[i]; + MFEM_ASSERT(num_children > 0, ""); + const int fine_el = cf_j[cf_i[i]].two; + // Assuming the coarse and the fine elements have the same geometry: + const Geometry::Type geom = new_mesh.GetElementBaseGeometry(fine_el); + const RefType ref_type(geom, num_children, &cf_j[cf_i[i]]); + pair::iterator,bool> res = + ref_type_map.insert( + pair(ref_type, (int)ref_type_map.size())); + coarse_to_ref_type[i] = res.first->second; + } + + ref_type_to_matrix.MakeI((int)ref_type_map.size()); + ref_type_to_geom.SetSize((int)ref_type_map.size()); + for (map::iterator it = ref_type_map.begin(); + it != ref_type_map.end(); ++it) + { + ref_type_to_matrix.AddColumnsInRow(it->second, it->first.num_children); + ref_type_to_geom[it->second] = it->first.geom; + } + + ref_type_to_matrix.MakeJ(); + for (map::iterator it = ref_type_map.begin(); + it != ref_type_map.end(); ++it) + { + const RefType &rt = it->first; + for (int j = 0; j < rt.num_children; j++) + { + ref_type_to_matrix.AddConnection(it->second, rt.children[j].one); + } + } + ref_type_to_matrix.ShiftUpI(); + }*/ + const int total_ref_types = ref_type_to_geom.Size(); int num_ref_types[Geometry::NumGeom], num_fine_elems[Geometry::NumGeom]; Array ref_type_to_coarse_elem_offset(total_ref_types); diff --git a/mesh/mesh.cpp b/mesh/mesh.cpp index a845dbc87f..0b4f1bbe52 100644 --- a/mesh/mesh.cpp +++ b/mesh/mesh.cpp @@ -8508,8 +8508,8 @@ void Mesh::LocalRefinement(const Array &marked_el, int type) elements[new_e] = new Segment(new_v, vert[1], attr); vert[1] = new_v; - CoarseFineTr.embeddings[i] = Embedding(i, 1); - CoarseFineTr.embeddings[new_e] = Embedding(i, 2); + CoarseFineTr.embeddings[i] = Embedding(i, Geometry::SEGMENT, 1); + CoarseFineTr.embeddings[new_e] = Embedding(i, Geometry::SEGMENT, 2); } static double seg_children[3*2] = { 0.0,1.0, 0.0,0.5, 0.5,1.0 }; @@ -9257,7 +9257,7 @@ void Mesh::Bisection(int i, const DSTable &v_to_v, int coarse = FindCoarseElement(i); CoarseFineTr.embeddings[i].parent = coarse; - CoarseFineTr.embeddings.Append(Embedding(coarse)); + CoarseFineTr.embeddings.Append(Embedding(coarse, Geometry::TRIANGLE)); // 3. edge1 and edge2 may have to be changed for the second triangle. if (v[1][0] < v_to_v.NumberOfRows() && v[1][1] < v_to_v.NumberOfRows()) @@ -9377,7 +9377,7 @@ void Mesh::Bisection(int i, HashTable &v_to_v) int coarse = FindCoarseElement(i); CoarseFineTr.embeddings[i].parent = coarse; - CoarseFineTr.embeddings.Append(Embedding(coarse)); + CoarseFineTr.embeddings.Append(Embedding(coarse, Geometry::TETRAHEDRON)); // 3. Set the bisection flag switch (type) @@ -9515,10 +9515,10 @@ void Mesh::UniformRefinement(int i, const DSTable &v_to_v, // set parent indices int coarse = FindCoarseElement(i); - CoarseFineTr.embeddings[i] = Embedding(coarse); - CoarseFineTr.embeddings.Append(Embedding(coarse)); - CoarseFineTr.embeddings.Append(Embedding(coarse)); - CoarseFineTr.embeddings.Append(Embedding(coarse)); + CoarseFineTr.embeddings[i] = Embedding(coarse, Geometry::TRIANGLE); + CoarseFineTr.embeddings.Append(Embedding(coarse, Geometry::TRIANGLE)); + CoarseFineTr.embeddings.Append(Embedding(coarse, Geometry::TRIANGLE)); + CoarseFineTr.embeddings.Append(Embedding(coarse, Geometry::TRIANGLE)); NumOfElements += 3; } @@ -9536,7 +9536,7 @@ void Mesh::InitRefinementTransforms() for (int i = 0; i < NumOfElements; i++) { elements[i]->ResetTransform(0); - CoarseFineTr.embeddings[i] = Embedding(i); + CoarseFineTr.embeddings[i] = Embedding(i, GetElementGeometry(i)); } } diff --git a/mesh/ncmesh.cpp b/mesh/ncmesh.cpp index fefc8a3128..3c52121465 100644 --- a/mesh/ncmesh.cpp +++ b/mesh/ncmesh.cpp @@ -1865,8 +1865,12 @@ void NCMesh::InitDerefTransforms() transforms.embeddings.SetSize(nfine); for (int i = 0; i < nfine; i++) { - transforms.embeddings[i].parent = -1; - transforms.embeddings[i].matrix = 0; + Embedding &emb = transforms.embeddings[i]; + emb.parent = -1; + emb.matrix = 0; + Element &el =elements[leaf_elements[i]]; + emb.geom = el.Geom(); + emb.ghost = IsGhost(el); } } @@ -1879,7 +1883,7 @@ void NCMesh::SetDerefMatrixCodes(int parent, Array &fine_coarse) Element &ch = elements[prn.child[i]]; if (ch.index >= 0) { - int code = (prn.ref_type << 8) | (i << 4) | prn.geom; + int code = (prn.ref_type << 4) | i; transforms.embeddings[ch.index].matrix = code; fine_coarse[ch.index] = parent; } @@ -4291,6 +4295,8 @@ void NCMesh::TraverseRefinements(int elem, int coarse_index, Embedding &emb = transforms.embeddings[el.index]; emb.parent = coarse_index; emb.matrix = matrix - 1; + emb.geom = el.Geom(); + emb.ghost = IsGhost(el); } else { @@ -4378,17 +4384,14 @@ const CoarseFineTransformations& NCMesh::GetDerefinementTransforms() // assign numbers to the different matrices used for (int i = 0; i < transforms.embeddings.Size(); i++) { - int code = transforms.embeddings[i].matrix; + Embedding &emb = transforms.embeddings[i]; + int code = emb.matrix; // see SetDerefMatrixCodes() if (code) { - int geom = code & 0xf; // see SetDerefMatrixCodes() - int ref_type_child = code >> 4; + int &matrix = mat_no[emb.geom][code]; + if (!matrix) { matrix = mat_no[emb.geom].size(); } - int &matrix = mat_no[geom][ref_type_child]; - if (!matrix) { matrix = mat_no[geom].size(); } - - transforms.embeddings[i].matrix = matrix - 1; - transforms.embeddings[i].geom = geom; // TODO + emb.matrix = matrix - 1; } } @@ -4423,36 +4426,6 @@ const CoarseFineTransformations& NCMesh::GetDerefinementTransforms() return transforms; } -namespace internal -{ - -// Used in CoarseFineTransformations::GetCoarseToFineMap() below. -struct RefType -{ - Geometry::Type geom; - int num_children; - const Pair *children; - - RefType(Geometry::Type g, int n, const Pair *c) - : geom(g), num_children(n), children(c) { } - - bool operator<(const RefType &other) const - { - if (geom < other.geom) { return true; } - if (geom > other.geom) { return false; } - if (num_children < other.num_children) { return true; } - if (num_children > other.num_children) { return false; } - for (int i = 0; i < num_children; i++) - { - if (children[i].one < other.children[i].one) { return true; } - if (children[i].one > other.children[i].one) { return false; } - } - return false; // everything is equal - } -}; - -} // namespace internal - void CoarseFineTransformations::MakeCoarseToFineTable(Table &coarse_to_fine, bool want_ghosts) const { @@ -4515,63 +4488,6 @@ void CoarseFineTransformations::MakeCoarseToFineTable(Table &coarse_to_fine, } } - -/*{ - coarse_to_ref_type.SetSize(coarse_ne); - - using internal::RefType; - using std::map; - using std::pair; - - map ref_type_map; - for (int i = 0; i < coarse_ne; i++) - { - const int num_children = cf_i[i+1]-cf_i[i]; - MFEM_ASSERT(num_children > 0, ""); - const int fine_el = cf_j[cf_i[i]].two; - // Assuming the coarse and the fine elements have the same geometry: - const Geometry::Type geom = new_mesh.GetElementBaseGeometry(fine_el); - const RefType ref_type(geom, num_children, &cf_j[cf_i[i]]); - pair::iterator,bool> res = - ref_type_map.insert( - pair(ref_type, (int)ref_type_map.size())); - coarse_to_ref_type[i] = res.first->second; - } - - ref_type_to_matrix.MakeI((int)ref_type_map.size()); - ref_type_to_geom.SetSize((int)ref_type_map.size()); - for (map::iterator it = ref_type_map.begin(); - it != ref_type_map.end(); ++it) - { - ref_type_to_matrix.AddColumnsInRow(it->second, it->first.num_children); - ref_type_to_geom[it->second] = it->first.geom; - } - - ref_type_to_matrix.MakeJ(); - for (map::iterator it = ref_type_map.begin(); - it != ref_type_map.end(); ++it) - { - const RefType &rt = it->first; - for (int j = 0; j < rt.num_children; j++) - { - ref_type_to_matrix.AddConnection(it->second, rt.children[j].one); - } - } - ref_type_to_matrix.ShiftUpI(); -}*/ - -/*void CoarseFineTransformations::GetCoarseToFineMap(const Mesh &new_mesh, - Table &coarse_to_fine) const -{ - Array coarse_to_ref_type; - Table ref_type_to_matrix; - Array ref_type_to_geom; - bool get_coarse_to_fine_only = true; - GetCoarseToFineMap(new_mesh, coarse_to_fine, coarse_to_ref_type, - ref_type_to_matrix, ref_type_to_geom, - get_coarse_to_fine_only); -}*/ - void NCMesh::ClearTransforms() { coarse_elements.DeleteAll(); @@ -4599,7 +4515,7 @@ bool CoarseFineTransformations::IsInitialized() const void Swap(CoarseFineTransformations &a, CoarseFineTransformations &b) { - for (int g=0; g embeddings; diff --git a/mesh/pmesh.cpp b/mesh/pmesh.cpp index 57fb2e5ee2..be8816ba09 100644 --- a/mesh/pmesh.cpp +++ b/mesh/pmesh.cpp @@ -3706,8 +3706,8 @@ void ParMesh::LocalRefinement(const Array &marked_el, int type) elements[new_e] = new Segment(new_v, vert[1], attr); vert[1] = new_v; - CoarseFineTr.embeddings[i] = Embedding(i, 1); - CoarseFineTr.embeddings[new_e] = Embedding(i, 2); + CoarseFineTr.embeddings[i] = Embedding(i, Geometry::SEGMENT, 1); + CoarseFineTr.embeddings[new_e] = Embedding(i, Geometry::SEGMENT, 2); } static double seg_children[3*2] = { 0.0,1.0, 0.0,0.5, 0.5,1.0 }; From a747244a6f05ed1e4e015a097c178052772be641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C4=8Cerven=C3=BD?= Date: Tue, 14 Sep 2021 13:08:58 +0200 Subject: [PATCH 113/198] WIP refactoring DerefinementOperator constructor --- fem/fespace.cpp | 86 ++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 48 deletions(-) diff --git a/fem/fespace.cpp b/fem/fespace.cpp index 21fda41aba..c8209fbe39 100644 --- a/fem/fespace.cpp +++ b/fem/fespace.cpp @@ -1723,10 +1723,7 @@ void FiniteElementSpace::RefinementOperator } } -namespace internal -{ - -// Used in CoarseFineTransformations::GetCoarseToFineMap() below. +// Used in DerefinementOperator::DerefinementOperator() below. struct RefType { Geometry::Type geom; @@ -1751,8 +1748,6 @@ struct RefType } }; -} // namespace internal - /// TODO: Implement DofTransformation support FiniteElementSpace::DerefinementOperator::DerefinementOperator( const FiniteElementSpace *f_fes, const FiniteElementSpace *c_fes, @@ -1793,55 +1788,50 @@ FiniteElementSpace::DerefinementOperator::DerefinementOperator( } } - Table ref_type_to_matrix; - rtrans.GetCoarseToFineMap(*f_mesh, coarse_to_fine, coarse_to_ref_type, - ref_type_to_matrix, ref_type_to_geom); + rtrans.MakeCoarseToFineTable(coarse_to_fine, true); MFEM_ASSERT(coarse_to_fine.Size() == c_fes->GetNE(), ""); + using std::map; + using std::pair; - /*{ - coarse_to_ref_type.SetSize(coarse_ne); + // create coarse_to_ref_type + map ref_type_map; + coarse_to_ref_type.SetSize(coarse_to_fine.Size()); + for (int i = 0; i < coarse_ne; i++) + { + const int num_children = coarse_to_fine.RowSize(i); + const int *children = coarse_to_fine.GetRow(i); + MFEM_ASSERT(num_children > 0, ""); + // Assuming the coarse and the fine elements have the same geometry: + const Geometry::Type geom = new_mesh.GetElementBaseGeometry(children[0]); + const RefType ref_type(geom, num_children, children); + auto res = ref_type_map.insert( + pair(ref_type, (int)ref_type_map.size())); + coarse_to_ref_type[i] = res.first->second; + } - using internal::RefType; - using std::map; - using std::pair; + // create ref_type_to_geom, ref_type_to_matrix + Table ref_type_to_matrix; + ref_type_to_matrix.MakeI((int)ref_type_map.size()); + ref_type_to_geom.SetSize((int)ref_type_map.size()); + for (map::iterator it = ref_type_map.begin(); + it != ref_type_map.end(); ++it) + { + ref_type_to_matrix.AddColumnsInRow(it->second, it->first.num_children); + ref_type_to_geom[it->second] = it->first.geom; + } - map ref_type_map; - for (int i = 0; i < coarse_ne; i++) + ref_type_to_matrix.MakeJ(); + for (map::iterator it = ref_type_map.begin(); + it != ref_type_map.end(); ++it) + { + const RefType &rt = it->first; + for (int j = 0; j < rt.num_children; j++) { - const int num_children = cf_i[i+1]-cf_i[i]; - MFEM_ASSERT(num_children > 0, ""); - const int fine_el = cf_j[cf_i[i]].two; - // Assuming the coarse and the fine elements have the same geometry: - const Geometry::Type geom = new_mesh.GetElementBaseGeometry(fine_el); - const RefType ref_type(geom, num_children, &cf_j[cf_i[i]]); - pair::iterator,bool> res = - ref_type_map.insert( - pair(ref_type, (int)ref_type_map.size())); - coarse_to_ref_type[i] = res.first->second; + ref_type_to_matrix.AddConnection(it->second, rt.children[j].one); } - - ref_type_to_matrix.MakeI((int)ref_type_map.size()); - ref_type_to_geom.SetSize((int)ref_type_map.size()); - for (map::iterator it = ref_type_map.begin(); - it != ref_type_map.end(); ++it) - { - ref_type_to_matrix.AddColumnsInRow(it->second, it->first.num_children); - ref_type_to_geom[it->second] = it->first.geom; - } - - ref_type_to_matrix.MakeJ(); - for (map::iterator it = ref_type_map.begin(); - it != ref_type_map.end(); ++it) - { - const RefType &rt = it->first; - for (int j = 0; j < rt.num_children; j++) - { - ref_type_to_matrix.AddConnection(it->second, rt.children[j].one); - } - } - ref_type_to_matrix.ShiftUpI(); - }*/ + } + ref_type_to_matrix.ShiftUpI(); const int total_ref_types = ref_type_to_geom.Size(); int num_ref_types[Geometry::NumGeom], num_fine_elems[Geometry::NumGeom]; From 61e9c368d79730522a8d81d411edb7759318e1cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C4=8Cerven=C3=BD?= Date: Fri, 17 Sep 2021 12:52:20 +0200 Subject: [PATCH 114/198] DerefinementOperator: uses original GetCoarseToFineMap, now in fespace.cpp. --- fem/fespace.cpp | 141 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 96 insertions(+), 45 deletions(-) diff --git a/fem/fespace.cpp b/fem/fespace.cpp index c8209fbe39..6e6d799224 100644 --- a/fem/fespace.cpp +++ b/fem/fespace.cpp @@ -1723,7 +1723,10 @@ void FiniteElementSpace::RefinementOperator } } -// Used in DerefinementOperator::DerefinementOperator() below. +namespace internal +{ + +// Used in GetCoarseToFineMap() below. struct RefType { Geometry::Type geom; @@ -1748,6 +1751,94 @@ struct RefType } }; +void GetCoarseToFineMap(const CoarseFineTransformations &cft, + const mfem::Mesh &fine_mesh, + Table &coarse_to_fine, + Array &coarse_to_ref_type, + Table &ref_type_to_matrix, + Array &ref_type_to_geom) +{ + const int fine_ne = cft.embeddings.Size(); + int coarse_ne = -1; + for (int i = 0; i < fine_ne; i++) + { + coarse_ne = std::max(coarse_ne, cft.embeddings[i].parent); + } + coarse_ne++; + + coarse_to_ref_type.SetSize(coarse_ne); + coarse_to_fine.SetDims(coarse_ne, fine_ne); + + Array cf_i(coarse_to_fine.GetI(), coarse_ne+1); + Array > cf_j(fine_ne); + cf_i = 0; + for (int i = 0; i < fine_ne; i++) + { + cf_i[cft.embeddings[i].parent+1]++; + } + cf_i.PartialSum(); + MFEM_ASSERT(cf_i.Last() == cf_j.Size(), "internal error"); + for (int i = 0; i < fine_ne; i++) + { + const Embedding &e = cft.embeddings[i]; + cf_j[cf_i[e.parent]].one = e.matrix; // used as sort key below + cf_j[cf_i[e.parent]].two = i; + cf_i[e.parent]++; + } + std::copy_backward(cf_i.begin(), cf_i.end()-1, cf_i.end()); + cf_i[0] = 0; + for (int i = 0; i < coarse_ne; i++) + { + std::sort(&cf_j[cf_i[i]], cf_j.GetData() + cf_i[i+1]); + } + for (int i = 0; i < fine_ne; i++) + { + coarse_to_fine.GetJ()[i] = cf_j[i].two; + } + + using std::map; + using std::pair; + + map ref_type_map; + for (int i = 0; i < coarse_ne; i++) + { + const int num_children = cf_i[i+1]-cf_i[i]; + MFEM_ASSERT(num_children > 0, ""); + const int fine_el = cf_j[cf_i[i]].two; + // Assuming the coarse and the fine elements have the same geometry: + const Geometry::Type geom = fine_mesh.GetElementBaseGeometry(fine_el); + const RefType ref_type(geom, num_children, &cf_j[cf_i[i]]); + pair::iterator,bool> res = + ref_type_map.insert( + pair(ref_type, (int)ref_type_map.size())); + coarse_to_ref_type[i] = res.first->second; + } + + ref_type_to_matrix.MakeI((int)ref_type_map.size()); + ref_type_to_geom.SetSize((int)ref_type_map.size()); + for (map::iterator it = ref_type_map.begin(); + it != ref_type_map.end(); ++it) + { + ref_type_to_matrix.AddColumnsInRow(it->second, it->first.num_children); + ref_type_to_geom[it->second] = it->first.geom; + } + + ref_type_to_matrix.MakeJ(); + for (map::iterator it = ref_type_map.begin(); + it != ref_type_map.end(); ++it) + { + const RefType &rt = it->first; + for (int j = 0; j < rt.num_children; j++) + { + ref_type_to_matrix.AddConnection(it->second, rt.children[j].one); + } + } + ref_type_to_matrix.ShiftUpI(); +} + +} // namespace internal + + /// TODO: Implement DofTransformation support FiniteElementSpace::DerefinementOperator::DerefinementOperator( const FiniteElementSpace *f_fes, const FiniteElementSpace *c_fes, @@ -1788,50 +1879,11 @@ FiniteElementSpace::DerefinementOperator::DerefinementOperator( } } - rtrans.MakeCoarseToFineTable(coarse_to_fine, true); - MFEM_ASSERT(coarse_to_fine.Size() == c_fes->GetNE(), ""); - - using std::map; - using std::pair; - - // create coarse_to_ref_type - map ref_type_map; - coarse_to_ref_type.SetSize(coarse_to_fine.Size()); - for (int i = 0; i < coarse_ne; i++) - { - const int num_children = coarse_to_fine.RowSize(i); - const int *children = coarse_to_fine.GetRow(i); - MFEM_ASSERT(num_children > 0, ""); - // Assuming the coarse and the fine elements have the same geometry: - const Geometry::Type geom = new_mesh.GetElementBaseGeometry(children[0]); - const RefType ref_type(geom, num_children, children); - auto res = ref_type_map.insert( - pair(ref_type, (int)ref_type_map.size())); - coarse_to_ref_type[i] = res.first->second; - } - - // create ref_type_to_geom, ref_type_to_matrix Table ref_type_to_matrix; - ref_type_to_matrix.MakeI((int)ref_type_map.size()); - ref_type_to_geom.SetSize((int)ref_type_map.size()); - for (map::iterator it = ref_type_map.begin(); - it != ref_type_map.end(); ++it) - { - ref_type_to_matrix.AddColumnsInRow(it->second, it->first.num_children); - ref_type_to_geom[it->second] = it->first.geom; - } - - ref_type_to_matrix.MakeJ(); - for (map::iterator it = ref_type_map.begin(); - it != ref_type_map.end(); ++it) - { - const RefType &rt = it->first; - for (int j = 0; j < rt.num_children; j++) - { - ref_type_to_matrix.AddConnection(it->second, rt.children[j].one); - } - } - ref_type_to_matrix.ShiftUpI(); + internal::GetCoarseToFineMap(rtrans, *f_mesh, coarse_to_fine, + coarse_to_ref_type, ref_type_to_matrix, + ref_type_to_geom); + MFEM_ASSERT(coarse_to_fine.Size() == c_fes->GetNE(), ""); const int total_ref_types = ref_type_to_geom.Size(); int num_ref_types[Geometry::NumGeom], num_fine_elems[Geometry::NumGeom]; @@ -1847,7 +1899,6 @@ FiniteElementSpace::DerefinementOperator::DerefinementOperator( num_ref_types[g]++; num_fine_elems[g] += ref_type_to_matrix.RowSize(i); } - DenseTensor localPtMP[Geometry::NumGeom]; for (int g = 0; g < Geometry::NumGeom; g++) { From a53706b57c3179ad4624ab0c7c034aa1f42d63c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C4=8Cerven=C3=BD?= Date: Fri, 17 Sep 2021 13:53:45 +0200 Subject: [PATCH 115/198] Simplified CoarseFineTransformations::MakeCoarseToFineTable. --- fem/tmop_amr.cpp | 14 ++++++----- mesh/ncmesh.cpp | 64 +++++++++--------------------------------------- mesh/ncmesh.hpp | 5 ++-- 3 files changed, 23 insertions(+), 60 deletions(-) diff --git a/fem/tmop_amr.cpp b/fem/tmop_amr.cpp index e3e7d8642a..9d853d2793 100755 --- a/fem/tmop_amr.cpp +++ b/fem/tmop_amr.cpp @@ -394,12 +394,13 @@ bool TMOPDeRefinerEstimator::GetDerefineEnergyForIntegrator( const CoarseFineTransformations &dtrans = meshcopy.ncmesh->GetDerefinementTransforms(); - Table coarse_to_fine; - dtrans.GetCoarseToFineMap(meshcopy, coarse_to_fine); + Table coarse_to_fine; + dtrans.MakeCoarseToFineTable(coarse_to_fine); + + Array tabrow; for (int pe = 0; pe < coarse_to_fine.Size(); pe++) { - Array tabrow; coarse_to_fine.GetRow(pe, tabrow); int nchild = tabrow.Size(); double parent_energy = coarse_energy(pe); @@ -446,12 +447,13 @@ bool TMOPDeRefinerEstimator::GetDerefineEnergyForIntegrator( const CoarseFineTransformations &dtrans = meshcopy.pncmesh->GetDerefinementTransforms(); - Table coarse_to_fine; - dtrans.GetCoarseToFineMap(meshcopy, coarse_to_fine); + Table coarse_to_fine; + dtrans.MakeCoarseToFineTable(coarse_to_fine); + + Array tabrow; for (int pe = 0; pe < meshcopy.GetNE(); pe++) { - Array tabrow; coarse_to_fine.GetRow(pe, tabrow); int nchild = tabrow.Size(); double parent_energy = coarse_energy(pe); diff --git a/mesh/ncmesh.cpp b/mesh/ncmesh.cpp index 3c52121465..35fb626ed4 100644 --- a/mesh/ncmesh.cpp +++ b/mesh/ncmesh.cpp @@ -4429,63 +4429,23 @@ const CoarseFineTransformations& NCMesh::GetDerefinementTransforms() void CoarseFineTransformations::MakeCoarseToFineTable(Table &coarse_to_fine, bool want_ghosts) const { - // count fine elements - int fine_ne; - if (want_ghosts) + Array conn; + conn.Reserve(embeddings.Size()); + + int max_parent = -1; + for (int i = 0; i < embeddings.Size(); i++) { - fine_ne = embeddings.Size(); - } - else - { - fine_ne = 0; - for (int i = 0; i < embeddings.Size(); i++) + const Embedding &emb = embeddings[i]; + if ((emb.parent >= 0) && + (!emb.ghost || want_ghosts)) { - if (!embeddings[i].ghost) { fine_ne++; } + conn.Append(Connection(emb.parent, i)); + max_parent = std::max(emb.parent, max_parent); } } - // count coarse elements - int coarse_ne = -1; - for (int i = 0; i < fine_ne; i++) - { - coarse_ne = std::max(coarse_ne, embeddings[i].parent); - } - coarse_ne++; - - coarse_to_fine.SetDims(coarse_ne, fine_ne); - - // count table row sizes - Array cf_i(coarse_to_fine.GetI(), coarse_ne+1); - cf_i = 0; - for (int i = 0; i < fine_ne; i++) - { - const Embedding &e = embeddings[i]; - if (!want_ghosts && e.ghost) { continue; } - if (e.parent >= 0) { cf_i[e.parent + 1]++; } - } - cf_i.PartialSum(); - MFEM_ASSERT(cf_i.Last() == fine_ne, "internal error"); - - // fill and sort rows - Array > cf_j(fine_ne); - for (int i = 0; i < fine_ne; i++) - { - const Embedding &e = embeddings[i]; - if (!want_ghosts && e.ghost) { continue; } - cf_j[cf_i[e.parent]].one = e.matrix; // used as sort key below - cf_j[cf_i[e.parent]].two = i; - cf_i[e.parent]++; - } - std::copy_backward(cf_i.begin(), cf_i.end()-1, cf_i.end()); - cf_i[0] = 0; - for (int i = 0; i < coarse_ne; i++) - { - std::sort(&cf_j[cf_i[i]], cf_j.GetData() + cf_i[i+1]); - } - for (int i = 0; i < fine_ne; i++) - { - coarse_to_fine.GetJ()[i] = cf_j[i].two; - } + conn.Sort(); // NOTE: unique is not necessary + coarse_to_fine.MakeFromList(max_parent+1, conn); } void NCMesh::ClearTransforms() diff --git a/mesh/ncmesh.hpp b/mesh/ncmesh.hpp index b9d9d2df51..3df20bdc7d 100644 --- a/mesh/ncmesh.hpp +++ b/mesh/ncmesh.hpp @@ -66,11 +66,12 @@ struct Embedding /// Defines the coarse-fine transformations of all fine elements. struct CoarseFineTransformations { + /// Fine element positions in their parents. + Array embeddings; + /** A "dictionary" of matrices for IsoparametricTransformation. Use Embedding::geom and ::matrix to access a fine element point matrix. */ DenseTensor point_matrices[Geometry::NumGeom]; - /// Fine element positions in their parents. - Array embeddings; /** Invert the 'embeddings' array: create a Table with coarse elements as rows and fine elements as columns. If 'want_ghosts' is false, parallel From 4f0c54ff06dd4384c0bb377a2751c8ea0cebf9f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C4=8Cerven=C3=BD?= Date: Fri, 17 Sep 2021 14:12:23 +0200 Subject: [PATCH 116/198] Updated derefinement test in test_derefine.cpp --- mesh/ncmesh.cpp | 2 +- tests/unit/fem/test_derefine.cpp | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/mesh/ncmesh.cpp b/mesh/ncmesh.cpp index 35fb626ed4..89eac28cfd 100644 --- a/mesh/ncmesh.cpp +++ b/mesh/ncmesh.cpp @@ -1868,7 +1868,7 @@ void NCMesh::InitDerefTransforms() Embedding &emb = transforms.embeddings[i]; emb.parent = -1; emb.matrix = 0; - Element &el =elements[leaf_elements[i]]; + Element &el = elements[leaf_elements[i]]; emb.geom = el.Geom(); emb.ghost = IsGhost(el); } diff --git a/tests/unit/fem/test_derefine.cpp b/tests/unit/fem/test_derefine.cpp index 9ceb3a6846..92aa47e004 100644 --- a/tests/unit/fem/test_derefine.cpp +++ b/tests/unit/fem/test_derefine.cpp @@ -91,12 +91,8 @@ TEST_CASE("Derefine") // Derefine by setting 0 error on the fine elements in coarse element 2. Table coarse_to_fine_; - Table ref_type_to_matrix; - Array coarse_to_ref_type; - Array ref_type_to_geom; const CoarseFineTransformations &rtrans = mesh.GetRefinementTransforms(); - rtrans.GetCoarseToFineMap(mesh, coarse_to_fine_, coarse_to_ref_type, - ref_type_to_matrix, ref_type_to_geom); + rtrans.MakeCoarseToFineTable(coarse_to_fine_); Array tabrow; Vector local_err(mesh.GetNE()); From 6708f9c19bc87694460225396ea595b6b860b931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C4=8Cerven=C3=BD?= Date: Fri, 17 Sep 2021 14:17:32 +0200 Subject: [PATCH 117/198] Fix Doxygen error --- mesh/ncmesh.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesh/ncmesh.hpp b/mesh/ncmesh.hpp index 3df20bdc7d..477478ebd0 100644 --- a/mesh/ncmesh.hpp +++ b/mesh/ncmesh.hpp @@ -70,7 +70,7 @@ struct CoarseFineTransformations Array embeddings; /** A "dictionary" of matrices for IsoparametricTransformation. Use - Embedding::geom and ::matrix to access a fine element point matrix. */ + Embedding::{geom,matrix} to access a fine element point matrix. */ DenseTensor point_matrices[Geometry::NumGeom]; /** Invert the 'embeddings' array: create a Table with coarse elements as From 654663a2217fdf9b222e34e6fa241adfec260a62 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Mon, 20 Sep 2021 14:22:48 -0700 Subject: [PATCH 118/198] Parallel implementation of GridFunction::GetDerivative(). --- examples/ex1p.cpp | 24 +++++++++++++++++++++++- fem/gridfunc.cpp | 21 +++++++++++++-------- fem/gridfunc.hpp | 16 ++++++++++++++++ fem/pgridfunc.cpp | 21 +++++++++++++++++++++ fem/pgridfunc.hpp | 3 +++ 5 files changed, 76 insertions(+), 9 deletions(-) diff --git a/examples/ex1p.cpp b/examples/ex1p.cpp index 55303a21f7..77573c42d4 100644 --- a/examples/ex1p.cpp +++ b/examples/ex1p.cpp @@ -203,7 +203,29 @@ int main(int argc, char *argv[]) // function corresponding to fespace. Initialize x with initial guess of // zero, which satisfies the boundary conditions. ParGridFunction x(&fespace); - x = 0.0; + + auto func = [&](Vector coord) { return std::sin(coord(0)*coord(1)); }; + FunctionCoefficient x_coeff(func); + + x.ProjectCoefficient(x_coeff); + ParGridFunction grad_x(&fespace); + ConstantCoefficient zero(0.0); + for (int i = 0; i < dim; i++) + { + x.GetDerivative(1, i, grad_x); + //x.GridFunction::GetDerivative(1, i, grad_x); // old behavior. + double error = grad_x.ComputeL2Error(zero); + if (myid == 0) { cout << setprecision(14) << error << endl; } + } + + char vishost[] = "localhost"; + int visport = 19916; + socketstream sol_sock(vishost, visport); + sol_sock << "parallel " << num_procs << " " << myid << "\n"; + sol_sock.precision(8); + sol_sock << "solution\n" << pmesh << x << flush; + + MFEM_ABORT("test getderiv"); // 11. Set up the parallel bilinear form a(.,.) on the finite element space // corresponding to the Laplacian operator -Delta, by adding the diff --git a/fem/gridfunc.cpp b/fem/gridfunc.cpp index 7a7c8ef9ec..a4a5dede8f 100644 --- a/fem/gridfunc.cpp +++ b/fem/gridfunc.cpp @@ -1341,21 +1341,20 @@ void GridFunction::ProjectVectorFieldOn(GridFunction &vec_field, int comp) } } -void GridFunction::GetDerivative(int comp, int der_comp, GridFunction &der) +void GridFunction::AccumulateAndCountDerivativeValues(int comp, int der_comp, + GridFunction &der, + Array &zones_per_dof) { FiniteElementSpace * der_fes = der.FESpace(); ElementTransformation * transf; - Array overlap(der_fes->GetVSize()); + zones_per_dof.SetSize(der_fes->GetVSize()); Array der_dofs, vdofs; DenseMatrix dshape, inv_jac; Vector pt_grad, loc_func; int i, j, k, dim, dof, der_dof, ind; double a; - for (i = 0; i < overlap.Size(); i++) - { - overlap[i] = 0; - } + zones_per_dof = 0; der = 0.0; comp--; @@ -1390,11 +1389,17 @@ void GridFunction::GetDerivative(int comp, int der_comp, GridFunction &der) a += inv_jac(j, der_comp) * pt_grad(j); } der(der_dofs[k]) += a; - overlap[der_dofs[k]]++; + zones_per_dof[der_dofs[k]]++; } } +} - for (i = 0; i < overlap.Size(); i++) +void GridFunction::GetDerivative(int comp, int der_comp, GridFunction &der) +{ + Array overlap; + AccumulateAndCountDerivativeValues(comp, der_comp, der, overlap); + + for (int i = 0; i < overlap.Size(); i++) { der(i) /= overlap[i]; } diff --git a/fem/gridfunc.hpp b/fem/gridfunc.hpp index 378d620382..765b8940f7 100644 --- a/fem/gridfunc.hpp +++ b/fem/gridfunc.hpp @@ -310,6 +310,16 @@ public: void ProjectVectorFieldOn(GridFunction &vec_field, int comp = 0); + /** @brief Compute a certain derivative of a function's component. + Derivatives of the function are computed at the DOF locations of @a der, + and averaged over overlapping DOFs. Thus this function projects the + derivative to the FiniteElementSpace of @a der. + @param[in] comp Index of the function's component to be differentiated. + The index is 1-based, i.e., use 1 for scalar functions. + @param[in] der_comp Use 0/1/2 for derivatives in x/y/z directions. + @param[out] der The resulting derivative (scalar function). The + FiniteElementSpace of this function must be set + before the call. */ void GetDerivative(int comp, int der_comp, GridFunction &der); double GetDivergence(ElementTransformation &tr) const; @@ -411,6 +421,12 @@ protected: void AccumulateAndCountZones(VectorCoefficient &vcoeff, AvgType type, Array &zones_per_vdof); + /** @brief Used for the serial and parallel implementations of the + GetDerivative() method; see its documentation. */ + void AccumulateAndCountDerivativeValues(int comp, int der_comp, + GridFunction &der, + Array &zones_per_dof); + void AccumulateAndCountBdrValues(Coefficient *coeff[], VectorCoefficient *vcoeff, Array &attr, Array &values_counter); diff --git a/fem/pgridfunc.cpp b/fem/pgridfunc.cpp index 520b8015d8..2f49a4a3eb 100644 --- a/fem/pgridfunc.cpp +++ b/fem/pgridfunc.cpp @@ -481,6 +481,27 @@ void ParGridFunction::GetVectorValue(ElementTransformation &T, } } +void ParGridFunction::GetDerivative(int comp, int der_comp, + ParGridFunction &der) +{ + Array overlap; + AccumulateAndCountDerivativeValues(comp, der_comp, der, overlap); + + // Count the zones globally. + GroupCommunicator &gcomm = der.ParFESpace()->GroupComm(); + gcomm.Reduce(overlap, GroupCommunicator::Sum); + gcomm.Bcast(overlap); + + // Accumulate for all dofs. + gcomm.Reduce(der.GetData(), GroupCommunicator::Sum); + gcomm.Bcast(der.GetData()); + + for (int i = 0; i < overlap.Size(); i++) + { + der(i) /= overlap[i]; + } +} + void ParGridFunction::GetElementDofValues(int el, Vector &dof_vals) const { int ne = fes->GetNE(); diff --git a/fem/pgridfunc.hpp b/fem/pgridfunc.hpp index 8a15eae840..3841dfba53 100644 --- a/fem/pgridfunc.hpp +++ b/fem/pgridfunc.hpp @@ -226,6 +226,9 @@ public: const IntegrationPoint &ip, Vector &val, Vector *tr = NULL) const; + /// Parallel version of GridFunction::GetDerivative(); see its documentation. + void GetDerivative(int comp, int der_comp, ParGridFunction &der); + /** Sets the output vector @a dof_vals to the values of the degrees of freedom of element @a el. If @a el is greater than or equal to the number of local elements, it will be interpreted as a shifted index of a face From 3562662d93f093aff12ab880b04d421d317fd2f6 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Mon, 20 Sep 2021 14:50:17 -0700 Subject: [PATCH 119/198] Revert ex1p. --- examples/ex1p.cpp | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/examples/ex1p.cpp b/examples/ex1p.cpp index 77573c42d4..55303a21f7 100644 --- a/examples/ex1p.cpp +++ b/examples/ex1p.cpp @@ -203,29 +203,7 @@ int main(int argc, char *argv[]) // function corresponding to fespace. Initialize x with initial guess of // zero, which satisfies the boundary conditions. ParGridFunction x(&fespace); - - auto func = [&](Vector coord) { return std::sin(coord(0)*coord(1)); }; - FunctionCoefficient x_coeff(func); - - x.ProjectCoefficient(x_coeff); - ParGridFunction grad_x(&fespace); - ConstantCoefficient zero(0.0); - for (int i = 0; i < dim; i++) - { - x.GetDerivative(1, i, grad_x); - //x.GridFunction::GetDerivative(1, i, grad_x); // old behavior. - double error = grad_x.ComputeL2Error(zero); - if (myid == 0) { cout << setprecision(14) << error << endl; } - } - - char vishost[] = "localhost"; - int visport = 19916; - socketstream sol_sock(vishost, visport); - sol_sock << "parallel " << num_procs << " " << myid << "\n"; - sol_sock.precision(8); - sol_sock << "solution\n" << pmesh << x << flush; - - MFEM_ABORT("test getderiv"); + x = 0.0; // 11. Set up the parallel bilinear form a(.,.) on the finite element space // corresponding to the Laplacian operator -Delta, by adding the From c216a8694351e57454d076a4a9a2e51b40262cab Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Tue, 21 Sep 2021 18:06:44 -0700 Subject: [PATCH 120/198] Minor edits. --- miniapps/shifted/diffusion.cpp | 25 ++++++++++++++----------- miniapps/shifted/dist_solver.hpp | 3 ++- miniapps/shifted/distance.cpp | 17 ++++++++++------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index 42458bfc18..f94b734fcc 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -40,12 +40,12 @@ // mpirun -np 4 diffusion -rs 3 -o 1 -vis -nlst 1 -ho 1 // // Problem 2: Circular hole of radius 0.2 at the center of the domain. -// Solves -nabla^2 u = f with inhomogeneous boundary conditions, and -// f is setup such that u = x^p + y^p, where p = 2 by default. +// Solves -nabla^2 u = f with inhomogeneous boundary conditions, +// and f is setup such that u = x^p + y^p, where p = 2 by default. // This is a 2D convergence test. -// Dirichlet boundary condition +// Dirichlet BC // mpirun -np 4 diffusion -rs 2 -o 2 -vis -lst 2 -// Neumann boundary condition (inhomogeneous condition derived using exact solution) +// Neumann BC (inhomogeneous condition derived using exact solution) // mpirun -np 4 diffusion -rs 2 -o 2 -vis -nlst 2 -ho 1 // // Problem 3: Domain is y = [0, 1] but mesh is shifted to [-1.e-4, 1]. @@ -67,8 +67,8 @@ // mpirun -np 4 diffusion -m ../../data/inline-tet.mesh -rs 3 -lst 8 -alpha 10 // // Problem 5: Circular hole with homogeneous Neumann, triangular hole with -// inhomogeneous Dirichlet, and square hole with homogeneous Dirichlet -// boundary condition. +// inhomogeneous Dirichlet, and a square hole with homogeneous +// Dirichlet boundary condition. // mpirun -np 4 diffusion -rs 3 -o 1 -vis -lst 5 -ho 1 -nlst 7 -alpha 10.0 -dc #include "mfem.hpp" @@ -158,6 +158,10 @@ int main(int argc, char *argv[]) Mesh mesh(mesh_file, 1, 1); int dim = mesh.Dimension(); for (int lev = 0; lev < ser_ref_levels; lev++) { mesh.UniformRefinement(); } + if (myid == 0) + { + std::cout << "Number of elements: " << mesh.GetNE() << std::endl; + } // MPI distribution. ParMesh pmesh(MPI_COMM_WORLD, mesh); @@ -316,9 +320,8 @@ int main(int argc, char *argv[]) // Discrete distance vector. double dx = AvgElementSize(pmesh); ParGridFunction filt_gf(&pfespace); - PDEFilter *filter = new PDEFilter(pmesh, 2.0 * dx); - filter->Filter(combo_dist_coef, filt_gf); - delete filter; + PDEFilter filter(pmesh, 2.0 * dx); + filter.Filter(combo_dist_coef, filt_gf); GridFunctionCoefficient ls_filt_coeff(&filt_gf); if (visualization) @@ -330,7 +333,7 @@ int main(int argc, char *argv[]) "Input Level Set", 0, 2*s, s, s, "Rjmm"); } - HeatDistanceSolver dist_func(2.0 * dx* dx); + HeatDistanceSolver dist_func(2.0 * dx * dx); dist_func.print_level = 1; dist_func.smooth_steps = 1; dist_func.ComputeVectorDistance(ls_filt_coeff, distance); @@ -633,7 +636,7 @@ int main(int argc, char *argv[]) } const double norm = x.ComputeL1Error(one); - if (myid == 0) { std::cout << setprecision(8) << norm << std::endl; } + if (myid == 0) { std::cout << setprecision(10) << norm << std::endl; } // Free the used memory. delete prec; diff --git a/miniapps/shifted/dist_solver.hpp b/miniapps/shifted/dist_solver.hpp index 603260e53c..1f4589cce1 100644 --- a/miniapps/shifted/dist_solver.hpp +++ b/miniapps/shifted/dist_solver.hpp @@ -229,7 +229,8 @@ class PDEFilter { public: PDEFilter(ParMesh &mesh, double rh, int order = 2, - int maxiter=100, double rtol=1e-7, double atol=1e-15, int print_lv=0) + int maxiter = 100, double rtol = 1e-12, + double atol = 1e-15, int print_lv = 0) : rr(rh), fecp(order, mesh.Dimension()), fesp(&mesh, &fecp, 1), diff --git a/miniapps/shifted/distance.cpp b/miniapps/shifted/distance.cpp index ccc73d552a..dc571bea7a 100644 --- a/miniapps/shifted/distance.cpp +++ b/miniapps/shifted/distance.cpp @@ -199,7 +199,8 @@ int main(int argc, char *argv[]) "0: Point source\n\t" "1: Circle / sphere level set in 2D / 3D\n\t" "2: 2D sine-looking level set\n\t" - "3: Gyroid level set in 2D or 3D"); + "3: Gyroid level set in 2D or 3D\n\t" + "4: Combo of a doughnut and swiss cheese shapes in 3D."); args.AddOption(&rs_levels, "-rs", "--refine-serial", "Number of times to refine the mesh uniformly in serial."); args.AddOption(&order, "-o", "--order", @@ -238,7 +239,7 @@ int main(int argc, char *argv[]) ParMesh pmesh(MPI_COMM_WORLD, mesh); mesh.Clear(); - Coefficient *ls_coeff; + Coefficient *ls_coeff = nullptr; int smooth_steps; if (problem == 0) { @@ -265,6 +266,7 @@ int main(int argc, char *argv[]) ls_coeff = new FunctionCoefficient(doughnut_cheese); smooth_steps = 0; } + else { MFEM_ABORT("Unrecognized -problem option."); } const double dx = AvgElementSize(pmesh); DistanceSolver *dist_solver = NULL; @@ -296,13 +298,12 @@ int main(int argc, char *argv[]) // Smooth-out Gibbs oscillations from the input level set. The smoothing // parameter here is specified to be mesh dependent with length scale dx. ParGridFunction filt_gf(&pfes_s); - PDEFilter *filter = new PDEFilter(pmesh, 1.0 * dx); if (problem != 0) { - filter->Filter(*ls_coeff, filt_gf); + PDEFilter filter(pmesh, 1.0 * dx); + filter.Filter(*ls_coeff, filt_gf); } else { filt_gf.ProjectCoefficient(*ls_coeff); } - delete filter; delete ls_coeff; GridFunctionCoefficient ls_filt_coeff(&filt_gf); @@ -345,10 +346,12 @@ int main(int argc, char *argv[]) dacol.Save(); ConstantCoefficient zero(0.0); - const double d_norm = distance_s.ComputeL2Error(zero); + const double s_norm = distance_s.ComputeL2Error(zero), + v_norm = distance_v.ComputeL2Error(zero); if (myid == 0) { - cout << fixed << setprecision(10) << "Norm: " << d_norm << std::endl; + cout << fixed << setprecision(10) << "Norms: " + << s_norm << " " << v_norm << std::endl; } delete dist_solver; From e04fc276730854a003f064072704ca28c966c0e5 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Thu, 23 Sep 2021 13:27:31 -0700 Subject: [PATCH 121/198] Moved the reproducer to tmp.cpp and reverted ex1p.cpp. --- examples/ex1p.cpp | 24 +---------- examples/tmp.cpp | 107 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 23 deletions(-) create mode 100644 examples/tmp.cpp diff --git a/examples/ex1p.cpp b/examples/ex1p.cpp index 77573c42d4..55303a21f7 100644 --- a/examples/ex1p.cpp +++ b/examples/ex1p.cpp @@ -203,29 +203,7 @@ int main(int argc, char *argv[]) // function corresponding to fespace. Initialize x with initial guess of // zero, which satisfies the boundary conditions. ParGridFunction x(&fespace); - - auto func = [&](Vector coord) { return std::sin(coord(0)*coord(1)); }; - FunctionCoefficient x_coeff(func); - - x.ProjectCoefficient(x_coeff); - ParGridFunction grad_x(&fespace); - ConstantCoefficient zero(0.0); - for (int i = 0; i < dim; i++) - { - x.GetDerivative(1, i, grad_x); - //x.GridFunction::GetDerivative(1, i, grad_x); // old behavior. - double error = grad_x.ComputeL2Error(zero); - if (myid == 0) { cout << setprecision(14) << error << endl; } - } - - char vishost[] = "localhost"; - int visport = 19916; - socketstream sol_sock(vishost, visport); - sol_sock << "parallel " << num_procs << " " << myid << "\n"; - sol_sock.precision(8); - sol_sock << "solution\n" << pmesh << x << flush; - - MFEM_ABORT("test getderiv"); + x = 0.0; // 11. Set up the parallel bilinear form a(.,.) on the finite element space // corresponding to the Laplacian operator -Delta, by adding the diff --git a/examples/tmp.cpp b/examples/tmp.cpp new file mode 100644 index 0000000000..9afa02e5fc --- /dev/null +++ b/examples/tmp.cpp @@ -0,0 +1,107 @@ + +#include "mfem.hpp" + +using namespace std; +using namespace mfem; + +int main(int argc, char *argv[]) +{ + // 1. Initialize MPI. + MPI_Session mpi; + int num_procs = mpi.WorldSize(); + int myid = mpi.WorldRank(); + + // 2. Parse command-line options. + const char *mesh_file = "../data/star.mesh"; + int order = 1; + bool visualization = true; + + OptionsParser args(argc, argv); + args.AddOption(&mesh_file, "-m", "--mesh", + "Mesh file to use."); + args.AddOption(&order, "-o", "--order", + "Finite element order (polynomial degree) or -1 for" + " isoparametric space."); + args.AddOption(&visualization, "-vis", "--visualization", "-no-vis", + "--no-visualization", + "Enable or disable GLVis visualization."); + args.Parse(); + if (!args.Good()) + { + if (myid == 0) + { + args.PrintUsage(cout); + } + return 1; + } + if (myid == 0) + { + args.PrintOptions(cout); + } + + // 4. Read the (serial) mesh from the given mesh file on all processors. We + // can handle triangular, quadrilateral, tetrahedral, hexahedral, surface + // and volume meshes with the same code. + Mesh mesh(mesh_file, 1, 1); + int dim = mesh.Dimension(); + + // 5. Refine the serial mesh on all processors to increase the resolution. In + // this example we do 'ref_levels' of uniform refinement. We choose + // 'ref_levels' to be the largest number that gives a final mesh with no + // more than 10,000 elements. + { + int ref_levels = + (int)floor(log(10000./mesh.GetNE())/log(2.)/dim); + for (int l = 0; l < ref_levels; l++) + { + mesh.UniformRefinement(); + } + } + + // 6. Define a parallel mesh by a partitioning of the serial mesh. Refine + // this mesh further in parallel to increase the resolution. Once the + // parallel mesh is defined, the serial mesh can be deleted. + ParMesh pmesh(MPI_COMM_WORLD, mesh); + mesh.Clear(); + { + int par_ref_levels = 2; + for (int l = 0; l < par_ref_levels; l++) + { + pmesh.UniformRefinement(); + } + } + + // 7. Define a parallel finite element space on the parallel mesh. Here we + // use continuous Lagrange finite elements of the specified order. If + // order < 1, we instead use an isoparametric/isogeometric space. + H1_FECollection fec(order, dim); + ParFiniteElementSpace fespace(&pmesh, &fec); + + // 10. Define the solution vector x as a parallel finite element grid + // function corresponding to fespace. Initialize x with initial guess of + // zero, which satisfies the boundary conditions. + ParGridFunction x(&fespace); + + auto func = [&](Vector coord) { return std::sin(coord(0)*coord(1)); }; + FunctionCoefficient x_coeff(func); + + x.ProjectCoefficient(x_coeff); + ParGridFunction grad_x(&fespace); + ConstantCoefficient zero(0.0); + for (int i = 0; i < dim; i++) + { + x.GetDerivative(1, i, grad_x); + //x.GridFunction::GetDerivative(1, i, grad_x); // old behavior. + double error = grad_x.ComputeL2Error(zero); + if (myid == 0) { cout << setprecision(14) << error << endl; } + } + + char vishost[] = "localhost"; + int visport = 19916; + socketstream sol_sock(vishost, visport); + sol_sock << "parallel " << num_procs << " " << myid << "\n"; + sol_sock.precision(8); + sol_sock << "solution\n" << pmesh << x << flush; + + return 0; +} From 7cf96edc2f2c12b4457c65350518770bba936c0b Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Fri, 24 Sep 2021 14:23:22 -0700 Subject: [PATCH 122/198] Makefile improvements to avoid extra recompilation. --- miniapps/shifted/diffusion.cpp | 2 +- miniapps/shifted/makefile | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index f94b734fcc..f277ef0955 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -100,7 +100,7 @@ int main(int argc, char *argv[]) int order = 2; bool visualization = true; int ser_ref_levels = 0; - int dirichlet_level_set_type = -1; + int dirichlet_level_set_type = 1; int neumann_level_set_type = -1; bool dirichlet_combo = false; int ho_terms = 0; diff --git a/miniapps/shifted/makefile b/miniapps/shifted/makefile index 0173538711..8208c722bb 100644 --- a/miniapps/shifted/makefile +++ b/miniapps/shifted/makefile @@ -25,8 +25,10 @@ include $(DEFAULTS_MK) MFEM_LIB_FILE = mfem_is_not_built -include $(CONFIG_MK) -SHIFTED_COMMON_SRC = dist_solver.cpp sbm_solver.cpp marking.cpp -SHIFTED_COMMON_OBJ = $(SHIFTED_COMMON_SRC:.cpp=.o) +DIFFUSION_SRC = dist_solver.cpp sbm_solver.cpp marking.cpp +DIFFUSION_OBJ = $(DIFFUSION_SRC:.cpp=.o) +DISTANCE_SRC = dist_solver.cpp +DISTANCE_OBJ = $(DISTANCE_SRC:.cpp=.o) PAR_MINIAPPS = distance diffusion @@ -51,14 +53,17 @@ COMMON_LIB += $(if $(MFEM_SHARED:YES=),,\ %: %.cpp %.o: %.cpp -%.o: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK) | lib-common +%.o: $(SRC)%.cpp $(SRC)%.hpp $(MFEM_LIB_FILE) $(CONFIG_MK) | lib-common $(MFEM_CXX) $(MFEM_FLAGS) -c $< -o $@ - -%: %.o $(SHIFTED_COMMON_OBJ) - $(MFEM_CXX) $(MFEM_LINK_FLAGS) $^ -o $@ $(COMMON_LIB) $(MFEM_LIBS) - + all: $(MINIAPPS) +distance: distance.cpp sbm_aux.hpp $(DISTANCE_OBJ) + $(MFEM_CXX) $(MFEM_LINK_FLAGS) $@.cpp -o $@ $(DISTANCE_OBJ) $(COMMON_LIB) $(MFEM_LIBS) + +diffusion: diffusion.cpp sbm_aux.hpp $(DIFFUSION_OBJ) + $(MFEM_CXX) $(MFEM_LINK_FLAGS) $@.cpp -o $@ $(DIFFUSION_OBJ) $(COMMON_LIB) $(MFEM_LIBS) + # Rule for building lib-common lib-common: $(MAKE) -C $(MFEM_BUILD_DIR)/miniapps/common From 621a7842b1609ed826a1430f372f7056b4cd227a Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Fri, 24 Sep 2021 14:43:56 -0700 Subject: [PATCH 123/198] AMR - sample run for the distance app, abort for the diffusion app. --- miniapps/shifted/diffusion.cpp | 1 + miniapps/shifted/distance.cpp | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index f277ef0955..63c91acb35 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -162,6 +162,7 @@ int main(int argc, char *argv[]) { std::cout << "Number of elements: " << mesh.GetNE() << std::endl; } + MFEM_VERIFY(mesh.Conforming(), "AMR capability is not implemented yet!"); // MPI distribution. ParMesh pmesh(MPI_COMM_WORLD, mesh); diff --git a/miniapps/shifted/distance.cpp b/miniapps/shifted/distance.cpp index dc571bea7a..da5fbe12d4 100644 --- a/miniapps/shifted/distance.cpp +++ b/miniapps/shifted/distance.cpp @@ -67,15 +67,16 @@ // mpirun -np 4 distance -m ./corners.mesh -p 0 -rs 3 -t 200.0 // // Problem 1: zero level set: circle / sphere at the center of the mesh -// mpirun -np 4 distance -m ../../data/inline-quad.mesh -rs 3 -o 2 -t 1.0 -p 1 +// mpirun -np 4 distance -m ../../data/inline-quad.mesh -rs 3 -o 2 -t 1.0 -p 1 // mpirun -np 4 distance -m ../../data/periodic-cube.mesh -rs 2 -o 2 -p 1 -s 1 // // Problem 2: zero level set: perturbed sine // mpirun -np 4 distance -m ../../data/inline-quad.mesh -rs 3 -o 2 -t 1.0 -p 2 +// mpirun -np 4 distance -m ../../data/amr-quad.mesh -rs 3 -o 2 -t 1.0 -p 2 // // Problem 3: level set: Gyroid // mpirun -np 4 distance -m ../../data/periodic-square.mesh -rs 5 -o 2 -t 1.0 -p 3 -// mpirun -np 4 distance -m ../../data/periodic-cube.mesh -rs 3 -o 2 -t 1.0 -p 3 +// mpirun -np 4 distance -m ../../data/periodic-cube.mesh -rs 3 -o 2 -t 1.0 -p 3 // // Problem 4: level set: Union of doughnut and swiss cheese shapes // mpirun -np 4 distance -m ../../data/inline-hex.mesh -rs 3 -o 2 -t 1.0 -p 4 From 7e9dd26f57656047b3c1ced036c678d923ad9b0f Mon Sep 17 00:00:00 2001 From: Tom Stitt Date: Fri, 24 Sep 2021 14:45:09 -0700 Subject: [PATCH 124/198] fix empty mat check Co-authored-by: Yohann --- linalg/kernels.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linalg/kernels.hpp b/linalg/kernels.hpp index 21c67846b0..c92a4e7d96 100644 --- a/linalg/kernels.hpp +++ b/linalg/kernels.hpp @@ -196,9 +196,9 @@ MFEM_HOST_DEVICE inline void MultT(const int height, const int width, const TA *data, const TX *x, TY *y) { - if (width == 0) + if (height == 0) { - for (int row = 0; row < height; row++) + for (int row = 0; row < width; row++) { y[row] = 0.0; } From b74049fff18a61afb31f4c7decbb4b835e663e5e Mon Sep 17 00:00:00 2001 From: Tom Stitt Date: Fri, 24 Sep 2021 14:46:12 -0700 Subject: [PATCH 125/198] MultT -> MultTranspose --- linalg/kernels.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linalg/kernels.hpp b/linalg/kernels.hpp index c92a4e7d96..244e3a3f55 100644 --- a/linalg/kernels.hpp +++ b/linalg/kernels.hpp @@ -193,7 +193,7 @@ void Mult(const int height, const int width, const TA *data, const TX *x, TY *y) specify the data of the input and output vectors. */ template MFEM_HOST_DEVICE inline -void MultT(const int height, const int width, const TA *data, const TX *x, +void MultTranspose(const int height, const int width, const TA *data, const TX *x, TY *y) { if (height == 0) From f7b7c5388bce22905eba9601e9e4b25cf7999832 Mon Sep 17 00:00:00 2001 From: Tzanio Date: Sun, 26 Sep 2021 17:26:44 -0700 Subject: [PATCH 126/198] make style --- linalg/kernels.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linalg/kernels.hpp b/linalg/kernels.hpp index 244e3a3f55..051d3e5a94 100644 --- a/linalg/kernels.hpp +++ b/linalg/kernels.hpp @@ -193,8 +193,8 @@ void Mult(const int height, const int width, const TA *data, const TX *x, TY *y) specify the data of the input and output vectors. */ template MFEM_HOST_DEVICE inline -void MultTranspose(const int height, const int width, const TA *data, const TX *x, - TY *y) +void MultTranspose(const int height, const int width, const TA *data, + const TX *x, TY *y) { if (height == 0) { From c753e3bf8e3a3cd82df935b29df8baab5fa170b4 Mon Sep 17 00:00:00 2001 From: Tzanio Date: Sun, 26 Sep 2021 18:35:02 -0700 Subject: [PATCH 127/198] Incorporater PR review rules in CONTRIBUTING.md --- CONTRIBUTING.md | 87 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ce7f584928..d22cf7302e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,6 +42,7 @@ back to them before issuing pull requests: - [New Feature Development](#new-feature-development) - [Developer Guidelines](#developer-guidelines) - [Pull Requests](#pull-requests) + - [MFEM Pull Request Rules](#mfem-pull-request-rules) - [Pull Request Checklist](#pull-request-checklist) - [Master/Next Workflow](#masternext-workflow) - [Releases](#releases) @@ -67,8 +68,9 @@ Origin](#developers-certificate-of-origin-11) at the end of this file.* with regards to documentation and code styling. - Please do not commit large/binary files to the central repository (use a fork instead). -- Pull requests should be issued toward `mfem:master`. Make sure - to check the items off the [Pull Request Checklist](#pull-request-checklist). +- Pull requests should be issued toward `mfem:master`. Make sure + to check the items off the [Pull Request Checklist](#pull-request-checklist) and + follow the [MFEM Pull Request Rules](#mfem-pull-request-rules). - When your contribution is fully working and ready to be reviewed, add the `ready-for-review` label. - PRs are treated similarly to journal submission with an "editor" assigning two @@ -335,6 +337,7 @@ Before you can start, you need a GitHub account, here are a few suggestions: - When manually resolving conflicts during a merge, make sure to mention the conflicted files in the commit message. + ### Pull Requests - When your branch is ready for other developers to review / comment on @@ -399,6 +402,79 @@ Before you can start, you need a GitHub account, here are a few suggestions: - If triggered, track the status of the LLNL GitLab tests. If failing, ask one of the _LLNL developers_ for details. + +### MFEM Pull Request Rules + +The Pull Request (PR) approval process in MFEM is similar to the approval of papers in a peer-reviewed journal. In particular: + +1. There is an MFEM board of "editors" that evaluates new PRs and assigns "reviewers" for each PR. + +2. The assigned reviewers are responsible to carefully review and test the proposed PR. + +3. A PR can be (manually) merged in the *next* branch only if 2 of the assigned reviewers have approved it and it has passed internal testing. This merge can be performed by any of the assigned reviewers or by any of the editors. + +4. A PR can be merged in the *master* branch only if it has been tested successfully for a week in *next* and an editor has (optionally) taken a final look. This merge can be performed only by one of the editors. + +#### Responsibilities of Editors + +The current list of MFEM editors is: + +- @v-dobrev (Veselin Dobrev) +- @tzanio (Tzanio Kolev) + +**The responsibilities of the editors are:** + +1. To assign appropriate milestone and labels for new PRs, e.g. *bugfix*, *minor*, *api-change*, *high-impact*, etc. + +2. To assign at least 2 reviewers for new PRs. An editor can also be a reviewer. The editor, reviewers, and author should be listed as "Assignees" on the GitHub PR page. After assignment, the `in-review` label should be added. + +3. To complete the initial PR evaluation and assignments in a timely manner: 1 week from submission. + +4. To assist reviewers when they need help with their reviews (but also to stay out of the way when they don't). + +5. To remind the reviewers about timely completion of their review. + +6. To take a final look and complete the PR merge in *master*. The final look step is optional and shouldn't take more than 3 days. + +7. The assignment of bugfixes should be expedited proportional to their importance, e.g. in some cases the editor can assign much shorter review window. + +#### Responsibilities of Reviewers + +Everyone on the MFEM team can be asked to serve as a reviewer on a PR in their area of expertise. + +**The responsibilities of the reviewers are:** + +1. To let the editors know if the proposed assignment is not a good match for them. + +2. To communicate with the PR author, provide feedback and ensure the quality of the PR. + +3. To seek help form the editors in case of difficulties. + +4. To complete the review in a timely manner: 3 weeks from assignment. + +5. To test the PR thoroughly before merging in *next*. The PR author is also encouraged to perform testing and inform the reviewers about the results. + +6. To monitor the PR impact on the testing in the *next* branch and alert the editors that the PR is ready for merging in *master*. + +7. The review of bugfixes should be expedited proportional to their importance. The review window can be much less than three weeks in such cases. + +#### Responsibilities of Authors + +Authors should clearly indicate when a PR is ready for review (before that the PR should be marked as `Draft` or `[WIP]`). + +**The responsibilities of the authors are:** + +1. To follow the instructions and PR checklist in the `CONTRIBUTING.md` document in the MFEM repository. + +2. To respond to reviewer feedback in a timely manner. + +3. Authors are encouraged to perform testing and inform the reviewers about the results. + +4. Authors can use the "Reviewers" section of the GitHub PR page to suggest reviewers, but the "Assignees" section will show who the editor has assigned to do the reviews. + +5. To indicate when the PR is ready for review by adding the `ready-for-review` label. + + ### Pull Request Checklist Before a PR can be merged, it should satisfy the following: @@ -464,6 +540,7 @@ Before a PR can be merged, it should satisfy the following: - [ ] (LLNL only) After merging: - [ ] Update internal tests to include the new features. + ### Master/Next Workflow MFEM uses a `master`/`next`-branch workflow as described below: @@ -555,8 +632,10 @@ MFEM uses a `master`/`next`-branch workflow as described below: - Update version and shortlinks in `src/index.md` and `src/download.md`. - Use [cloc-1.62.pl](http://cloc.sourceforge.net/) and `ls -lh` to estimate the SLOC and the tarball size in `src/download.md`. + ## LLNL Workflow + ### Mirroring on Bitbucket - The GitHub `master` and `next` branches are mirrored to the LLNL institutional @@ -576,6 +655,7 @@ MFEM uses a `master`/`next`-branch workflow as described below: - `mfem:gh-next` -- Bleeding-edge development version, may be broken, use at your own risk. + ### Mirroring on GitLab - MFEM repository is also mirrored on the LLNL GitLab instance, in a @@ -598,6 +678,7 @@ In addition, developers can set local git hooks to run some quick checks on commit or push, see the [README](config/githooks/README.md) in the `config/githooks` directory. + ### Linux and Mac smoke tests We use GitHub Actions to drive the default tests on the `master` and `next` branches. See the `.github/workflows` files and the logs at @@ -609,6 +690,7 @@ constraint on jobs. Two virtual machines are configured - Mac (OS X) and Linux. - Tests on the `master` branch are triggered whenever a PR is issued on this branch. - Tests on the `next` branch are currently scheduled to run each night. + ### Windows smoke test We use Appveyor to test building with the MS Visual C++ compiler in a Windows environment, as well as to test the CMake build. See the `.appveyor` file and the @@ -618,6 +700,7 @@ build logs at CMake is used to generate the MSVC Project files and drive the build. A release and debug build is performed with a simple run of `ex1` to verify the executable. + ### Tests at LLNL - We mirror the `master` and `next` branches internally (to `gh-master` and From b34831a308d6f852d507d1afa8a8dfa28bcfe163 Mon Sep 17 00:00:00 2001 From: Tzanio Date: Sun, 26 Sep 2021 18:48:00 -0700 Subject: [PATCH 128/198] Incorporate Aaron suggestions --- CONTRIBUTING.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d22cf7302e..2322317db3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,7 +42,7 @@ back to them before issuing pull requests: - [New Feature Development](#new-feature-development) - [Developer Guidelines](#developer-guidelines) - [Pull Requests](#pull-requests) - - [MFEM Pull Request Rules](#mfem-pull-request-rules) + - [MFEM PR Rules](#mfem-pr-rules) - [Pull Request Checklist](#pull-request-checklist) - [Master/Next Workflow](#masternext-workflow) - [Releases](#releases) @@ -70,7 +70,7 @@ Origin](#developers-certificate-of-origin-11) at the end of this file.* instead). - Pull requests should be issued toward `mfem:master`. Make sure to check the items off the [Pull Request Checklist](#pull-request-checklist) and - follow the [MFEM Pull Request Rules](#mfem-pull-request-rules). + follow the [MFEM PR Rules](#mfem-pr-rules). - When your contribution is fully working and ready to be reviewed, add the `ready-for-review` label. - PRs are treated similarly to journal submission with an "editor" assigning two @@ -329,6 +329,9 @@ Before you can start, you need a GitHub account, here are a few suggestions: - Code specifics - All new public, protected, and private classes, methods, data members, and functions have Doxygen-style documentation in source comments. + - In addition to arguments and functionality, documentation should include the + current limitations of the code, and any background information that is + implicitly assumed in the implementation. - Consistent code styling is enforced with `make style` in the top-level directory. This requires [Artistic Style](http://astyle.sourceforge.net) (we specifically use version 3.1). See also the file `config/mfem.astylerc`. @@ -336,6 +339,8 @@ Before you can start, you need a GitHub account, here are a few suggestions: internal library code. (You can use `std` in examples and miniapps.) - When manually resolving conflicts during a merge, make sure to mention the conflicted files in the commit message. + - All significant new features and changes should be documented in CHANGELOG. + - New examples and miniapps should have documentation on the MFEM webpage. ### Pull Requests @@ -403,7 +408,7 @@ Before you can start, you need a GitHub account, here are a few suggestions: one of the _LLNL developers_ for details. -### MFEM Pull Request Rules +### MFEM PR Rules The Pull Request (PR) approval process in MFEM is similar to the approval of papers in a peer-reviewed journal. In particular: @@ -446,7 +451,9 @@ Everyone on the MFEM team can be asked to serve as a reviewer on a PR in their a 1. To let the editors know if the proposed assignment is not a good match for them. -2. To communicate with the PR author, provide feedback and ensure the quality of the PR. +2. To communicate with the PR author, provide feedback and work with them to resolve issues. + +3. To ensure the quality of the PR by making sure that the code adheres to the [Developer Guidelines](#developer-guidelines), e.g. all methods, data members, and functions have documentation, new examples/miniapps have a corresponding PR in mfem/web, major features have `CHANGELOG` entries, etc. 3. To seek help form the editors in case of difficulties. From 946ab2a8934804a07c12008563db93b5c0a5054a Mon Sep 17 00:00:00 2001 From: Tzanio Date: Sun, 26 Sep 2021 19:02:31 -0700 Subject: [PATCH 129/198] Incorporate suggestion from Denis --- CONTRIBUTING.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2322317db3..0aeceef436 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -330,8 +330,9 @@ Before you can start, you need a GitHub account, here are a few suggestions: - All new public, protected, and private classes, methods, data members, and functions have Doxygen-style documentation in source comments. - In addition to arguments and functionality, documentation should include the - current limitations of the code, and any background information that is - implicitly assumed in the implementation. + current limitations of the code, any background information that is + implicitly assumed in the implementation, and the ownership and lifetime + of data. - Consistent code styling is enforced with `make style` in the top-level directory. This requires [Artistic Style](http://astyle.sourceforge.net) (we specifically use version 3.1). See also the file `config/mfem.astylerc`. @@ -453,7 +454,7 @@ Everyone on the MFEM team can be asked to serve as a reviewer on a PR in their a 2. To communicate with the PR author, provide feedback and work with them to resolve issues. -3. To ensure the quality of the PR by making sure that the code adheres to the [Developer Guidelines](#developer-guidelines), e.g. all methods, data members, and functions have documentation, new examples/miniapps have a corresponding PR in mfem/web, major features have `CHANGELOG` entries, etc. +3. To ensure the quality of the PR by making sure that the code adheres to the [Developer Guidelines](#developer-guidelines), e.g. all methods, data members, and functions have documentation, including data ownership and lifetime, new examples/miniapps have a corresponding PR in mfem/web, major features have `CHANGELOG` entries, etc. 3. To seek help form the editors in case of difficulties. From 0e48fd5f94363c5fd232183f38a2e100424f5d68 Mon Sep 17 00:00:00 2001 From: "Stowell, Mark L" Date: Mon, 27 Sep 2021 09:29:20 -0700 Subject: [PATCH 130/198] Fixing a small typo --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0aeceef436..6c58c63701 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -456,7 +456,7 @@ Everyone on the MFEM team can be asked to serve as a reviewer on a PR in their a 3. To ensure the quality of the PR by making sure that the code adheres to the [Developer Guidelines](#developer-guidelines), e.g. all methods, data members, and functions have documentation, including data ownership and lifetime, new examples/miniapps have a corresponding PR in mfem/web, major features have `CHANGELOG` entries, etc. -3. To seek help form the editors in case of difficulties. +3. To seek help from the editors in case of difficulties. 4. To complete the review in a timely manner: 3 weeks from assignment. From 5231ec314a261e65697b676708b45c3eddaf6a81 Mon Sep 17 00:00:00 2001 From: Keith Date: Mon, 27 Sep 2021 15:01:01 -0700 Subject: [PATCH 131/198] Socratis's comments --- examples/osc.cpp | 2 +- examples/oscp.cpp | 2 +- mesh/mesh_operators.cpp | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/osc.cpp b/examples/osc.cpp index 732a348e4f..8100e80479 100644 --- a/examples/osc.cpp +++ b/examples/osc.cpp @@ -24,7 +24,7 @@ // resolved on the initial mesh. This missing fine scale data // reduces the accuracy of the solution as well as the accuracy // of some local error estimators. By preprocessing the mesh -// before the solving the PDE, many issues can be avoided. +// before solving the PDE, many issues can be avoided. // // [1] Morin, P., Nochetto, R. H., & Siebert, K. G. (2000). // Data oscillation and convergence of adaptive FEM. SIAM diff --git a/examples/oscp.cpp b/examples/oscp.cpp index dbba9b03fb..5e2a33e37d 100644 --- a/examples/oscp.cpp +++ b/examples/oscp.cpp @@ -24,7 +24,7 @@ // resolved on the initial mesh. This missing fine scale data // reduces the accuracy of the solution as well as the accuracy // of some local error estimators. By preprocessing the mesh -// before the solving the PDE, many issues can be avoided. +// before solving the PDE, many issues can be avoided. // // [1] Morin, P., Nochetto, R. H., & Siebert, K. G. (2000). // Data oscillation and convergence of adaptive FEM. SIAM diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index 52019db520..0575036391 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -204,7 +204,6 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) for (int i = 0; i < max_it; i++) { - // Compute number of elements and L2-norm of f. int NE = mesh.GetNE(); int globalNE = 0; From 7bac71736452d6bdb21ef7d9ad37b73bb5eef011 Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Tue, 28 Sep 2021 10:58:28 -0700 Subject: [PATCH 132/198] Host/Device fix for Hypre-AMGX solver. --- linalg/amgxsolver.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linalg/amgxsolver.cpp b/linalg/amgxsolver.cpp index 418d498b2a..899e384134 100644 --- a/linalg/amgxsolver.cpp +++ b/linalg/amgxsolver.cpp @@ -604,6 +604,9 @@ void AmgXSolver::SetMatrix(const HypreParMatrix &A, const bool update_mat) mfem_error("Hypre version 2.16+ is required when using AmgX \n"); #endif + //Ensure HypreParMatrix is on the host + A.HostRead(); + hypre_ParCSRMatrix * A_ptr = (hypre_ParCSRMatrix *)const_cast(A); From fc070abf6b224cca06775babaffa7f51c59a6cf4 Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Tue, 28 Sep 2021 11:35:01 -0700 Subject: [PATCH 133/198] make style --- linalg/amgxsolver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linalg/amgxsolver.cpp b/linalg/amgxsolver.cpp index 899e384134..4fbd1c4561 100644 --- a/linalg/amgxsolver.cpp +++ b/linalg/amgxsolver.cpp @@ -604,9 +604,9 @@ void AmgXSolver::SetMatrix(const HypreParMatrix &A, const bool update_mat) mfem_error("Hypre version 2.16+ is required when using AmgX \n"); #endif - //Ensure HypreParMatrix is on the host + //Ensure HypreParMatrix is on the host A.HostRead(); - + hypre_ParCSRMatrix * A_ptr = (hypre_ParCSRMatrix *)const_cast(A); From e424d758be6265b2399c2bf33ede17bf9b8d9a7d Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Tue, 28 Sep 2021 12:40:48 -0700 Subject: [PATCH 134/198] Remove factor of 0.5 in BR2 integrator --- fem/bilininteg_br2.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fem/bilininteg_br2.cpp b/fem/bilininteg_br2.cpp index c84263bd1b..9ebcc8675e 100644 --- a/fem/bilininteg_br2.cpp +++ b/fem/bilininteg_br2.cpp @@ -162,10 +162,6 @@ void DGDiffusionBR2Integrator::AssembleFaceMatrix( } double w = factor*sqrt(eta)*ip.weight*Trans.Face->Weight(); - if (ndof2) - { - w /= 2; - } for (int i = 0; i < ndof1; i++) { From 3829693441a71b5a5a45d167a761ed1f68fb3eff Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Tue, 28 Sep 2021 15:35:26 -0700 Subject: [PATCH 135/198] Better triangular mesh, added parallel sample runs. --- miniapps/meshing/mesh-optimizer.hpp | 2 +- miniapps/meshing/pmesh-optimizer.cpp | 4 + miniapps/meshing/square01_tri.mesh | 166 ++++++++++++++++++++------- 3 files changed, 129 insertions(+), 43 deletions(-) diff --git a/miniapps/meshing/mesh-optimizer.hpp b/miniapps/meshing/mesh-optimizer.hpp index cfe34d69b6..190f50426c 100644 --- a/miniapps/meshing/mesh-optimizer.hpp +++ b/miniapps/meshing/mesh-optimizer.hpp @@ -250,7 +250,7 @@ double surface_level_set(const Vector &x) { const double xc = x(0) - 0.5, yc = x(1) - 0.5; const double r = sqrt(xc*xc + yc*yc); - return std::tanh(2.0*(r-0.2)); + return std::tanh(2.0*(r-0.3)); } else { diff --git a/miniapps/meshing/pmesh-optimizer.cpp b/miniapps/meshing/pmesh-optimizer.cpp index bc259fde9d..dd5ea7d415 100644 --- a/miniapps/meshing/pmesh-optimizer.cpp +++ b/miniapps/meshing/pmesh-optimizer.cpp @@ -60,6 +60,10 @@ // Adaptive limiting through FD (requires GSLIB): // * mpirun -np 4 pmesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 0.5 -fd -ae 1 // +// Adaptive surface fitting: +// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 3 -rs 1 -mid 58 -tid 1 -ni 200 -vl 1 -sfc 1e5 -rtol 1e-5 -nor +// mpirun -np 4 pmesh-optimizer -m square01_tri.mesh -o 3 -rs 0 -mid 58 -tid 1 -ni 200 -vl 1 -sfc 1e4 -rtol 1e-5 -nor +// // Blade shape: // mpirun -np 4 pmesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -ni 30 -ls 3 -art 1 -bnd -qt 1 -qo 8 // Blade shape with FD-based solver: diff --git a/miniapps/meshing/square01_tri.mesh b/miniapps/meshing/square01_tri.mesh index 013a419ca8..a807803d45 100644 --- a/miniapps/meshing/square01_tri.mesh +++ b/miniapps/meshing/square01_tri.mesh @@ -9,57 +9,139 @@ MFEM mesh v1.0 # SQUARE = 3 # TETRAHEDRON = 4 # CUBE = 5 +# PRISM = 6 # dimension 2 elements -8 -1 2 0 1 4 -1 2 4 3 0 -1 2 1 2 5 -1 2 5 4 1 -1 2 3 4 7 -1 2 7 6 3 -1 2 4 5 8 -1 2 8 7 4 +64 +1 2 0 1 25 +1 2 1 6 25 +1 2 6 5 25 +1 2 5 0 25 +1 2 1 2 26 +1 2 2 7 26 +1 2 7 6 26 +1 2 6 1 26 +1 2 2 3 27 +1 2 3 8 27 +1 2 8 7 27 +1 2 7 2 27 +1 2 3 4 28 +1 2 4 9 28 +1 2 9 8 28 +1 2 8 3 28 +1 2 5 6 29 +1 2 6 11 29 +1 2 11 10 29 +1 2 10 5 29 +1 2 6 7 30 +1 2 7 12 30 +1 2 12 11 30 +1 2 11 6 30 +1 2 7 8 31 +1 2 8 13 31 +1 2 13 12 31 +1 2 12 7 31 +1 2 8 9 32 +1 2 9 14 32 +1 2 14 13 32 +1 2 13 8 32 +1 2 10 11 33 +1 2 11 16 33 +1 2 16 15 33 +1 2 15 10 33 +1 2 11 12 34 +1 2 12 17 34 +1 2 17 16 34 +1 2 16 11 34 +1 2 12 13 35 +1 2 13 18 35 +1 2 18 17 35 +1 2 17 12 35 +1 2 13 14 36 +1 2 14 19 36 +1 2 19 18 36 +1 2 18 13 36 +1 2 15 16 37 +1 2 16 21 37 +1 2 21 20 37 +1 2 20 15 37 +1 2 16 17 38 +1 2 17 22 38 +1 2 22 21 38 +1 2 21 16 38 +1 2 17 18 39 +1 2 18 23 39 +1 2 23 22 39 +1 2 22 17 39 +1 2 18 19 40 +1 2 19 24 40 +1 2 24 23 40 +1 2 23 18 40 boundary -8 +16 2 1 0 1 +1 1 5 0 2 1 1 2 -2 1 7 6 -2 1 8 7 -1 1 3 0 -1 1 6 3 -1 1 2 5 -1 1 5 8 +2 1 2 3 +2 1 3 4 +1 1 4 9 +1 1 10 5 +1 1 9 14 +1 1 15 10 +1 1 14 19 +2 1 21 20 +1 1 20 15 +2 1 22 21 +2 1 23 22 +1 1 19 24 +2 1 24 23 vertices -9 - -nodes -FiniteElementSpace -FiniteElementCollection: Linear -VDim: 2 -Ordering: 0 - -0 -0.5 -1 -0 -0.5 -1 -0 -0.5 -1 -0 -0 -0 -0.5 -0.5 -0.5 -1 -1 -1 +41 +2 +0.000000 0.000000 +0.250000 0.000000 +0.500000 0.000000 +0.750000 0.000000 +1.000000 0.000000 +0.000000 0.250000 +0.250000 0.250000 +0.500000 0.250000 +0.750000 0.250000 +1.000000 0.250000 +0.000000 0.500000 +0.250000 0.500000 +0.500000 0.500000 +0.750000 0.500000 +1.000000 0.500000 +0.000000 0.750000 +0.250000 0.750000 +0.500000 0.750000 +0.750000 0.750000 +1.000000 0.750000 +0.000000 1.000000 +0.250000 1.000000 +0.500000 1.000000 +0.750000 1.000000 +1.000000 1.000000 +0.125000 0.125000 +0.375000 0.125000 +0.625000 0.125000 +0.875000 0.125000 +0.125000 0.375000 +0.375000 0.375000 +0.625000 0.375000 +0.875000 0.375000 +0.125000 0.625000 +0.375000 0.625000 +0.625000 0.625000 +0.875000 0.625000 +0.125000 0.875000 +0.375000 0.875000 +0.625000 0.875000 +0.875000 0.875000 From 211a221c50d41aa9370e3e6d0003208c664ebd3d Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Tue, 28 Sep 2021 16:00:10 -0700 Subject: [PATCH 136/198] Fixed a merge issue. --- fem/tmop.hpp | 65 +++++++++++++++++----------------------------------- 1 file changed, 21 insertions(+), 44 deletions(-) diff --git a/fem/tmop.hpp b/fem/tmop.hpp index 797fb0b2f3..134fd1f405 100644 --- a/fem/tmop.hpp +++ b/fem/tmop.hpp @@ -592,6 +592,27 @@ public: virtual int Id() const { return 321; } }; +/// 3D barrier Shape+Size (VS) metric (polyconvex). +class TMOP_Metric_328 : public TMOP_Combo_QualityMetric +{ +protected: + mutable InvariantsEvaluator2D ie; + double gamma; + TMOP_QualityMetric *sh_metric, *sz_metric; + +public: + TMOP_Metric_328(double gamma_) : gamma(gamma_), + sh_metric(new TMOP_Metric_301), + sz_metric(new TMOP_Metric_316) + { + // (1-gamma) mu_301 + gamma mu_316 + AddQualityMetric(sh_metric, 1.-gamma_); + AddQualityMetric(sz_metric, gamma_); + } + + virtual ~TMOP_Metric_328() { delete sh_metric; delete sz_metric; } +}; + /// 3D barrier Shape+Size (VS) metric (polyconvex). class TMOP_Metric_332 : public TMOP_Combo_QualityMetric { @@ -615,50 +636,6 @@ public: virtual ~TMOP_Metric_332() { delete sh_metric; delete sz_metric; } }; -/// 3D barrier Shape+Size (VS) metric (polyconvex). -class TMOP_Metric_333 : public TMOP_Combo_QualityMetric -{ -protected: - double gamma; - TMOP_QualityMetric *sh_metric, *sz_metric; - -public: - TMOP_Metric_333(double gamma_) : gamma(gamma_), - sh_metric(new TMOP_Metric_302), - sz_metric(new TMOP_Metric_316) - { - // (1-gamma) mu_302 + gamma mu_316 - AddQualityMetric(sh_metric, 1.-gamma_); - AddQualityMetric(sz_metric, gamma_); - } - - virtual int Id() const { return 333; } - double GetGamma() const { return gamma; } - - virtual ~TMOP_Metric_333() { delete sh_metric; delete sz_metric; } -}; - -/// 3D barrier Shape+Size (VS) metric (polyconvex). -class TMOP_Metric_328 : public TMOP_Combo_QualityMetric -{ -protected: - mutable InvariantsEvaluator2D ie; - double gamma; - TMOP_QualityMetric *sh_metric, *sz_metric; - -public: - TMOP_Metric_328(double gamma_) : gamma(gamma_), - sh_metric(new TMOP_Metric_301), - sz_metric(new TMOP_Metric_316) - { - // (1-gamma) mu_301 + gamma mu_316 - AddQualityMetric(sh_metric, 1.-gamma_); - AddQualityMetric(sz_metric, gamma_); - } - - virtual ~TMOP_Metric_328() { delete sh_metric; delete sz_metric; } -}; - /// 3D barrier Shape+Size (VS) metric (polyconvex). class TMOP_Metric_333 : public TMOP_Combo_QualityMetric { From a1ba9f93c4d58ba5026fdc0e9e7bedc81e2b761c Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Tue, 28 Sep 2021 16:09:21 -0700 Subject: [PATCH 137/198] Reverted changes in pmesh.cpp. --- mesh/pmesh.cpp | 36 +++++------------------------------- 1 file changed, 5 insertions(+), 31 deletions(-) diff --git a/mesh/pmesh.cpp b/mesh/pmesh.cpp index ee8da6b95c..f19d6e92a2 100644 --- a/mesh/pmesh.cpp +++ b/mesh/pmesh.cpp @@ -4765,13 +4765,11 @@ static void dump_element(const Element* elem, Array &data) void ParMesh::PrintAsOne(std::ostream &out) const { - int i, j, k, p, nv_ne[3], &nv = nv_ne[0], &ne = nv_ne[1], &nc = nv_ne[2], vc; + int i, j, k, p, nv_ne[2], &nv = nv_ne[0], &ne = nv_ne[1], vc; const int *v; MPI_Status status; Array vert; Array ints; - Array attr_ne; - int attr; if (MyRank == 0) { @@ -4793,18 +4791,14 @@ void ParMesh::PrintAsOne(std::ostream &out) const } nv = NumOfElements; - nc = NumOfElements; MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm); - MPI_Allreduce(&nv, &nc, 1, MPI_INT, MPI_SUM, MyComm); if (MyRank == 0) { out << "\n\nelements\n" << ne << '\n'; for (i = 0; i < NumOfElements; i++) { - attr = elements[i]->GetAttribute(); // processor number + 1 as attribute and geometry type - //out << 1 << ' ' << elements[i]->GetGeometryType(); - out << attr << ' ' << elements[i]->GetGeometryType(); + out << 1 << ' ' << elements[i]->GetGeometryType(); // vertices nv = elements[i]->GetNVertices(); v = elements[i]->GetVertices(); @@ -4817,24 +4811,16 @@ void ParMesh::PrintAsOne(std::ostream &out) const vc = NumOfVertices; for (p = 1; p < NRanks; p++) { - MPI_Recv(nv_ne, 3, MPI_INT, p, 444, MyComm, &status); + MPI_Recv(nv_ne, 2, MPI_INT, p, 444, MyComm, &status); ints.SetSize(ne); - attr_ne.SetSize(nc); if (ne) { MPI_Recv(&ints[0], ne, MPI_INT, p, 445, MyComm, &status); } - if (nc) - { - MPI_Recv(&attr_ne[0], nc, MPI_INT, p, 446, MyComm, &status); - } - - int m = 0; for (i = 0; i < ne; ) { // processor number + 1 as attribute and geometry type - // out << p+1 << ' ' << ints[i]; - out << attr_ne[m] << ' ' << ints[i]; + out << p+1 << ' ' << ints[i]; // vertices k = Geometries.GetVertices(ints[i++])->GetNPoints(); for (j = 0; j < k; j++) @@ -4842,7 +4828,6 @@ void ParMesh::PrintAsOne(std::ostream &out) const out << ' ' << vc + ints[i++]; } out << '\n'; - m++; } vc += nv; } @@ -4856,7 +4841,7 @@ void ParMesh::PrintAsOne(std::ostream &out) const ne += 1 + elements[i]->GetNVertices(); } nv = NumOfVertices; - MPI_Send(nv_ne, 3, MPI_INT, 0, 444, MyComm); + MPI_Send(nv_ne, 2, MPI_INT, 0, 444, MyComm); ints.Reserve(ne); ints.SetSize(0); @@ -4869,17 +4854,6 @@ void ParMesh::PrintAsOne(std::ostream &out) const { MPI_Send(&ints[0], ne, MPI_INT, 0, 445, MyComm); } - - attr_ne.SetSize(nc); - for (i = 0; i < NumOfElements; i++) - { - attr_ne[i] = elements[i]->GetAttribute(); - } - MFEM_ASSERT(attr_ne.Size() == nc, ""); - if (nc) - { - MPI_Send(&attr_ne[0], nc, MPI_INT, 0, 446, MyComm); - } } // boundary + shared boundary From 76617db79e9c8c81626c717c65c9db72f44e5616 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Tue, 28 Sep 2021 17:24:23 -0700 Subject: [PATCH 138/198] Serial miniapp and some comments. --- fem/tmop.cpp | 26 +++++- fem/tmop.hpp | 20 ++++- miniapps/meshing/mesh-optimizer.cpp | 117 ++++++++++++++++++++++++--- miniapps/meshing/pmesh-optimizer.cpp | 6 +- 4 files changed, 153 insertions(+), 16 deletions(-) diff --git a/fem/tmop.cpp b/fem/tmop.cpp index 2b6efc2b8d..d9a77e3959 100644 --- a/fem/tmop.cpp +++ b/fem/tmop.cpp @@ -2395,6 +2395,29 @@ void TMOP_Integrator::EnableAdaptiveLimiting(const ParGridFunction &z0, } #endif +void TMOP_Integrator::EnableSurfaceFitting(const GridFunction &s0, + const Array &smarker, + Coefficient &coeff, + AdaptivityEvaluator &ae) +{ + sigma = new GridFunction(s0); + sigma_marker = &smarker; + coeff_sigma = &coeff; + sigma_eval = &ae; + + // Compute the restricted sigma. + sigma_bar = new GridFunction(*sigma); + for (int i = 0; i < sigma_marker->Size(); i++) + { + if ((*sigma_marker)[i] == false) { (*sigma_bar)(i) = 0.0; } + } + + sigma_eval->SetSerialMetaInfo(*s0.FESpace()->GetMesh(), + *s0.FESpace()->FEColl(), 1); + sigma_eval->SetInitialField + (*sigma->FESpace()->GetMesh()->GetNodes(), *sigma); +} + #ifdef MFEM_USE_MPI void TMOP_Integrator::EnableSurfaceFitting(const ParGridFunction &s0, const Array &smarker, @@ -3455,7 +3478,8 @@ void TMOP_Integrator::EnableNormalization(const GridFunction &x) ComputeNormalizationEnergies(x, metric_normal, lim_normal, sigma_normal); metric_normal = 1.0 / metric_normal; lim_normal = 1.0 / lim_normal; - if (sigma) { sigma_normal = 1.0 / sigma_normal; } + //if (sigma) { sigma_normal = 1.0 / sigma_normal; } + if (sigma) { sigma_normal = lim_normal; } } #ifdef MFEM_USE_MPI diff --git a/fem/tmop.hpp b/fem/tmop.hpp index 134fd1f405..31975dd7d9 100644 --- a/fem/tmop.hpp +++ b/fem/tmop.hpp @@ -1585,7 +1585,7 @@ public: Adds the term @f$ \int c (z(x) - z_0(x_0))^2 @f$, where z0(x0) is a given function on the starting mesh, and z(x) is its image on the new mesh. - Minimizing this, means that a node at x0 is allowed to move to a + Minimizing this term means that a node at x0 is allowed to move to a position x(x0) only if z(x) ~ z0(x0). Such term can be used for tangential mesh relaxation. @@ -1600,6 +1600,24 @@ public: AdaptivityEvaluator &ae); #endif + /** @brief Fitting of certain DOFs to the zero level set of a function. + + Having a level set function s0(x0) on the starting mesh, and a set of + marked nodes (or DOFs), we move these nodes to the zero level set of s0. + If s(x) is the image of s0(x0) on the current mesh, this function adds to + the TMOP functional the term @f$ \int c \bar{s}(x))^2 @f$, where + @f$\bar{s}(x)@f$ is the restriction of s(x) on the aligned DOFs. + Minimizing this term means that a marked node at x0 is allowed to move to + a position x(x0) only if s(x) ~ 0. + Such term can be used for surface fitting and tangential relaxation. + + @param[in] s0 The level set function on the initial mesh. + @param[in] smarker Indicates which DOFs will be aligned. + @param[in] coeff Coefficient c for the above integral. + @param[in] ae AdaptivityEvaluator to compute s(x) from s0(x0). */ + void EnableSurfaceFitting(const GridFunction &s0, + const Array &smarker, Coefficient &coeff, + AdaptivityEvaluator &ae); #ifdef MFEM_USE_MPI void EnableSurfaceFitting(const ParGridFunction &s0, const Array &smarker, Coefficient &coeff, diff --git a/miniapps/meshing/mesh-optimizer.cpp b/miniapps/meshing/mesh-optimizer.cpp index 94780d185e..cfef71e853 100644 --- a/miniapps/meshing/mesh-optimizer.cpp +++ b/miniapps/meshing/mesh-optimizer.cpp @@ -69,6 +69,10 @@ // Adaptive limiting through FD (requires GSLIB): // * mesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 0.5 -fd -ae 1 // +// Adaptive surface fitting: +// mesh-optimizer -m square01.mesh -o 3 -rs 1 -mid 58 -tid 1 -ni 200 -vl 1 -sfc 5e4 -rtol 1e-5 -nor +// mesh-optimizer -m square01_tri.mesh -o 3 -rs 0 -mid 58 -tid 1 -ni 200 -vl 1 -sfc 1e4 -rtol 1e-5 -nor +// // Blade shape: // mesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -ni 30 -ls 3 -art 1 -bnd -qt 1 -qo 8 // Blade shape with FD-based solver: @@ -117,7 +121,8 @@ int main(int argc, char *argv[]) int metric_id = 1; int target_id = 1; double lim_const = 0.0; - double adapt_lim_const = 0.0; + double adapt_lim_const = 0.0; + double surface_fit_const = 0.0; int quad_type = 1; int quad_order = 8; int solver_type = 0; @@ -195,6 +200,8 @@ int main(int argc, char *argv[]) args.AddOption(&lim_const, "-lc", "--limit-const", "Limiting constant."); args.AddOption(&adapt_lim_const, "-alc", "--adapt-limit-const", "Adaptive limiting coefficient constant."); + args.AddOption(&surface_fit_const, "-sfc", "--surface-fit-const", + "Surface preservation constant."); args.AddOption(&quad_type, "-qt", "--quad-type", "Quadrature rule type:\n\t" "1: Gauss-Lobatto\n\t" @@ -722,8 +729,6 @@ int main(int argc, char *argv[]) << irules->Get(Geometry::PRISM, quad_order).GetNPoints() << endl; } - if (normalization) { he_nlf_integ->EnableNormalization(x0); } - // Limit the node movement. // The limiting distances can be given by a general function of space. FiniteElementSpace dist_fespace(mesh, fec); // scalar space @@ -765,6 +770,72 @@ int main(int argc, char *argv[]) } } + // Surface fitting. + L2_FECollection mat_coll(0, dim); + H1_FECollection sigma_fec(mesh_poly_deg, dim); + FiniteElementSpace sigma_fes(mesh, &sigma_fec); + FiniteElementSpace mat_fes(mesh, &mat_coll); + GridFunction mat(&mat_fes); + GridFunction marker_gf(&sigma_fes); + GridFunction ls_0(&sigma_fes); + Array marker(ls_0.Size()); + ConstantCoefficient coef_ls(surface_fit_const); + AdaptivityEvaluator *adapt_surface = NULL; + if (surface_fit_const > 0.0) + { + FunctionCoefficient ls_coeff(surface_level_set); + ls_0.ProjectCoefficient(ls_coeff); + + for (int i = 0; i < mesh->GetNE(); i++) + { + mat(i) = material_id(i, ls_0); + mesh->SetAttribute(i, mat(i) + 1); + } + + GridFunctionCoefficient coeff_mat(&mat); + marker_gf.ProjectDiscCoefficient(coeff_mat, GridFunction::ARITHMETIC); + for (int j = 0; j < marker.Size(); j++) + { + if (marker_gf(j) > 0.1 && marker_gf(j) < 0.9) + { + marker[j] = true; + marker_gf(j) = 1.0; + } + else + { + marker[j] = false; + marker_gf(j) = 0.0; + } + } + + if (adapt_eval == 0) { adapt_surface = new AdvectorCG; } + else if (adapt_eval == 1) + { +#ifdef MFEM_USE_GSLIB + adapt_surface = new InterpolatorFP; +#else + MFEM_ABORT("MFEM is not built with GSLIB support!"); +#endif + } + else { MFEM_ABORT("Bad interpolation option."); } + + he_nlf_integ->EnableSurfaceFitting(ls_0, marker, coef_ls, *adapt_surface); + if (visualization) + { + socketstream vis1, vis2, vis3; + common::VisualizeField(vis1, "localhost", 19916, ls_0, "Level Set 0", + 300, 600, 300, 300); + common::VisualizeField(vis2, "localhost", 19916, mat, "Materials", + 600, 600, 300, 300); + common::VisualizeField(vis3, "localhost", 19916, marker_gf, "Dofs to Move", + 900, 600, 300, 300); + } + } + + // Has to be after the enabling of the limiting / alignment, as it computes + // normalization factors for these terms as well. + if (normalization) { he_nlf_integ->EnableNormalization(x0); } + // 12. Setup the final NonlinearForm (which defines the integral of interest, // its first and second derivatives). Here we can use a combination of // metrics, i.e., optimize the sum of two integrals, where both are @@ -855,6 +926,18 @@ int main(int argc, char *argv[]) // For HR tests, the energy is normalized by the number of elements. const double init_energy = a.GetGridFunctionEnergy(x) / (hradaptivity ? mesh->GetNE() : 1); + double init_metric_energy = init_energy; + if (lim_const > 0.0 || adapt_lim_const > 0.0 || surface_fit_const > 0.0) + { + lim_coeff.constant = 0.0; + coef_zeta.constant = 0.0; + coef_ls.constant = 0.0; + init_metric_energy = a.GetGridFunctionEnergy(x) / + (hradaptivity ? mesh->GetNE() : 1); + lim_coeff.constant = lim_const; + coef_zeta.constant = adapt_lim_const; + coef_ls.constant = surface_fit_const; + } // Visualize the starting mesh and metric values. // Note that for combinations of metrics, this only shows the first metric. @@ -1010,25 +1093,37 @@ int main(int argc, char *argv[]) mesh->Print(mesh_ofs); } + if (visualization && surface_fit_const > 0.0) + { + socketstream vis2, vis3; + common::VisualizeField(vis2, "localhost", 19916, mat, "Materials", + 600, 900, 300, 300); + common::VisualizeField(vis3, "localhost", 19916, marker_gf, "Surface dof", + 900, 900, 300, 300); + } + const double fin_energy = a.GetGridFunctionEnergy(x) / (hradaptivity ? mesh->GetNE() : 1); - double metric_part = fin_energy; + double fin_metric_energy = fin_energy; if (lim_const > 0.0 || adapt_lim_const > 0.0) { lim_coeff.constant = 0.0; coef_zeta.constant = 0.0; - metric_part = a.GetGridFunctionEnergy(x) / - (hradaptivity ? mesh->GetNE() : 1); + coef_ls.constant = 0.0; + fin_metric_energy = a.GetGridFunctionEnergy(x) / + (hradaptivity ? mesh->GetNE() : 1); lim_coeff.constant = lim_const; coef_zeta.constant = adapt_lim_const; + coef_ls.constant = surface_fit_const; } + std::cout << std::scientific << std::setprecision(4); cout << "Initial strain energy: " << init_energy - << " = metrics: " << init_energy - << " + limiting term: " << 0.0 << endl; + << " = metrics: " << init_metric_energy + << " + extra terms: " << init_energy - init_metric_energy << endl; cout << " Final strain energy: " << fin_energy - << " = metrics: " << metric_part - << " + limiting term: " << fin_energy - metric_part << endl; - cout << "The strain energy decreased by: " << setprecision(12) + << " = metrics: " << fin_metric_energy + << " + extra terms: " << fin_energy - fin_metric_energy << endl; + cout << "The strain energy decreased by: " << (init_energy - fin_energy) * 100.0 / init_energy << " %." << endl; // 16. Visualize the final mesh and metric values. diff --git a/miniapps/meshing/pmesh-optimizer.cpp b/miniapps/meshing/pmesh-optimizer.cpp index af7996707c..ae53e5fbe0 100644 --- a/miniapps/meshing/pmesh-optimizer.cpp +++ b/miniapps/meshing/pmesh-optimizer.cpp @@ -70,7 +70,7 @@ // * mpirun -np 4 pmesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 0.5 -fd -ae 1 // // Adaptive surface fitting: -// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 3 -rs 1 -mid 58 -tid 1 -ni 200 -vl 1 -sfc 1e5 -rtol 1e-5 -nor +// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 3 -rs 1 -mid 58 -tid 1 -ni 200 -vl 1 -sfc 5e4 -rtol 1e-5 -nor // mpirun -np 4 pmesh-optimizer -m square01_tri.mesh -o 3 -rs 0 -mid 58 -tid 1 -ni 200 -vl 1 -sfc 1e4 -rtol 1e-5 -nor // // Blade shape: @@ -128,8 +128,8 @@ int main (int argc, char *argv[]) int metric_id = 1; int target_id = 1; double lim_const = 0.0; - double adapt_lim_const = 0.0; - double surface_fit_const = 0.0; + double adapt_lim_const = 0.0; + double surface_fit_const = 0.0; int quad_type = 1; int quad_order = 8; int solver_type = 0; From e7a7bfe3fbbaeb1875ff12505e8012e42f956a3b Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Tue, 28 Sep 2021 18:11:45 -0700 Subject: [PATCH 139/198] Removed unused function. Better computation and output of fitting errors. --- fem/tmop.cpp | 12 +++-- fem/tmop.hpp | 11 +++-- miniapps/meshing/mesh-optimizer.cpp | 25 +++++++---- miniapps/meshing/mesh-optimizer.hpp | 67 ---------------------------- miniapps/meshing/pmesh-optimizer.cpp | 28 ++++++++---- 5 files changed, 52 insertions(+), 91 deletions(-) diff --git a/fem/tmop.cpp b/fem/tmop.cpp index d9a77e3959..c90e03635a 100644 --- a/fem/tmop.cpp +++ b/fem/tmop.cpp @@ -2458,12 +2458,18 @@ void TMOP_Integrator::GetSurfaceFittingErrors(double &err_avg, double &err_max) loc_sum += std::abs((*sigma_bar)(i)); } } + err_avg = loc_sum / loc_cnt; + err_max = loc_max; +#ifdef MFEM_USE_MPI + if (targetC->Parallel() == false) { return; } int glob_cnt; - MPI_Allreduce(&loc_max, &err_max, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); - MPI_Allreduce(&loc_cnt, &glob_cnt, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(&loc_sum, &err_avg, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Comm comm = targetC->GetComm(); + MPI_Allreduce(&loc_max, &err_max, 1, MPI_DOUBLE, MPI_MAX, comm); + MPI_Allreduce(&loc_cnt, &glob_cnt, 1, MPI_INT, MPI_SUM, comm); + MPI_Allreduce(&loc_sum, &err_avg, 1, MPI_DOUBLE, MPI_SUM, comm); err_avg = err_avg / glob_cnt; +#endif } void TMOP_Integrator::UpdateAfterMeshTopologyChange() diff --git a/fem/tmop.hpp b/fem/tmop.hpp index 31975dd7d9..feab3f7fb1 100644 --- a/fem/tmop.hpp +++ b/fem/tmop.hpp @@ -937,9 +937,6 @@ protected: #ifdef MFEM_USE_MPI MPI_Comm comm; - bool Parallel() const { return (comm != MPI_COMM_NULL); } -#else - bool Parallel() const { return false; } #endif // should be called only if avg_volume == 0.0, i.e. avg_volume is not @@ -976,6 +973,13 @@ public: #endif virtual ~TargetConstructor() { } +#ifdef MFEM_USE_MPI + bool Parallel() const { return (comm != MPI_COMM_NULL); } + MPI_Comm GetComm() const { return comm; } +#else + bool Parallel() const { return false; } +#endif + /** @brief Set the nodes to be used in the target-matrix construction. This method should be called every time the target nodes are updated @@ -1619,6 +1623,7 @@ public: const Array &smarker, Coefficient &coeff, AdaptivityEvaluator &ae); #ifdef MFEM_USE_MPI + /// Parallel support for surface fitting. void EnableSurfaceFitting(const ParGridFunction &s0, const Array &smarker, Coefficient &coeff, AdaptivityEvaluator &ae); diff --git a/miniapps/meshing/mesh-optimizer.cpp b/miniapps/meshing/mesh-optimizer.cpp index cfef71e853..7db023be07 100644 --- a/miniapps/meshing/mesh-optimizer.cpp +++ b/miniapps/meshing/mesh-optimizer.cpp @@ -1093,15 +1093,6 @@ int main(int argc, char *argv[]) mesh->Print(mesh_ofs); } - if (visualization && surface_fit_const > 0.0) - { - socketstream vis2, vis3; - common::VisualizeField(vis2, "localhost", 19916, mat, "Materials", - 600, 900, 300, 300); - common::VisualizeField(vis3, "localhost", 19916, marker_gf, "Surface dof", - 900, 900, 300, 300); - } - const double fin_energy = a.GetGridFunctionEnergy(x) / (hradaptivity ? mesh->GetNE() : 1); double fin_metric_energy = fin_energy; @@ -1140,6 +1131,22 @@ int main(int argc, char *argv[]) 600, 600, 300, 300); } + if (surface_fit_const > 0.0) + { + if (visualization) + { + socketstream vis2, vis3; + common::VisualizeField(vis2, "localhost", 19916, mat, "Materials", + 600, 900, 300, 300); + common::VisualizeField(vis3, "localhost", 19916, marker_gf, "Surface dof", + 900, 900, 300, 300); + } + double err_avg, err_max; + he_nlf_integ->GetSurfaceFittingErrors(err_avg, err_max); + std::cout << "Avg fitting error: " << err_avg << std::endl + << "Max fitting error: " << err_max << std::endl; + } + // 17. Visualize the mesh displacement. if (visualization) { diff --git a/miniapps/meshing/mesh-optimizer.hpp b/miniapps/meshing/mesh-optimizer.hpp index ca2cac8c65..e930dacbbc 100644 --- a/miniapps/meshing/mesh-optimizer.hpp +++ b/miniapps/meshing/mesh-optimizer.hpp @@ -470,70 +470,3 @@ void DiffuseField(ParGridFunction &field, int smooth_steps) delete Lap; } #endif - -void DiffuseField2(ParGridFunction &field, double coeff) -{ - ParFiniteElementSpace &pfes = *field.ParFESpace(); - - // Compute average mesh size (assumes similar cells). - double loc_area = 0.0, dx; - ParMesh &pmesh = *pfes.GetParMesh(); - for (int i = 0; i < pmesh.GetNE(); i++) - { - loc_area += pmesh.GetElementVolume(i); - } - double glob_area; - MPI_Allreduce(&loc_area, &glob_area, 1, MPI_DOUBLE, - MPI_SUM, pfes.GetComm()); - - const int glob_zones = pmesh.GetGlobalNE(); - switch (pmesh.GetElementBaseGeometry(0)) - { - case Geometry::SEGMENT: - dx = glob_area / glob_zones; break; - case Geometry::SQUARE: - dx = sqrt(glob_area / glob_zones); break; - case Geometry::TRIANGLE: - dx = sqrt(2.0 * glob_area / glob_zones); break; - case Geometry::CUBE: - dx = pow(glob_area / glob_zones, 1.0/3.0); break; - case Geometry::TETRAHEDRON: - dx = pow(6.0 * glob_area / glob_zones, 1.0/3.0); break; - default: MFEM_ABORT("Unknown zone type!"); - } - dx /= pfes.GetOrder(0); - - // Set up RHS. - ParLinearForm b(&pfes); - GridFunctionCoefficient src_coeff(&field); - b.AddDomainIntegrator(new DomainLFIntegrator(src_coeff)); - b.Assemble(); - - // Diffusion and mass terms in the LHS. - ParBilinearForm a(&pfes); - a.AddDomainIntegrator(new MassIntegrator); - ConstantCoefficient diffuse_coeff(coeff * dx * dx); - a.AddDomainIntegrator(new DiffusionIntegrator(diffuse_coeff)); - a.Assemble(); - - // Solve with Neumann BC. - ParGridFunction u_neumann(&pfes); - Array ess_tdof_list; - ess_tdof_list.DeleteAll(); - // Solver. - CGSolver cg(MPI_COMM_WORLD); - cg.SetRelTol(1e-12); - cg.SetMaxIter(100); - cg.SetPrintLevel(1); - OperatorPtr A; - Vector B, X; - a.FormLinearSystem(ess_tdof_list, u_neumann, b, A, X, B); - Solver *prec = new HypreBoomerAMG; - cg.SetPreconditioner(*prec); - cg.SetOperator(*A); - cg.Mult(B, X); - a.RecoverFEMSolution(X, b, u_neumann); - delete prec; - - field = u_neumann; -} diff --git a/miniapps/meshing/pmesh-optimizer.cpp b/miniapps/meshing/pmesh-optimizer.cpp index ae53e5fbe0..1a7d7deff8 100644 --- a/miniapps/meshing/pmesh-optimizer.cpp +++ b/miniapps/meshing/pmesh-optimizer.cpp @@ -1142,15 +1142,6 @@ int main (int argc, char *argv[]) pmesh->PrintAsOne(mesh_ofs); } - if (visualization && surface_fit_const > 0.0) - { - socketstream vis2, vis3; - common::VisualizeField(vis2, "localhost", 19916, mat, "Materials", - 600, 900, 300, 300); - common::VisualizeField(vis3, "localhost", 19916, marker_gf, "Surface dof", - 900, 900, 300, 300); - } - // Compute the final energy of the functional. const double fin_energy = a.GetParGridFunctionEnergy(x) / (hradaptivity ? pmesh->GetGlobalNE() : 1); @@ -1193,6 +1184,25 @@ int main (int argc, char *argv[]) 600, 600, 300, 300); } + if (surface_fit_const > 0.0) + { + if (visualization) + { + socketstream vis2, vis3; + common::VisualizeField(vis2, "localhost", 19916, mat, + "Materials", 600, 900, 300, 300); + common::VisualizeField(vis3, "localhost", 19916, marker_gf, + "Surface dof", 900, 900, 300, 300); + } + double err_avg, err_max; + he_nlf_integ->GetSurfaceFittingErrors(err_avg, err_max); + if (myid == 0) + { + std::cout << "Avg fitting error: " << err_avg << std::endl + << "Max fitting error: " << err_max << std::endl; + } + } + // 19. Visualize the mesh displacement. if (visualization) { From bb085f9866119703af182ad8f205d741b308facf Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Tue, 28 Sep 2021 22:24:40 -0700 Subject: [PATCH 140/198] Aborts for unsupported setups, mem leak. --- miniapps/meshing/mesh-optimizer.cpp | 6 ++++++ miniapps/meshing/pmesh-optimizer.cpp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/miniapps/meshing/mesh-optimizer.cpp b/miniapps/meshing/mesh-optimizer.cpp index 7db023be07..546695e5c6 100644 --- a/miniapps/meshing/mesh-optimizer.cpp +++ b/miniapps/meshing/mesh-optimizer.cpp @@ -783,6 +783,11 @@ int main(int argc, char *argv[]) AdaptivityEvaluator *adapt_surface = NULL; if (surface_fit_const > 0.0) { + MFEM_VERIFY(hradaptivity == false, + "Surface fitting with HR is not implemented yet."); + MFEM_VERIFY(pa == false, + "Surface fitting with PA is not implemented yet."); + FunctionCoefficient ls_coeff(surface_level_set); ls_0.ProjectCoefficient(ls_coeff); @@ -1168,6 +1173,7 @@ int main(int argc, char *argv[]) delete metric2; delete coeff1; delete adapt_evaluator; + delete adapt_surface; delete target_c; delete hr_adapt_coeff; delete adapt_coeff; diff --git a/miniapps/meshing/pmesh-optimizer.cpp b/miniapps/meshing/pmesh-optimizer.cpp index 1a7d7deff8..93ca2df38b 100644 --- a/miniapps/meshing/pmesh-optimizer.cpp +++ b/miniapps/meshing/pmesh-optimizer.cpp @@ -821,6 +821,11 @@ int main (int argc, char *argv[]) AdaptivityEvaluator *adapt_surface = NULL; if (surface_fit_const > 0.0) { + MFEM_VERIFY(hradaptivity == false, + "Surface fitting with HR is not implemented yet."); + MFEM_VERIFY(pa == false, + "Surface fitting with PA is not implemented yet."); + FunctionCoefficient ls_coeff(surface_level_set); ls_0.ProjectCoefficient(ls_coeff); @@ -1231,6 +1236,7 @@ int main (int argc, char *argv[]) delete metric2; delete coeff1; delete adapt_evaluator; + delete adapt_surface; delete target_c; delete hr_adapt_coeff; delete adapt_coeff; From 1e5602f139887940817172380af08e16a4b17815 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Tue, 28 Sep 2021 22:41:50 -0700 Subject: [PATCH 141/198] Removed the `mesh curvature` output, as it doesn't take into account the user-selected mesh order. --- miniapps/meshing/mesh-optimizer.cpp | 4 ---- miniapps/meshing/pmesh-optimizer.cpp | 7 ------- 2 files changed, 11 deletions(-) diff --git a/miniapps/meshing/mesh-optimizer.cpp b/miniapps/meshing/mesh-optimizer.cpp index 546695e5c6..03505a6bb3 100644 --- a/miniapps/meshing/mesh-optimizer.cpp +++ b/miniapps/meshing/mesh-optimizer.cpp @@ -290,10 +290,6 @@ int main(int argc, char *argv[]) Mesh *mesh = new Mesh(mesh_file, 1, 1, false); for (int lev = 0; lev < rs_levels; lev++) { mesh->UniformRefinement(); } const int dim = mesh->Dimension(); - cout << "Mesh curvature: "; - if (mesh->GetNodes()) { cout << mesh->GetNodes()->OwnFEC()->Name(); } - else { cout << "(NONE)"; } - cout << endl; if (hradaptivity) { mesh->EnsureNCMesh(); } diff --git a/miniapps/meshing/pmesh-optimizer.cpp b/miniapps/meshing/pmesh-optimizer.cpp index 93ca2df38b..83d306259e 100644 --- a/miniapps/meshing/pmesh-optimizer.cpp +++ b/miniapps/meshing/pmesh-optimizer.cpp @@ -301,13 +301,6 @@ int main (int argc, char *argv[]) mesh->UniformRefinement(); } const int dim = mesh->Dimension(); - if (myid == 0) - { - cout << "Mesh curvature: "; - if (mesh->GetNodes()) { cout << mesh->GetNodes()->OwnFEC()->Name(); } - else { cout << "(NONE)"; } - cout << endl; - } if (hradaptivity) { mesh->EnsureNCMesh(); } ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh); From 3f78a06411c2ca750f1990fc9616616752aab05f Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Wed, 29 Sep 2021 09:44:32 -0700 Subject: [PATCH 142/198] add hypre read call --- linalg/amgxsolver.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linalg/amgxsolver.cpp b/linalg/amgxsolver.cpp index 4fbd1c4561..cc3550153b 100644 --- a/linalg/amgxsolver.cpp +++ b/linalg/amgxsolver.cpp @@ -612,6 +612,8 @@ void AmgXSolver::SetMatrix(const HypreParMatrix &A, const bool update_mat) hypre_CSRMatrix *A_csr = hypre_MergeDiagAndOffd(A_ptr); + A.HypreRead(); + Array loc_A(A_csr->data, (int)A_csr->num_nonzeros); const Array loc_I(A_csr->i, (int)A_csr->num_rows+1); From 98be69f851a35cf8aeb1bab281dab714dcce9176 Mon Sep 17 00:00:00 2001 From: "Stowell, Mark L" Date: Wed, 29 Sep 2021 09:53:11 -0700 Subject: [PATCH 143/198] Adding MatrixFunctionCoefficient::SetTime thanks to @vladotomov --- fem/coefficient.cpp | 6 ++++++ fem/coefficient.hpp | 3 +++ 2 files changed, 9 insertions(+) diff --git a/fem/coefficient.cpp b/fem/coefficient.cpp index 90d634ff4d..a76faaa938 100644 --- a/fem/coefficient.cpp +++ b/fem/coefficient.cpp @@ -331,6 +331,12 @@ void VectorRestrictedCoefficient::Eval( } } +void MatrixFunctionCoefficient::SetTime(double t) +{ + if (Q) { Q->SetTime(t); } + this->MatrixCoefficient::SetTime(t); +} + void MatrixFunctionCoefficient::Eval(DenseMatrix &K, ElementTransformation &T, const IntegrationPoint &ip) { diff --git a/fem/coefficient.hpp b/fem/coefficient.hpp index acbe9bb1c2..acc402fa71 100644 --- a/fem/coefficient.hpp +++ b/fem/coefficient.hpp @@ -835,6 +835,9 @@ public: : MatrixCoefficient(dim), TDFunction(std::move(TDF)), Q(q) { } + /// Set the time for internally stored coefficients + void SetTime(double t); + /// Evaluate the matrix coefficient at @a ip. virtual void Eval(DenseMatrix &K, ElementTransformation &T, const IntegrationPoint &ip); From 8880e17cd56b2b09d730195093afc1871b7fe335 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Wed, 29 Sep 2021 12:48:39 -0700 Subject: [PATCH 144/198] Unit test. --- examples/tmp.cpp | 107 -------------------------- fem/pgridfunc.cpp | 4 +- tests/unit/CMakeLists.txt | 1 + tests/unit/fem/test_getderivative.cpp | 76 ++++++++++++++++++ 4 files changed, 79 insertions(+), 109 deletions(-) delete mode 100644 examples/tmp.cpp create mode 100644 tests/unit/fem/test_getderivative.cpp diff --git a/examples/tmp.cpp b/examples/tmp.cpp deleted file mode 100644 index 9afa02e5fc..0000000000 --- a/examples/tmp.cpp +++ /dev/null @@ -1,107 +0,0 @@ - -#include "mfem.hpp" - -using namespace std; -using namespace mfem; - -int main(int argc, char *argv[]) -{ - // 1. Initialize MPI. - MPI_Session mpi; - int num_procs = mpi.WorldSize(); - int myid = mpi.WorldRank(); - - // 2. Parse command-line options. - const char *mesh_file = "../data/star.mesh"; - int order = 1; - bool visualization = true; - - OptionsParser args(argc, argv); - args.AddOption(&mesh_file, "-m", "--mesh", - "Mesh file to use."); - args.AddOption(&order, "-o", "--order", - "Finite element order (polynomial degree) or -1 for" - " isoparametric space."); - args.AddOption(&visualization, "-vis", "--visualization", "-no-vis", - "--no-visualization", - "Enable or disable GLVis visualization."); - args.Parse(); - if (!args.Good()) - { - if (myid == 0) - { - args.PrintUsage(cout); - } - return 1; - } - if (myid == 0) - { - args.PrintOptions(cout); - } - - // 4. Read the (serial) mesh from the given mesh file on all processors. We - // can handle triangular, quadrilateral, tetrahedral, hexahedral, surface - // and volume meshes with the same code. - Mesh mesh(mesh_file, 1, 1); - int dim = mesh.Dimension(); - - // 5. Refine the serial mesh on all processors to increase the resolution. In - // this example we do 'ref_levels' of uniform refinement. We choose - // 'ref_levels' to be the largest number that gives a final mesh with no - // more than 10,000 elements. - { - int ref_levels = - (int)floor(log(10000./mesh.GetNE())/log(2.)/dim); - for (int l = 0; l < ref_levels; l++) - { - mesh.UniformRefinement(); - } - } - - // 6. Define a parallel mesh by a partitioning of the serial mesh. Refine - // this mesh further in parallel to increase the resolution. Once the - // parallel mesh is defined, the serial mesh can be deleted. - ParMesh pmesh(MPI_COMM_WORLD, mesh); - mesh.Clear(); - { - int par_ref_levels = 2; - for (int l = 0; l < par_ref_levels; l++) - { - pmesh.UniformRefinement(); - } - } - - // 7. Define a parallel finite element space on the parallel mesh. Here we - // use continuous Lagrange finite elements of the specified order. If - // order < 1, we instead use an isoparametric/isogeometric space. - H1_FECollection fec(order, dim); - ParFiniteElementSpace fespace(&pmesh, &fec); - - // 10. Define the solution vector x as a parallel finite element grid - // function corresponding to fespace. Initialize x with initial guess of - // zero, which satisfies the boundary conditions. - ParGridFunction x(&fespace); - - auto func = [&](Vector coord) { return std::sin(coord(0)*coord(1)); }; - FunctionCoefficient x_coeff(func); - - x.ProjectCoefficient(x_coeff); - ParGridFunction grad_x(&fespace); - ConstantCoefficient zero(0.0); - for (int i = 0; i < dim; i++) - { - x.GetDerivative(1, i, grad_x); - //x.GridFunction::GetDerivative(1, i, grad_x); // old behavior. - double error = grad_x.ComputeL2Error(zero); - if (myid == 0) { cout << setprecision(14) << error << endl; } - } - - char vishost[] = "localhost"; - int visport = 19916; - socketstream sol_sock(vishost, visport); - sol_sock << "parallel " << num_procs << " " << myid << "\n"; - sol_sock.precision(8); - sol_sock << "solution\n" << pmesh << x << flush; - - return 0; -} diff --git a/fem/pgridfunc.cpp b/fem/pgridfunc.cpp index 2f49a4a3eb..a82572a705 100644 --- a/fem/pgridfunc.cpp +++ b/fem/pgridfunc.cpp @@ -493,8 +493,8 @@ void ParGridFunction::GetDerivative(int comp, int der_comp, gcomm.Bcast(overlap); // Accumulate for all dofs. - gcomm.Reduce(der.GetData(), GroupCommunicator::Sum); - gcomm.Bcast(der.GetData()); + gcomm.Reduce(der.GetMemory(), GroupCommunicator::Sum); + gcomm.Bcast(der.GetMemory()); for (int i = 0; i < overlap.Size(); i++) { diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 7fb3e36e1c..fd4e7f5ec1 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -61,6 +61,7 @@ set(UNIT_TESTS_SRCS fem/test_face_permutation.cpp fem/test_fe.cpp fem/test_get_value.cpp + fem/test_getderivative.cpp fem/test_intrules.cpp fem/test_intruletypes.cpp fem/test_inversetransform.cpp diff --git a/tests/unit/fem/test_getderivative.cpp b/tests/unit/fem/test_getderivative.cpp new file mode 100644 index 0000000000..90685b498d --- /dev/null +++ b/tests/unit/fem/test_getderivative.cpp @@ -0,0 +1,76 @@ +// Copyright (c) 2010-2021, 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 "unit_tests.hpp" +#include "mfem.hpp" + +using namespace mfem; + +double func(const Vector &coord) +{ + if (coord.Size() == 1) { return std::sin(coord(0)); } + if (coord.Size() == 2) { return std::sin(coord(0)*coord(1)); } + return std::sin(coord(0)*coord(1)*coord(2)); +} + +#ifdef MFEM_USE_MPI + +// Compares serial vs parallel result of GetDerivative. +TEST_CASE("GetDerivative", "[Parallel]") +{ + for (int dimension = 1; dimension <= 3; ++dimension) + { + int num_procs; + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + int myid; + MPI_Comm_rank(MPI_COMM_WORLD, &myid); + + Mesh mesh; + if (dimension == 1) + { + mesh = Mesh::MakeCartesian1D(100, 1.0); + } + else if (dimension == 2) + { + mesh = Mesh::LoadFromFile("../../data/star-mixed-p2.mesh"); + } + else + { + mesh = Mesh::LoadFromFile("../../data/fichera-mixed-p2.mesh"); + } + for (int i = 0; i < 2; i++) { mesh.UniformRefinement(); } + ParMesh pmesh(MPI_COMM_WORLD, mesh); + + FunctionCoefficient x_coeff(func); + H1_FECollection fec(3, dimension); + + // Serial. + FiniteElementSpace fes(&mesh, &fec); + GridFunction gf(&fes), gf_grad(&fes); + gf.ProjectCoefficient(x_coeff); + + // Parallel. + ParFiniteElementSpace pfes(&pmesh, &fec); + ParGridFunction pgf(&pfes), pgf_grad(&pfes); + pgf.ProjectCoefficient(x_coeff); + + ConstantCoefficient zero(0.0); + for (int d = 0; d < dimension; d++) + { + gf.GetDerivative(1, d, gf_grad); + pgf.GetDerivative(1, d, pgf_grad); + REQUIRE(gf_grad.ComputeL2Error(zero) - + pgf_grad.ComputeL2Error(zero) == MFEM_Approx(0.0)); + } + } +} + +#endif From 3b325a597cbeb406a95caef66cd1d648ed5f662c Mon Sep 17 00:00:00 2001 From: Keith Date: Wed, 29 Sep 2021 13:02:24 -0700 Subject: [PATCH 145/198] supress warnings by default --- examples/osc.cpp | 3 ++- examples/oscp.cpp | 3 ++- mesh/mesh_operators.cpp | 3 ++- mesh/mesh_operators.hpp | 4 ++++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/examples/osc.cpp b/examples/osc.cpp index 8100e80479..8969e0918d 100644 --- a/examples/osc.cpp +++ b/examples/osc.cpp @@ -157,9 +157,10 @@ int main(int argc, char *argv[]) // 6. Apply custom refiner settings. coeffrefiner.SetIntRule(irs); - coeffrefiner.SetMaxElements( max_elems); + coeffrefiner.SetMaxElements(max_elems); coeffrefiner.SetThreshold(osc_threshold); coeffrefiner.SetNCLimit(nc_limit); + coeffrefiner.PrintWarnings(); // 7. Preprocess mesh to control osc (piecewise-affine function). // This is mostly just a verification check. The oscillation should diff --git a/examples/oscp.cpp b/examples/oscp.cpp index 5e2a33e37d..39b0084eb1 100644 --- a/examples/oscp.cpp +++ b/examples/oscp.cpp @@ -186,9 +186,10 @@ int main(int argc, char *argv[]) // 8. Apply custom refiner settings. coeffrefiner.SetIntRule(irs); - coeffrefiner.SetMaxElements( max_elems); + coeffrefiner.SetMaxElements(max_elems); coeffrefiner.SetThreshold(osc_threshold); coeffrefiner.SetNCLimit(nc_limit); + coeffrefiner.PrintWarnings(); // 9. Preprocess mesh to control osc (piecewise-affine function). // This is mostly just a verification check. The oscillation should diff --git a/mesh/mesh_operators.cpp b/mesh/mesh_operators.cpp index 0575036391..043749ad78 100644 --- a/mesh/mesh_operators.cpp +++ b/mesh/mesh_operators.cpp @@ -261,7 +261,8 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) // Exit if the global threshold or maximum number of elements is reached. if (global_osc < threshold || globalNE > max_elements) { - if (global_osc > threshold && globalNE > max_elements && rank == 0) + if (global_osc > threshold && globalNE > max_elements && rank == 0 && + print_level) { MFEM_WARNING("Reached maximum number of elements " "before resolving data to tolerance."); diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index f671ba8ecb..7bdee84362 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -328,6 +328,7 @@ public: class CoefficientRefiner : public MeshOperator { protected: + bool print_level = false; int nc_limit = 1; int nonconforming = -1; int order; @@ -400,6 +401,9 @@ public: // Set a custom integration rule void SetIntRule(const IntegrationRule *irs_[]) { irs = irs_; } + // Set print level + void PrintWarnings() { print_level = true; } + // Return the value of the global relative data oscillation double GetOsc() { return global_osc; } From 092b5079fdbb48da520c21ad4c093af0aa17ff65 Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Wed, 29 Sep 2021 13:12:08 -0700 Subject: [PATCH 146/198] Used HostReadWrite as suggested. --- fem/pgridfunc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fem/pgridfunc.cpp b/fem/pgridfunc.cpp index a82572a705..c2dc555459 100644 --- a/fem/pgridfunc.cpp +++ b/fem/pgridfunc.cpp @@ -493,8 +493,8 @@ void ParGridFunction::GetDerivative(int comp, int der_comp, gcomm.Bcast(overlap); // Accumulate for all dofs. - gcomm.Reduce(der.GetMemory(), GroupCommunicator::Sum); - gcomm.Bcast(der.GetMemory()); + gcomm.Reduce(der.HostReadWrite(), GroupCommunicator::Sum); + gcomm.Bcast(der.HostReadWrite()); for (int i = 0; i < overlap.Size(); i++) { From d6a5eea4e727bf2603e9884c7d943ca00013e1c6 Mon Sep 17 00:00:00 2001 From: Keith Date: Wed, 29 Sep 2021 14:37:32 -0700 Subject: [PATCH 147/198] const member function fix --- mesh/mesh_operators.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesh/mesh_operators.hpp b/mesh/mesh_operators.hpp index 7bdee84362..63fce0f1b7 100644 --- a/mesh/mesh_operators.hpp +++ b/mesh/mesh_operators.hpp @@ -405,7 +405,7 @@ public: void PrintWarnings() { print_level = true; } // Return the value of the global relative data oscillation - double GetOsc() { return global_osc; } + double GetOsc() const { return global_osc; } // Return the local relative data oscillation errors const Vector & GetLocalOscs() const From 037855173edbce5d165fb403e0f2108bef92b88e Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Wed, 29 Sep 2021 15:32:13 -0700 Subject: [PATCH 148/198] Use smaller problem for ex14 sample run --- examples/ex14p.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ex14p.cpp b/examples/ex14p.cpp index 2339c3253b..721ffe3f0e 100644 --- a/examples/ex14p.cpp +++ b/examples/ex14p.cpp @@ -5,7 +5,7 @@ // Sample runs: mpirun -np 4 ex14p -m ../data/inline-quad.mesh -o 0 // mpirun -np 4 ex14p -m ../data/star.mesh -o 2 // mpirun -np 4 ex14p -m ../data/star-mixed.mesh -o 2 -// mpirun -np 4 ex14p -m ../data/star-mixed.mesh -o 2 -k 0 -e 1 +// mpirun -np 4 ex14p -m ../data/star-mixed.mesh -rs 0 -rp 1 -o 2 -k 0 -e 1 // mpirun -np 4 ex14p -m ../data/escher.mesh -s 1 // mpirun -np 4 ex14p -m ../data/fichera.mesh -s 1 -k 1 // mpirun -np 4 ex14p -m ../data/fichera-mixed.mesh -s 1 -k 1 From 8f59bf3005d6688a0b3dde41644c1aea060fd5e8 Mon Sep 17 00:00:00 2001 From: Tzanio Date: Wed, 29 Sep 2021 20:55:20 -0700 Subject: [PATCH 149/198] minor --- linalg/amgxsolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linalg/amgxsolver.cpp b/linalg/amgxsolver.cpp index cc3550153b..aff32f4909 100644 --- a/linalg/amgxsolver.cpp +++ b/linalg/amgxsolver.cpp @@ -604,7 +604,7 @@ void AmgXSolver::SetMatrix(const HypreParMatrix &A, const bool update_mat) mfem_error("Hypre version 2.16+ is required when using AmgX \n"); #endif - //Ensure HypreParMatrix is on the host + // Ensure HypreParMatrix is on the host A.HostRead(); hypre_ParCSRMatrix * A_ptr = From 2888c61e59d4ee69478622de31b327c88bf3bdf0 Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Thu, 30 Sep 2021 08:28:07 -0700 Subject: [PATCH 150/198] Increase maximum iterations in ex14p. --- examples/ex14p.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/ex14p.cpp b/examples/ex14p.cpp index 721ffe3f0e..b3a5749575 100644 --- a/examples/ex14p.cpp +++ b/examples/ex14p.cpp @@ -221,7 +221,7 @@ int main(int argc, char *argv[]) { HyprePCG pcg(*A); pcg.SetTol(1e-12); - pcg.SetMaxIter(200); + pcg.SetMaxIter(500); pcg.SetPrintLevel(2); pcg.SetPreconditioner(*amg); pcg.Mult(*B, *X); @@ -232,7 +232,7 @@ int main(int argc, char *argv[]) GMRESSolver gmres(MPI_COMM_WORLD); gmres.SetAbsTol(0.0); gmres.SetRelTol(1e-12); - gmres.SetMaxIter(200); + gmres.SetMaxIter(500); gmres.SetKDim(10); gmres.SetPrintLevel(1); gmres.SetOperator(*A); From ee8578e22a038b28b6667b0df698ffb5dc25aedc Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Thu, 30 Sep 2021 17:37:44 -0700 Subject: [PATCH 151/198] Take sqrt of BR2 factor --- fem/bilininteg_br2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fem/bilininteg_br2.cpp b/fem/bilininteg_br2.cpp index 9ebcc8675e..7bf1af15f3 100644 --- a/fem/bilininteg_br2.cpp +++ b/fem/bilininteg_br2.cpp @@ -161,7 +161,7 @@ void DGDiffusionBR2Integrator::AssembleFaceMatrix( el2.CalcShape(eip2, shape2); } - double w = factor*sqrt(eta)*ip.weight*Trans.Face->Weight(); + double w = sqrt((factor + 1)*eta)*ip.weight*Trans.Face->Weight(); for (int i = 0; i < ndof1; i++) { From c7856f70eade40c24d99b4f243c8bb397d6d2106 Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Mon, 4 Oct 2021 15:34:28 -0700 Subject: [PATCH 152/198] Fix possible dereferencing of NULL pointer --- fem/lor.cpp | 14 ++++++++++++-- fem/lor.hpp | 3 ++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/fem/lor.cpp b/fem/lor.cpp index 80a15bf48e..558b565707 100644 --- a/fem/lor.cpp +++ b/fem/lor.cpp @@ -34,7 +34,8 @@ void LORBase::AddIntegratorsAndMarkers(BilinearForm &a_from, BilinearForm &a_to, GetIntegratorsFn get_integrators, GetMarkersFn get_markers, - AddIntegratorMarkersFn add_integrator, + AddIntegratorMarkersFn add_integrator_marker, + AddIntegratorFn add_integrator, const IntegrationRule *ir) { Array *integrators = (a_from.*get_integrators)(); @@ -42,7 +43,14 @@ void LORBase::AddIntegratorsAndMarkers(BilinearForm &a_from, for (int i=0; iSize(); ++i) { - (a_to.*add_integrator)((*integrators)[i], *(*markers[i])); + if (*markers[i]) + { + (a_to.*add_integrator_marker)((*integrators)[i], *(*markers[i])); + } + else + { + (a_to.*add_integrator)((*integrators)[i]); + } ir_map[(*integrators)[i]] = ((*integrators)[i])->GetIntegrationRule(); if (ir) { ((*integrators)[i])->SetIntegrationRule(*ir); } } @@ -262,9 +270,11 @@ void LORBase::AssembleSystem_(BilinearForm &a_ho, const Array &ess_dofs) &BilinearForm::AddInteriorFaceIntegrator, ir_face); AddIntegratorsAndMarkers(a_ho, *a, &BilinearForm::GetBBFI, &BilinearForm::GetBBFI_Marker, + &BilinearForm::AddBoundaryIntegrator, &BilinearForm::AddBoundaryIntegrator, ir_face); AddIntegratorsAndMarkers(a_ho, *a, &BilinearForm::GetBFBFI, &BilinearForm::GetBFBFI_Marker, + &BilinearForm::AddBdrFaceIntegrator, &BilinearForm::AddBdrFaceIntegrator, ir_face); a->Assemble(); a->FormSystemMatrix(ess_dofs, A); diff --git a/fem/lor.hpp b/fem/lor.hpp index 186b2effca..2863e86256 100644 --- a/fem/lor.hpp +++ b/fem/lor.hpp @@ -49,7 +49,8 @@ private: BilinearForm &a_to, GetIntegratorsFn get_integrators, GetMarkersFn get_markers, - AddIntegratorMarkersFn add_integrator, + AddIntegratorMarkersFn add_integrator_marker, + AddIntegratorFn add_integrator, const IntegrationRule *ir); /// Resets the integration rules of the integrators of @a a to their original From 2fe688deea74beb42d06d98cedce1f20647b5b65 Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Mon, 4 Oct 2021 15:45:55 -0700 Subject: [PATCH 153/198] Add version of BR2 integrator with coefficient --- fem/bilininteg.hpp | 25 ++++++++++++++++++++----- fem/bilininteg_br2.cpp | 40 ++++++++++++++++++++++++++++++---------- 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/fem/bilininteg.hpp b/fem/bilininteg.hpp index 759ac0faa6..6d8eab46f8 100644 --- a/fem/bilininteg.hpp +++ b/fem/bilininteg.hpp @@ -2938,10 +2938,11 @@ public: sum_e eta (r_e([u]), r_e([v])) - where r_e is the lifting operator defined on each edge e. The parameter eta - can be chosen to be one to obtain a stable discretization. The constructor - for this integrator requires the finite element space because the lifting - operator depends on the element-wise inverse mass matrix. + where r_e is the lifting operator defined on each edge e (potentially + weighted by a coefficient Q). The parameter eta can be chosen to be one to + obtain a stable discretization. The constructor for this integrator requires + the finite element space because the lifting operator depends on the + element-wise inverse mass matrix. BR2 stands for the second method of Bassi and Rebay: @@ -2964,14 +2965,28 @@ protected: Array ipiv; Array ipiv_offsets, Minv_offsets; + Coefficient *Q; + Vector shape1, shape2; DenseMatrix R11, R12, R21, R22; DenseMatrix MinvR11, MinvR12, MinvR21, MinvR22; DenseMatrix Re, MinvRe; + /// Precomputes the inverses (LU factorizations) of the local mass matrices. + /** @a fes must be a DG space, so the mass matrix is block diagonal, and its + inverse can be computed locally. This is required for the computation of + the lifting operators @a r_e. + */ + void PrecomputeMassInverse(class FiniteElementSpace &fes); + public: - DGDiffusionBR2Integrator(class FiniteElementSpace *fes, double e = 1.0); + DGDiffusionBR2Integrator(class FiniteElementSpace &fes, double e = 1.0); + DGDiffusionBR2Integrator(class FiniteElementSpace &fes, Coefficient &Q_, + double e = 1.0); + MFEM_DEPRECATED DGDiffusionBR2Integrator(class FiniteElementSpace *fes, + double e = 1.0); + using BilinearFormIntegrator::AssembleFaceMatrix; virtual void AssembleFaceMatrix(const FiniteElement &el1, const FiniteElement &el2, diff --git a/fem/bilininteg_br2.cpp b/fem/bilininteg_br2.cpp index 7bf1af15f3..dfa8c54c86 100644 --- a/fem/bilininteg_br2.cpp +++ b/fem/bilininteg_br2.cpp @@ -16,20 +16,39 @@ namespace mfem { -DGDiffusionBR2Integrator::DGDiffusionBR2Integrator(FiniteElementSpace *fes, - double e) : eta(e) +DGDiffusionBR2Integrator::DGDiffusionBR2Integrator( + FiniteElementSpace &fes, double e) : eta(e), Q(NULL) { + PrecomputeMassInverse(fes); +} + +DGDiffusionBR2Integrator::DGDiffusionBR2Integrator( + FiniteElementSpace &fes, Coefficient &Q_, double e) : eta(e), Q(&Q_) +{ + PrecomputeMassInverse(fes); +} + +DGDiffusionBR2Integrator::DGDiffusionBR2Integrator( + FiniteElementSpace *fes, double e) : eta(e), Q(NULL) +{ + PrecomputeMassInverse(*fes); +} + +void DGDiffusionBR2Integrator::PrecomputeMassInverse(FiniteElementSpace &fes) +{ + MFEM_VERIFY(fes.IsDGSpace(), + "The BR2 integrator is only defined for DG spaces."); // Precompute local mass matrix inverses needed for the lifting operators // First compute offsets and total size needed (e.g. for mixed meshes or // p-refinement) - int nel = fes->GetNE(); + int nel = fes.GetNE(); Minv_offsets.SetSize(nel+1); ipiv_offsets.SetSize(nel+1); ipiv_offsets[0] = 0; Minv_offsets[0] = 0; for (int i=0; iGetFE(i)->GetDof(); + int dof = fes.GetFE(i)->GetDof(); ipiv_offsets[i+1] = ipiv_offsets[i] + dof; Minv_offsets[i+1] = Minv_offsets[i] + dof*dof; } @@ -37,7 +56,7 @@ DGDiffusionBR2Integrator::DGDiffusionBR2Integrator(FiniteElementSpace *fes, #ifdef MFEM_USE_MPI // When running in parallel, we also need to compute the local mass matrices // of face neighbor elements - ParFiniteElementSpace *pfes = dynamic_cast(fes); + ParFiniteElementSpace *pfes = dynamic_cast(&fes); if (pfes != NULL) { ParMesh *pmesh = pfes->GetParMesh(); @@ -64,15 +83,15 @@ DGDiffusionBR2Integrator::DGDiffusionBR2Integrator(FiniteElementSpace *fes, { const FiniteElement *fe = NULL; ElementTransformation *tr = NULL; - if (i < fes->GetNE()) + if (i < fes.GetNE()) { - fe = fes->GetFE(i); - tr = fes->GetElementTransformation(i); + fe = fes.GetFE(i); + tr = fes.GetElementTransformation(i); } else { #ifdef MFEM_USE_MPI - int inbr = i - fes->GetNE(); + int inbr = i - fes.GetNE(); fe = pfes->GetFaceNbrFE(inbr); tr = pfes->GetParMesh()->GetFaceNbrElementTransformation(inbr); #endif @@ -161,7 +180,8 @@ void DGDiffusionBR2Integrator::AssembleFaceMatrix( el2.CalcShape(eip2, shape2); } - double w = sqrt((factor + 1)*eta)*ip.weight*Trans.Face->Weight(); + double q = Q ? Q->Eval(*Trans.Elem1, eip1) : 1.0; + double w = sqrt((factor + 1)*eta*q)*ip.weight*Trans.Face->Weight(); for (int i = 0; i < ndof1; i++) { From 5df3ef4b9e218596a661f4c790082b76851fdfce Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Mon, 4 Oct 2021 16:56:43 -0700 Subject: [PATCH 154/198] Pass fespace by reference to BR2 integrator in ex14 and ex14p --- examples/ex14.cpp | 4 ++-- examples/ex14p.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/ex14.cpp b/examples/ex14.cpp index 1603704b7c..314138deb8 100644 --- a/examples/ex14.cpp +++ b/examples/ex14.cpp @@ -135,8 +135,8 @@ int main(int argc, char *argv[]) a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa)); if (eta > 0) { - a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta)); - a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta)); + a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta)); + a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta)); } a->Assemble(); a->Finalize(); diff --git a/examples/ex14p.cpp b/examples/ex14p.cpp index b3a5749575..466b8c534b 100644 --- a/examples/ex14p.cpp +++ b/examples/ex14p.cpp @@ -199,8 +199,8 @@ int main(int argc, char *argv[]) a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa)); if (eta > 0) { - a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta)); - a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta)); + a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta)); + a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta)); } a->Assemble(); a->Finalize(); From c0486fec51b3d2133c1abf1c9b0e60e12b1d41df Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Tue, 5 Oct 2021 10:25:07 -0700 Subject: [PATCH 155/198] Take average of coefficient across face in BR2 integrator --- fem/bilininteg_br2.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fem/bilininteg_br2.cpp b/fem/bilininteg_br2.cpp index dfa8c54c86..02ae79c137 100644 --- a/fem/bilininteg_br2.cpp +++ b/fem/bilininteg_br2.cpp @@ -174,13 +174,14 @@ void DGDiffusionBR2Integrator::AssembleFaceMatrix( Trans.Loc1.Transform(ip, eip1); el1.CalcShape(eip1, shape1); + + double q = Q ? Q->Eval(*Trans.Elem1, eip1) : 1.0; if (ndof2) { Trans.Loc2.Transform(ip, eip2); el2.CalcShape(eip2, shape2); + if (Q) { q = 0.5*(q + Q->Eval(*Trans.Elem2, eip2)); } } - - double q = Q ? Q->Eval(*Trans.Elem1, eip1) : 1.0; double w = sqrt((factor + 1)*eta*q)*ip.weight*Trans.Face->Weight(); for (int i = 0; i < ndof1; i++) From 31545fb3d7579e42b16bdd707f21d89cf5664440 Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Wed, 6 Oct 2021 13:25:13 -0700 Subject: [PATCH 156/198] Comments and fix for BR2 integrator. Use SetAllIntPoints for the face transformation, re-add factor of 0.5 corresponding to average term in lifting operator. Also revert ex14p sample run. --- examples/ex14p.cpp | 2 +- fem/bilininteg_br2.cpp | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/examples/ex14p.cpp b/examples/ex14p.cpp index 466b8c534b..ee9cfbd4b7 100644 --- a/examples/ex14p.cpp +++ b/examples/ex14p.cpp @@ -5,7 +5,7 @@ // Sample runs: mpirun -np 4 ex14p -m ../data/inline-quad.mesh -o 0 // mpirun -np 4 ex14p -m ../data/star.mesh -o 2 // mpirun -np 4 ex14p -m ../data/star-mixed.mesh -o 2 -// mpirun -np 4 ex14p -m ../data/star-mixed.mesh -rs 0 -rp 1 -o 2 -k 0 -e 1 +// mpirun -np 4 ex14p -m ../data/star-mixed.mesh -o 2 -k 0 -e 1 // mpirun -np 4 ex14p -m ../data/escher.mesh -s 1 // mpirun -np 4 ex14p -m ../data/fichera.mesh -s 1 -k 1 // mpirun -np 4 ex14p -m ../data/fichera-mixed.mesh -s 1 -k 1 diff --git a/fem/bilininteg_br2.cpp b/fem/bilininteg_br2.cpp index 02ae79c137..0c430beaf9 100644 --- a/fem/bilininteg_br2.cpp +++ b/fem/bilininteg_br2.cpp @@ -170,19 +170,24 @@ void DGDiffusionBR2Integrator::AssembleFaceMatrix( for (int p = 0; p < ir->GetNPoints(); p++) { const IntegrationPoint &ip = ir->IntPoint(p); - IntegrationPoint eip1, eip2; + Trans.SetAllIntPoints(&ip); - Trans.Loc1.Transform(ip, eip1); + const IntegrationPoint &eip1 = Trans.Elem1->GetIntPoint(); el1.CalcShape(eip1, shape1); - double q = Q ? Q->Eval(*Trans.Elem1, eip1) : 1.0; if (ndof2) { - Trans.Loc2.Transform(ip, eip2); + const IntegrationPoint &eip2 = Trans.Elem2->GetIntPoint(); el2.CalcShape(eip2, shape2); + // Set coefficient value q to the average of the values on either side if (Q) { q = 0.5*(q + Q->Eval(*Trans.Elem2, eip2)); } } + // Take sqrt here because + // eta (r_e([u]), r_e([v])) = (sqrt(eta) r_e([u]), sqrt(eta) r_e([v])) double w = sqrt((factor + 1)*eta*q)*ip.weight*Trans.Face->Weight(); + // r_e is defined by, (r_e([u]), tau) = <[u], {tau}>, so we pick up a + // factor of 0.5 on interior faces from the average term. + if (ndof2) { w *= 0.5; } for (int i = 0; i < ndof1; i++) { From d6c70dd0770da78dfab87555371c68b212452bdc Mon Sep 17 00:00:00 2001 From: Veselin Dobrev Date: Wed, 6 Oct 2021 16:46:19 -0700 Subject: [PATCH 157/198] Specify a custom directory to be used by the Gitlab CI. --- .gitlab-ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 91bb21059d..60cb4c655f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -27,6 +27,9 @@ stages: - sub-pipelines +variables: + CUSTOM_CI_BUILDS_DIR: "/usr/workspace/mfem/gitlab-runner" + # Trigger subpipelines: quartz-build-and-test: stage: sub-pipelines From 3db650c24f425dced642381d17e13b43698d715d Mon Sep 17 00:00:00 2001 From: Tom Stitt Date: Wed, 6 Oct 2021 18:04:05 -0700 Subject: [PATCH 158/198] umpire 6 requires us to include the specifc strategy header we are using --- general/mem_manager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/general/mem_manager.cpp b/general/mem_manager.cpp index ce3d26df55..b5d850f89e 100644 --- a/general/mem_manager.cpp +++ b/general/mem_manager.cpp @@ -33,7 +33,8 @@ #endif #ifdef MFEM_USE_UMPIRE -#include "umpire/Umpire.hpp" +#include +#include // Make sure Umpire is build with CUDA support if MFEM is built with it. #if defined(MFEM_USE_CUDA) && !defined(UMPIRE_ENABLE_CUDA) From 0bd6208942bbcdde52890c8174c1e2e2e49aea89 Mon Sep 17 00:00:00 2001 From: Tom Stitt Date: Thu, 7 Oct 2021 16:44:48 -0700 Subject: [PATCH 159/198] use QuickPool instead of DynamicPool; requires Umpire >=3 so bump the version in INSTALL --- INSTALL | 2 +- general/mem_manager.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/INSTALL b/INSTALL index 6ca1a48b6c..c4f80a39ee 100644 --- a/INSTALL +++ b/INSTALL @@ -753,7 +753,7 @@ The specific libraries and their options are: Umpire requires camp when the Umpire version is >= 3.0.0. URL: https://github.com/LLNL/Umpire Options: UMPIRE_DIR, UMPIRE_OPT, UMPIRE_LIB. - Versions: Umpire >= 2.0.0. + Versions: Umpire >= 3.0.0. - Benchmark, used when MFEM_USE_BENCHMARK = YES. URL: https://github.com/google/benchmark diff --git a/general/mem_manager.cpp b/general/mem_manager.cpp index b5d850f89e..77310e5be4 100644 --- a/general/mem_manager.cpp +++ b/general/mem_manager.cpp @@ -34,7 +34,7 @@ #ifdef MFEM_USE_UMPIRE #include -#include +#include // Make sure Umpire is build with CUDA support if MFEM is built with it. #if defined(MFEM_USE_CUDA) && !defined(UMPIRE_ENABLE_CUDA) @@ -536,7 +536,7 @@ public: { if (!rm.isAllocator(name)) { - allocator = rm.makeAllocator( + allocator = rm.makeAllocator( name, rm.getAllocator(space)); owns_allocator = true; } From e9af593bd36ae50e24e65cd5ff3e6edd014c524e Mon Sep 17 00:00:00 2001 From: Veselin Dobrev Date: Thu, 7 Oct 2021 19:04:12 -0700 Subject: [PATCH 160/198] Update an MFEM_ASSERT for a special case that comes up when using 'host-umpire' memory which is the default when MFEM is built with Umpire. --- general/mem_manager.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/general/mem_manager.cpp b/general/mem_manager.cpp index 77310e5be4..ca1b959e64 100644 --- a/general/mem_manager.cpp +++ b/general/mem_manager.cpp @@ -911,7 +911,12 @@ MemoryType MemoryManager::Delete_(void *h_ptr, MemoryType h_mt, unsigned flags) MFEM_ASSERT(IsHostMemory(h_mt), "invalid h_mt = " << (int)h_mt); // MFEM_ASSERT(registered || IsHostMemory(h_mt),""); MFEM_ASSERT(!owns_device || owns_internal, "invalid Memory state"); - MFEM_ASSERT(registered || !(owns_host || owns_device || owns_internal), + // If at least one of the 'own_*' flags is true then 'registered' must be + // true too. An acceptable exception is the special case when 'h_ptr' is + // NULL, and both 'own_device' and 'own_internal' are false -- this case is + // an exception only when 'own_host' is true and 'registered' is false. + MFEM_ASSERT(registered || !(owns_host || owns_device || owns_internal) || + (!(owns_device || owns_internal) && h_ptr == nullptr), "invalid Memory state"); if (!mm.exists || !registered) { return h_mt; } if (alias) From cbc44b22b6f0c829e698f4abba28436b9a3b9819 Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Fri, 8 Oct 2021 11:07:52 -0700 Subject: [PATCH 161/198] Fix bug in CEED integration The requested integration rule was assumed to be GaussLegendre, but this is not always the case. --- fem/ceed/util.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fem/ceed/util.cpp b/fem/ceed/util.cpp index 9b02f30d0e..c857d0aa02 100644 --- a/fem/ceed/util.cpp +++ b/fem/ceed/util.cpp @@ -186,14 +186,18 @@ static void InitTensorBasis(const mfem::FiniteElementSpace &fes, const int ndofs = maps.ndof; const int nqpts = maps.nqpt; mfem::Vector qX(nqpts), qW(nqpts); - const mfem::IntegrationRule &ir1d = - IntRules.Get(Geometry::SEGMENT, ir.GetOrder()); + // The x-coordinates of the first `nqpts` points of the integration rule are + // the points of the corresponding 1D rule. We also scale the weights + // accordingly. + double w_sum = 0.0; for (int i = 0; i < nqpts; i++) { - const mfem::IntegrationPoint &ip = ir1d.IntPoint(i); + const mfem::IntegrationPoint &ip = ir.IntPoint(i); qX(i) = ip.x; qW(i) = ip.weight; + w_sum += ip.weight; } + qW *= 1.0/w_sum; CeedBasisCreateTensorH1(ceed, mesh->Dimension(), fes.GetVDim(), ndofs, nqpts, maps.Bt.GetData(), maps.Gt.GetData(), qX.GetData(), From 15b9085ad5aa198ef70451808913462df44c418f Mon Sep 17 00:00:00 2001 From: Julian Andrej Date: Fri, 8 Oct 2021 11:39:33 -0700 Subject: [PATCH 162/198] add unit test --- tests/unit/ceed/test_ceed.cpp | 94 +++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/tests/unit/ceed/test_ceed.cpp b/tests/unit/ceed/test_ceed.cpp index d2d5cc3ca8..4d1d30722d 100644 --- a/tests/unit/ceed/test_ceed.cpp +++ b/tests/unit/ceed/test_ceed.cpp @@ -41,6 +41,47 @@ void velocity_function(const Vector &x, Vector &v) } } +// Vector valued quantity to convect +void quantity(const Vector &x, Vector &u) +{ + int dim = x.Size(); + + switch (dim) + { + case 1: u(0) = x[0]*x[0]; break; + case 2: u(0) = x[0]*x[0]; u(1) = x[1]*x[1]; break; + case 3: u(0) = x[0]*x[0]; u(1) = x[1]*x[1]; u(2) = x[2]*x[2]; break; + } +} + +// Quantity after explicit convect +// (u \cdot \nabla) v +void convected_quantity(const Vector &x, Vector &u) +{ + double a, b, c; + + int dim = x.Size(); + switch (dim) + { + case 1: + u(0) = 2.*x[0]*(x[0]*x[0]+1.0); + break; + case 2: + a = sqrt(2./3.); + b = sqrt(1./3.); + u(0) = 2.*a*x[0]*(x[0]*x[0]+1.0); + u(1) = 2.*b*x[1]*(x[0]*x[0]+1.0); + break; + case 3: + a = sqrt(3./6.); + b = sqrt(2./6.); + c = sqrt(1./6.); + u(0) = 2.*a*x[0]*(x[0]*x[0]+1.0); + u(1) = 2.*b*x[1]*(x[0]*x[0]+1.0); + u(2) = 2.*c*x[2]*(x[0]*x[0]+1.0); + } +} + std::string getString(AssemblyLevel assembly) { switch (assembly) @@ -328,6 +369,56 @@ void test_ceed_nloperator(const char* input, int order, delete vcoeff; } +// This function specifically tests convection of a vector valued quantity and +// using a custom integration rule. The integration rule is chosen s.t. in +// combination with an appropriate order, it can represent the analytical +// polynomial functions correctly. +void test_ceed_convection(const char* input, int order, + const AssemblyLevel assembly) +{ + Mesh mesh(input, 1, 1); + mesh.EnsureNodes(); + int dim = mesh.Dimension(); + H1_FECollection fec(order, dim); + + VectorFunctionCoefficient velocity_coeff(dim, velocity_function); + + FiniteElementSpace fes(&mesh, &fec, dim); + BilinearForm conv_op(&fes); + + IntegrationRules rules(0, Quadrature1D::GaussLobatto); + const IntegrationRule &ir = rules.Get(fes.GetFE(0)->GetGeomType(), + 2 * order - 1); + + ConvectionIntegrator *conv_integ = new ConvectionIntegrator(velocity_coeff, 1); + conv_integ->SetIntRule(&ir); + conv_op.AddDomainIntegrator(conv_integ); + conv_op.SetAssemblyLevel(AssemblyLevel::PARTIAL); + conv_op.Assemble(); + + GridFunction q(&fes), r(&fes), ex(&fes); + + VectorFunctionCoefficient quantity_coeff(dim, quantity); + q.ProjectCoefficient(quantity_coeff); + + VectorFunctionCoefficient convected_quantity_coeff(dim, convected_quantity); + ex.ProjectCoefficient(convected_quantity_coeff); + + r = 0.0; + conv_op.Mult(q, r); + + LinearForm f(&fes); + VectorDomainLFIntegrator *vlf_integ = new VectorDomainLFIntegrator( + convected_quantity_coeff); + vlf_integ->SetIntRule(&ir); + f.AddDomainIntegrator(vlf_integ); + f.Assemble(); + + r -= f; + + REQUIRE(r.Norml2() < 1e-12); +} + TEST_CASE("CEED mass & diffusion", "[CEED]") { auto assembly = GENERATE(AssemblyLevel::PARTIAL,AssemblyLevel::NONE); @@ -357,6 +448,9 @@ TEST_CASE("CEED convection", "[CEED],[Convection]") "../../data/amr-quad.mesh", "../../data/fichera-amr.mesh"); test_ceed_operator(mesh, order, coeff_type, pb, assembly); + + int high_order = 4; + test_ceed_convection(mesh, high_order, assembly); } // test case TEST_CASE("CEED non-linear convection", "[CEED],[NLConvection]") From af6426d24021c9e8bf846d3d7effd2ec1cac8d7f Mon Sep 17 00:00:00 2001 From: Vladimir Z Tomov Date: Fri, 8 Oct 2021 13:09:17 -0700 Subject: [PATCH 163/198] Addressed reviewer comments. --- CHANGELOG | 4 ++++ fem/tmop.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index cfbc95a619..53f31c535f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -51,6 +51,10 @@ Version 4.3.1 (development) - Remove the 'u' flag in the ar command, to update all files in the archive, avoiding file name collisions from different subdirectories. + +- Added initial TMOP-based capabilities for surface fitting and tangential + relaxation in the mesh-optimizer and pmesh-optimizer miniapps. + Version 4.3, released on July 29, 2021 ====================================== diff --git a/fem/tmop.cpp b/fem/tmop.cpp index c90e03635a..7e4b445190 100644 --- a/fem/tmop.cpp +++ b/fem/tmop.cpp @@ -2400,12 +2400,14 @@ void TMOP_Integrator::EnableSurfaceFitting(const GridFunction &s0, Coefficient &coeff, AdaptivityEvaluator &ae) { + delete sigma; sigma = new GridFunction(s0); sigma_marker = &smarker; coeff_sigma = &coeff; sigma_eval = &ae; // Compute the restricted sigma. + delete sigma_bar; sigma_bar = new GridFunction(*sigma); for (int i = 0; i < sigma_marker->Size(); i++) { @@ -2424,12 +2426,14 @@ void TMOP_Integrator::EnableSurfaceFitting(const ParGridFunction &s0, Coefficient &coeff, AdaptivityEvaluator &ae) { + delete sigma; sigma = new GridFunction(s0); sigma_marker = &smarker; coeff_sigma = &coeff; sigma_eval = &ae; // Compute the restricted sigma. + delete sigma_bar; sigma_bar = new GridFunction(*sigma); for (int i = 0; i < sigma_marker->Size(); i++) { From 8bb634ffcb88de9b1baf1ab20a3a24fc7d1cd9cd Mon Sep 17 00:00:00 2001 From: Julian Andrej <5412886+jandrej@users.noreply.github.com> Date: Fri, 8 Oct 2021 13:44:58 -0700 Subject: [PATCH 164/198] Update tests/unit/ceed/test_ceed.cpp Co-authored-by: Yohann --- tests/unit/ceed/test_ceed.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/ceed/test_ceed.cpp b/tests/unit/ceed/test_ceed.cpp index 4d1d30722d..0d4509c69d 100644 --- a/tests/unit/ceed/test_ceed.cpp +++ b/tests/unit/ceed/test_ceed.cpp @@ -393,7 +393,7 @@ void test_ceed_convection(const char* input, int order, ConvectionIntegrator *conv_integ = new ConvectionIntegrator(velocity_coeff, 1); conv_integ->SetIntRule(&ir); conv_op.AddDomainIntegrator(conv_integ); - conv_op.SetAssemblyLevel(AssemblyLevel::PARTIAL); + conv_op.SetAssemblyLevel(assembly); conv_op.Assemble(); GridFunction q(&fes), r(&fes), ex(&fes); From d72cd3f5f7ccc42e6516b3e738fb4218cab2d7b7 Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Sat, 9 Oct 2021 09:32:23 -0600 Subject: [PATCH 165/198] Small modifications to CEED convection unit test --- tests/unit/ceed/test_ceed.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/unit/ceed/test_ceed.cpp b/tests/unit/ceed/test_ceed.cpp index 0d4509c69d..168952bad8 100644 --- a/tests/unit/ceed/test_ceed.cpp +++ b/tests/unit/ceed/test_ceed.cpp @@ -311,7 +311,7 @@ void test_ceed_operator(const char* input, int order, delete vcoeff; } -void test_ceed_nloperator(const char* input, int order, +void test_ceed_nloperator(const char* mesh_filename, int order, const CeedCoeffType coeff_type, const NLProblem pb, const AssemblyLevel assembly) { @@ -319,9 +319,9 @@ void test_ceed_nloperator(const char* input, int order, "coeff_type: " + getString(coeff_type) + "\n" + "pb: " + getString(pb) + "\n" + "order: " + std::to_string(order) + "\n" + - "mesh: " + input; + "mesh: " + mesh_filename; INFO(section); - Mesh mesh(input, 1, 1); + Mesh mesh(mesh_filename, 1, 1); mesh.EnsureNodes(); int dim = mesh.Dimension(); H1_FECollection fec(order, dim); @@ -373,10 +373,10 @@ void test_ceed_nloperator(const char* input, int order, // using a custom integration rule. The integration rule is chosen s.t. in // combination with an appropriate order, it can represent the analytical // polynomial functions correctly. -void test_ceed_convection(const char* input, int order, +void test_ceed_convection(const char* mesh_filename, int order, const AssemblyLevel assembly) { - Mesh mesh(input, 1, 1); + Mesh mesh(mesh_filename, 1, 1); mesh.EnsureNodes(); int dim = mesh.Dimension(); H1_FECollection fec(order, dim); @@ -439,16 +439,20 @@ TEST_CASE("CEED convection", "[CEED],[Convection]") auto assembly = GENERATE(AssemblyLevel::PARTIAL,AssemblyLevel::NONE); auto coeff_type = GENERATE(CeedCoeffType::VecConst,CeedCoeffType::VecGrid, CeedCoeffType::VecQuad); - auto pb = GENERATE(Problem::Convection); - auto order = GENERATE(1); auto mesh = GENERATE("../../data/inline-quad.mesh", "../../data/inline-hex.mesh", "../../data/star-q2.mesh", "../../data/fichera-q2.mesh", "../../data/amr-quad.mesh", "../../data/fichera-amr.mesh"); - test_ceed_operator(mesh, order, coeff_type, pb, assembly); + Problem pb = Problem::Convection; + // Test that the CEED and MFEM integrators give the same answer + int low_order = 1; + test_ceed_operator(mesh, low_order, coeff_type, pb, assembly); + + // Apply the CEED convection integrator applied to a vector quantity, check + // that we get the exact answer (with sufficiently high polynomial degree) int high_order = 4; test_ceed_convection(mesh, high_order, assembly); } // test case From 47008f92fb69d8b07afa9a8a19b73bb0e9fa6e58 Mon Sep 17 00:00:00 2001 From: "Stowell, Mark L" Date: Tue, 12 Oct 2021 11:40:30 -0700 Subject: [PATCH 166/198] Adding statement about documenting pointer arguments/return values --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c58c63701..15a6eb749f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -537,6 +537,7 @@ Before a PR can be merged, it should satisfy the following: - [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`. - [ ] New capability: - [ ] All new public, protected, and private classes, methods, data members, and functions have full Doxygen-style documentation in source comments. Documentation should include descriptions of member data, function arguments and return values, template parameters, and prerequisites for calling new functions. + - [ ] Pointer arguments and return values must specify whether ownership is being transferred or lent with the call. - [ ] Any new functions should include descriptions of their intended use e.g. for internal use only, user-facing, etc., along with references to example code whenever possible/appropriate. - [ ] Consider adding new sample runs in existing examples to highlight the new capability. - [ ] Consider saving cool simulation pictures with the new capability in the Confluence gallery (LLNL only) or submitting them, via pull request, to the gallery section of the `mfem/web` repo. From 320bf0a9807f2bf585579f25a9936f2bbf2f12cf Mon Sep 17 00:00:00 2001 From: Tom Stitt Date: Thu, 14 Oct 2021 10:48:43 -0700 Subject: [PATCH 167/198] add new ctor to array that takes a size and a memorytype --- general/array.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/general/array.hpp b/general/array.hpp index 5767323e78..328503f753 100644 --- a/general/array.hpp +++ b/general/array.hpp @@ -70,6 +70,10 @@ public: explicit inline Array(int asize) : size(asize) { asize > 0 ? data.New(asize) : data.Reset(); } + /// Creates array of @a asize elements with a given MemoryType + inline Array(int asize, MemoryType mt) + : size(asize) { asize > 0 ? data.New(asize, mt) : data.Reset(mt); } + /** @brief Creates array using an existing c-array of asize elements; allocsize is set to -asize to indicate that the data will not be deleted. */ From f1e503e70f85d7237ee217e2bfe6b0bbcfe66e20 Mon Sep 17 00:00:00 2001 From: Veselin Dobrev Date: Thu, 14 Oct 2021 15:59:58 -0700 Subject: [PATCH 168/198] Move or remove CHANGELOG entries in the v4.3 section which were added/changed after v4.3. Update the RAJA version requirement in INSTALL. --- CHANGELOG | 12 ++++++------ INSTALL | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 5748f6a909..1940424344 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -47,6 +47,11 @@ Version 4.3.1 (development) output format if no physical groups are defined) are now successfully loaded, and elements are reassigned attribute number 1. +- Added new miniapps that use the ParELAG library, its hybrid smoothers, and the + hierarchy of spaces created by the element-based AMG (AMGe) methodology in + ParELAG to build multigrid solvers for H(curl) and H(div) forms. See the + miniapps/parelag directory for more details. + - Fixed several MinGW build issues on Windows. - Remove the 'u' flag in the ar command, to update all files in the archive, @@ -306,7 +311,7 @@ Miscellaneous * HYPRE >= 2.22.0 for CUDA support * libCEED >= 0.8 * PETSc >= 3.15.0 for CUDA support - * RAJA >= 0.14.0 + * RAJA >= 0.13.0 see INSTALL for more details. - Added a "scaled Jacobian" visualization option in the Mesh Explorer miniapp to @@ -320,11 +325,6 @@ Miscellaneous - Various other simplifications, extensions, and bugfixes in the code. -- Added new miniapps that use the ParELAG library, its hybrid smoothers, and the - hierarchy of spaces created by the element-based AMG (AMGe) methodology in - ParELAG to build multigrid solvers for H(curl) and H(div) forms. See the - miniapps/parelag directory for more details. - API changes ----------- - Added an abstract interface `mfem::FaceRestriction` for `H1FaceRestriction` diff --git a/INSTALL b/INSTALL index c4f80a39ee..bc8919020d 100644 --- a/INSTALL +++ b/INSTALL @@ -739,10 +739,10 @@ The specific libraries and their options are: Versions: libCEED >= 0.8. - RAJA (optional), used when MFEM_USE_RAJA = YES. - Beginning with MFEM v4.3, only RAJA v0.13.0+ is supported. + Beginning with MFEM v4.3, only RAJA v0.14.0+ is supported. URL: https://github.com/LLNL/RAJA Options: RAJA_DIR, RAJA_OPT, RAJA_LIB. - Versions: RAJA >= 0.13.0. + Versions: RAJA >= 0.14.0. - Caliper (optional), used when MFEM_USE_CALIPER = YES. URL: https://github.com/LLNL/Caliper From c76cbfa0103e30cdbbac4f52602d98a22981271f Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Fri, 15 Oct 2021 13:03:13 -0700 Subject: [PATCH 169/198] Add HypreParVector::Read --- linalg/hypre.cpp | 12 ++++++++++++ linalg/hypre.hpp | 3 +++ 2 files changed, 15 insertions(+) diff --git a/linalg/hypre.cpp b/linalg/hypre.cpp index 6ca80e9872..3936874b7d 100644 --- a/linalg/hypre.cpp +++ b/linalg/hypre.cpp @@ -303,6 +303,18 @@ void HypreParVector::Print(const char *fname) const hypre_ParVectorPrint(x,fname); } +void HypreParVector::Read(MPI_Comm comm, const char *fname) +{ + if (own_ParVector) + { + hypre_ParVectorDestroy(x); + } + data.Delete(); + x = hypre_ParVectorRead(comm, fname); + own_ParVector = true; + _SetDataAndSize_(); +} + HypreParVector::~HypreParVector() { if (own_ParVector) diff --git a/linalg/hypre.hpp b/linalg/hypre.hpp index 36ebe13a95..4ec95ca92c 100644 --- a/linalg/hypre.hpp +++ b/linalg/hypre.hpp @@ -252,6 +252,9 @@ public: /// Prints the locally owned rows in parallel void Print(const char *fname) const; + /// Reads a HypreParVector from files saved with HypreParVector::Print + void Read(MPI_Comm comm, const char *fname); + /// Calls hypre's destroy function ~HypreParVector(); From 407a3321aa938a2d6280d4482ad06df897bf10d4 Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Fri, 15 Oct 2021 13:25:23 -0700 Subject: [PATCH 170/198] Add unit test for HypreParVector::Read --- tests/unit/linalg/test_hypre_vector.cpp | 70 +++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 tests/unit/linalg/test_hypre_vector.cpp diff --git a/tests/unit/linalg/test_hypre_vector.cpp b/tests/unit/linalg/test_hypre_vector.cpp new file mode 100644 index 0000000000..98bdbcdc18 --- /dev/null +++ b/tests/unit/linalg/test_hypre_vector.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2010-2021, 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 "unit_tests.hpp" +#include "mfem.hpp" + +namespace mfem +{ + +#ifdef MFEM_USE_MPI + +TEST_CASE("HypreParVector I/O", "[Parallel], [HypreParVector]") +{ + // Create a test vector (two entries per rank) with entries increasing + // sequentially. Write the vector to a file, read it into another vector, and + // make sure we get the same answer. + + int world_size, rank; + MPI_Comm_size(MPI_COMM_WORLD, &world_size); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + int size_per_rank = 2; + + HYPRE_BigInt glob_size = world_size*size_per_rank; + std::vector col; + if (HYPRE_AssumedPartitionCheck()) + { + int offset = rank*size_per_rank; + col = {offset, offset + size_per_rank}; + } + else + { + col.resize(world_size+1); + for (int i=0; i Date: Fri, 15 Oct 2021 16:30:54 -0700 Subject: [PATCH 171/198] Wrap exec policies to avoid triple compilation. --- general/forall.hpp | 161 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 149 insertions(+), 12 deletions(-) diff --git a/general/forall.hpp b/general/forall.hpp index 755e60c230..1e8dc99704 100644 --- a/general/forall.hpp +++ b/general/forall.hpp @@ -183,6 +183,42 @@ void RajaCuWrap3D(const int N, DBODY &&d_body, MFEM_GPU_CHECK(cudaGetLastError()); } +template +struct RajaCuWrap; + +template <> +struct RajaCuWrap<1> +{ + template + void operator()(const int N, DBODY &&d_body, + const int X, const int Y, const int Z, const int G) + { + RajaCuWrap1D(N, d_body); + } +}; + +template <> +struct RajaCuWrap<2> +{ + template + void operator()(const int N, DBODY &&d_body, + const int X, const int Y, const int Z, const int G) + { + RajaCuWrap2D(N, d_body, X, Y, Z); + } +}; + +template <> +struct RajaCuWrap<3> +{ + template + void operator()(const int N, DBODY &&d_body, + const int X, const int Y, const int Z, const int G) + { + RajaCuWrap3D(N, d_body, X, Y, Z, G); + } +}; + #endif #if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP) @@ -248,6 +284,43 @@ void RajaHipWrap3D(const int N, DBODY &&d_body, MFEM_GPU_CHECK(hipGetLastError()); } + +template +struct RajaHipWrap; + +template <> +struct RajaHipWrap<1> +{ + template + void operator()(const int N, DBODY &&d_body, + const int X, const int Y, const int Z, const int G) + { + RajaHipWrap1D(N, d_body); + } +}; + +template <> +struct RajaHipWrap<2> +{ + template + void operator()(const int N, DBODY &&d_body, + const int X, const int Y, const int Z, const int G) + { + RajaHipWrap2D(N, d_body, X, Y, Z); + } +}; + +template <> +struct RajaHipWrap<3> +{ + template + void operator()(const int N, DBODY &&d_body, + const int X, const int Y, const int Z, const int G) + { + RajaHipWrap3D(N, d_body, X, Y, Z, G); + } +}; + #endif /// RAJA OpenMP backend @@ -333,6 +406,42 @@ void CuWrap3D(const int N, DBODY &&d_body, MFEM_GPU_CHECK(cudaGetLastError()); } +template +struct CuWrap; + +template <> +struct CuWrap<1> +{ + template + void operator()(const int N, DBODY &&d_body, + const int X, const int Y, const int Z, const int G) + { + CuWrap1D(N, d_body); + } +}; + +template <> +struct CuWrap<2> +{ + template + void operator()(const int N, DBODY &&d_body, + const int X, const int Y, const int Z, const int G) + { + CuWrap2D(N, d_body, X, Y, Z); + } +}; + +template <> +struct CuWrap<3> +{ + template + void operator()(const int N, DBODY &&d_body, + const int X, const int Y, const int Z, const int G) + { + CuWrap3D(N, d_body, X, Y, Z, G); + } +}; + #endif // MFEM_USE_CUDA @@ -392,6 +501,42 @@ void HipWrap3D(const int N, DBODY &&d_body, MFEM_GPU_CHECK(hipGetLastError()); } +template +struct HipWrap; + +template <> +struct HipWrap<1> +{ + template + void operator()(const int N, DBODY &&d_body, + const int X, const int Y, const int Z, const int G) + { + HipWrap1D(N, d_body); + } +}; + +template <> +struct HipWrap<2> +{ + template + void operator()(const int N, DBODY &&d_body, + const int X, const int Y, const int Z, const int G) + { + HipWrap2D(N, d_body, X, Y, Z); + } +}; + +template <> +struct HipWrap<3> +{ + template + void operator()(const int N, DBODY &&d_body, + const int X, const int Y, const int Z, const int G) + { + HipWrap3D(N, d_body, X, Y, Z, G); + } +}; + #endif // MFEM_USE_HIP @@ -413,9 +558,7 @@ inline void ForallWrap(const bool use_dev, const int N, // If Backend::RAJA_CUDA is allowed, use it if (Device::Allows(Backend::RAJA_CUDA)) { - if (DIM == 1) { return RajaCuWrap1D(N, d_body); } - if (DIM == 2) { return RajaCuWrap2D(N, d_body, X, Y, Z); } - if (DIM == 3) { return RajaCuWrap3D(N, d_body, X, Y, Z, G); } + return RajaCuWrap(N, d_body, X, Y, Z, G); } #endif @@ -423,9 +566,7 @@ inline void ForallWrap(const bool use_dev, const int N, // If Backend::RAJA_HIP is allowed, use it if (Device::Allows(Backend::RAJA_HIP)) { - if (DIM == 1) { return RajaHipWrap1D(N, d_body); } - if (DIM == 2) { return RajaHipWrap2D(N, d_body, X, Y, Z); } - if (DIM == 3) { return RajaHipWrap3D(N, d_body, X, Y, Z, G); } + return RajaHipWrap(N, d_body, X, Y, Z, G); } #endif @@ -433,9 +574,7 @@ inline void ForallWrap(const bool use_dev, const int N, // If Backend::CUDA is allowed, use it if (Device::Allows(Backend::CUDA)) { - if (DIM == 1) { return CuWrap1D(N, d_body); } - if (DIM == 2) { return CuWrap2D(N, d_body, X, Y, Z); } - if (DIM == 3) { return CuWrap3D(N, d_body, X, Y, Z, G); } + return CuWrap(N, d_body, X, Y, Z, G); } #endif @@ -443,9 +582,7 @@ inline void ForallWrap(const bool use_dev, const int N, // If Backend::HIP is allowed, use it if (Device::Allows(Backend::HIP)) { - if (DIM == 1) { return HipWrap1D(N, d_body); } - if (DIM == 2) { return HipWrap2D(N, d_body, X, Y, Z); } - if (DIM == 3) { return HipWrap3D(N, d_body, X, Y, Z, G); } + return HipWrap(N, d_body, X, Y, Z, G); } #endif From 21417add4b283e5d536cacebf5e00f61ee9aa601 Mon Sep 17 00:00:00 2001 From: Yohann Dudouit Date: Fri, 15 Oct 2021 16:32:35 -0700 Subject: [PATCH 172/198] Dim -> DIM. --- general/forall.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/general/forall.hpp b/general/forall.hpp index 1e8dc99704..dd102148a6 100644 --- a/general/forall.hpp +++ b/general/forall.hpp @@ -558,7 +558,7 @@ inline void ForallWrap(const bool use_dev, const int N, // If Backend::RAJA_CUDA is allowed, use it if (Device::Allows(Backend::RAJA_CUDA)) { - return RajaCuWrap(N, d_body, X, Y, Z, G); + return RajaCuWrap(N, d_body, X, Y, Z, G); } #endif @@ -566,7 +566,7 @@ inline void ForallWrap(const bool use_dev, const int N, // If Backend::RAJA_HIP is allowed, use it if (Device::Allows(Backend::RAJA_HIP)) { - return RajaHipWrap(N, d_body, X, Y, Z, G); + return RajaHipWrap(N, d_body, X, Y, Z, G); } #endif @@ -574,7 +574,7 @@ inline void ForallWrap(const bool use_dev, const int N, // If Backend::CUDA is allowed, use it if (Device::Allows(Backend::CUDA)) { - return CuWrap(N, d_body, X, Y, Z, G); + return CuWrap(N, d_body, X, Y, Z, G); } #endif @@ -582,7 +582,7 @@ inline void ForallWrap(const bool use_dev, const int N, // If Backend::HIP is allowed, use it if (Device::Allows(Backend::HIP)) { - return HipWrap(N, d_body, X, Y, Z, G); + return HipWrap(N, d_body, X, Y, Z, G); } #endif From ddf40b7909d5c4b1580ff447190975f3012c3737 Mon Sep 17 00:00:00 2001 From: Yohann Dudouit Date: Fri, 15 Oct 2021 16:34:54 -0700 Subject: [PATCH 173/198] Use static method. --- general/forall.hpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/general/forall.hpp b/general/forall.hpp index dd102148a6..4c3905d491 100644 --- a/general/forall.hpp +++ b/general/forall.hpp @@ -190,7 +190,7 @@ template <> struct RajaCuWrap<1> { template - void operator()(const int N, DBODY &&d_body, + static void run(const int N, DBODY &&d_body, const int X, const int Y, const int Z, const int G) { RajaCuWrap1D(N, d_body); @@ -201,7 +201,7 @@ template <> struct RajaCuWrap<2> { template - void operator()(const int N, DBODY &&d_body, + static void run(const int N, DBODY &&d_body, const int X, const int Y, const int Z, const int G) { RajaCuWrap2D(N, d_body, X, Y, Z); @@ -212,7 +212,7 @@ template <> struct RajaCuWrap<3> { template - void operator()(const int N, DBODY &&d_body, + static void run(const int N, DBODY &&d_body, const int X, const int Y, const int Z, const int G) { RajaCuWrap3D(N, d_body, X, Y, Z, G); @@ -292,7 +292,7 @@ template <> struct RajaHipWrap<1> { template - void operator()(const int N, DBODY &&d_body, + static void run(const int N, DBODY &&d_body, const int X, const int Y, const int Z, const int G) { RajaHipWrap1D(N, d_body); @@ -303,7 +303,7 @@ template <> struct RajaHipWrap<2> { template - void operator()(const int N, DBODY &&d_body, + static void run(const int N, DBODY &&d_body, const int X, const int Y, const int Z, const int G) { RajaHipWrap2D(N, d_body, X, Y, Z); @@ -314,7 +314,7 @@ template <> struct RajaHipWrap<3> { template - void operator()(const int N, DBODY &&d_body, + static void run(const int N, DBODY &&d_body, const int X, const int Y, const int Z, const int G) { RajaHipWrap3D(N, d_body, X, Y, Z, G); @@ -413,7 +413,7 @@ template <> struct CuWrap<1> { template - void operator()(const int N, DBODY &&d_body, + static void run(const int N, DBODY &&d_body, const int X, const int Y, const int Z, const int G) { CuWrap1D(N, d_body); @@ -424,7 +424,7 @@ template <> struct CuWrap<2> { template - void operator()(const int N, DBODY &&d_body, + static void run(const int N, DBODY &&d_body, const int X, const int Y, const int Z, const int G) { CuWrap2D(N, d_body, X, Y, Z); @@ -435,7 +435,7 @@ template <> struct CuWrap<3> { template - void operator()(const int N, DBODY &&d_body, + static void run(const int N, DBODY &&d_body, const int X, const int Y, const int Z, const int G) { CuWrap3D(N, d_body, X, Y, Z, G); @@ -508,7 +508,7 @@ template <> struct HipWrap<1> { template - void operator()(const int N, DBODY &&d_body, + static void run(const int N, DBODY &&d_body, const int X, const int Y, const int Z, const int G) { HipWrap1D(N, d_body); @@ -519,7 +519,7 @@ template <> struct HipWrap<2> { template - void operator()(const int N, DBODY &&d_body, + static void run(const int N, DBODY &&d_body, const int X, const int Y, const int Z, const int G) { HipWrap2D(N, d_body, X, Y, Z); @@ -530,7 +530,7 @@ template <> struct HipWrap<3> { template - void operator()(const int N, DBODY &&d_body, + static void run(const int N, DBODY &&d_body, const int X, const int Y, const int Z, const int G) { HipWrap3D(N, d_body, X, Y, Z, G); @@ -558,7 +558,7 @@ inline void ForallWrap(const bool use_dev, const int N, // If Backend::RAJA_CUDA is allowed, use it if (Device::Allows(Backend::RAJA_CUDA)) { - return RajaCuWrap(N, d_body, X, Y, Z, G); + return RajaCuWrap::run(N, d_body, X, Y, Z, G); } #endif @@ -566,7 +566,7 @@ inline void ForallWrap(const bool use_dev, const int N, // If Backend::RAJA_HIP is allowed, use it if (Device::Allows(Backend::RAJA_HIP)) { - return RajaHipWrap(N, d_body, X, Y, Z, G); + return RajaHipWrap::run(N, d_body, X, Y, Z, G); } #endif @@ -574,7 +574,7 @@ inline void ForallWrap(const bool use_dev, const int N, // If Backend::CUDA is allowed, use it if (Device::Allows(Backend::CUDA)) { - return CuWrap(N, d_body, X, Y, Z, G); + return CuWrap::run(N, d_body, X, Y, Z, G); } #endif @@ -582,7 +582,7 @@ inline void ForallWrap(const bool use_dev, const int N, // If Backend::HIP is allowed, use it if (Device::Allows(Backend::HIP)) { - return HipWrap(N, d_body, X, Y, Z, G); + return HipWrap::run(N, d_body, X, Y, Z, G); } #endif From 1ed1a29e689ca2f7cfff92f05cc6a82b188ccebe Mon Sep 17 00:00:00 2001 From: Keith Date: Mon, 18 Oct 2021 12:07:22 -0700 Subject: [PATCH 174/198] PR checklist --- .gitignore | 2 ++ CHANGELOG | 5 +++++ examples/CMakeLists.txt | 2 ++ examples/{osc.cpp => ex30.cpp} | 0 examples/{oscp.cpp => ex30p.cpp} | 0 examples/makefile | 4 ++-- 6 files changed, 11 insertions(+), 2 deletions(-) rename examples/{osc.cpp => ex30.cpp} (100%) rename examples/{oscp.cpp => ex30p.cpp} (100%) diff --git a/.gitignore b/.gitignore index d33970f01f..6a87f952e7 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,8 @@ examples/ex1[04-9] examples/ex1[0-9]p examples/ex2[0-9] examples/ex2[0-9]p +examples/ex30 +examples/ex30p examples/refined.mesh examples/displaced.mesh diff --git a/CHANGELOG b/CHANGELOG index 3c98fcb26a..865f935411 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,6 +10,11 @@ Version 4.3.1 (development) =========================== + +- Added support for mesh preprocessing to resolve fine scale problem data + before simulation. This feature uses adaptive mesh refinement to control the + associated data oscillation error. Its usage is demonstrated in ex30. + - Switched from Artistic Style (astyle) version 2.05.1 to version 3.1 for code formatting. See the "make style" target. diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index a10dc42950..710505163d 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -37,6 +37,7 @@ list(APPEND ALL_EXE_SRCS ex27.cpp ex28.cpp ex29.cpp + ex30.cpp ) if (MFEM_USE_MPI) @@ -70,6 +71,7 @@ if (MFEM_USE_MPI) ex27p.cpp ex28p.cpp ex29p.cpp + ex30p.cpp ) endif() diff --git a/examples/osc.cpp b/examples/ex30.cpp similarity index 100% rename from examples/osc.cpp rename to examples/ex30.cpp diff --git a/examples/oscp.cpp b/examples/ex30p.cpp similarity index 100% rename from examples/oscp.cpp rename to examples/ex30p.cpp diff --git a/examples/makefile b/examples/makefile index 9369194ee2..769beae789 100644 --- a/examples/makefile +++ b/examples/makefile @@ -22,10 +22,10 @@ MFEM_LIB_FILE = mfem_is_not_built -include $(CONFIG_MK) SEQ_EXAMPLES = ex0 ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 ex10 ex14 ex15 ex16 \ - ex17 ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25 ex26 ex27 ex28 ex29 + ex17 ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25 ex26 ex27 ex28 ex29 ex30 PAR_EXAMPLES = ex0p ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex8p ex9p ex10p ex11p \ ex12p ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p ex22p ex24p \ - ex25p ex26p ex27p ex28p ex29p + ex25p ex26p ex27p ex28p ex29p ex30p SEQ_DEVICE_EXAMPLES = ex1 ex3 ex4 ex5 ex6 ex9 ex22 ex24 ex25 ex26 PAR_DEVICE_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex22p \ ex24p ex25p ex26p From 23f110f9b7a5edcfa31c78a1c6be1a257aec4e64 Mon Sep 17 00:00:00 2001 From: Tzanio Kolev Date: Tue, 19 Oct 2021 12:13:59 -0700 Subject: [PATCH 175/198] Adding Mark and Will as MFEM editors --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 15a6eb749f..2484ab4940 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -427,6 +427,8 @@ The current list of MFEM editors is: - @v-dobrev (Veselin Dobrev) - @tzanio (Tzanio Kolev) +- @pazner (Will Pazner) +- @mlstowell (Mark Stowell) **The responsibilities of the editors are:** From 4056c529a17d4d5903b57e3273f417438fa591f3 Mon Sep 17 00:00:00 2001 From: Tom Stitt Date: Tue, 19 Oct 2021 15:06:15 -0700 Subject: [PATCH 176/198] additional template instances --- fem/bilininteg_diffusion_pa.cpp | 3 +++ fem/bilininteg_mass_pa.cpp | 2 ++ 2 files changed, 5 insertions(+) diff --git a/fem/bilininteg_diffusion_pa.cpp b/fem/bilininteg_diffusion_pa.cpp index 10a384db19..b117ce21d8 100644 --- a/fem/bilininteg_diffusion_pa.cpp +++ b/fem/bilininteg_diffusion_pa.cpp @@ -903,9 +903,11 @@ static void PADiffusionAssembleDiagonal(const int dim, { switch ((D1D << 4 ) | Q1D) { + case 0x22: return SmemPADiffusionDiagonal3D<2,2>(NE,symm,B,G,D,Y); case 0x23: return SmemPADiffusionDiagonal3D<2,3>(NE,symm,B,G,D,Y); case 0x34: return SmemPADiffusionDiagonal3D<3,4>(NE,symm,B,G,D,Y); case 0x45: return SmemPADiffusionDiagonal3D<4,5>(NE,symm,B,G,D,Y); + case 0x46: return SmemPADiffusionDiagonal3D<4,6>(NE,symm,B,G,D,Y); case 0x56: return SmemPADiffusionDiagonal3D<5,6>(NE,symm,B,G,D,Y); case 0x67: return SmemPADiffusionDiagonal3D<6,7>(NE,symm,B,G,D,Y); case 0x78: return SmemPADiffusionDiagonal3D<7,8>(NE,symm,B,G,D,Y); @@ -1877,6 +1879,7 @@ static void PADiffusionApply(const int dim, { switch (ID) { + case 0x22: return SmemPADiffusionApply3D<2,2>(NE,symm,B,G,D,X,Y); case 0x23: return SmemPADiffusionApply3D<2,3>(NE,symm,B,G,D,X,Y); case 0x34: return SmemPADiffusionApply3D<3,4>(NE,symm,B,G,D,X,Y); case 0x45: return SmemPADiffusionApply3D<4,5>(NE,symm,B,G,D,X,Y); diff --git a/fem/bilininteg_mass_pa.cpp b/fem/bilininteg_mass_pa.cpp index 5f5089919f..fdf519e9ae 100644 --- a/fem/bilininteg_mass_pa.cpp +++ b/fem/bilininteg_mass_pa.cpp @@ -1203,8 +1203,10 @@ static void PAMassApply(const int dim, { switch (id) { + case 0x22: return SmemPAMassApply3D<2,2>(NE,B,Bt,D,X,Y); case 0x23: return SmemPAMassApply3D<2,3>(NE,B,Bt,D,X,Y); case 0x24: return SmemPAMassApply3D<2,4>(NE,B,Bt,D,X,Y); + case 0x26: return SmemPAMassApply3D<2,6>(NE,B,Bt,D,X,Y); case 0x34: return SmemPAMassApply3D<3,4>(NE,B,Bt,D,X,Y); case 0x35: return SmemPAMassApply3D<3,5>(NE,B,Bt,D,X,Y); case 0x36: return SmemPAMassApply3D<3,6>(NE,B,Bt,D,X,Y); From be505c9af0a14c7d01a23a9f9e092eb609472b5f Mon Sep 17 00:00:00 2001 From: Keith Date: Wed, 20 Oct 2021 17:00:50 -0700 Subject: [PATCH 177/198] fixed function signature which creates PyMFEM conflict --- linalg/operator.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linalg/operator.hpp b/linalg/operator.hpp index 1e454a79f2..d9ca0e5839 100644 --- a/linalg/operator.hpp +++ b/linalg/operator.hpp @@ -242,7 +242,7 @@ public: void FormDiscreteOperator(Operator* &A); /// Prints operator with input size n and output size m in Matlab format. - void PrintMatlab(std::ostream & out, int n = 0, int m = 0) const; + void PrintMatlab(std::ostream & out, int n, int m = 0) const; /// Prints operator in Matlab format. virtual void PrintMatlab(std::ostream & out) const; From 3b232cd0c18025fde9ad407c9a440bd09522751b Mon Sep 17 00:00:00 2001 From: Tzanio Kolev Date: Sun, 24 Oct 2021 19:50:19 -0700 Subject: [PATCH 178/198] Update CHANGELOG --- CHANGELOG | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 865f935411..3e0c2c185d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,10 +10,9 @@ Version 4.3.1 (development) =========================== - - Added support for mesh preprocessing to resolve fine scale problem data before simulation. This feature uses adaptive mesh refinement to control the - associated data oscillation error. Its usage is demonstrated in ex30. + associated data oscillation error. See the new Example 30/30p. - Switched from Artistic Style (astyle) version 2.05.1 to version 3.1 for code formatting. See the "make style" target. From f991e44f2ecef6d818694b4e7ef2bbbe0f2d05da Mon Sep 17 00:00:00 2001 From: Tzanio Kolev Date: Sun, 24 Oct 2021 19:51:52 -0700 Subject: [PATCH 179/198] Update ex30.cpp --- examples/ex30.cpp | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/examples/ex30.cpp b/examples/ex30.cpp index 8969e0918d..ea1f37fa1d 100644 --- a/examples/ex30.cpp +++ b/examples/ex30.cpp @@ -1,18 +1,18 @@ -// MFEM Example 30+ +// MFEM Example 30 // -// Compile with: make osc +// Compile with: make ex30 // -// Sample runs: osc -m ../data/square-disc.mesh -o 1 -// osc -m ../data/square-disc.mesh -o 2 -// osc -m ../data/square-disc.mesh -o 2 -me 1e3 -// osc -m ../data/square-disc-nurbs.mesh -o 2 -// osc -m ../data/star.mesh -o 2 -eo 4 -// osc -m ../data/fichera.mesh -o 2 -me 1e4 -// osc -m ../data/disc-nurbs.mesh -o 2 -// osc -m ../data/ball-nurbs.mesh -o 2 -eo 3 -e 1e-2 -me 1e4 -// osc -m ../data/star-surf.mesh -o 2 -// osc -m ../data/square-disc-surf.mesh -o 2 -// osc -m ../data/amr-quad.mesh -l 2 +// Sample runs: ex30 -m ../data/square-disc.mesh -o 1 +// ex30 -m ../data/square-disc.mesh -o 2 +// ex30 -m ../data/square-disc.mesh -o 2 -me 1e3 +// ex30 -m ../data/square-disc-nurbs.mesh -o 2 +// ex30 -m ../data/star.mesh -o 2 -eo 4 +// ex30 -m ../data/fichera.mesh -o 2 -me 1e4 +// ex30 -m ../data/disc-nurbs.mesh -o 2 +// ex30 -m ../data/ball-nurbs.mesh -o 2 -eo 3 -e 1e-2 -me 1e4 +// ex30 -m ../data/star-surf.mesh -o 2 +// ex30 -m ../data/square-disc-surf.mesh -o 2 +// ex30 -m ../data/amr-quad.mesh -l 2 // // Description: This is an example of adaptive mesh refinement preprocessing // which lowers the data oscillation [1] to a user-defined @@ -34,8 +34,6 @@ // problems for testing adaptive grid refinement algorithms. // Applied mathematics and computation, 220, 350-364. - - #include "mfem.hpp" #include #include @@ -193,6 +191,5 @@ int main(int argc, char *argv[]) sol_sock.precision(8); sol_sock << "mesh\n" << mesh << flush; - return 0; } From ca612b872fa0308b42baf7dcffc082d4e643d9e5 Mon Sep 17 00:00:00 2001 From: Tzanio Kolev Date: Sun, 24 Oct 2021 19:52:46 -0700 Subject: [PATCH 180/198] Update ex30p.cpp --- examples/ex30p.cpp | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/examples/ex30p.cpp b/examples/ex30p.cpp index 39b0084eb1..1f7d8456c5 100644 --- a/examples/ex30p.cpp +++ b/examples/ex30p.cpp @@ -1,18 +1,18 @@ -// MFEM Example 30+ - Parallel Version +// MFEM Example 30 - Parallel Version // -// Compile with: make oscp +// Compile with: make ex30p // -// Sample runs: mpirun -np 4 oscp -m ../data/square-disc.mesh -o 1 -// mpirun -np 4 oscp -m ../data/square-disc.mesh -o 2 -// mpirun -np 4 oscp -m ../data/square-disc.mesh -o 2 -me 1e3 -// mpirun -np 4 oscp -m ../data/square-disc-nurbs.mesh -o 2 -// mpirun -np 4 oscp -m ../data/star.mesh -o 2 -eo 4 +// Sample runs: mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 1 +// mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 2 +// mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 2 -me 1e3 +// mpirun -np 4 ex30p -m ../data/square-disc-nurbs.mesh -o 2 +// mpirun -np 4 ex30p -m ../data/star.mesh -o 2 -eo 4 // mpirun -np 4 oscp -m ../data/fichera.mesh -o 2 -me 1e4 -// mpirun -np 4 oscp -m ../data/disc-nurbs.mesh -o 2 -// mpirun -np 4 oscp -m ../data/ball-nurbs.mesh -o 2 -eo 3 -e 1e-2 -// mpirun -np 4 oscp -m ../data/star-surf.mesh -o 2 -// mpirun -np 4 oscp -m ../data/square-disc-surf.mesh -o 2 -// mpirun -np 4 oscp -m ../data/amr-quad.mesh -l 2 +// mpirun -np 4 ex30p -m ../data/disc-nurbs.mesh -o 2 +// mpirun -np 4 ex30p -m ../data/ball-nurbs.mesh -o 2 -eo 3 -e 1e-2 +// mpirun -np 4 ex30p -m ../data/star-surf.mesh -o 2 +// mpirun -np 4 ex30p -m ../data/square-disc-surf.mesh -o 2 +// mpirun -np 4 ex30p -m ../data/amr-quad.mesh -l 2 // // Description: This is an example of adaptive mesh refinement preprocessing // which lowers the data oscillation [1] to a user-defined @@ -34,7 +34,6 @@ // problems for testing adaptive grid refinement algorithms. // Applied mathematics and computation, 220, 350-364. - #include "mfem.hpp" #include #include @@ -42,7 +41,6 @@ using namespace std; using namespace mfem; - // Piecewise-affine function which is sometimes mesh-conforming double affine_function(const Vector &p) { @@ -238,7 +236,6 @@ int main(int argc, char *argv[]) sol_sock << "parallel " << num_procs << " " << myid << "\n"; sol_sock << "mesh\n" << pmesh << flush; - MPI_Finalize(); return 0; } From 7d5bf6e3e11d4f8f13e3d7c855769feffb592847 Mon Sep 17 00:00:00 2001 From: Tzanio Date: Mon, 25 Oct 2021 10:17:05 -0700 Subject: [PATCH 181/198] square01_tri.mesh -> square01-tri.mesh; other minor --- CHANGELOG | 2 +- miniapps/meshing/mesh-optimizer.cpp | 3 +-- miniapps/meshing/pmesh-optimizer.cpp | 3 +-- miniapps/meshing/{square01_tri.mesh => square01-tri.mesh} | 0 4 files changed, 3 insertions(+), 5 deletions(-) rename miniapps/meshing/{square01_tri.mesh => square01-tri.mesh} (100%) diff --git a/CHANGELOG b/CHANGELOG index 53f31c535f..50914b18b7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -51,7 +51,7 @@ Version 4.3.1 (development) - Remove the 'u' flag in the ar command, to update all files in the archive, avoiding file name collisions from different subdirectories. - + - Added initial TMOP-based capabilities for surface fitting and tangential relaxation in the mesh-optimizer and pmesh-optimizer miniapps. diff --git a/miniapps/meshing/mesh-optimizer.cpp b/miniapps/meshing/mesh-optimizer.cpp index 03505a6bb3..913bcc10ab 100644 --- a/miniapps/meshing/mesh-optimizer.cpp +++ b/miniapps/meshing/mesh-optimizer.cpp @@ -71,7 +71,7 @@ // // Adaptive surface fitting: // mesh-optimizer -m square01.mesh -o 3 -rs 1 -mid 58 -tid 1 -ni 200 -vl 1 -sfc 5e4 -rtol 1e-5 -nor -// mesh-optimizer -m square01_tri.mesh -o 3 -rs 0 -mid 58 -tid 1 -ni 200 -vl 1 -sfc 1e4 -rtol 1e-5 -nor +// mesh-optimizer -m square01-tri.mesh -o 3 -rs 0 -mid 58 -tid 1 -ni 200 -vl 1 -sfc 1e4 -rtol 1e-5 -nor // // Blade shape: // mesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -ni 30 -ls 3 -art 1 -bnd -qt 1 -qo 8 @@ -100,7 +100,6 @@ // mesh-optimizer -m jagged.mesh -o 2 -mid 22 -tid 1 -ni 50 -li 50 -qo 4 -fd -vl 1 // 3D untangling (the mesh is in the mfem/data GitHub repository): // * mesh-optimizer -m ../../../mfem_data/cube-holes-inv.mesh -o 3 -mid 313 -tid 1 -rtol 1e-5 -li 50 -qo 4 -fd -vl 1 -// #include "mfem.hpp" #include "../common/mfem-common.hpp" diff --git a/miniapps/meshing/pmesh-optimizer.cpp b/miniapps/meshing/pmesh-optimizer.cpp index 83d306259e..4633defb70 100644 --- a/miniapps/meshing/pmesh-optimizer.cpp +++ b/miniapps/meshing/pmesh-optimizer.cpp @@ -71,7 +71,7 @@ // // Adaptive surface fitting: // mpirun -np 4 pmesh-optimizer -m square01.mesh -o 3 -rs 1 -mid 58 -tid 1 -ni 200 -vl 1 -sfc 5e4 -rtol 1e-5 -nor -// mpirun -np 4 pmesh-optimizer -m square01_tri.mesh -o 3 -rs 0 -mid 58 -tid 1 -ni 200 -vl 1 -sfc 1e4 -rtol 1e-5 -nor +// mpirun -np 4 pmesh-optimizer -m square01-tri.mesh -o 3 -rs 0 -mid 58 -tid 1 -ni 200 -vl 1 -sfc 1e4 -rtol 1e-5 -nor // // Blade shape: // mpirun -np 4 pmesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -ni 30 -ls 3 -art 1 -bnd -qt 1 -qo 8 @@ -100,7 +100,6 @@ // mpirun -np 4 pmesh-optimizer -m jagged.mesh -o 2 -mid 22 -tid 1 -ni 50 -li 50 -qo 4 -fd -vl 1 // 3D untangling (the mesh is in the mfem/data GitHub repository): // * mpirun -np 4 pmesh-optimizer -m ../../../mfem_data/cube-holes-inv.mesh -o 3 -mid 313 -tid 1 -rtol 1e-5 -li 50 -qo 4 -fd -vl 1 -// #include "mfem.hpp" #include "../common/mfem-common.hpp" diff --git a/miniapps/meshing/square01_tri.mesh b/miniapps/meshing/square01-tri.mesh similarity index 100% rename from miniapps/meshing/square01_tri.mesh rename to miniapps/meshing/square01-tri.mesh From 26fdb711bcb68281916545aa665d34d8949035c9 Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Wed, 27 Oct 2021 16:45:13 -0700 Subject: [PATCH 182/198] remove default dirichlet level set --- miniapps/shifted/diffusion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index 63c91acb35..f68af37d8f 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -100,7 +100,7 @@ int main(int argc, char *argv[]) int order = 2; bool visualization = true; int ser_ref_levels = 0; - int dirichlet_level_set_type = 1; + int dirichlet_level_set_type = -1; int neumann_level_set_type = -1; bool dirichlet_combo = false; int ho_terms = 0; From b8590d7c0ff2fde064e2f0926dbdf5cef387b630 Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Fri, 29 Oct 2021 09:02:46 -0700 Subject: [PATCH 183/198] set level set if none is specified --- miniapps/shifted/diffusion.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index f68af37d8f..bc6e9de131 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -144,8 +144,11 @@ int main(int argc, char *argv[]) } if (myid == 0) { args.PrintOptions(cout); } - MFEM_VERIFY(dirichlet_level_set_type >= 0 || neumann_level_set_type >= 0, - "The level set and type of BC are not specified."); + // Use Dirichlet level set if no level sets are specified. + if (dirichlet_level_set_type == 0 || neumann_level_set_type == 0) + { + dirichlet_level_set_type = 1; + } MFEM_VERIFY((neumann_level_set_type >= 0 && ho_terms < 1) == false, "Shifted Neumann BC requires extra terms, i.e., -ho >= 1."); From fa2e6fa2f4a1e9c86e4ebbc990a2c5d68136701a Mon Sep 17 00:00:00 2001 From: Ketan Mittal Date: Fri, 29 Oct 2021 13:58:29 -0700 Subject: [PATCH 184/198] minor --- miniapps/shifted/diffusion.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/miniapps/shifted/diffusion.cpp b/miniapps/shifted/diffusion.cpp index bc6e9de131..7311bec91c 100644 --- a/miniapps/shifted/diffusion.cpp +++ b/miniapps/shifted/diffusion.cpp @@ -145,11 +145,11 @@ int main(int argc, char *argv[]) if (myid == 0) { args.PrintOptions(cout); } // Use Dirichlet level set if no level sets are specified. - if (dirichlet_level_set_type == 0 || neumann_level_set_type == 0) + if (dirichlet_level_set_type <= 0 && neumann_level_set_type <= 0) { dirichlet_level_set_type = 1; } - MFEM_VERIFY((neumann_level_set_type >= 0 && ho_terms < 1) == false, + MFEM_VERIFY((neumann_level_set_type > 0 && ho_terms < 1) == false, "Shifted Neumann BC requires extra terms, i.e., -ho >= 1."); // Enable hardware devices such as GPUs, and programming models such as CUDA, From 5565b5066f1db043f85877aa2cb3612d1fc23dda Mon Sep 17 00:00:00 2001 From: Veselin Dobrev Date: Wed, 3 Nov 2021 02:15:58 -0700 Subject: [PATCH 185/198] Update Gitlab CI to fix some issues: - Use a single (per user) clone of the internal MFEM/autotests repo - Use a single (per user) clone of the Github MFEM/data repo - Properly set the location for the pipeline-common temporary directory, BUILD_ROOT; add a cleanup step for BUILD_ROOT - Various other small tweaks and additions --- .gitlab-ci.yml | 14 ++- .gitlab/configs/common.yml | 15 ++- .gitlab/configs/corona-config.yml | 15 ++- .gitlab/configs/lassen-config.yml | 14 ++- .gitlab/configs/quartz-config.yml | 15 ++- .gitlab/configs/setup-baseline.yml | 44 +++++++-- .gitlab/configs/setup-build-and-test.yml | 83 +++++++++++++--- .gitlab/corona-build-and-test.yml | 16 ++-- .gitlab/lassen-build-and-test.yml | 13 +-- .gitlab/quartz-baseline.yml | 95 ++++++++++++++++--- .gitlab/quartz-build-and-test.yml | 16 ++-- .gitlab/scripts/baseline | 10 +- .gitlab/scripts/report_build_and_test_failure | 27 ++++-- .gitlab/scripts/report_build_and_test_success | 26 +++-- tests/gitlab/README.md | 5 +- tests/gitlab/build_and_test | 64 ++++++++----- .../gitlab/reproduce-ci-jobs-interactively.md | 8 +- 17 files changed, 351 insertions(+), 129 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 60cb4c655f..0fdfb85f2e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,36 +29,34 @@ stages: variables: CUSTOM_CI_BUILDS_DIR: "/usr/workspace/mfem/gitlab-runner" + USER_CI_TOP_DIR: "${CUSTOM_CI_BUILDS_DIR}/${GITLAB_USER_LOGIN}" + SHARED_REPOS_DIR: "${USER_CI_TOP_DIR}/repos" + AUTOTEST_ROOT: "${SHARED_REPOS_DIR}" + # MFEM_DATA_DIR is setup in '.gitlab/configs/setup-build-and-test.yml' and + # used in '.gitlab/configs/-config.yml': + MFEM_DATA_DIR: "${SHARED_REPOS_DIR}/mfem-data" # Trigger subpipelines: quartz-build-and-test: stage: sub-pipelines - variables: - _AUTOTEST: $AUTOTEST trigger: include: .gitlab/quartz-build-and-test.yml strategy: depend quartz-baseline: stage: sub-pipelines - variables: - _AUTOTEST: $AUTOTEST trigger: include: .gitlab/quartz-baseline.yml strategy: depend lassen-build-and-test: stage: sub-pipelines - variables: - _AUTOTEST: $AUTOTEST trigger: include: .gitlab/lassen-build-and-test.yml strategy: depend corona-build-and-test: stage: sub-pipelines - variables: - _AUTOTEST: $AUTOTEST trigger: include: .gitlab/corona-build-and-test.yml strategy: depend diff --git a/.gitlab/configs/common.yml b/.gitlab/configs/common.yml index b796125892..1e0ba44312 100644 --- a/.gitlab/configs/common.yml +++ b/.gitlab/configs/common.yml @@ -18,7 +18,7 @@ variables: # the pipeline, preventing any form of concurrency with other pipelines. This # also means that the BUILD_ROOT directory will never be cleaned. # TODO: add a clean-up mechanism - BUILD_ROOT: ${CI_BUILDS_DIR}/MFEM_${MACHINE_NAME}/${CI_PROJECT_NAME}_${CI_COMMIT_REF_SLUG}_${CI_PIPELINE_ID} + BUILD_ROOT: ${USER_CI_TOP_DIR}/${CI_PROJECT_NAME}-${MACHINE_NAME}-pipeline-${CI_PIPELINE_ID} # On LLNL's quartz, there is only one allocation shared among jobs in order to # save time and resource. This allocation has to be uniquely named so that we @@ -28,8 +28,15 @@ variables: # Defines the default choice for updating the saved baseline results. By default # the baseline can only be updated from the master branch. This variable offers # the option to manually ask for rebaselining from another branch if necessary. - _REBASELINE: "NO" - _AUTOTEST: "NO" + REBASELINE: "NO" + AUTOTEST: "NO" + # AUTOTEST_COMMIT: used only when AUTOTEST is set to YES. + # * If AUTOTEST_COMMIT is set to YES (default), reporting jobs will commit + # their files to the MFEM/autotest repo. + # * If AUTOTEST_COMMIT is set to NO, reporting jobs will NOT commit their + # files to the MFEM/autotest repo. Instead they will just show the contents + # of the report files and remove them. + AUTOTEST_COMMIT: "YES" # Git repositories used in the pipeline TPLS_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/tpls.git @@ -40,5 +47,3 @@ variables: # Directory used to place artifacts. ARTIFACTS_DIR: artifacts SLURM_OVERLAP: 1 - - diff --git a/.gitlab/configs/corona-config.yml b/.gitlab/configs/corona-config.yml index f6362d7071..c39a270c63 100644 --- a/.gitlab/configs/corona-config.yml +++ b/.gitlab/configs/corona-config.yml @@ -26,17 +26,20 @@ variables: - if: '$CI_COMMIT_BRANCH =~ /_cnone/ || $ON_CORONA != "ON"' when: never # Don’t run autotest update if... - - if: '$CI_JOB_NAME =~ /report/ && $_AUTOTEST != "YES"' + - if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "YES"' when: never # Report success on success status - - if: '$CI_JOB_NAME =~ /report_job_success/ && $_AUTOTEST == "YES"' + - if: '$CI_JOB_NAME =~ /report_job_success/ && $AUTOTEST == "YES"' when: on_success # Report failure on failure status - - if: '$CI_JOB_NAME =~ /report_job_failure/ && $_AUTOTEST == "YES"' + - if: '$CI_JOB_NAME =~ /report_job_failure/ && $AUTOTEST == "YES"' when: on_failure # Always release resource - if: '$CI_JOB_NAME =~ /release_resource/' when: always + # Always cleanup + - if: '$CI_JOB_NAME =~ /cleanup/' + when: always # Default is to run if previous stage succeeded - when: on_success @@ -46,9 +49,11 @@ variables: extends: [.on_corona] stage: build_and_test script: + # THREADS is used by 'tests/gitlab/build_and_test', run below - export THREADS=12 - echo ${ALLOC_NAME} - export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A) - echo ${JOBID} - - srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 15 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --build-root "${BUILD_ROOT}" --data - + - echo ${MFEM_DATA_DIR} + - echo ${SPEC} + - srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 15 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data diff --git a/.gitlab/configs/lassen-config.yml b/.gitlab/configs/lassen-config.yml index bedc802470..e5adc94596 100644 --- a/.gitlab/configs/lassen-config.yml +++ b/.gitlab/configs/lassen-config.yml @@ -21,14 +21,17 @@ variables: - if: '$CI_COMMIT_BRANCH =~ /_lnone/ || $ON_LASSEN == "OFF"' #run except if ... when: never # Don't run autotest update if... - - if: '$CI_JOB_NAME =~ /report/ && $_AUTOTEST != "YES"' + - if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "YES"' when: never # Report success on success status - - if: '$CI_JOB_NAME =~ /report_job_success/ && $_AUTOTEST == "YES"' + - if: '$CI_JOB_NAME =~ /report_job_success/ && $AUTOTEST == "YES"' when: on_success # Report failure on failure status - - if: '$CI_JOB_NAME =~ /report_job_failure/ && $_AUTOTEST == "YES"' + - if: '$CI_JOB_NAME =~ /report_job_failure/ && $AUTOTEST == "YES"' when: on_failure + # Always cleanup + - if: '$CI_JOB_NAME =~ /cleanup/' + when: always - when: on_success # Lassen uses a different job scheduler (spectrum lsf) that does not allow @@ -39,5 +42,8 @@ variables: extends: [.on_lassen] stage: build_and_test script: - - lalloc 1 -W 30 -q pdebug tests/gitlab/build_and_test --spec "${SPEC}" --build-root "${BUILD_ROOT}" --data + - echo ${MFEM_DATA_DIR} + - echo ${SPEC} + # Next script uses 'THREADS': leaving it empty --> it uses 'make all -j' + - lalloc 1 -W 30 -q pdebug tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data needs: [setup] diff --git a/.gitlab/configs/quartz-config.yml b/.gitlab/configs/quartz-config.yml index 774313c9f6..06a6d44170 100644 --- a/.gitlab/configs/quartz-config.yml +++ b/.gitlab/configs/quartz-config.yml @@ -22,17 +22,20 @@ variables: - if: '$CI_COMMIT_BRANCH =~ /_qnone/ || $ON_QUARTZ == "OFF"' when: never # Don't run autotest update if... - - if: '$CI_JOB_NAME =~ /report/ && $_AUTOTEST != "YES"' + - if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "YES"' when: never # Report success on success status - - if: '$CI_JOB_NAME =~ /report_job_success/ && $_AUTOTEST == "YES"' + - if: '$CI_JOB_NAME =~ /report_job_success/ && $AUTOTEST == "YES"' when: on_success # Report failure on failure status - - if: '$CI_JOB_NAME =~ /report_job_failure/ && $_AUTOTEST == "YES"' + - if: '$CI_JOB_NAME =~ /report_job_failure/ && $AUTOTEST == "YES"' when: on_failure # Always release resource - if: '$CI_JOB_NAME =~ /release_resource/' when: always + # Always cleanup + - if: '$CI_JOB_NAME =~ /cleanup/' + when: always # Default is to run if previous stage succeeded - when: on_success @@ -42,9 +45,11 @@ variables: extends: [.on_quartz] stage: build_and_test script: + # THREADS is used by 'tests/gitlab/build_and_test', run below - export THREADS=12 - echo ${ALLOC_NAME} - export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A) - echo ${JOBID} - - srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 30 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --build-root "${BUILD_ROOT}" --data - + - echo ${MFEM_DATA_DIR} + - echo ${SPEC} + - srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 30 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data diff --git a/.gitlab/configs/setup-baseline.yml b/.gitlab/configs/setup-baseline.yml index ee07dfd17f..8aaaca1e02 100644 --- a/.gitlab/configs/setup-baseline.yml +++ b/.gitlab/configs/setup-baseline.yml @@ -9,13 +9,6 @@ # terms of the BSD-3 license. We welcome feedback and contributions, see file # CONTRIBUTING.md for details. -# TPLS_DIR is used in .gitlab/scripts/baseline to provide the tpls location -# when call the runtest script in MFEM test repo. -# Note: the value must be consistent with what setup_baseline does. -variables: - TPLS_DIR: ${BUILD_ROOT}/tpls - AUTOTEST_ROOT: ${CI_BUILDS_DIR}/MFEM_${MACHINE_NAME}_baseline - # The setup_baseline job doesn't rely on MFEM git repo. It prepares a # pipeline-wide working directory downloading/updating external repos. # TODO: @@ -30,13 +23,46 @@ setup_baseline: variables: GIT_STRATEGY: none script: + # + # Setup ${BUILD_ROOT}/tpls and ${BUILD_ROOT}/tests: + # - echo "BUILD_ROOT ${BUILD_ROOT}" - mkdir -p ${BUILD_ROOT} && cd ${BUILD_ROOT} - if [ ! -d "tpls" ]; then git clone ${TPLS_REPO}; fi - if [ ! -d "tests" ]; then git clone ${TESTS_REPO}; fi - cd tpls && git pull && cd .. - cd tests && git pull origin && cd .. + # + # Setup ${AUTOTEST_ROOT}/autotest: + # - echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}" - mkdir -p ${AUTOTEST_ROOT} && cd ${AUTOTEST_ROOT} - - if [ ! -d "autotest" ]; then git clone ${AUTOTEST_REPO}; fi - - cd autotest && git pull && cd .. + - command -v flock || echo "Required command 'flock' not found" + - | + ( + date + echo "Waiting to aquire lock on '$PWD/autotest.lock' ..." + # try to get an excusive lock on fd 9 (autotest.lock) repeating the try + # every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the + # command to hang indefinitely sometimes, so we use the timeout & retry + # as a workaround; we may want to add a counter for the number of + # retries to interrupt a potential infinite loop + while ! flock -w 5 9; do + true + done + echo "Aquired lock on '$PWD/autotest.lock'" + date + # clone/update the autotest repo while holding the file lock on + # 'autotest.lock' + err=0 + if [[ ! -d "autotest" ]]; then + git clone ${AUTOTEST_REPO} + else + cd autotest && git pull && cd .. + fi || err=1 + # sleep for a period to allow NFS to propagate the above changes; + # clearly, there is no guarantee that other NFS clients will see the + # changes even after the timeout + sleep 10 + exit $err + ) 9> autotest.lock diff --git a/.gitlab/configs/setup-build-and-test.yml b/.gitlab/configs/setup-build-and-test.yml index 84838f36af..109c37cba9 100644 --- a/.gitlab/configs/setup-build-and-test.yml +++ b/.gitlab/configs/setup-build-and-test.yml @@ -9,13 +9,10 @@ # terms of the BSD-3 license. We welcome feedback and contributions, see file # CONTRIBUTING.md for details. -variables: - AUTOTEST_ROOT: ${CI_BUILDS_DIR}/MFEM_${MACHINE_NAME}_build_and_test - -# setup clones the mfem/data repo in ${BUILD_ROOT}. The build_and_test script -# then symlinks the repo to the parent directory of the MFEM source directory. -# Unit tests that depend on the mfem/data repo will then detect that this -# directory is present and be enabled. +# Setup clones the mfem/data repo in ${SHARED_REPOS_DIR}. The build_and_test +# script then symlinks the repo to the parent directory of the MFEM source +# directory. Unit tests that depend on the mfem/data repo will then detect that +# this directory is present and be enabled. setup: tags: - shell @@ -24,11 +21,71 @@ setup: variables: GIT_STRATEGY: none script: - - echo "BUILD_ROOT ${BUILD_ROOT}" - - mkdir -p ${BUILD_ROOT} && cd ${BUILD_ROOT} - - if [ ! -d data ]; then git clone ${MFEM_DATA_REPO}; fi + # + # Setup MFEM_DATA_DIR=${SHARED_REPOS_DIR}/mfem-data, see '.gitlab-ci.yml' + # and '.gitlab/configs/-config.yml' + # + - echo "SHARED_REPOS_DIR ${SHARED_REPOS_DIR}" + - mkdir -p ${SHARED_REPOS_DIR} && cd ${SHARED_REPOS_DIR} + - command -v flock || echo "Required command 'flock' not found" + - | + ( + date + echo "Waiting to aquire lock on '$PWD/mfem-data.lock' ..." + # try to get an excusive lock on fd 9 (mfem-data.lock) repeating the try + # every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the + # command to hang indefinitely sometimes, so we use the timeout & retry + # as a workaround; we may want to add a counter for the number of + # retries to interrupt a potential infinite loop + while ! flock -w 5 9; do + true + done + echo "Aquired lock on '$PWD/mfem-data.lock'" + date + # clone/update the mfem/data repo while holding the file lock on + # 'mfem-data.lock' + err=0 + if [[ ! -d "mfem-data" ]]; then + git clone ${MFEM_DATA_REPO} "mfem-data" + else + cd "mfem-data" && git pull && cd .. + fi || err=1 + # sleep for a period to allow NFS to propagate the above changes; + # clearly, there is no guarantee that other NFS clients will see the + # changes even after the timeout + sleep 10 + exit $err + ) 9> mfem-data.lock + # + # Setup ${AUTOTEST_ROOT}/autotest: + # - echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}" - mkdir -p ${AUTOTEST_ROOT} && cd ${AUTOTEST_ROOT} - - if [ ! -d "autotest" ]; then git clone ${AUTOTEST_REPO}; fi - - cd autotest && git pull && cd .. - + - | + ( + date + echo "Waiting to aquire lock on '$PWD/autotest.lock' ..." + # try to get an excusive lock on fd 9 (autotest.lock) repeating the try + # every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the + # command to hang indefinitely sometimes, so we use the timeout & retry + # as a workaround; we may want to add a counter for the number of + # retries to interrupt a potential infinite loop + while ! flock -w 5 9; do + true + done + echo "Aquired lock on '$PWD/autotest.lock'" + date + # clone/update the autotest repo while holding the file lock on + # 'autotest.lock' + err=0 + if [[ ! -d "autotest" ]]; then + git clone ${AUTOTEST_REPO} + else + cd autotest && git pull && cd .. + fi || err=1 + # sleep for a period to allow NFS to propagate the above changes; + # clearly, there is no guarantee that other NFS clients will see the + # changes even after the timeout + sleep 10 + exit $err + ) 9> autotest.lock diff --git a/.gitlab/corona-build-and-test.yml b/.gitlab/corona-build-and-test.yml index 6ee152d624..2e5c210b25 100644 --- a/.gitlab/corona-build-and-test.yml +++ b/.gitlab/corona-build-and-test.yml @@ -22,6 +22,7 @@ allocate_resource: extends: .on_corona stage: allocate_resource script: + - echo ${ALLOC_NAME} - salloc --exclusive --nodes=1 --partition=mi60 --time=30 --no-shell --job-name=${ALLOC_NAME} timeout: 6h needs: [setup] @@ -40,24 +41,27 @@ release_resource: extends: .on_corona stage: release_resource_and_report script: + - echo ${ALLOC_NAME} - export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A) + - echo ${JOBID} - ([[ -n "${JOBID}" ]] && scancel ${JOBID}) needs: [rocm_gcc_8.3.1] # Jobs report report_job_success: - extends: .on_corona stage: release_resource_and_report - script: - - .gitlab/scripts/report_build_and_test_success + extends: + - .on_corona + - .report_job_success report_job_failure: - extends: .on_corona stage: release_resource_and_report - script: - - .gitlab/scripts/report_build_and_test_failure + extends: + - .on_corona + - .report_job_failure include: - local: .gitlab/configs/common.yml - local: .gitlab/configs/corona-config.yml - local: .gitlab/configs/setup-build-and-test.yml + - local: .gitlab/configs/report-build-and-test.yml diff --git a/.gitlab/lassen-build-and-test.yml b/.gitlab/lassen-build-and-test.yml index ce437828d0..7f7069ddda 100644 --- a/.gitlab/lassen-build-and-test.yml +++ b/.gitlab/lassen-build-and-test.yml @@ -21,18 +21,19 @@ opt_mpi_cuda_xl_16_1_1_8: # Jobs report report_job_success: - extends: .on_lassen stage: report - script: - - .gitlab/scripts/report_build_and_test_success + extends: + - .on_lassen + - .report_job_success report_job_failure: - extends: .on_lassen stage: report - script: - - .gitlab/scripts/report_build_and_test_failure + extends: + - .on_lassen + - .report_job_failure include: - local: .gitlab/configs/common.yml - local: .gitlab/configs/lassen-config.yml - local: .gitlab/configs/setup-build-and-test.yml + - local: .gitlab/configs/report-build-and-test.yml diff --git a/.gitlab/quartz-baseline.yml b/.gitlab/quartz-baseline.yml index c36680ec3a..f5be14d531 100644 --- a/.gitlab/quartz-baseline.yml +++ b/.gitlab/quartz-baseline.yml @@ -16,12 +16,26 @@ stages: - setup - baseline_check - baseline_report + - cleanup - baseline_publish baselinecheck_mfem_intel_quartz: extends: [.on_quartz] stage: baseline_check + variables: + # TPLS_DIR is used in .gitlab/scripts/baseline to provide the tpls location + # when call the runtest script in MFEM test repo. + # Note: the value must be consistent with the setup performed in + # .gitlab/configs/setup-baseline.yml. + TPLS_DIR: ${BUILD_ROOT}/tpls script: + - echo ${BUILD_ROOT} + - echo ${TPLS_DIR} + # Used by the tests in MFEM/tests: + - export MFEM_TEST_NP=32 + # The next script uses the following environment variables: + # * BASELINE_TEST, SYS_TYPE, CI_PROJECT_DIR, ARTIFACTS_DIR, + # * BUILD_ROOT, TPLS_DIR, MACHINE_NAME - .gitlab/scripts/baseline artifacts: when: always @@ -29,25 +43,74 @@ baselinecheck_mfem_intel_quartz: - ${ARTIFACTS_DIR} allow_failure: true +cleanup: + extends: .on_quartz + stage: cleanup + variables: + GIT_STRATEGY: none + script: + - echo "BUILD_ROOT=${BUILD_ROOT}" + - rm -rf "${BUILD_ROOT}" || true + report_baseline: extends: [.on_quartz] stage: baseline_report script: - - cd ${AUTOTEST_ROOT}/autotest && git pull - - mkdir -p ${MACHINE_NAME} - - rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-${BASELINE_TEST}-${CI_COMMIT_REF_SLUG}" - - rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir ${rundir}) - - cp ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/* ${rundir} - # We create an autotest-email.html file, because that's how we signal that there was a diff (temporary). + - echo ${AUTOTEST} + - echo ${AUTOTEST_COMMIT} + - echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}" + - cd ${AUTOTEST_ROOT} - | - if [[ -f ${rundir}/*.err ]] - then - echo "See the pipeline here -> $CI_PIPELINE_URL" >> ${rundir}/*.err - cp ${rundir}/*.err ${rundir}/autotest-email.html - fi - - git add ${rundir} - - git commit -am "GitLab CI log for ${BASELINE_TEST} on ${MACHINE_NAME} with intel ($(date +%Y-%m-%d))" - - git push origin master + ( + date + echo "Waiting to aquire lock on '$PWD/autotest.lock' ..." + # try to get an excusive lock on fd 9 (autotest.lock) repeating the try + # every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the + # command to hang indefinitely sometimes, so we use the timeout & retry + # as a workaround; we may want to add a counter for the number of + # retries to interrupt a potential infinite loop + while ! flock -w 5 9; do + true + done + echo "Aquired lock on '$PWD/autotest.lock'" + date + # ---------------------- + cd ${AUTOTEST_ROOT}/autotest || \ + { echo "Invalid 'autotest' dir: ${AUTOTEST_ROOT}/autotest"; exit 1; } + mkdir -p ${MACHINE_NAME} + rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-${BASELINE_TEST}-${CI_COMMIT_REF_SLUG}" + rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir ${rundir}) + cp ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/* ${rundir} + # We create an autotest-email.html file, because that's how we signal that there was a diff (temporary). + if [[ -f ${rundir}/${BASELINE_TEST}.err ]]; then + cp ${rundir}/${BASELINE_TEST}.err ${rundir}/autotest-email.html + fi + printf "%s\n" "" "Pipeline URL:" "$CI_PIPELINE_URL" \ + >> ${rundir}/pipeline.txt + msg="GitLab CI log for ${BASELINE_TEST} on ${MACHINE_NAME} ($(date +%Y-%m-%d))" + if [[ "$AUTOTEST_COMMIT" == "YES" ]]; then + git pull && \ + git add ${rundir} && \ + git commit -m "${msg}" && \ + git push origin master + else + for file in ${rundir}/*; do + echo "------------------------------" + echo "Content of '$file'" + echo "******************************" + cat $file + echo "******************************" + done + rm -rf ${rundir} || true + fi + err=$? + # ---------------------- + # sleep for a period to allow NFS to propagate the above changes; + # clearly, there is no guarantee that other NFS clients will see the + # changes even after the timeout + sleep 10 + exit $err + ) 9> autotest.lock baselinepublish_mfem_quartz: extends: [.on_quartz] @@ -56,6 +119,10 @@ baselinepublish_mfem_quartz: - if: '$CI_COMMIT_BRANCH == "master" || $REBASELINE == "YES"' when: manual script: + - echo ${BUILD_ROOT} + - echo ${PWD} + - echo ${ARTIFACTS_DIR} + - ls -lA ${ARTIFACTS_DIR} - .gitlab/scripts/rebaseline include: diff --git a/.gitlab/quartz-build-and-test.yml b/.gitlab/quartz-build-and-test.yml index ccf12fcc3b..74841d8266 100644 --- a/.gitlab/quartz-build-and-test.yml +++ b/.gitlab/quartz-build-and-test.yml @@ -22,6 +22,7 @@ allocate_resource: extends: .on_quartz stage: allocate_resource script: + - echo ${ALLOC_NAME} - salloc --exclusive --nodes=1 --partition=pdebug --time=30 --no-shell --job-name=${ALLOC_NAME} timeout: 6h @@ -73,23 +74,26 @@ release_resource: extends: .on_quartz stage: release_resource_and_report script: + - echo ${ALLOC_NAME} - export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A) + - echo ${JOBID} - ([[ -n "${JOBID}" ]] && scancel ${JOBID}) # Jobs report report_job_success: - extends: .on_quartz stage: release_resource_and_report - script: - - .gitlab/scripts/report_build_and_test_success + extends: + - .on_quartz + - .report_job_success report_job_failure: - extends: .on_quartz stage: release_resource_and_report - script: - - .gitlab/scripts/report_build_and_test_failure + extends: + - .on_quartz + - .report_job_failure include: - local: .gitlab/configs/common.yml - local: .gitlab/configs/quartz-config.yml - local: .gitlab/configs/setup-build-and-test.yml + - local: .gitlab/configs/report-build-and-test.yml diff --git a/.gitlab/scripts/baseline b/.gitlab/scripts/baseline index 9991ed8a0b..6825426097 100755 --- a/.gitlab/scripts/baseline +++ b/.gitlab/scripts/baseline @@ -20,7 +20,8 @@ base_out=${base}.out artifacts_path=${CI_PROJECT_DIR}/${ARTIFACTS_DIR} # prepare -cd ${BUILD_ROOT} +cd ${BUILD_ROOT} || \ + { echo "Invalid BUILD_ROOT=$BUILD_ROOT"; exit 1; } ln -snf ${CI_PROJECT_DIR} mfem cd tests [[ -d _${BASELINE_TEST} ]] && rm -rf _${BASELINE_TEST} @@ -33,6 +34,9 @@ elif [[ ${MACHINE_NAME} == "corona" ]]; then srun --nodes=1 -t 60 -p mi60 ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}" elif [[ ${MACHINE_NAME} == "lassen" ]]; then lalloc 1 -q pdebug ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}" +else + echo "Unknown machine: MACHINE_NAME=$MACHINE_NAME" + exit 1 fi # post @@ -60,6 +64,10 @@ then cp ${base_out} ${artifacts_path}/${base_out} fi +if [[ -f ${BASELINE_TEST}.out ]]; then + cp ${BASELINE_TEST}.out ${artifacts_path} +fi + # base_diff won't even exist if there is no difference. if [[ -f ${base_diff} ]] then diff --git a/.gitlab/scripts/report_build_and_test_failure b/.gitlab/scripts/report_build_and_test_failure index 608a4aeb5b..95d0e47f03 100755 --- a/.gitlab/scripts/report_build_and_test_failure +++ b/.gitlab/scripts/report_build_and_test_failure @@ -13,20 +13,33 @@ echo "Runs if there was at least one failure on ${MACHINE_NAME}" -cd ${AUTOTEST_ROOT}/autotest && git pull +cd ${AUTOTEST_ROOT}/autotest || \ + { echo "Invalid 'autotest' dir: ${AUTOTEST_ROOT}/autotest"; exit 1; } mkdir -p ${MACHINE_NAME} rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-ci-${CI_COMMIT_REF_SLUG}" rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir $rundir) -echo "There was an error while running CI on ${MACHINE_NAME}" > ${rundir}/gitlab.err -echo "See the pipeline here -> $CI_PIPELINE_URL" >> ${rundir}/gitlab.err +printf "%s\n" "Some 'build-and-test' jobs on ${MACHINE_NAME} FAILED." \ + "Pipeline URL:" "$CI_PIPELINE_URL" > ${rundir}/gitlab.err msg="GitLab CI log for build-and-test on ${MACHINE_NAME} ($(date +%Y-%m-%d))" +# Create 'autotest-email.html' to indicate failure: cp ${rundir}/gitlab.err ${rundir}/autotest-email.html -git pull -git add ${rundir} -git commit -am "${msg}" -git push origin master +if [[ "$AUTOTEST_COMMIT" == "YES" ]]; then + git pull && \ + git add ${rundir} && \ + git commit -m "${msg}" && \ + git push origin master +else + for file in ${rundir}/*; do + echo "------------------------------" + echo "Content of '$file'" + echo "******************************" + cat $file + echo "******************************" + done + rm -rf ${rundir} || true +fi diff --git a/.gitlab/scripts/report_build_and_test_success b/.gitlab/scripts/report_build_and_test_success index e714a08f5a..d7c0429650 100755 --- a/.gitlab/scripts/report_build_and_test_success +++ b/.gitlab/scripts/report_build_and_test_success @@ -13,18 +13,30 @@ echo "Can only run if all the ${MACHINE_NAME} jobs passed" -cd ${AUTOTEST_ROOT}/autotest && git pull +cd ${AUTOTEST_ROOT}/autotest || \ + { echo "Invalid 'autotest' dir: ${AUTOTEST_ROOT}/autotest"; exit 1; } mkdir -p ${MACHINE_NAME} rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-ci-${CI_COMMIT_REF_SLUG}" rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir $rundir) -echo "The ${MACHINE_NAME} jobs were successful" > ${rundir}/gitlab.out -echo "See the pipeline here -> $CI_PIPELINE_URL" >> ${rundir}/gitlab.err +printf "%s\n" "The 'build-and-test' jobs on ${MACHINE_NAME} were SUCCESSFUL." \ + "Pipeline URL:" "$CI_PIPELINE_URL" > ${rundir}/gitlab.out msg="GitLab CI log for build-and-test on ${MACHINE_NAME} ($(date +%Y-%m-%d))" -git pull -git add ${rundir} -git commit -am "${msg}" -git push origin master +if [[ "$AUTOTEST_COMMIT" == "YES" ]]; then + git pull && \ + git add ${rundir} && \ + git commit -m "${msg}" && \ + git push origin master +else + for file in ${rundir}/*; do + echo "------------------------------" + echo "Content of '$file'" + echo "******************************" + cat $file + echo "******************************" + done + rm -rf ${rundir} || true +fi diff --git a/tests/gitlab/README.md b/tests/gitlab/README.md index 8bd267c7b3..bccdcc3c90 100644 --- a/tests/gitlab/README.md +++ b/tests/gitlab/README.md @@ -13,10 +13,7 @@ This directory contains utility scripts related to GitLab testing at LLNL. perform testing. While designed to be used in CI context, this script can also be used - standalone on LLNL's LC in order to reproduce a similar build. The script - uses environment variables for configuration (a place for improvement), such - as "BUILD_ROOT", "HOST_CONFIG", "SPEC", etc. Some are mandatory, while - others have default values. + standalone on LLNL's LC in order to reproduce a similar build. Please refer to tests/gitlab/reproduce-ci-jobs-interactively.md for details. * `get_mfem_uberenv` sets uberenv up for use with MFEM, notably to install TPLs diff --git a/tests/gitlab/build_and_test b/tests/gitlab/build_and_test index 95c820b201..66e78bc516 100755 --- a/tests/gitlab/build_and_test +++ b/tests/gitlab/build_and_test @@ -22,13 +22,13 @@ function usage() echo "" echo "Syntax:" echo "> ${script_name} --spec \"spack spec\" [--deps-only] [--data]" - echo " [--build-root /path/to/build/resource]" + echo " [--data-dir=/path/to/mfem/data]" echo "" echo "> ${script_name} --build-only [--data]" - echo " [--build-root /path/to/build/resource]" + echo " [--data-dir=/path/to/mfem/data]" echo "" echo "> ${script_name} --test-only [--data]" - echo " [--build-root /path/to/build/resource]" + echo " [--data-dir=/path/to/mfem/data]" echo "" echo "Options:" echo " --spec" @@ -53,24 +53,20 @@ function usage() echo " data directory is not present in the parent of the mfem root directory." echo " Note: default behavior is to run data tests if data dir is present." echo "" - echo " --build-root=/path/to/build/resource" - echo " The script will use this directory to find the external resource" - echo " needed, e.g. the data directory. Defaults to the parent location" - echo " of the MFEM clone." + echo " --data-dir=/path/to/mfem/data" + echo " Path to a clone of the MFEM/data repo: https://github.com/mfem/data" + echo " The default path is: '../data'." echo "" } -hostname="$(hostname)" project_dir="$(pwd)" mode="" -build_root="" spec="" +data_dir="" with_data=false sys_type=${SYS_TYPE:-""} -py_env_path=${PYTHON_ENVIRONMENT_PATH:-""} -ci_context=${CI:-""} threads=${THREADS:-""} @@ -93,8 +89,8 @@ do with_data=true shift # past argument ;; - --build-root) - build_root="$2" + --data-dir) + data_dir="$2" shift # past argument shift # past value ;; @@ -134,14 +130,16 @@ then # otherwise. if [[ -d "/dev/shm" && "${mode}" != "--deps-only" ]] then - prefix="/dev/shm/${hostname}/${CI_PIPELINE_ID:-"NONE"}_${RANDOM}" + prefix="/dev/shm/${USER}_${CI_PIPELINE_ID:-"NONE"}_${RANDOM}" mkdir -p ${prefix} - echo ${spec} > spec.txt + # clean up ${prefix} at exit: + trap 'rm -rf "${prefix}"' EXIT prefix_opt="--prefix=${prefix}" fi + echo ${spec} > spec.txt echo "Fetching uberenv." - tests/gitlab/get_mfem_uberenv || ( echo "Error fetching Uberenv" && exit 1 ); + tests/gitlab/get_mfem_uberenv || { echo "Error fetching Uberenv"; exit 1; } echo "Removing existing configuration" make distclean @@ -172,20 +170,30 @@ then exit 1 fi - # Build and Data Directories - if [[ -z ${build_root} ]] + # Setup the MFEM/data repository directory + # Some additional unit tests are enabled when '../data' is present + if [[ -z "${data_dir}" ]] then - # By default, build_root is the project parent dir. - build_root="${project_dir}/.." + # By default, data_dir is ../data. + data_dir="../data" else - # build_root is specified, so we need to link its content into the + # data_dir is specified, so we need to link its content into the # project parent dir. - ln -sf ${build_root}/data ${project_dir}/../ + if [[ -e "../data" && ! -L "../data" ]]; then + echo "Error: '../data' already exists and it's NOT a link" + exit 1 + fi + ln -sf "${data_dir}" "../data" + fi + # The PUMI examples expect the PUMI datafiles to be in 'data/pumi' + if [[ -d "../data/pumi" ]]; then + ln -sf "../../data/pumi" "data" fi - if [[ "$with_data" == "true" && ! -d ${build_root}/data ]] + if [[ "$with_data" == "true" && ! -d "../data" ]] then - echo "ERROR: ${build_root}/data not found while asking for --data". + echo "ERROR: '$data_dir' is not a directory while asking for --data" + exit 1 fi fi @@ -194,9 +202,15 @@ if [[ "${mode}" != "--deps-only" ]] then echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" echo "~ Project Dir: ${project_dir}" - echo "~ Build Root: ${build_root}" + echo "~ Data Dir: ${data_dir}" echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + echo "~~~~~ MFEM configuration" + echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + + make info + echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" echo "~~~~~ Building MFEM" echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" diff --git a/tests/gitlab/reproduce-ci-jobs-interactively.md b/tests/gitlab/reproduce-ci-jobs-interactively.md index 9c6d59f5ea..e06c9110e6 100644 --- a/tests/gitlab/reproduce-ci-jobs-interactively.md +++ b/tests/gitlab/reproduce-ci-jobs-interactively.md @@ -66,12 +66,12 @@ those files were generated, otherwise `make all` would just regenerate them. **NOTE** -The `build_and_test` script behaves slightly differently between CI context and -elsewhere (depending on environment variable $CI). In CI, and if launched on -quartz, ruby or corona, the script will build and install dependencies in +When `build_and_test` needs to build the dependencies, i.e. (a) `--deps-only` is +used, or (b) none of the `--XXX-only` options is used, then the script behaves +slightly differently. In case (b), it will build and install dependencies in `/dev/shm` for better performance. However, this is only valid if we don’t want the installation to persist. Installation will happen locally to the uberenv -directory if not in CI context. +directory in case (a), i.e. if `--deps-only` is used. ### Option #2: Calling uberenv directly From 3f036b943ebb36dae9f5739219551e050a9174d2 Mon Sep 17 00:00:00 2001 From: Veselin Dobrev Date: Wed, 3 Nov 2021 02:32:28 -0700 Subject: [PATCH 186/198] Add a new Gitlab CI file forgotten in the previous commit --- .gitlab/configs/report-build-and-test.yml | 79 +++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 .gitlab/configs/report-build-and-test.yml diff --git a/.gitlab/configs/report-build-and-test.yml b/.gitlab/configs/report-build-and-test.yml new file mode 100644 index 0000000000..a925c078d2 --- /dev/null +++ b/.gitlab/configs/report-build-and-test.yml @@ -0,0 +1,79 @@ +# Copyright (c) 2010-2021, 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. + +# Jobs report +.report_job_success: + script: + # DEBUG + - export + - echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}" + - cd ${AUTOTEST_ROOT} + - | + ( + date + echo "Waiting to aquire lock on '$PWD/autotest.lock' ..." + # try to get an excusive lock on fd 9 (autotest.lock) repeating the try + # every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the + # command to hang indefinitely sometimes, so we use the timeout & retry + # as a workaround; we may want to add a counter for the number of + # retries to interrupt a potential infinite loop + while ! flock -w 5 9; do + true + done + echo "Aquired lock on '$PWD/autotest.lock'" + date + # Report SUCCESS while holding the file lock on 'autotest.lock'. + # The next script uses the following environment variables: + # - MACHINE_NAME, AUTOTEST_ROOT, AUTOTEST_COMMIT + # - CI_COMMIT_REF_SLUG, CI_PROJECT_DIR, CI_PIPELINE_URL + # It also calls the script '.gitlab/scripts/safe_create_rundir'. + ${CI_PROJECT_DIR}/.gitlab/scripts/report_build_and_test_success + err=$? + # sleep for a period to allow NFS to propagate the above changes; + # clearly, there is no guarantee that other NFS clients will see the + # changes even after the timeout + sleep 10 + exit $err + ) 9> autotest.lock + +.report_job_failure: + script: + # DEBUG + - export + - echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}" + - cd ${AUTOTEST_ROOT} + - | + ( + date + echo "Waiting to aquire lock on '$PWD/autotest.lock' ..." + # try to get an excusive lock on fd 9 (autotest.lock) repeating the try + # every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the + # command to hang indefinitely sometimes, so we use the timeout & retry + # as a workaround; we may want to add a counter for the number of + # retries to interrupt a potential infinite loop + while ! flock -w 5 9; do + true + done + echo "Aquired lock on '$PWD/autotest.lock'" + date + # Report FAILURE while holding the file lock on 'autotest.lock'. + # The next script uses the following environment variables: + # - MACHINE_NAME, AUTOTEST_ROOT, AUTOTEST_COMMIT + # - CI_COMMIT_REF_SLUG, CI_PROJECT_DIR, CI_PIPELINE_URL + # It also calls the script '.gitlab/scripts/safe_create_rundir'. + ${CI_PROJECT_DIR}/.gitlab/scripts/report_build_and_test_failure + err=$? + # sleep for a period to allow NFS to propagate the above changes; + # clearly, there is no guarantee that other NFS clients will see the + # changes even after the timeout + sleep 10 + exit $err + ) 9> autotest.lock From a0172dfeb34d108483dec50a2679e35745fbd911 Mon Sep 17 00:00:00 2001 From: Veselin Dobrev Date: Wed, 3 Nov 2021 21:42:47 -0700 Subject: [PATCH 187/198] In Gitlab CI, remove some debug output --- .gitlab/configs/report-build-and-test.yml | 10 ++++++---- .gitlab/quartz-baseline.yml | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.gitlab/configs/report-build-and-test.yml b/.gitlab/configs/report-build-and-test.yml index a925c078d2..9df21939f2 100644 --- a/.gitlab/configs/report-build-and-test.yml +++ b/.gitlab/configs/report-build-and-test.yml @@ -12,8 +12,9 @@ # Jobs report .report_job_success: script: - # DEBUG - - export + - echo ${MACHINE_NAME} + - echo ${AUTOTEST} + - echo ${AUTOTEST_COMMIT} - echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}" - cd ${AUTOTEST_ROOT} - | @@ -46,8 +47,9 @@ .report_job_failure: script: - # DEBUG - - export + - echo ${MACHINE_NAME} + - echo ${AUTOTEST} + - echo ${AUTOTEST_COMMIT} - echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}" - cd ${AUTOTEST_ROOT} - | diff --git a/.gitlab/quartz-baseline.yml b/.gitlab/quartz-baseline.yml index f5be14d531..acb2749991 100644 --- a/.gitlab/quartz-baseline.yml +++ b/.gitlab/quartz-baseline.yml @@ -56,6 +56,7 @@ report_baseline: extends: [.on_quartz] stage: baseline_report script: + - echo ${MACHINE_NAME} - echo ${AUTOTEST} - echo ${AUTOTEST_COMMIT} - echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}" From 134780da7730737d10c12075dd1d065989855bbb Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Thu, 4 Nov 2021 12:37:05 -0700 Subject: [PATCH 188/198] Try to fix hipcc errors in shifted miniapp --- miniapps/shifted/makefile | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/miniapps/shifted/makefile b/miniapps/shifted/makefile index 8208c722bb..f23a44bec0 100644 --- a/miniapps/shifted/makefile +++ b/miniapps/shifted/makefile @@ -25,9 +25,9 @@ include $(DEFAULTS_MK) MFEM_LIB_FILE = mfem_is_not_built -include $(CONFIG_MK) -DIFFUSION_SRC = dist_solver.cpp sbm_solver.cpp marking.cpp +DIFFUSION_SRC = diffusion.cpp dist_solver.cpp sbm_solver.cpp marking.cpp DIFFUSION_OBJ = $(DIFFUSION_SRC:.cpp=.o) -DISTANCE_SRC = dist_solver.cpp +DISTANCE_SRC = distance.cpp dist_solver.cpp DISTANCE_OBJ = $(DISTANCE_SRC:.cpp=.o) PAR_MINIAPPS = distance diffusion @@ -53,16 +53,16 @@ COMMON_LIB += $(if $(MFEM_SHARED:YES=),,\ %: %.cpp %.o: %.cpp -%.o: $(SRC)%.cpp $(SRC)%.hpp $(MFEM_LIB_FILE) $(CONFIG_MK) | lib-common +%.o: $(SRC)%.cpp $(wildcard $(SRC)%.hpp) $(MFEM_LIB_FILE) $(CONFIG_MK) | lib-common $(MFEM_CXX) $(MFEM_FLAGS) -c $< -o $@ - + all: $(MINIAPPS) -distance: distance.cpp sbm_aux.hpp $(DISTANCE_OBJ) - $(MFEM_CXX) $(MFEM_LINK_FLAGS) $@.cpp -o $@ $(DISTANCE_OBJ) $(COMMON_LIB) $(MFEM_LIBS) +distance: sbm_aux.hpp $(DISTANCE_OBJ) + $(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(DISTANCE_OBJ) $(COMMON_LIB) $(MFEM_LIBS) -diffusion: diffusion.cpp sbm_aux.hpp $(DIFFUSION_OBJ) - $(MFEM_CXX) $(MFEM_LINK_FLAGS) $@.cpp -o $@ $(DIFFUSION_OBJ) $(COMMON_LIB) $(MFEM_LIBS) +diffusion: sbm_aux.hpp $(DIFFUSION_OBJ) + $(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(DIFFUSION_OBJ) $(COMMON_LIB) $(MFEM_LIBS) # Rule for building lib-common lib-common: From 2900a6ecd016c3b3573b2b5fa88cf9a5140c0ccc Mon Sep 17 00:00:00 2001 From: Will Pazner Date: Thu, 4 Nov 2021 16:25:17 -0700 Subject: [PATCH 189/198] Remove testing data_dir symlink if it exists --- tests/gitlab/build_and_test | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/gitlab/build_and_test b/tests/gitlab/build_and_test index 66e78bc516..09e27cb53e 100755 --- a/tests/gitlab/build_and_test +++ b/tests/gitlab/build_and_test @@ -179,9 +179,14 @@ then else # data_dir is specified, so we need to link its content into the # project parent dir. - if [[ -e "../data" && ! -L "../data" ]]; then - echo "Error: '../data' already exists and it's NOT a link" - exit 1 + if [[ -e "../data" ]]; then + if [[ -L "../data" ]]; then + echo "'../data' link already exists. Deleting." + rm "../data" + else + echo "Error: '../data' already exists and it's NOT a link" + exit 1 + fi fi ln -sf "${data_dir}" "../data" fi From a25c9c575bfd88da86d1abae5c59ebd78c4ee3bc Mon Sep 17 00:00:00 2001 From: Veselin Dobrev Date: Thu, 4 Nov 2021 18:48:38 -0700 Subject: [PATCH 190/198] In Gitlab CI, move the definitions of some global variables from .gitlab/configs/common.yml to .gitlab-ci.yml --- .gitlab-ci.yml | 13 +++++++++++++ .gitlab/configs/common.yml | 13 ------------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0fdfb85f2e..ea6aef2c6e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -36,6 +36,19 @@ variables: # used in '.gitlab/configs/-config.yml': MFEM_DATA_DIR: "${SHARED_REPOS_DIR}/mfem-data" +# Defines the default choice for updating the saved baseline results. By default +# the baseline can only be updated from the master branch. This variable offers +# the option to manually ask for rebaselining from another branch if necessary. + REBASELINE: "NO" + AUTOTEST: "NO" + # AUTOTEST_COMMIT: used only when AUTOTEST is set to YES. + # * If AUTOTEST_COMMIT is set to YES (default), reporting jobs will commit + # their files to the MFEM/autotest repo. + # * If AUTOTEST_COMMIT is set to NO, reporting jobs will NOT commit their + # files to the MFEM/autotest repo. Instead they will just show the contents + # of the report files and remove them. + AUTOTEST_COMMIT: "YES" + # Trigger subpipelines: quartz-build-and-test: stage: sub-pipelines diff --git a/.gitlab/configs/common.yml b/.gitlab/configs/common.yml index 1e0ba44312..b8aab3ef75 100644 --- a/.gitlab/configs/common.yml +++ b/.gitlab/configs/common.yml @@ -25,19 +25,6 @@ variables: # are sure to retrieve it. ALLOC_NAME: ${CI_PROJECT_NAME}_ci_${CI_PIPELINE_ID} -# Defines the default choice for updating the saved baseline results. By default -# the baseline can only be updated from the master branch. This variable offers -# the option to manually ask for rebaselining from another branch if necessary. - REBASELINE: "NO" - AUTOTEST: "NO" - # AUTOTEST_COMMIT: used only when AUTOTEST is set to YES. - # * If AUTOTEST_COMMIT is set to YES (default), reporting jobs will commit - # their files to the MFEM/autotest repo. - # * If AUTOTEST_COMMIT is set to NO, reporting jobs will NOT commit their - # files to the MFEM/autotest repo. Instead they will just show the contents - # of the report files and remove them. - AUTOTEST_COMMIT: "YES" - # Git repositories used in the pipeline TPLS_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/tpls.git TESTS_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/tests.git From aeac01c130b34dda19e0e01dd9336539471e482e Mon Sep 17 00:00:00 2001 From: Veselin Dobrev Date: Thu, 4 Nov 2021 19:15:29 -0700 Subject: [PATCH 191/198] In Gitlab CI, add some debug "echo" commands --- .gitlab/configs/setup-baseline.yml | 2 ++ .gitlab/configs/setup-build-and-test.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.gitlab/configs/setup-baseline.yml b/.gitlab/configs/setup-baseline.yml index 8aaaca1e02..3f8c44eb0a 100644 --- a/.gitlab/configs/setup-baseline.yml +++ b/.gitlab/configs/setup-baseline.yml @@ -26,6 +26,8 @@ setup_baseline: # # Setup ${BUILD_ROOT}/tpls and ${BUILD_ROOT}/tests: # + - echo "AUTOTEST = ${AUTOTEST}" + - echo "AUTOTEST_COMMIT = ${AUTOTEST_COMMIT}" - echo "BUILD_ROOT ${BUILD_ROOT}" - mkdir -p ${BUILD_ROOT} && cd ${BUILD_ROOT} - if [ ! -d "tpls" ]; then git clone ${TPLS_REPO}; fi diff --git a/.gitlab/configs/setup-build-and-test.yml b/.gitlab/configs/setup-build-and-test.yml index 109c37cba9..ee0d8d9f7b 100644 --- a/.gitlab/configs/setup-build-and-test.yml +++ b/.gitlab/configs/setup-build-and-test.yml @@ -25,6 +25,8 @@ setup: # Setup MFEM_DATA_DIR=${SHARED_REPOS_DIR}/mfem-data, see '.gitlab-ci.yml' # and '.gitlab/configs/-config.yml' # + - echo "AUTOTEST = ${AUTOTEST}" + - echo "AUTOTEST_COMMIT = ${AUTOTEST_COMMIT}" - echo "SHARED_REPOS_DIR ${SHARED_REPOS_DIR}" - mkdir -p ${SHARED_REPOS_DIR} && cd ${SHARED_REPOS_DIR} - command -v flock || echo "Required command 'flock' not found" From c8a8ab5cbab162c5e30d2f12c5632561cfdcfe25 Mon Sep 17 00:00:00 2001 From: Veselin Dobrev Date: Thu, 4 Nov 2021 20:21:35 -0700 Subject: [PATCH 192/198] In Gitlab CI, attempt to explicitly inherit global variables in the sub-pipelines --- .gitlab-ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ea6aef2c6e..6767c26d2d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -52,24 +52,32 @@ variables: # Trigger subpipelines: quartz-build-and-test: stage: sub-pipelines + inherit: + variables: true trigger: include: .gitlab/quartz-build-and-test.yml strategy: depend quartz-baseline: stage: sub-pipelines + inherit: + variables: true trigger: include: .gitlab/quartz-baseline.yml strategy: depend lassen-build-and-test: stage: sub-pipelines + inherit: + variables: true trigger: include: .gitlab/lassen-build-and-test.yml strategy: depend corona-build-and-test: stage: sub-pipelines + inherit: + variables: true trigger: include: .gitlab/corona-build-and-test.yml strategy: depend From 2ec5efc7d5537704a6ab1fa9100e4dbff1bed3a0 Mon Sep 17 00:00:00 2001 From: Veselin Dobrev Date: Thu, 4 Nov 2021 20:37:10 -0700 Subject: [PATCH 193/198] In Gitlab CI, in sub-pipeline definitions, explicitly pass some global variables --- .gitlab-ci.yml | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6767c26d2d..d46b2b657e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -52,32 +52,36 @@ variables: # Trigger subpipelines: quartz-build-and-test: stage: sub-pipelines - inherit: - variables: true + variables: + AUTOTEST: "${AUTOTEST}" + AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}" trigger: include: .gitlab/quartz-build-and-test.yml strategy: depend quartz-baseline: stage: sub-pipelines - inherit: - variables: true + variables: + AUTOTEST: "${AUTOTEST}" + AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}" trigger: include: .gitlab/quartz-baseline.yml strategy: depend lassen-build-and-test: stage: sub-pipelines - inherit: - variables: true + variables: + AUTOTEST: "${AUTOTEST}" + AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}" trigger: include: .gitlab/lassen-build-and-test.yml strategy: depend corona-build-and-test: stage: sub-pipelines - inherit: - variables: true + variables: + AUTOTEST: "${AUTOTEST}" + AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}" trigger: include: .gitlab/corona-build-and-test.yml strategy: depend From 0ccc63af049ee06acd2173ce5a5d13f2ffcd9937 Mon Sep 17 00:00:00 2001 From: Tobias Duswald Date: Fri, 5 Nov 2021 08:43:30 +0100 Subject: [PATCH 194/198] Include `omp` headers on macOS --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7006057ddd..ae27a9b7fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -252,6 +252,11 @@ if (MFEM_USE_OPENMP OR MFEM_USE_LEGACY_OPENMP) endif() find_package(OpenMP REQUIRED) set(OPENMP_LIBRARIES ${OpenMP_CXX_LIBRARIES}) + if(APPLE) + # On macOS, the compiler needs additional help to find the header. + # See issue #2642 for more information. + include_directories(${OpenMP_CXX_INCLUDE_DIRS}) + endif(APPLE) endif() # SuiteSparse (before SUNDIALS which may depend on KLU) From f6f802e0cd15e1d349efe20bd1e05ebddab8c211 Mon Sep 17 00:00:00 2001 From: Veselin Dobrev Date: Fri, 5 Nov 2021 11:26:22 -0700 Subject: [PATCH 195/198] In Gitlab CI, test another tweak --- .gitlab-ci.yml | 19 ++++++++++--------- .gitlab/configs/setup-baseline.yml | 2 ++ .gitlab/configs/setup-build-and-test.yml | 1 + 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d46b2b657e..02b52709e1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -39,22 +39,22 @@ variables: # Defines the default choice for updating the saved baseline results. By default # the baseline can only be updated from the master branch. This variable offers # the option to manually ask for rebaselining from another branch if necessary. - REBASELINE: "NO" - AUTOTEST: "NO" + # REBASELINE: "NO" # keep commented out + # AUTOTEST: "NO" # keep commented out # AUTOTEST_COMMIT: used only when AUTOTEST is set to YES. - # * If AUTOTEST_COMMIT is set to YES (default), reporting jobs will commit - # their files to the MFEM/autotest repo. - # * If AUTOTEST_COMMIT is set to NO, reporting jobs will NOT commit their + # * If AUTOTEST_COMMIT is set to YES, reporting jobs will commit their files + # to the MFEM/autotest repo. + # * If AUTOTEST_COMMIT is NOT set to YES, reporting jobs will NOT commit their # files to the MFEM/autotest repo. Instead they will just show the contents # of the report files and remove them. - AUTOTEST_COMMIT: "YES" + # AUTOTEST_COMMIT: "NO" # keep commented out # Trigger subpipelines: quartz-build-and-test: stage: sub-pipelines - variables: - AUTOTEST: "${AUTOTEST}" - AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}" + # variables: + # AUTOTEST: "${AUTOTEST}" + # AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}" trigger: include: .gitlab/quartz-build-and-test.yml strategy: depend @@ -62,6 +62,7 @@ quartz-build-and-test: quartz-baseline: stage: sub-pipelines variables: + REBASELINE: "${REBASELINE}" AUTOTEST: "${AUTOTEST}" AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}" trigger: diff --git a/.gitlab/configs/setup-baseline.yml b/.gitlab/configs/setup-baseline.yml index 3f8c44eb0a..2227b4cfe8 100644 --- a/.gitlab/configs/setup-baseline.yml +++ b/.gitlab/configs/setup-baseline.yml @@ -26,6 +26,8 @@ setup_baseline: # # Setup ${BUILD_ROOT}/tpls and ${BUILD_ROOT}/tests: # + - echo "MACHINE_NAME = ${MACHINE_NAME}" + - echo "REBASELINE = ${REBASELINE}" - echo "AUTOTEST = ${AUTOTEST}" - echo "AUTOTEST_COMMIT = ${AUTOTEST_COMMIT}" - echo "BUILD_ROOT ${BUILD_ROOT}" diff --git a/.gitlab/configs/setup-build-and-test.yml b/.gitlab/configs/setup-build-and-test.yml index ee0d8d9f7b..0ce79843e1 100644 --- a/.gitlab/configs/setup-build-and-test.yml +++ b/.gitlab/configs/setup-build-and-test.yml @@ -25,6 +25,7 @@ setup: # Setup MFEM_DATA_DIR=${SHARED_REPOS_DIR}/mfem-data, see '.gitlab-ci.yml' # and '.gitlab/configs/-config.yml' # + - echo "MACHINE_NAME = ${MACHINE_NAME}" - echo "AUTOTEST = ${AUTOTEST}" - echo "AUTOTEST_COMMIT = ${AUTOTEST_COMMIT}" - echo "SHARED_REPOS_DIR ${SHARED_REPOS_DIR}" From 29fc4c45b6e0b8f2ee05b55505a827888caadab2 Mon Sep 17 00:00:00 2001 From: Veselin Dobrev Date: Fri, 5 Nov 2021 12:16:09 -0700 Subject: [PATCH 196/198] In Gitlab CI, apply another set of tweaks --- .gitlab-ci.yml | 26 ++++++++++++------- .gitlab/quartz-baseline.yml | 2 +- .gitlab/scripts/report_build_and_test_failure | 2 +- .gitlab/scripts/report_build_and_test_success | 2 +- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 02b52709e1..c9cc74206e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -39,22 +39,24 @@ variables: # Defines the default choice for updating the saved baseline results. By default # the baseline can only be updated from the master branch. This variable offers # the option to manually ask for rebaselining from another branch if necessary. - # REBASELINE: "NO" # keep commented out - # AUTOTEST: "NO" # keep commented out + REBASELINE: "NO" + AUTOTEST: "NO" # AUTOTEST_COMMIT: used only when AUTOTEST is set to YES. - # * If AUTOTEST_COMMIT is set to YES, reporting jobs will commit their files - # to the MFEM/autotest repo. - # * If AUTOTEST_COMMIT is NOT set to YES, reporting jobs will NOT commit their + # * If AUTOTEST_COMMIT is NOT set to NO, reporting jobs will commit their + # files to the MFEM/autotest repo. + # * If AUTOTEST_COMMIT is set to NO, reporting jobs will NOT commit their # files to the MFEM/autotest repo. Instead they will just show the contents # of the report files and remove them. - # AUTOTEST_COMMIT: "NO" # keep commented out + AUTOTEST_COMMIT: "YES" # Trigger subpipelines: quartz-build-and-test: stage: sub-pipelines - # variables: - # AUTOTEST: "${AUTOTEST}" - # AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}" + variables: + # Explicitly pass down values that we want to be able to set when triggering + # pipelines manually or using scheduling + AUTOTEST: "${AUTOTEST}" + AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}" trigger: include: .gitlab/quartz-build-and-test.yml strategy: depend @@ -62,6 +64,8 @@ quartz-build-and-test: quartz-baseline: stage: sub-pipelines variables: + # Explicitly pass down values that we want to be able to set when triggering + # pipelines manually or using scheduling REBASELINE: "${REBASELINE}" AUTOTEST: "${AUTOTEST}" AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}" @@ -72,6 +76,8 @@ quartz-baseline: lassen-build-and-test: stage: sub-pipelines variables: + # Explicitly pass down values that we want to be able to set when triggering + # pipelines manually or using scheduling AUTOTEST: "${AUTOTEST}" AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}" trigger: @@ -81,6 +87,8 @@ lassen-build-and-test: corona-build-and-test: stage: sub-pipelines variables: + # Explicitly pass down values that we want to be able to set when triggering + # pipelines manually or using scheduling AUTOTEST: "${AUTOTEST}" AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}" trigger: diff --git a/.gitlab/quartz-baseline.yml b/.gitlab/quartz-baseline.yml index acb2749991..fd1660357e 100644 --- a/.gitlab/quartz-baseline.yml +++ b/.gitlab/quartz-baseline.yml @@ -89,7 +89,7 @@ report_baseline: printf "%s\n" "" "Pipeline URL:" "$CI_PIPELINE_URL" \ >> ${rundir}/pipeline.txt msg="GitLab CI log for ${BASELINE_TEST} on ${MACHINE_NAME} ($(date +%Y-%m-%d))" - if [[ "$AUTOTEST_COMMIT" == "YES" ]]; then + if [[ "$AUTOTEST_COMMIT" != "NO" ]]; then git pull && \ git add ${rundir} && \ git commit -m "${msg}" && \ diff --git a/.gitlab/scripts/report_build_and_test_failure b/.gitlab/scripts/report_build_and_test_failure index 95d0e47f03..fa03761d6c 100755 --- a/.gitlab/scripts/report_build_and_test_failure +++ b/.gitlab/scripts/report_build_and_test_failure @@ -28,7 +28,7 @@ msg="GitLab CI log for build-and-test on ${MACHINE_NAME} ($(date +%Y-%m-%d))" # Create 'autotest-email.html' to indicate failure: cp ${rundir}/gitlab.err ${rundir}/autotest-email.html -if [[ "$AUTOTEST_COMMIT" == "YES" ]]; then +if [[ "$AUTOTEST_COMMIT" != "NO" ]]; then git pull && \ git add ${rundir} && \ git commit -m "${msg}" && \ diff --git a/.gitlab/scripts/report_build_and_test_success b/.gitlab/scripts/report_build_and_test_success index d7c0429650..805ace0d19 100755 --- a/.gitlab/scripts/report_build_and_test_success +++ b/.gitlab/scripts/report_build_and_test_success @@ -25,7 +25,7 @@ printf "%s\n" "The 'build-and-test' jobs on ${MACHINE_NAME} were SUCCESSFUL." \ msg="GitLab CI log for build-and-test on ${MACHINE_NAME} ($(date +%Y-%m-%d))" -if [[ "$AUTOTEST_COMMIT" == "YES" ]]; then +if [[ "$AUTOTEST_COMMIT" != "NO" ]]; then git pull && \ git add ${rundir} && \ git commit -m "${msg}" && \ From 9f64008a553394cfe9f0dbefe2a7b408a1a4761c Mon Sep 17 00:00:00 2001 From: Veselin Dobrev Date: Sat, 6 Nov 2021 17:30:26 -0700 Subject: [PATCH 197/198] Fix the 'gitignore' github action. Test with the external github action (remove before merge): mfem/github-actions/build-mfem@v2.0-tweak --- .github/workflows/builds-and-tests.yml | 2 +- .github/workflows/mfem-analysis.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/builds-and-tests.yml b/.github/workflows/builds-and-tests.yml index a4bb989e12..2d1aefa058 100644 --- a/.github/workflows/builds-and-tests.yml +++ b/.github/workflows/builds-and-tests.yml @@ -166,7 +166,7 @@ jobs: # MFEM build and test - name: build - uses: mfem/github-actions/build-mfem@v2.0 + uses: mfem/github-actions/build-mfem@v2.0-tweak with: os: ${{ matrix.os }} target: ${{ matrix.target }} diff --git a/.github/workflows/mfem-analysis.yml b/.github/workflows/mfem-analysis.yml index 2ff9283569..c901e9dee5 100644 --- a/.github/workflows/mfem-analysis.yml +++ b/.github/workflows/mfem-analysis.yml @@ -79,12 +79,12 @@ jobs: # MFEM build and test - name: build-mfem - uses: mfem/github-actions/build-mfem@v2.0 + uses: mfem/github-actions/build-mfem@v2.0-tweak with: os: ${{ runner.os }} - target: optim + target: opt codecov: NO - mpi: parallel + mpi: par build-system: make hypre-dir: ${{ env.HYPRE_TOP_DIR }} metis-dir: ${{ env.METIS_TOP_DIR }} From 88a190e034b97ce996c07341f7456e05bdec1705 Mon Sep 17 00:00:00 2001 From: Veselin Dobrev Date: Sat, 6 Nov 2021 18:51:36 -0700 Subject: [PATCH 198/198] In Github CI, remove testing change 'v2.0-tweak' -> 'v2.0' --- .github/workflows/builds-and-tests.yml | 2 +- .github/workflows/mfem-analysis.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/builds-and-tests.yml b/.github/workflows/builds-and-tests.yml index 2d1aefa058..a4bb989e12 100644 --- a/.github/workflows/builds-and-tests.yml +++ b/.github/workflows/builds-and-tests.yml @@ -166,7 +166,7 @@ jobs: # MFEM build and test - name: build - uses: mfem/github-actions/build-mfem@v2.0-tweak + uses: mfem/github-actions/build-mfem@v2.0 with: os: ${{ matrix.os }} target: ${{ matrix.target }} diff --git a/.github/workflows/mfem-analysis.yml b/.github/workflows/mfem-analysis.yml index c901e9dee5..20e7c05dd3 100644 --- a/.github/workflows/mfem-analysis.yml +++ b/.github/workflows/mfem-analysis.yml @@ -79,7 +79,7 @@ jobs: # MFEM build and test - name: build-mfem - uses: mfem/github-actions/build-mfem@v2.0-tweak + uses: mfem/github-actions/build-mfem@v2.0 with: os: ${{ runner.os }} target: opt