// Copyright (c) 2010-2023, 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 "mesh_operators.hpp" #include "pmesh.hpp" namespace mfem { MeshOperatorSequence::~MeshOperatorSequence() { // delete in reverse order for (int i = sequence.Size()-1; i >= 0; i--) { delete sequence[i]; } } int MeshOperatorSequence::ApplyImpl(Mesh &mesh) { if (sequence.Size() == 0) { return NONE; } next_step: step = (step + 1) % sequence.Size(); bool last = (step == sequence.Size() - 1); int mod = sequence[step]->ApplyImpl(mesh); switch (mod & MASK_ACTION) { case NONE: if (last) { return NONE; } goto next_step; case CONTINUE: return last ? mod : (REPEAT | (mod & MASK_INFO)); case STOP: return STOP; case REPEAT: --step; return mod; } return NONE; } void MeshOperatorSequence::Reset() { for (int i = 0; i < sequence.Size(); i++) { sequence[i]->Reset(); } step = 0; } ThresholdRefiner::ThresholdRefiner(ErrorEstimator &est) : estimator(est) { aniso_estimator = dynamic_cast(&estimator); total_norm_p = infinity(); total_err_goal = 0.0; total_fraction = 0.5; local_err_goal = 0.0; max_elements = std::numeric_limits::max(); threshold = 0.0; num_marked_elements = 0LL; current_sequence = -1; non_conforming = -1; nc_limit = 0; } double ThresholdRefiner::GetNorm(const Vector &local_err, Mesh &mesh) const { #ifdef MFEM_USE_MPI ParMesh *pmesh = dynamic_cast(&mesh); if (pmesh) { return ParNormlp(local_err, total_norm_p, pmesh->GetComm()); } #endif return local_err.Normlp(total_norm_p); } int ThresholdRefiner::ApplyImpl(Mesh &mesh) { threshold = 0.0; num_marked_elements = 0LL; marked_elements.SetSize(0); current_sequence = mesh.GetSequence(); const long long num_elements = mesh.GetGlobalNE(); if (num_elements >= max_elements) { return STOP; } const int NE = mesh.GetNE(); const Vector &local_err = estimator.GetLocalErrors(); MFEM_ASSERT(local_err.Size() == NE, "invalid size of local_err"); const double total_err = GetNorm(local_err, mesh); if (total_err <= total_err_goal) { return STOP; } if (total_norm_p < infinity()) { threshold = std::max(total_err * total_fraction * std::pow(num_elements, -1.0/total_norm_p), local_err_goal); } else { threshold = std::max(total_err * total_fraction, local_err_goal); } for (int el = 0; el < NE; el++) { if (local_err(el) > threshold) { marked_elements.Append(Refinement(el)); } } if (aniso_estimator) { const Array &aniso_flags = aniso_estimator->GetAnisotropicFlags(); if (aniso_flags.Size() > 0) { for (int i = 0; i < marked_elements.Size(); i++) { Refinement &ref = marked_elements[i]; ref.ref_type = aniso_flags[ref.index]; } } } num_marked_elements = mesh.ReduceInt(marked_elements.Size()); if (num_marked_elements == 0LL) { return STOP; } mesh.GeneralRefinement(marked_elements, non_conforming, nc_limit); return CONTINUE + REFINED; } void ThresholdRefiner::Reset() { estimator.Reset(); current_sequence = -1; num_marked_elements = 0LL; // marked_elements.SetSize(0); // not necessary } int ThresholdDerefiner::ApplyImpl(Mesh &mesh) { if (mesh.Conforming()) { return NONE; } const Vector &local_err = estimator.GetLocalErrors(); bool derefs = mesh.DerefineByError(local_err, threshold, nc_limit, op); return derefs ? CONTINUE + DEREFINED : NONE; } int CoefficientRefiner::ApplyImpl(Mesh &mesh) { int max_it = 1; return PreprocessMesh(mesh, max_it); } int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it) { int rank = 0; MFEM_VERIFY(max_it > 0, "max_it must be strictly positive") int dim = mesh.Dimension(); L2_FECollection l2fec(order, dim); FiniteElementSpace* l2fes = NULL; bool par = false; GridFunction *gf = NULL; #ifdef MFEM_USE_MPI ParMesh* pmesh = dynamic_cast(&mesh); if (pmesh && pmesh->Nonconforming()) { par = true; l2fes = new ParFiniteElementSpace(pmesh, &l2fec); gf = new ParGridFunction(static_cast(l2fes)); } #endif if (!par) { l2fes = new FiniteElementSpace(&mesh, &l2fec); gf = new GridFunction(l2fes); } // 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_default[i] = &(IntRules.Get(i, order_quad)); } irs = ir_default; } for (int i = 0; i < max_it; i++) { // Compute number of elements and L2-norm of f. int NE = mesh.GetNE(); 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); } // Compute average L2-norm of f double av_norm_of_coeff = norm_of_coeff / sqrt(globalNE); // 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_K(f) := || h ⋅ (I - Π) f ||_K and select elements // for refinement based on threshold. Also record relative osc(f). 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); 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, &global_osc, 1, MPI_DOUBLE, MPI_SUM, comm); MPI_Comm_rank(comm, &rank); } #endif global_osc = sqrt(global_osc)/(norm_of_coeff + 1e-10); // 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 && print_level) { MFEM_WARNING("Reached maximum number of elements " "before resolving data to tolerance."); } delete l2fes; delete gf; return STOP; } // Refine elements. mesh.GeneralRefinement(mesh_refinements, nonconforming, nc_limit); l2fes->Update(false); gf->Update(); } delete l2fes; delete gf; return CONTINUE + REFINED; } void CoefficientRefiner::Reset() { element_oscs.Destroy(); global_osc = 0.0; coeff = NULL; irs = NULL; } int Rebalancer::ApplyImpl(Mesh &mesh) { #ifdef MFEM_USE_MPI ParMesh *pmesh = dynamic_cast(&mesh); if (pmesh && pmesh->Nonconforming()) { pmesh->Rebalance(); return CONTINUE + REBALANCED; } #endif return NONE; } } // namespace mfem