Line data Source code
1 : // The libMesh Finite Element Library. 2 : // Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner 3 : 4 : // This library is free software; you can redistribute it and/or 5 : // modify it under the terms of the GNU Lesser General Public 6 : // License as published by the Free Software Foundation; either 7 : // version 2.1 of the License, or (at your option) any later version. 8 : 9 : // This library is distributed in the hope that it will be useful, 10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 : // Lesser General Public License for more details. 13 : 14 : // You should have received a copy of the GNU Lesser General Public 15 : // License along with this library; if not, write to the Free Software 16 : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 : 18 : #include "libmesh/libmesh_config.h" 19 : #if defined(LIBMESH_ENABLE_VSMOOTHER) 20 : 21 : // Local includes 22 : #include "libmesh/mesh_smoother_vsmoother.h" 23 : #include "libmesh/mesh_tools.h" 24 : #include "libmesh/elem.h" 25 : #include "libmesh/unstructured_mesh.h" 26 : #include "libmesh/utility.h" 27 : #include "libmesh/boundary_info.h" 28 : #include "libmesh/equation_systems.h" 29 : #include "libmesh/distributed_mesh.h" 30 : #include "libmesh/steady_solver.h" 31 : #include "libmesh/diff_solver.h" 32 : #include "libmesh/parallel_ghost_sync.h" 33 : #include "libmesh/libmesh_logging.h" 34 : 35 : // C++ includes 36 : #include <time.h> // for clock_t, clock() 37 : #include <cstdlib> // *must* precede <cmath> for proper std:abs() on PGI, Sun Studio CC 38 : #include <cmath> 39 : #include <iomanip> 40 : #include <limits> 41 : 42 : namespace libMesh 43 : { 44 : 45 : // Optimization at -O2 or greater seem to break Intel's icc. So if we are 46 : // being compiled with icc let's dumb-down the optimizations for this file 47 : #ifdef __INTEL_COMPILER 48 : # pragma optimize ( "", off ) 49 : #endif 50 : 51 : // Member functions for the Variational Smoother 52 2556 : VariationalMeshSmoother::VariationalMeshSmoother( 53 : UnstructuredMesh &mesh, Real dilation_weight, 54 : const bool preserve_subdomain_boundaries, 55 : const Real relative_residual_tolerance, 56 2556 : const Real absolute_residual_tolerance, const unsigned int verbosity) 57 2412 : : MeshSmoother(mesh), _verbosity(verbosity), 58 2412 : _dilation_weight(dilation_weight), 59 2412 : _preserve_subdomain_boundaries(preserve_subdomain_boundaries), 60 2412 : _setup_called(false), 61 2412 : _relative_residual_tolerance(relative_residual_tolerance), 62 2628 : _absolute_residual_tolerance(absolute_residual_tolerance) {} 63 : 64 2556 : void VariationalMeshSmoother::setup() 65 : { 66 : // Check for multiple dimensions 67 2628 : if (!_mesh.preparation().has_cached_elem_data) 68 923 : _mesh.cache_elem_data(); 69 : 70 2628 : if (_mesh.elem_dimensions().size() > 1) 71 0 : libmesh_not_implemented_msg("Meshes containing elements of differing dimension are not yet supported."); 72 : 73 : /* 74 : Ideally we'd want to update _mesh directly even if it already has an 75 : EquationSystems attached and doing work on it ... and that's thwarted by the 76 : problems: 77 : 78 : 1. We can't easily tell if there's already an EquationSystems attached. 79 : 2. We can't attach a second EquationSystems safely (if it already has a system 0) 80 : because the DoF indexing will need to be overwritten by our system. 81 : 3. The destructor of es won't even clean up after itself (we generally expect 82 : a mesh to go unused after its EquationSystems is destroyed), much less know 83 : how to restore anything from a previous EquationSystems. 84 : 85 : To avoid these issues, we'll just construct a new DistributedMesh _mesh_copy 86 : from _mesh, then do the solve on _mesh_copy, then copy its node locations back 87 : to _mesh after the solve is done. That'll be slightly less memory-efficient 88 : and somewhat more CPU-efficient in the case where _mesh is serial, though 89 : it'll be significantly less memory-efficient when _mesh is already distributed, 90 : but either way the robustness is probably worth it. 91 : */ 92 : 93 : // Create a new mesh, EquationSystems, and System 94 5040 : _mesh_copy = std::make_unique<DistributedMesh>(_mesh); 95 : 96 : // If the _mesh wasn't prepared, that's fine (we'll just be moving 97 : // its nodes), but we do need the copy to be prepared before our 98 : // solve does things like looking at neighbors. We'll disable 99 : // repartitioning and renumbering first to make sure that we can 100 : // transfer our geometry changes back to the original mesh. 101 72 : _mesh_copy->allow_renumbering(false); 102 72 : _mesh_copy->skip_partitioning(true); 103 2556 : _mesh_copy->complete_preparation(); 104 : 105 5040 : _equation_systems = std::make_unique<EquationSystems>(*_mesh_copy); 106 2556 : _system = 107 2556 : &(_equation_systems->add_system<VariationalSmootherSystem>("variational_smoother_system")); 108 : 109 : // Set this to something > 0 to add more quadrature points than the default 110 : // rule that integrates order 2 * fe_order + 1 polynomials exactly. 111 : // Using higher quadrature orders has not had a significant effect on observed solutions. 112 : //system()->extra_quadrature_order = 0; 113 : 114 : // The default Gauss rule samples the element interior only, so it can miss 115 : // degeneracies localized at element corners (e.g. an element collapsing 116 : // toward one of its nodes). Selecting a vertex-sampling rule (QTRAP, 117 : // QSIMPSON, QNODAL, QGAUSS_LOBATTO) evaluates the metric at the element 118 : // nodes instead. Defaults to QGAUSS. 119 2556 : system()->set_quadrature_type(_quadrature_type); 120 : 121 : // Uncomment these to debug 122 : //system()->print_element_solutions=true; 123 : //system()->print_element_residuals=true; 124 : //system()->print_element_jacobians=true; 125 : 126 : // Add boundary node and hanging node constraints 127 4968 : _constraint = std::make_unique<VariationalSmootherConstraint>( 128 72 : *_system, _preserve_subdomain_boundaries, _verbosity); 129 2628 : system()->attach_constraint_object(*_constraint); 130 : 131 : // Set system parameters 132 2556 : system()->set_verbosity(_verbosity); 133 2556 : system()->get_dilation_weight() = _dilation_weight; 134 : 135 : // Set up solver 136 2628 : system()->time_solver = std::make_unique<SteadySolver>(*_system); 137 : 138 : // Uncomment this line and use -snes_test_jacobian and -snes_test_jacobian_view 139 : // flags to compare the hand-coded jacobian in VariationalSmootherSystem 140 : // to finite difference jacobians. 141 : //system()->time_solver->diff_solver() = std::make_unique<PetscDiffSolver>(*_system); 142 : 143 2556 : _equation_systems->init(); 144 : 145 : // Solver verbosity 146 2556 : if (_verbosity > 15) { 147 146 : DiffSolver &solver = *(system()->time_solver->diff_solver().get()); 148 142 : solver.quiet = false; 149 142 : solver.verbose = true; 150 : } 151 : 152 : // Solver convergence tolerances 153 2628 : system()->time_solver->diff_solver()->relative_residual_tolerance = _relative_residual_tolerance; 154 2628 : system()->time_solver->diff_solver()->absolute_residual_tolerance = _absolute_residual_tolerance; 155 : 156 2556 : _setup_called = true; 157 2556 : } 158 : 159 2556 : void VariationalMeshSmoother::smooth() 160 : { 161 72 : LOG_SCOPE("smooth()", "VariationalMeshSmoother"); 162 : 163 2556 : if (!_setup_called) 164 2414 : setup(); 165 : 166 : libmesh_try 167 : { 168 2556 : system()->solve(); 169 : } 170 : 171 0 : libmesh_catch (ConvergenceFailure& e) 172 : { 173 : #ifdef LIBMESH_ENABLE_EXCEPTIONS 174 0 : throw ConvergenceFailure("The VariationalMeshSmoother solve failed to converge."); 175 : #endif 176 0 : } 177 : 178 : // Update _mesh from _mesh_copy 179 77772 : for (auto * node_copy : _mesh_copy->local_node_ptr_range()) 180 : { 181 39672 : auto & node = _mesh.node_ref(node_copy->id()); 182 154332 : for (const auto d : make_range(_mesh_copy->mesh_dimension())) 183 114660 : node(d) = (*node_copy)(d); 184 2412 : } 185 : 186 2556 : SyncNodalPositions sync_object(_mesh); 187 5040 : Parallel::sync_dofobject_data_by_id (_mesh.comm(), _mesh.nodes_begin(), _mesh.nodes_end(), sync_object); 188 : 189 : // Release memory occupied by _mesh_copy 190 : // Destruct this before _mesh_copy because it references _mesh_copy 191 72 : _equation_systems.reset(); 192 72 : _mesh_copy.reset(); 193 : // We'll need to call setup again since we'll have to reconstruct _mesh_copy 194 2556 : _setup_called = false; 195 2556 : } 196 : 197 142 : const MeshQualityInfo & VariationalMeshSmoother::get_mesh_info() const 198 : { 199 142 : libmesh_error_msg_if(!_setup_called, "Need to first call the setup() method of " 200 : << "this VariationalMeshSmoother object, then call get_mesh_info() prior " 201 : << "to calling smooth()."); 202 142 : return _system->get_mesh_info(); 203 : } 204 : 205 : } // namespace libMesh 206 : 207 : #endif // defined(LIBMESH_ENABLE_VSMOOTHER)