LCOV - code coverage report
Current view: top level - src/base - XFEMCutElem.C (source / functions) Hit Total Coverage
Test: idaholab/moose xfem: #31405 (292dce) with base fef103 Lines: 76 85 89.4 %
Date: 2025-09-04 07:58:55 Functions: 9 10 90.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       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 "XFEMCutElem.h"
      11             : 
      12             : #include "libmesh/mesh.h"
      13             : #include "libmesh/elem.h"
      14             : #include "libmesh/node.h"
      15             : #include "libmesh/quadrature.h"
      16             : #include "libmesh/fe.h"
      17             : #include "libmesh/enum_fe_family.h"
      18             : 
      19             : #include "EFANode.h"
      20             : #include "EFAElement.h"
      21             : 
      22       11858 : XFEMCutElem::XFEMCutElem(Elem * elem, unsigned int n_qpoints, unsigned int n_sides)
      23       23716 :   : _n_nodes(elem->n_nodes()),
      24       11858 :     _n_qpoints(n_qpoints),
      25       11858 :     _n_sides(n_sides),
      26       11858 :     _nodes(_n_nodes, nullptr),
      27       11858 :     _elem_side_area(n_sides),
      28       11858 :     _physical_areafrac(n_sides),
      29       11858 :     _have_weights(false),
      30       11858 :     _have_face_weights(n_sides),
      31       23716 :     _new_face_weights(n_sides)
      32             : {
      33       79534 :   for (unsigned int i = 0; i < _n_nodes; ++i)
      34       67676 :     _nodes[i] = elem->node_ptr(i);
      35             : 
      36       60730 :   for (unsigned int i = 0; i < _n_sides; ++i)
      37             :   {
      38             :     _have_face_weights[i] = false;
      39       48872 :     _physical_areafrac[i] = 1.0;
      40             :     // TODO: this line does not support RZ/RSpherical geoms
      41       48872 :     _elem_side_area[i] = elem->side_ptr(i)->volume();
      42             :   }
      43             : 
      44             :   // TODO: this line does not support RZ/RSpherical geoms
      45       11858 :   _elem_volume = elem->volume();
      46       11858 : }
      47             : 
      48       23716 : XFEMCutElem::~XFEMCutElem() {}
      49             : 
      50             : Real
      51      135527 : XFEMCutElem::getPhysicalVolumeFraction() const
      52             : {
      53      135527 :   return _physical_volfrac;
      54             : }
      55             : 
      56             : Real
      57          78 : XFEMCutElem::getPhysicalFaceAreaFraction(unsigned int side) const
      58             : {
      59          78 :   return _physical_areafrac[side];
      60             : }
      61             : 
      62             : void
      63      626171 : XFEMCutElem::getWeightMultipliers(MooseArray<Real> & weights,
      64             :                                   QBase * qrule,
      65             :                                   Xfem::XFEM_QRULE xfem_qrule,
      66             :                                   const MooseArray<Point> & q_points)
      67             : {
      68      626171 :   if (!_have_weights)
      69        8021 :     computeXFEMWeights(qrule, xfem_qrule, q_points);
      70             : 
      71      626171 :   weights.resize(_new_weights.size());
      72     5063807 :   for (unsigned int qp = 0; qp < _new_weights.size(); ++qp)
      73     4437636 :     weights[qp] = _new_weights[qp];
      74      626171 : }
      75             : 
      76             : void
      77         290 : XFEMCutElem::getFaceWeightMultipliers(MooseArray<Real> & face_weights,
      78             :                                       QBase * qrule,
      79             :                                       Xfem::XFEM_QRULE xfem_qrule,
      80             :                                       const MooseArray<Point> & q_points,
      81             :                                       unsigned int side)
      82             : {
      83         290 :   if (!_have_face_weights[side])
      84          78 :     computeXFEMFaceWeights(qrule, xfem_qrule, q_points, side);
      85             : 
      86         290 :   face_weights.resize(_new_face_weights[side].size());
      87        1150 :   for (unsigned int qp = 0; qp < _new_face_weights[side].size(); ++qp)
      88         860 :     face_weights[qp] = _new_face_weights[side][qp];
      89         290 : }
      90             : 
      91             : void
      92          78 : XFEMCutElem::computeXFEMFaceWeights(QBase * qrule,
      93             :                                     Xfem::XFEM_QRULE /*xfem_qrule*/,
      94             :                                     const MooseArray<Point> & /*q_points*/,
      95             :                                     unsigned int side)
      96             : {
      97          78 :   _new_face_weights[side].clear();
      98          78 :   _new_face_weights[side].resize(qrule->n_points(), 1.0);
      99             : 
     100          78 :   computePhysicalFaceAreaFraction(side);
     101          78 :   Real surffrac = getPhysicalFaceAreaFraction(side);
     102         354 :   for (unsigned qp = 0; qp < qrule->n_points(); ++qp)
     103         276 :     _new_face_weights[side][qp] = surffrac;
     104             : 
     105             :   _have_face_weights[side] = true;
     106          78 : }
     107             : 
     108             : void
     109        8021 : XFEMCutElem::computeXFEMWeights(QBase * qrule,
     110             :                                 Xfem::XFEM_QRULE xfem_qrule,
     111             :                                 const MooseArray<Point> & q_points)
     112             : {
     113        8021 :   _new_weights.clear();
     114             : 
     115        8021 :   _new_weights.resize(qrule->n_points(), 1.0);
     116             : 
     117        8021 :   switch (xfem_qrule)
     118             :   {
     119        7673 :     case Xfem::VOLFRAC:
     120             :     {
     121        7673 :       computePhysicalVolumeFraction();
     122        7673 :       Real volfrac = getPhysicalVolumeFraction();
     123       68719 :       for (unsigned qp = 0; qp < qrule->n_points(); ++qp)
     124       61046 :         _new_weights[qp] = volfrac;
     125             :       break;
     126             :     }
     127             :     case Xfem::MOMENT_FITTING:
     128             :     {
     129             :       // These are the coordinates in parametric coordinates
     130         348 :       _qp_points = qrule->get_points();
     131         348 :       _qp_weights = qrule->get_weights();
     132             : 
     133         348 :       computeMomentFittingWeights();
     134             : 
     135             :       // Blend weights from moment fitting and volume fraction to avoid negative weights
     136             :       Real alpha = 1.0;
     137         348 :       if (*std::min_element(_new_weights.begin(), _new_weights.end()) < 0.0)
     138             :       {
     139             :         // One or more of the weights computed by moment fitting is negative.
     140             :         // Blend moment and volume fraction weights to keep them nonnegative.
     141             :         // Find the largest value of alpha that will keep all of the weights nonnegative.
     142         906 :         for (unsigned int i = 0; i < _n_qpoints; ++i)
     143             :         {
     144         732 :           const Real denominator = _physical_volfrac - _new_weights[i];
     145         732 :           if (denominator > 0.0) // Negative values would give a negative value for alpha, which
     146             :                                  // must be between 0 and 1.
     147             :           {
     148         420 :             const Real alpha_i = _physical_volfrac / denominator;
     149         420 :             if (alpha_i < alpha)
     150             :               alpha = alpha_i;
     151             :           }
     152             :         }
     153         906 :         for (unsigned int i = 0; i < _n_qpoints; ++i)
     154             :         {
     155         732 :           _new_weights[i] = alpha * _new_weights[i] + (1.0 - alpha) * _physical_volfrac;
     156         732 :           if (_new_weights[i] < 0.0) // We can end up with small (roundoff) negative weights
     157          96 :             _new_weights[i] = 0.0;
     158             :         }
     159             :       }
     160             :       break;
     161             :     }
     162             :     case Xfem::DIRECT: // remove q-points outside the partial element's physical domain
     163             :     {
     164             :       bool nonzero = false;
     165           0 :       for (unsigned qp = 0; qp < qrule->n_points(); ++qp)
     166             :       {
     167             :         // q_points contains quadrature point locations in physical coordinates
     168           0 :         if (isPointPhysical(q_points[qp]))
     169             :         {
     170             :           nonzero = true;
     171           0 :           _new_weights[qp] = 1.0;
     172             :         }
     173             :         else
     174           0 :           _new_weights[qp] = 0.0;
     175             :       }
     176           0 :       if (!nonzero)
     177             :       {
     178             :         // Set the weights to a small value (1e-3) to avoid having DOFs
     179             :         // with zeros on the diagonal, which occurs for nodes that are
     180             :         // connected to no physical material.
     181           0 :         for (unsigned qp = 0; qp < qrule->n_points(); ++qp)
     182           0 :           _new_weights[qp] = 1e-3;
     183             :       }
     184             :       break;
     185             :     }
     186           0 :     default:
     187           0 :       mooseError("Undefined option for XFEM_QRULE");
     188             :   }
     189        8021 :   _have_weights = true;
     190        8021 : }
     191             : 
     192             : bool
     193      177040 : XFEMCutElem::isPointPhysical(const Point & p) const
     194             : {
     195             :   // determine whether the point is inside the physical domain of a partial element
     196             :   bool physical_flag = true;
     197      177040 :   unsigned int n_cut_planes = numCutPlanes();
     198      268386 :   for (unsigned int plane_id = 0; plane_id < n_cut_planes; ++plane_id)
     199             :   {
     200      177040 :     Point origin = getCutPlaneOrigin(plane_id);
     201      177040 :     Point normal = getCutPlaneNormal(plane_id);
     202             :     Point origin2qp = p - origin;
     203      177040 :     if (origin2qp * normal > 0.0)
     204             :     {
     205             :       physical_flag = false; // Point outside pysical domain
     206       85694 :       break;
     207             :     }
     208             :   }
     209      177040 :   return physical_flag;
     210             : }

Generated by: LCOV version 1.14