// 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. #ifndef MFEM_TMOP_AMR_HPP #define MFEM_TMOP_AMR_HPP #include "tmop_tools.hpp" #include "nonlinearform.hpp" #include "pnonlinearform.hpp" #include "estimators.hpp" #include "../mesh/mesh_operators.hpp" namespace mfem { class TMOPRefinerEstimator : public AnisotropicErrorEstimator { protected: Mesh *mesh; // not owned NonlinearForm *nlf; // not owned int order; int amrmetric; Array TriIntRule, QuadIntRule, TetIntRule, HexIntRule; long current_sequence; Vector error_estimates; Array aniso_flags; // An element is refined only if // [mean TMOPEnergy(children)]*energy_scaling_factor < TMOPEnergy(parent) double energy_scaling_factor; GridFunction *spat_gf; // If specified, can be used to specify the double spat_gf_critical; // region where hr-adaptivity is done. /// Check if the mesh of the solution was modified. bool MeshIsModified() { long mesh_sequence = mesh->GetSequence(); MFEM_ASSERT(mesh_sequence >= current_sequence, ""); return (mesh_sequence > current_sequence); } /// Compute the element error estimates. For an element E in the mesh, /// error(E) = TMOPEnergy(E)*energy_scaling_factor-Mean(TMOPEnergy(ChildofE)), /// where TMOPEnergy of Children of E is obtained by assuming the element E /// is refined using the refinement type being considered based on the TMOP /// mesh quality metric. void ComputeEstimates(); /// Construct the integration rules to model how each element type is split /// using different refinement types. ref_type = 0 is the original element /// and reftype \ in [1, 7] represent different refinement type based on /// NCMesh class. void SetQuadIntRules(); // supports ref_type = 1 to 3. void SetTriIntRules(); // currently supports only isotropic refinement. void SetHexIntRules(); // currently supports only isotropic refinement. void SetTetIntRules(); // currently supports only isotropic refinement. /// Get TMOP energy for each element corresponding to the refinement type /// specified. void GetTMOPRefinementEnergy(int reftype, Vector &el_energy_vec); /// Use a mesh to setup an integration rule that will mimic the different /// refinement types. IntegrationRule* SetIntRulesFromMesh(Mesh &meshsplit); public: TMOPRefinerEstimator(Mesh &mesh_, NonlinearForm &nlf_, int order_, int amrmetric_) : mesh(&mesh_), nlf(&nlf_), order(order_), amrmetric(amrmetric_), TriIntRule(0), QuadIntRule(0), TetIntRule(0), HexIntRule(0), current_sequence(-1), error_estimates(), aniso_flags(), energy_scaling_factor(1.), spat_gf(NULL), spat_gf_critical(0.) { if (mesh->Dimension() == 2) { SetQuadIntRules(); SetTriIntRules(); } else { SetHexIntRules(); SetTetIntRules(); } } ~TMOPRefinerEstimator() { for (int i = 0; i < QuadIntRule.Size(); i++) { delete QuadIntRule[i]; } for (int i = 0; i < TriIntRule.Size(); i++) { delete TriIntRule[i]; } for (int i = 0; i < HexIntRule.Size(); i++) { delete HexIntRule[i]; } for (int i = 0; i < TetIntRule.Size(); i++) { delete TetIntRule[i]; } } /// Get TMOP-based errors for each element in the mesh computed based on the /// refinement types being considered. virtual const Vector &GetLocalErrors() { if (MeshIsModified()) { ComputeEstimates(); } return error_estimates; } /// For anisotropic refinements, get the refinement type (e.g., x or y) virtual const Array &GetAnisotropicFlags() { if (MeshIsModified()) { ComputeEstimates(); } return aniso_flags; } /// Scaling factor for the TMOP refinement energy. An element is refined if /// [mean TMOPEnergy(children)]*energy_scaling_factor < TMOPEnergy(parent) void SetEnergyScalingFactor(double scale) { energy_scaling_factor = scale; } /// Spatial indicator function (eta) that can be used to prevent elements /// from being refined even if the energy criterion is met. Using this, /// an element E is not refined if mean(@a spat_gf(E)) < @a spat_gf_critical. void SetSpatialIndicator(GridFunction &spat_gf_, double spat_gf_critical_ = 0.5) { spat_gf = &spat_gf_; spat_gf_critical = spat_gf_critical_; } void SetSpatialIndicatorCritical(double val_) { spat_gf_critical = val_; } /// Reset the error estimator. virtual void Reset() { current_sequence = -1; } }; class TMOPDeRefinerEstimator : public ErrorEstimator { protected: Mesh *mesh; NonlinearForm *nlf; #ifdef MFEM_USE_MPI ParMesh *pmesh; ParNonlinearForm *pnlf; #endif int order; int amrmetric; long current_sequence; Vector error_estimates; bool serial; /// Check if the mesh of the solution was modified. bool MeshIsModified() { long mesh_sequence = mesh->GetSequence(); MFEM_ASSERT(mesh_sequence >= current_sequence, ""); return (mesh_sequence > current_sequence); } /// Compute the element error estimates. For a given element E in the mesh, /// error(E) = TMOPEnergy(parent_of_E)-TMOPEnergy(E). Children element of an /// element are derefined if the mean TMOP energy of children is greater than /// the TMOP energy associated with their parent. void ComputeEstimates(); void GetTMOPDerefinementEnergy(Mesh &cmesh, TMOP_Integrator &tmopi, Vector &el_energy_vec); bool GetDerefineEnergyForIntegrator(TMOP_Integrator &tmopi, Vector &fine_energy); public: TMOPDeRefinerEstimator(Mesh &mesh_, NonlinearForm &nlf_) : mesh(&mesh_), nlf(&nlf_), current_sequence(-1), error_estimates(), serial(true) { } #ifdef MFEM_USE_MPI TMOPDeRefinerEstimator(ParMesh &pmesh_, ParNonlinearForm &pnlf_) : mesh(&pmesh_), nlf(&pnlf_), pmesh(&pmesh_), pnlf(&pnlf_), current_sequence(-1), error_estimates(), serial(false) { } #endif ~TMOPDeRefinerEstimator() { } virtual const Vector &GetLocalErrors() { if (MeshIsModified()) { ComputeEstimates(); } return error_estimates; } /// Reset the error estimator. virtual void Reset() { current_sequence = -1; } }; // hr-adaptivity using TMOP. // If hr-adaptivity is disabled, r-adaptivity is done once using the // TMOPNewtonSolver. // Otherwise, "hr_iter" iterations of r-adaptivity are done followed by // "h_per_r_iter" iterations of h-adaptivity after each r-adaptivity iteration. // The solver terminates early if an h-adaptivity iteration does not // refine/derefine any element in the mesh. class TMOPHRSolver { protected: Mesh *mesh; NonlinearForm *nlf; TMOPNewtonSolver *tmopns; GridFunction *x; Array gridfuncarr; Array fespacearr; bool move_bnd, hradaptivity; const int mesh_poly_deg, amr_metric_id; #ifdef MFEM_USE_MPI ParMesh *pmesh; ParNonlinearForm *pnlf; Array pgridfuncarr; Array pfespacearr; #endif bool serial; // All are owned. TMOPRefinerEstimator *tmop_r_est; ThresholdRefiner *tmop_r; TMOPDeRefinerEstimator *tmop_dr_est; ThresholdDerefiner *tmop_dr; int hr_iter, h_per_r_iter; void Update(); #ifdef MFEM_USE_MPI void ParUpdate(); #endif void UpdateNonlinearFormAndBC(Mesh *mesh, NonlinearForm *nlf); #ifdef MFEM_USE_MPI // Rebalance ParMesh such that all the children elements are moved to the same // MPI rank where the parent will be if the mesh were to be derefined. void RebalanceParNCMesh(); #endif public: TMOPHRSolver(Mesh &mesh_, NonlinearForm &nlf_, TMOPNewtonSolver &tmopns_, GridFunction &x_, bool move_bnd_, bool hradaptivity_, int mesh_poly_deg_, int amr_metric_id_, int hr_iter_ = 5, int h_per_r_iter_ = 1); #ifdef MFEM_USE_MPI TMOPHRSolver(ParMesh &pmesh_, ParNonlinearForm &pnlf_, TMOPNewtonSolver &tmopns_, ParGridFunction &x_, bool move_bnd_, bool hradaptivity_, int mesh_poly_deg_, int amr_metric_id_, int hr_iter_ = 5, int h_per_r_iter_ = 1); #endif void Mult(); /// These are used to update spaces and functions that are not owned by the /// TMOPIntegrator or DiscreteAdaptTC. The owned ones are updated in the /// functions UpdateAfterMeshTopologyChange() of both classes. void AddGridFunctionForUpdate(GridFunction *gf) { gridfuncarr.Append(gf); } void AddFESpaceForUpdate(FiniteElementSpace *fes) { fespacearr.Append(fes); } #ifdef MFEM_USE_MPI void AddGridFunctionForUpdate(ParGridFunction *pgf_) { pgridfuncarr.Append(pgf_); } void AddFESpaceForUpdate(ParFiniteElementSpace *pfes_) { pfespacearr.Append(pfes_); } #endif ~TMOPHRSolver() { if (!hradaptivity) { return; } delete tmop_dr; delete tmop_dr_est; delete tmop_r; delete tmop_r_est; } /// Total number of hr-adaptivity iterations. At each iteration, we do an /// r-adaptivity iteration followed by a number of h-adaptivity iterations. void SetHRAdaptivityIterations(int iter) { hr_iter = iter; } /// Total number of h-adaptivity iterations per r-adaptivity iteration. void SetHAdaptivityIterations(int iter) { h_per_r_iter = iter; } }; } #endif