18#include "libmesh/variational_smoother_system.h"
20#include "libmesh/elem.h"
21#include "libmesh/face_tri3.h"
22#include "libmesh/face_tri6.h"
23#include "libmesh/fe_base.h"
24#include "libmesh/fe_interface.h"
25#include "libmesh/fem_context.h"
26#include "libmesh/mesh.h"
27#include "libmesh/numeric_vector.h"
28#include "libmesh/parallel_ghost_sync.h"
29#include "libmesh/quadrature.h"
30#include "libmesh/string_to_enum.h"
31#include "libmesh/utility.h"
32#include "libmesh/enum_to_string.h"
33#include <libmesh/reference_elem.h>
48 comm.
minloc(pair.first, rank);
59 comm.
maxloc(pair.first, rank);
68 return 0.5 * (x + std::sqrt(epsilon_squared + Utility::pow<2>(x)));
76 const unsigned int &
dim,
77 const unsigned int & qp)
79 libmesh_error_msg_if(
dim > 3,
"Unsupported dimension.");
108 bool apply_heterogeneous_constraints,
109 bool apply_no_constraints)
115 for (
auto * node :
mesh.local_node_ptr_range())
119 const auto dof_id = node->dof_number(this->
number(), d, 0);
138 const Real variable_component = 100. * Utility::pow<2>(
_ref_vol * min_S);
145 FEMSystem::assembly(get_residual, get_jacobian, apply_heterogeneous_constraints, apply_no_constraints);
164 if (mesh_info.mesh_is_tangled)
193 libmesh_error_msg_if(mesh_info.mesh_is_tangled,
"The smoothing solve tangled the mesh!");
201 const auto & elem_orders =
mesh.elem_default_orders();
202 libmesh_error_msg_if(elem_orders.size() != 1,
203 "The variational smoother cannot be used for mixed-order meshes!");
204 const auto fe_order = *elem_orders.begin();
216 for (
auto * node :
mesh.local_node_ptr_range())
220 const auto dof_id = node->dof_number(this->
number(), d, 0);
222 (*solution).set(dof_id, (*node)(d));
236 FEMContext & femcontext = cast_ref<FEMContext &>(*con);
241 Real elem_averaged_det_S_sum = 0.;
247 const auto & JxW = fe_map.get_JxW();
249 for (
const auto * elem :
mesh.active_local_element_ptr_range())
265 Real elem_integrated_det_S = 0.;
268 const auto ref_elem_vol = elem->reference_elem()->volume();
269 elem_averaged_det_S_sum += elem_integrated_det_S / ref_elem_vol;
274 mesh.comm().sum(elem_averaged_det_S_sum);
276 _ref_vol = elem_averaged_det_S_sum /
mesh.n_active_elem();
283 FEMContext & c = cast_ref<FEMContext &>(context);
291 const std::set<unsigned char> & elem_dims =
294 for (
const auto &
dim : elem_dims)
301 fe_map.get_dxyzdeta();
302 fe_map.get_dxyzdzeta();
306 fe_map.set_jacobian_tolerance(std::numeric_limits<Real>::lowest());
328 FEMContext & c = cast_ref<FEMContext &>(context);
334 unsigned int x_var = 0, y_var = 1, z_var = 2;
345 std::reference_wrapper<DenseSubVector<Number>> F[3] =
352 std::reference_wrapper<DenseSubMatrix<Number>> K[3][3] =
369 const auto & dphidxi_map = fe_map.get_dphidxi_map();
370 const auto & dphideta_map = fe_map.get_dphideta_map();
371 const auto & dphidzeta_map = fe_map.get_dphidzeta_map();
388 const auto quad_weight = quad_weights[qp] / target_jacobian_dets[qp];
408 const Real det_sq = det * det;
409 const Real det_cube = det_sq * det;
418 const Real tr_div_dim = tr /
dim;
421 const Real half_dim = 0.5 *
dim;
422 const std::vector<Real> trace_powers{
423 std::pow(tr_div_dim, half_dim),
424 std::pow(tr_div_dim, half_dim - 1.),
425 std::pow(tr_div_dim, half_dim - 2.),
440 const Real chi_sq = chi * chi;
443 const Real chi_prime = 0.5 * (1. + det / sqrt_term);
444 const Real chi_prime_sq = chi_prime * chi_prime;
446 const Real chi_2prime = 0.5 * (1. / sqrt_term - det_sq / Utility::pow<3>(sqrt_term));
450 const RealTensor dbeta_dS = (trace_powers[1] / chi) * S - (trace_powers[0] / chi_sq * chi_prime * det) * S_inv_T;
455 const Real alpha = (-chi_prime * det_cube + 2. * det_sq * chi - ref_vol_sq * det * chi_prime) / (2. *
_ref_vol * chi_sq);
463 std::vector<std::vector<std::vector<Real>>> dphi_maps = {dphidxi_map, dphideta_map, dphidzeta_map};
475 dS_dR(var_id, jj) = dphi_maps[jj][l][qp];
476 dS_dR *= target_jacobians[qp];
480 F[var_id](l) += quad_weight * dE_dS.
contract(dS_dR);
485 if (request_jacobian)
499 const std::vector<Real> d2beta_dS2_coefs_times_distortion_weight = {
503 (trace_powers[1] / chi) * distortion_weight,
505 (((
dim - 2.) /
dim) * trace_powers[2] / chi) * distortion_weight,
507 (-(trace_powers[1] / chi_sq) * chi_prime * det) * distortion_weight,
512 (-(trace_powers[1] / chi_sq) * chi_prime * det) * distortion_weight,
514 (trace_powers[0] * (det / chi_sq)
515 * ((2. * chi_prime_sq / chi - chi_2prime) * det - chi_prime)) * distortion_weight,
517 ((trace_powers[0] / chi_sq) * chi_prime * det) * distortion_weight,
521 const Real dalpha_dS_coef_times_dilation_weight = ((det / (2. *
_ref_vol * chi_sq))
522 * (-4. *
_ref_vol * alpha * chi * chi_prime - chi_2prime * det_cube
523 - chi_prime * det_sq + 4 * chi * det - ref_vol_sq * (chi_prime + det * chi_2prime))) *
_dilation_weight;
629 dS_dR_l(var_id1, ii) = dphi_maps[ii][l][qp];
630 dS_dR_l *= target_jacobians[qp];
641 dS_dR_p(var_id2, jj) = dphi_maps[jj][p][qp];
642 dS_dR_p *= target_jacobians[qp];
652 const auto S_ij = S(i,j);
653 const auto S_inv_ji = S_inv(j,i);
657 const std::vector<Real> d2beta_dS2_coefs_ij_applied{
658 d2beta_dS2_coefs_times_distortion_weight[1] * S_ij,
659 d2beta_dS2_coefs_times_distortion_weight[2] * S_ij,
660 d2beta_dS2_coefs_times_distortion_weight[3] * S_inv_ji,
661 d2beta_dS2_coefs_times_distortion_weight[4] * S_inv_ji,
664 const auto dalpha_dS_coef_ij_applied = dalpha_dS_coef_times_dilation_weight * S_inv_ji;
666 Real d2E_dSdR_l = 0.;
667 Real d2E_dSdR_p = 0.;
676 if (!(a == var_id1 && i == var_id2) &&
677 !(a == var_id2 && i == var_id1))
680 const auto S_inv_ja = S_inv(j,a);
682 const Real d2beta_dS2_coef_ia_applied = d2beta_dS2_coefs_times_distortion_weight[0] * I(i,a);
683 const Real d2beta_dS2_coef_ja_applied = d2beta_dS2_coefs_times_distortion_weight[5] * S_inv_ja;
684 const Real alpha_ja_applied = alpha_times_dilation_weight * S_inv_ja;
686 const auto b_limit = (a == i) ? j + 1 :
dim;
692 Real d2beta_dS2_times_distortion_weight = (
693 d2beta_dS2_coef_ia_applied * I(j,
b) +
694 d2beta_dS2_coefs_ij_applied[0] * S(a,
b) +
695 d2beta_dS2_coefs_ij_applied[1] * S_inv(
b,a) +
696 d2beta_dS2_coefs_ij_applied[2] * S(a,
b) +
697 d2beta_dS2_coefs_ij_applied[3] * S_inv(
b,a) +
698 d2beta_dS2_coef_ja_applied * S_inv(
b,i)
703 const Real d2mu_dS2_times_dilation_weight = dalpha_dS_coef_ij_applied * S_inv(
b,a) - alpha_ja_applied * S_inv(
b,i);
708 d2beta_dS2_times_distortion_weight +
709 d2mu_dS2_times_dilation_weight;
713 d2E_dSdR_l += d2E_dS2 * dS_dR_l(a,
b);
715 if (!(i == a && j ==
b))
718 d2E_dSdR_p += d2E_dS2 * dS_dR_p(a,
b);
723 d2E_dSdR_l * dS_dR_p(i, j) + d2E_dSdR_p * dS_dR_l(i, j);
728 const Real jacobian_contribution = quad_weight * d2E_dR2;
729 K[var_id1][var_id2](l, p) += jacobian_contribution;
734 K[var_id2][var_id1](p, l) += jacobian_contribution;
743 return request_jacobian;
761 FEMContext & femcontext = cast_ref<FEMContext &>(*con);
765 const auto dim =
mesh.mesh_dimension();
766 const Real half_dim = 0.5 *
dim;
774 const auto & JxW = fe_map.get_JxW();
775 fe_map.get_dxyzdxi();
776 fe_map.get_dxyzdeta();
777 fe_map.get_dxyzdzeta();
781 for (
const auto * elem :
mesh.active_local_element_ptr_range())
790 Real combined_int = 0.;
797 const auto quad_weight = quad_weights[qp] / target_jacobian_dets[qp];
806 const auto det = S.
det();
807 const auto det_sq = det * det;
809 if (det > info.max_qp_det_S)
810 info.max_qp_det_S = det;
811 else if (det < info.min_qp_det_S)
812 info.min_qp_det_S = det;
815 info.mesh_is_tangled =
true;
824 const Real beta = std::pow(tr /
dim, half_dim) / chi;
825 beta_int += beta * quad_weight;
829 mu_int += mu * quad_weight;
833 combined_int += E * quad_weight;
836 info.total_det_S += det_S_int;
837 if (det_S_int > info.max_elem_det_S.first)
838 info.max_elem_det_S = std::make_pair(det_S_int, elem->id());
839 if (det_S_int < info.min_elem_det_S.first)
840 info.min_elem_det_S = std::make_pair(det_S_int, elem->id());
842 info.total_distortion += beta_int;
843 if (beta_int > info.max_elem_distortion.first)
844 info.max_elem_distortion = std::make_pair(beta_int, elem->id());
845 if (beta_int < info.min_elem_distortion.first)
846 info.min_elem_distortion = std::make_pair(beta_int, elem->id());
848 info.total_dilation += mu_int;
849 if (mu_int > info.max_elem_dilation.first)
850 info.max_elem_dilation = std::make_pair(mu_int, elem->id());
851 if (mu_int < info.min_elem_dilation.first)
852 info.min_elem_dilation = std::make_pair(mu_int, elem->id());
854 info.total_combined += combined_int;
855 if (combined_int > info.max_elem_combined.first)
856 info.max_elem_combined = std::make_pair(combined_int, elem->id());
857 if (combined_int < info.min_elem_combined.first)
858 info.min_elem_combined = std::make_pair(combined_int, elem->id());
862 libMesh::out <<
"Elem " << elem->id() <<
" quality:" << std::endl
863 <<
" distortion-dilation metric: " << combined_int << std::endl
864 <<
" distortion metric: " << beta_int << std::endl
865 <<
" dilation metric: " << mu_int << std::endl
866 <<
" det(S): " << det_S_int << std::endl;
874 mesh.comm().max(info.max_qp_det_S);
875 mesh.comm().min(info.min_qp_det_S);
876 mesh.comm().sum(info.total_det_S);
880 mesh.comm().sum(info.total_distortion);
884 mesh.comm().sum(info.total_dilation);
888 mesh.comm().sum(info.total_combined);
890 mesh.comm().max(info.mesh_is_tangled);
892 info.initialized =
true;
901 const Elem *
const target_elem,
903 std::vector<RealTensor> & jacobians,
904 std::vector<Real> & jacobian_dets)
907 const auto dim = target_elem->
dim();
916 jacobians = std::vector<RealTensor>(nq_points,
RealTensor(
920 jacobian_dets = std::vector<Real>(nq_points, 1.0);
924 bool target_equals_reference =
true;
927 target_equals_reference &= target_elem->
node_ref(local_id) == ref_elem->node_ref(local_id);
928 if (target_equals_reference)
941 fe_map_target.
compute_map(
dim, qrule_weights, target_elem,
false);
951 jacobian_dets[qp] = jacobians[qp].det();
957 os <<
"Mesh quality info:" << std::endl
958 <<
" Mesh distortion-dilation metric: "
959 << info.total_combined << std::endl
960 <<
" Mesh distortion metric: "
961 << info.total_distortion << std::endl
962 <<
" Mesh dilation metric: "
963 << info.total_dilation << std::endl
964 <<
" Max distortion-dilation is in elem "
965 << info.max_elem_combined.second <<
": "
966 << info.max_elem_combined.first << std::endl
967 <<
" Max distortion is in elem "
968 << info.max_elem_distortion.second <<
": "
969 << info.max_elem_distortion.first << std::endl
970 <<
" Max dilation is in elem "
971 << info.max_elem_dilation.second <<
": "
972 << info.max_elem_dilation.first << std::endl
973 <<
" Max det(S) is in elem "
974 << info.max_elem_det_S.second <<
": "
975 << info.max_elem_det_S.first << std::endl
976 <<
" Min distortion-dilation is in elem "
977 << info.min_elem_combined.second <<
": "
978 << info.min_elem_combined.first << std::endl
979 <<
" Min distortion is in elem "
980 << info.min_elem_distortion.second <<
": "
981 << info.min_elem_distortion.first << std::endl
982 <<
" Min dilation is in elem "
983 << info.min_elem_dilation.second <<
": "
984 << info.min_elem_dilation.first << std::endl
985 <<
" Min det(S) is in elem "
986 << info.min_elem_det_S.second <<
": "
987 << info.min_elem_det_S.first << std::endl
988 <<
" Max qp det(S): " << info.max_qp_det_S << std::endl
989 <<
" Min qp det(S): " << info.min_qp_det_S << std::endl
990 <<
" Mesh-integrated det(S): " << info.total_det_S << std::endl
991 <<
" Tangled: " << info.mesh_is_tangled << std::endl;
void maxloc(T &r, unsigned int &max_id) const
void minloc(T &r, unsigned int &min_id) const
void broadcast(T &data, const unsigned int root_id=0, const bool identical_sizes=false) const
This class provides all data required for a physics package (e.g.
const DenseVector< Number > & get_elem_residual() const
Const accessor for element residual.
const DenseMatrix< Number > & get_elem_jacobian() const
Const accessor for element Jacobian.
This is the base class from which all geometric element types are derived.
const Node & node_ref(const unsigned int i) const
virtual unsigned int n_nodes() const =0
virtual unsigned short dim() const =0
const Elem * reference_elem() const
virtual ElemType type() const =0
IntRange< unsigned short > node_index_range() const
NumericVector< Number > * rhs
The system matrix.
const FEMap & get_fe_map() const
This class forms the foundation from which generic finite elements may be derived.
This class provides all data required for a physics package (e.g.
void get_side_fe(unsigned int var, FEGenericBase< OutputShape > *&fe) const
Accessor for edge/face (2D/3D) finite element object for variable var for the largest dimension in th...
const std::set< unsigned char > & elem_dimensions() const
virtual void pre_fe_reinit(const System &, const Elem *e)
Reinitializes local data vectors/matrices on the current geometric element.
const QBase & get_element_qrule() const
Accessor for element interior quadrature rule for the dimension of the current _elem.
unsigned char get_dim() const
Accessor for cached mesh dimension.
const Elem & get_elem() const
Accessor for current Elem object.
virtual void elem_fe_reinit(const std::vector< Point > *const pts=nullptr)
Reinitializes interior FE objects on the current geometric element.
void get_element_fe(unsigned int var, FEGenericBase< OutputShape > *&fe) const
Accessor for interior finite element object for variable var for the largest dimension in the mesh.
void use_quadrature_rules(QuadratureType qt, int extra_quadrature_order=0)
Use a specified quadrature rule type qt for every element, side, and edge dimension,...
virtual void solve() override
Invokes the solver associated with the system.
virtual void assembly(bool get_residual, bool get_jacobian, bool apply_heterogeneous_constraints=false, bool apply_no_constraints=false) override
Prepares matrix or rhs for matrix assembly.
virtual std::unique_ptr< DiffContext > build_context() override
Builds a FEMContext object with enough information to do evaluations on each element.
virtual void init_data() override
Initializes the member data fields associated with the system, so that, e.g., assemble() may be used.
virtual void init_context(DiffContext &) override
Class contained in FE that encapsulates mapping (i.e.
const std::vector< RealGradient > & get_dxyzdeta() const
const std::vector< RealGradient > & get_dxyzdzeta() const
void init_reference_to_physical_map(const std::vector< Point > &qp, const Elem *elem)
const std::vector< RealGradient > & get_dxyzdxi() const
virtual void compute_map(const unsigned int dim, const std::vector< Real > &qw, const Elem *elem, bool calculate_d2phi)
Compute the jacobian and some other additional data fields.
virtual void close()=0
Calls the NumericVector's internal assembly routines, ensuring that the values are consistent across ...
const std::vector< Point > & get_points() const
unsigned int n_points() const
const std::vector< Real > & get_weights() const
int extra_quadrature_order
A member int that can be employed to indicate increased or reduced quadrature order.
std::unique_ptr< NumericVector< Number > > current_local_solution
All the values I need to compute my contribution to the simulation at hand.
unsigned int add_variable(std::string_view var, const FEType &type, const std::set< subdomain_id_type > *const active_subdomains=nullptr)
Adds the variable var to the list of variables for this system.
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
unsigned int number() const
const MeshBase & get_mesh() const
This class defines a tensor in LIBMESH_DIM dimensional Real or Complex space.
TypeTensor< T > inverse() const
TypeTensor< T > transpose() const
LIBMESH_DEVICE_INLINE CompareTypes< T, T2 >::supertype contract(const TypeTensor< T2 > &) const
Multiply 2 tensors together to return a scalar, i.e.
MeshQualityInfo _mesh_info
Information about the mesh quality.
bool _untangling_solve
Flag to indicate if the current solve is to untangle or smooth.
const MeshQualityInfo & get_mesh_info()
Getter for the _mesh_info attribute.
void compute_mesh_quality_info()
QuadratureType _quadrature_type
The quadrature rule type used to integrate the distortion-dilation metric.
~VariationalSmootherSystem() override
unsigned int _verbosity
Verbosity setting.
Real _dilation_weight
The relative weight to give the dilation metric.
const Real _epsilon_squared
The small nonzero constant to prevent zero denominators (degenerate meshes only)
Real _epsilon_squared_assembly
Epsilon squared value determined at runtime during each assembly.
virtual void assembly(bool get_residual, bool get_jacobian, bool apply_heterogeneous_constraints=false, bool apply_no_constraints=false) override
Assembly method to update the mesh based on the smoother solve.
Real _ref_vol
The reference volume for each element.
std::map< ElemType, std::vector< RealTensor > > _target_jacobians
virtual void init_data() override
Initializes the member data fields associated with the system, so that, e.g., assemble() may be used.
virtual bool element_time_derivative(bool request_jacobian, libMesh::DiffContext &context) override
Adds the time derivative contribution on elem to elem_residual.
static void get_target_to_reference_jacobian(const Elem *const target_elem, const FEMContext &femcontext, std::vector< RealTensor > &jacobians, std::vector< Real > &jacobian_dets)
Get the jacobians (and determinants) of the target-to-reference element mapping.
std::map< ElemType, std::vector< Real > > _target_jacobian_dets
void prepare_for_smoothing()
virtual void solve() override
Solves the system to smooth the mesh.
virtual void init_context(libMesh::DiffContext &context) override
void sync_dofobject_data_by_id(const Communicator &comm, const Iterator &range_begin, const Iterator &range_end, SyncFunctor &sync)
Request data about a range of ghost dofobjects uniquely identified by their id.
std::pair< std::unique_ptr< Elem >, std::vector< std::unique_ptr< Node > > > ideal_target(const ElemType type)
The libMesh namespace provides an interface to certain functionality in the library.
Real trace(const RealTensor &A, const unsigned int &dim)
Compute the trace of a dim-dimensional matrix.
Real chi_epsilon(const Real &x, const Real epsilon_squared)
Function to prevent dividing by zero for degenerate elements.
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
std::ostream & operator<<(std::ostream &os, const OrderWrapper &order)
Overload stream operators.
void communicate_pair_min(std::pair< Real, dof_id_type > &pair, const Parallel::Communicator &comm)
LIBMESH_DEVICE_INLINE T libmesh_real(T a)
RealTensorValue RealTensor
static constexpr Real TOLERANCE
RealTensor get_jacobian_at_qp(const FEMap &fe_map, const unsigned int &dim, const unsigned int &qp)
Given an fe_map, element dimension, and quadrature point index, returns the Jacobian of the physical-...
RealVectorValue RealGradient
void communicate_pair_max(std::pair< Real, dof_id_type > &pair, const Parallel::Communicator &comm)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Struct to hold smoother-relevant information about the mesh quality.