@@ -2034,7 +2034,8 @@ MeshBase::copy_constraint_rows(const MeshBase & other_mesh)
20342034
20352035template < typename T >
20362036void
2037- MeshBase ::copy_constraint_rows (const SparseMatrix < T > & constraint_operator )
2037+ MeshBase ::copy_constraint_rows (const SparseMatrix < T > & constraint_operator ,
2038+ bool precondition_constraint_operator )
20382039{
20392040 LOG_SCOPE ("copy_constraint_rows(mat)" , "MeshBase" );
20402041
@@ -2070,6 +2071,14 @@ MeshBase::copy_constraint_rows(const SparseMatrix<T> & constraint_operator)
20702071 columns_type ;
20712072 columns_type columns (constraint_operator .n ());
20722073
2074+ // If we need to precondition the constraint operator (e.g. it's an
2075+ // unpreconditioned extraction operator for a Flex IGA matrix),
2076+ // we'll want to keep track of the sum of each column, because we'll
2077+ // be dividing each column by that sum (Jacobi preconditioning on
2078+ // the right, which then leads to symmetric preconditioning on a
2079+ // physics Jacobian).
2080+ std ::unordered_map < dof_id_type , Real > column_sums ;
2081+
20732082 // Work in parallel, though we'll have to sync shortly
20742083 for (auto i : make_range (constraint_operator .row_start (),
20752084 constraint_operator .row_stop ()))
@@ -2174,6 +2183,9 @@ MeshBase::copy_constraint_rows(const SparseMatrix<T> & constraint_operator)
21742183 ++ pids [constrained_node .processor_id ()];
21752184 }
21762185
2186+ if (precondition_constraint_operator )
2187+ column_sums [j ] = total_scaling ;
2188+
21772189 libmesh_error_msg_if
21782190 (!total_entries ,
21792191 "Empty column " << j <<
@@ -2246,8 +2258,23 @@ MeshBase::copy_constraint_rows(const SparseMatrix<T> & constraint_operator)
22462258
22472259 auto p = node_to_elem_ptrs [& constraining_node ];
22482260
2249- const Real coef = libmesh_real (values [jj ]);
2261+ Real coef = libmesh_real (values [jj ]);
22502262 libmesh_assert_equal_to (coef , values [jj ]);
2263+
2264+ // If we're preconditioning and we created a nodeelem then
2265+ // we can scale the meaning of that nodeelem's value to give
2266+ // us a better-conditioned matrix after the constraints are
2267+ // applied.
2268+ if (precondition_constraint_operator )
2269+ if (auto sum_it = column_sums .find (indices [jj ]);
2270+ sum_it != column_sums .end ())
2271+ {
2272+ const Real scaling = sum_it -> second ;
2273+
2274+ if (scaling > TOLERANCE )
2275+ coef /= scaling ;
2276+ }
2277+
22512278 constraint_row .emplace_back (std ::make_pair (p , coef ));
22522279 }
22532280
@@ -2341,11 +2368,13 @@ std::string MeshBase::get_local_constraints(bool print_nonlocal) const
23412368
23422369// Explicit instantiations for our template function
23432370template LIBMESH_EXPORT void
2344- MeshBase ::copy_constraint_rows (const SparseMatrix < Real > & constraint_operator );
2371+ MeshBase ::copy_constraint_rows (const SparseMatrix < Real > & constraint_operator ,
2372+ bool precondition_constraint_operator );
23452373
23462374#ifdef LIBMESH_USE_COMPLEX_NUMBERS
23472375template LIBMESH_EXPORT void
2348- MeshBase ::copy_constraint_rows (const SparseMatrix < Complex > & constraint_operator );
2376+ MeshBase ::copy_constraint_rows (const SparseMatrix < Complex > & constraint_operator ,
2377+ bool precondition_constraint_operator );
23492378#endif
23502379
23512380
0 commit comments