LCOV - code coverage report
Current view: top level - src/userobjects - ComputeBlockOrientationByRotation.C (source / functions) Hit Total Coverage
Test: idaholab/moose solid_mechanics: #32971 (54bef8) with base c6cf66 Lines: 58 62 93.5 %
Date: 2026-05-29 20:40:07 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://www.mooseframework.org
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #include "ComputeBlockOrientationByRotation.h"
      11             : #include "MooseMesh.h"
      12             : 
      13             : #include "libmesh/mesh_tools.h"
      14             : 
      15             : registerMooseObject("SolidMechanicsApp", ComputeBlockOrientationByRotation);
      16             : 
      17             : InputParameters
      18           8 : ComputeBlockOrientationByRotation::validParams()
      19             : {
      20           8 :   InputParameters params = ComputeBlockOrientationBase::validParams();
      21          16 :   params.addParam<unsigned int>("bins", 20, "Number of bins to segregate quaternions");
      22          16 :   params.addParam<Real>("L_norm", 1, "Specifies the type of average the user intends to perform");
      23           8 :   params.addClassDescription(
      24             :       "This object determines the orientation of each grain (block) by identifying the most common "
      25             :       "orientation direction among the material points within the grain.");
      26           8 :   return params;
      27           0 : }
      28             : 
      29           4 : ComputeBlockOrientationByRotation::ComputeBlockOrientationByRotation(
      30           4 :     const InputParameters & parameters)
      31             :   : ComputeBlockOrientationBase(parameters),
      32           4 :     _updated_rotation(getMaterialProperty<RankTwoTensor>("updated_rotation")),
      33           8 :     _bins(getParam<unsigned int>("bins")),
      34          12 :     _L_norm(getParam<Real>("L_norm"))
      35             : {
      36           4 : }
      37             : 
      38             : void
      39          17 : ComputeBlockOrientationByRotation::initialize()
      40             : {
      41          17 :   ComputeBlockOrientationBase::initialize();
      42             : 
      43             :   _block_ea_values.clear();
      44             :   _quat.clear();
      45          17 : }
      46             : 
      47             : void
      48         416 : ComputeBlockOrientationByRotation::execute()
      49             : {
      50         416 :   RankTwoTensor rot;
      51         416 :   MathUtils::mooseSetToZero(rot);
      52             : 
      53        3744 :   for (unsigned int _qp = 0; _qp < _qrule->n_points(); _qp++)
      54        3328 :     rot += _JxW[_qp] * _coord[_qp] * _updated_rotation[_qp];
      55         416 :   rot /= _current_elem_volume;
      56             : 
      57             :   // transform RankTwoTensor to Eigen::Matrix
      58             :   Eigen::Matrix<Real, 3, 3> rot_mat;
      59             : 
      60        1664 :   for (unsigned int i = 0; i < 3; ++i)
      61        4992 :     for (unsigned int j = 0; j < 3; ++j)
      62        3744 :       rot_mat(i, j) = rot(i, j);
      63             : 
      64             :   // SVD-based projection to SO(3)
      65             :   Eigen::JacobiSVD<Eigen::Matrix<Real, 3, 3>> svd(rot_mat,
      66         416 :                                                   Eigen::ComputeFullU | Eigen::ComputeFullV);
      67             : 
      68             :   Eigen::Matrix<Real, 3, 3> U = svd.matrixU();
      69             :   Eigen::Matrix<Real, 3, 3> V = svd.matrixV();
      70             : 
      71             :   // Enforce proper rotation (det = +1)
      72         416 :   Eigen::Matrix<Real, 3, 3> R = U * V.transpose();
      73         416 :   if (R.determinant() < 0.0)
      74             :   {
      75           0 :     Eigen::Matrix<Real, 3, 3> D = Eigen::Matrix<Real, 3, 3>::Identity();
      76           0 :     D(2, 2) = -1.0;
      77           0 :     R = U * D * V.transpose();
      78             :   }
      79             : 
      80             :   // compute Quaternion from rotation matrix
      81             :   Eigen::Quaternion<Real> q(R);
      82             :   // store quaternion values for the current element
      83         832 :   _quat[_current_elem->subdomain_id()].push_back(std::make_tuple(q.w(), q.x(), q.y(), q.z()));
      84         416 : }
      85             : 
      86             : void
      87          17 : ComputeBlockOrientationByRotation::finalize()
      88             : {
      89         153 :   for (const auto & block : _fe_problem.mesh().meshSubdomains())
      90             :   {
      91             :     // Use allgather to sync data from all processors
      92         136 :     _communicator.allgather(_quat[block]);
      93         136 :     _block_ea_values[block] = computeSubdomainEulerAngles(block);
      94             :   }
      95          17 : }
      96             : 
      97             : EulerAngles
      98         136 : ComputeBlockOrientationByRotation::computeSubdomainEulerAngles(const SubdomainID & sid)
      99             : {
     100             :   // creating a map to store the quaternion count for each bin index
     101             :   std::map<std::tuple<int, int, int, int>, unsigned int> feature_weights;
     102             : 
     103             :   // precompute the scaling from the bins once and reuse
     104         136 :   const Real bin_scale = 0.5 * _bins;
     105             : 
     106         680 :   for (const auto & [w, x, y, z] : _quat[sid])
     107             :   {
     108         544 :     const auto bin = std::make_tuple<int, int, int, int>(std::floor(w * bin_scale),
     109         544 :                                                          std::floor(x * bin_scale),
     110         544 :                                                          std::floor(y * bin_scale),
     111         544 :                                                          std::floor(z * bin_scale));
     112         544 :     feature_weights[bin]++;
     113             :   }
     114             : 
     115             :   // quaternion average matrix Q*w*Q^T
     116             :   typedef Eigen::Matrix<Real, 4, 4> Matrix4x4;
     117             :   Matrix4x4 quat_mat = Matrix4x4::Zero();
     118             :   typedef Eigen::Matrix<Real, 4, 1> Vector4;
     119             : 
     120             :   Real total_weight = 0.0;
     121             : 
     122         680 :   for (const auto & [w, x, y, z] : _quat[sid])
     123             :   {
     124             :     Vector4 v(w, x, y, z);
     125             : 
     126         544 :     const auto bin = std::make_tuple<int, int, int, int>(std::floor(w * bin_scale),
     127         544 :                                                          std::floor(x * bin_scale),
     128         544 :                                                          std::floor(y * bin_scale),
     129         544 :                                                          std::floor(z * bin_scale));
     130         544 :     const auto bin_size = feature_weights[bin];
     131         544 :     const auto weight = std::pow(bin_size, _L_norm);
     132         544 :     total_weight += weight;
     133             : 
     134         544 :     quat_mat += v * weight * v.transpose();
     135             :   }
     136             : 
     137         136 :   quat_mat *= 1.0 / total_weight;
     138             : 
     139             :   // compute eigenvalues and eigenvectors
     140         136 :   Eigen::EigenSolver<Matrix4x4> EigenSolver(quat_mat);
     141         136 :   Vector4 eigen_values = EigenSolver.eigenvalues().real();
     142         136 :   Matrix4x4 eigen_vectors = EigenSolver.eigenvectors().real();
     143             : 
     144             :   // Selecting eigenvector corresponding to max eigenvalue to compute average Euler angle
     145         136 :   Vector4::Index max_index = 0;
     146             :   eigen_values.maxCoeff(&max_index);
     147         136 :   const auto max_vec = eigen_vectors.col(max_index) / eigen_vectors.col(max_index).norm();
     148             :   const Eigen::Quaternion<Real> q(max_vec(0), max_vec(1), max_vec(2), max_vec(3));
     149             : 
     150         272 :   return EulerAngles(q);
     151             : }

Generated by: LCOV version 1.14