LCOV - code coverage report
Current view: top level - src/userobjects - SelfShadowSideUserObject.C (source / functions) Hit Total Coverage
Test: idaholab/moose heat_transfer: #31405 (292dce) with base fef103 Lines: 153 165 92.7 %
Date: 2025-09-04 07:53:51 Functions: 14 14 100.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 "SelfShadowSideUserObject.h"
      11             : #include "RotationMatrix.h"
      12             : 
      13             : #include "libmesh/parallel_algebra.h"
      14             : 
      15             : registerMooseObject("HeatTransferApp", SelfShadowSideUserObject);
      16             : 
      17             : InputParameters
      18         677 : SelfShadowSideUserObject::validParams()
      19             : {
      20         677 :   InputParameters params = SideUserObject::validParams();
      21         677 :   params.addClassDescription("Compute the illumination status for a self shadowing sideset");
      22        1354 :   params.addRequiredRangeCheckedParam<std::vector<PostprocessorName>>(
      23             :       "illumination_flux",
      24             :       "illumination_flux_size>0 && illumination_flux_size<=3",
      25             :       "Radiation direction vector. Each component of the vector can be a constant number or a "
      26             :       "postprocessor name (the latter enables time varying radiation directions - spatial "
      27             :       "variation is not supported)");
      28         677 :   return params;
      29           0 : }
      30             : 
      31         364 : SelfShadowSideUserObject::SelfShadowSideUserObject(const InputParameters & parameters)
      32             :   : SideUserObject(parameters),
      33         728 :     _dim(_mesh.dimension()),
      34         728 :     _raw_direction(coupledPostprocessors("illumination_flux")),
      35         364 :     _illumination_status(
      36        1092 :         declareRestartableData<std::map<SideIDType, unsigned int>>("illumination_status"))
      37             : {
      38             :   // we should check the coordinate system (i.e. permit only 0,0,+-1 for RZ)
      39             : 
      40             :   // check problem dimension
      41         364 :   if (_dim != 2 && _dim != 3)
      42           0 :     mooseError("SelfShadowSideUserObject works only for 2 and 3 dimensional problems.");
      43             : 
      44             :   // fetch raw direction
      45        1456 :   for (const auto i : index_range(_raw_direction))
      46        1092 :     _raw_direction[i] = &getPostprocessorValue("illumination_flux", i);
      47         364 : }
      48             : 
      49             : void
      50         284 : SelfShadowSideUserObject::initialize()
      51             : {
      52             :   // delete list of collected lines or triangles
      53         284 :   _lines.clear();
      54         284 :   _triangles.clear();
      55             : 
      56             :   // delete local qps
      57         284 :   _local_qps.clear();
      58             : 
      59             :   // update direction and rotation matrix
      60             :   RealVectorValue direction;
      61        1136 :   for (const auto i : index_range(_raw_direction))
      62         852 :     direction(i) = *_raw_direction[i];
      63         284 :   _rotation =
      64         284 :       _dim == 2 ? RotationMatrix::rotVec2DToX(direction) : RotationMatrix::rotVecToZ(direction);
      65         284 : }
      66             : 
      67             : void
      68       96048 : SelfShadowSideUserObject::execute()
      69             : {
      70       96048 :   const SideIDType id(_current_elem->id(), _current_side);
      71             : 
      72             :   // add triangulated sides to list
      73       96048 :   if (_dim == 2)
      74        3996 :     addLines(id);
      75             :   else
      76       92052 :     addTriangles(id);
      77             : 
      78             :   // save off rotated QP coordinates got local sides
      79       96048 :   _local_qps.emplace_back(id, _q_point);
      80       96048 :   rotate(_local_qps.back().second);
      81       96048 : }
      82             : 
      83             : void
      84          51 : SelfShadowSideUserObject::threadJoin(const UserObject & y)
      85             : {
      86             :   const auto & uo = static_cast<const SelfShadowSideUserObject &>(y);
      87             : 
      88             :   // merge lists
      89          51 :   _lines.insert(_lines.end(), uo._lines.begin(), uo._lines.end());
      90          51 :   _triangles.insert(_triangles.end(), uo._triangles.begin(), uo._triangles.end());
      91          51 :   _local_qps.insert(_local_qps.end(), uo._local_qps.begin(), uo._local_qps.end());
      92          51 : }
      93             : 
      94             : unsigned int
      95     2458476 : SelfShadowSideUserObject::illumination(const SideIDType & id) const
      96             : {
      97     2458476 :   const auto it = _illumination_status.find(id);
      98     2458476 :   if (it == _illumination_status.end())
      99           0 :     mooseError("Illumination status was not calculated for current side.");
     100     2458476 :   return it->second;
     101             : }
     102             : 
     103             : void
     104         233 : SelfShadowSideUserObject::finalize()
     105             : {
     106             :   // rotate triangulations
     107         233 :   if (_dim == 2)
     108        5908 :     for (auto & line : _lines)
     109        5796 :       rotate(line);
     110             :   else
     111      313609 :     for (auto & triangle : _triangles)
     112      313488 :       rotate(triangle);
     113             : 
     114             :   // [compute local projected bounding box (in x or xy)]
     115             : 
     116             :   // communicate
     117         233 :   if (_dim == 2)
     118         112 :     _communicator.allgather(_lines, /*identical_buffer_sizes=*/false);
     119             :   else
     120         121 :     _communicator.allgather(_triangles, /*identical_buffer_sizes=*/false);
     121             : 
     122             :   // otherwise we iterate over QPs and check if any other side is in the way of the radiation
     123       96281 :   for (const auto & [id, qps] : _local_qps)
     124             :   {
     125       96048 :     auto & illumination = _illumination_status[id];
     126             : 
     127             :     // start off assuming no illumination
     128       96048 :     illumination = 0;
     129             : 
     130             :     // current bit in the illumination mask
     131             :     unsigned int bit = 1;
     132             : 
     133             :     // iterate over QPs
     134      639126 :     for (const auto i : index_range(qps))
     135             :     {
     136             :       // we set the bit at position _qp in the bitmask if the QP is illuminated
     137      543078 :       if (_dim == 2 && check2DIllumination(qps[i], id))
     138        2979 :         illumination |= bit;
     139      543078 :       if (_dim == 3 && check3DIllumination(qps[i], id))
     140      186696 :         illumination |= bit;
     141             : 
     142             :       // shift to next bit
     143      543078 :       bit <<= 1;
     144             :     }
     145             :   }
     146         233 : }
     147             : 
     148             : void
     149        3996 : SelfShadowSideUserObject::addLines(const SideIDType & id)
     150             : {
     151        3996 :   const auto & cse = *_current_side_elem;
     152        3996 :   switch (cse.type())
     153             :   {
     154        2196 :     case libMesh::EDGE2:
     155        2196 :       _lines.emplace_back(cse.node_ref(0), cse.node_ref(1), id);
     156        2196 :       break;
     157             : 
     158        1800 :     case libMesh::EDGE3:
     159        1800 :       _lines.emplace_back(cse.node_ref(0), cse.node_ref(2), id);
     160        1800 :       _lines.emplace_back(cse.node_ref(2), cse.node_ref(1), id);
     161        1800 :       break;
     162             : 
     163           0 :     case libMesh::EDGE4:
     164           0 :       _lines.emplace_back(cse.node_ref(0), cse.node_ref(2), id);
     165           0 :       _lines.emplace_back(cse.node_ref(2), cse.node_ref(3), id);
     166           0 :       _lines.emplace_back(cse.node_ref(3), cse.node_ref(1), id);
     167           0 :       break;
     168             : 
     169           0 :     default:
     170           0 :       mooseError("Unsupported EDGE type");
     171             :   }
     172        3996 : }
     173             : 
     174             : void
     175       92052 : SelfShadowSideUserObject::addTriangles(const SideIDType & id)
     176             : {
     177       92052 :   const auto & cse = *_current_side_elem;
     178       92052 :   switch (cse.type())
     179             :   {
     180       33588 :     case libMesh::TRI3:
     181       33588 :       _triangles.emplace_back(cse.node_ref(0), cse.node_ref(1), cse.node_ref(2), id);
     182       33588 :       break;
     183             : 
     184        1656 :     case libMesh::TRI6:
     185             :     case libMesh::TRI7:
     186        1656 :       _triangles.emplace_back(cse.node_ref(0), cse.node_ref(3), cse.node_ref(5), id);
     187        1656 :       _triangles.emplace_back(cse.node_ref(3), cse.node_ref(1), cse.node_ref(4), id);
     188        1656 :       _triangles.emplace_back(cse.node_ref(4), cse.node_ref(2), cse.node_ref(5), id);
     189        1656 :       _triangles.emplace_back(cse.node_ref(3), cse.node_ref(4), cse.node_ref(5), id);
     190        1656 :       break;
     191             : 
     192       24876 :     case libMesh::QUAD4:
     193       24876 :       _triangles.emplace_back(cse.node_ref(0), cse.node_ref(1), cse.node_ref(2), id);
     194       24876 :       _triangles.emplace_back(cse.node_ref(2), cse.node_ref(3), cse.node_ref(0), id);
     195       24876 :       break;
     196             : 
     197       15966 :     case libMesh::QUAD8:
     198       15966 :       _triangles.emplace_back(cse.node_ref(0), cse.node_ref(4), cse.node_ref(7), id);
     199       15966 :       _triangles.emplace_back(cse.node_ref(4), cse.node_ref(1), cse.node_ref(5), id);
     200       15966 :       _triangles.emplace_back(cse.node_ref(5), cse.node_ref(2), cse.node_ref(6), id);
     201       15966 :       _triangles.emplace_back(cse.node_ref(6), cse.node_ref(3), cse.node_ref(7), id);
     202       15966 :       _triangles.emplace_back(cse.node_ref(6), cse.node_ref(7), cse.node_ref(4), id);
     203       15966 :       _triangles.emplace_back(cse.node_ref(4), cse.node_ref(5), cse.node_ref(6), id);
     204       15966 :       break;
     205             : 
     206       15966 :     case libMesh::QUAD9:
     207       15966 :       _triangles.emplace_back(cse.node_ref(0), cse.node_ref(4), cse.node_ref(7), id);
     208       15966 :       _triangles.emplace_back(cse.node_ref(4), cse.node_ref(1), cse.node_ref(5), id);
     209       15966 :       _triangles.emplace_back(cse.node_ref(5), cse.node_ref(2), cse.node_ref(6), id);
     210       15966 :       _triangles.emplace_back(cse.node_ref(6), cse.node_ref(3), cse.node_ref(7), id);
     211       15966 :       _triangles.emplace_back(cse.node_ref(8), cse.node_ref(6), cse.node_ref(7), id);
     212       15966 :       _triangles.emplace_back(cse.node_ref(8), cse.node_ref(5), cse.node_ref(6), id);
     213       15966 :       _triangles.emplace_back(cse.node_ref(8), cse.node_ref(4), cse.node_ref(5), id);
     214       15966 :       _triangles.emplace_back(cse.node_ref(8), cse.node_ref(7), cse.node_ref(4), id);
     215       15966 :       break;
     216             : 
     217           0 :     default:
     218           0 :       mooseError("Unsupported FACE type");
     219             :   }
     220       92052 : }
     221             : 
     222             : bool
     223       10242 : SelfShadowSideUserObject::check2DIllumination(const Point & qp, const SideIDType & id)
     224             : {
     225       10242 :   const auto x = qp(0);
     226       10242 :   const auto y = qp(1);
     227             : 
     228             :   // loop over all line segments until one is found that provides shade
     229      483898 :   for (const auto & line : _lines)
     230             :   {
     231             :     const auto & [p1, p2, line_id] = line;
     232             : 
     233             :     // make sure a side never shades itself
     234       11078 :     if (line_id == id)
     235       11078 :       continue;
     236             : 
     237      469841 :     const bool ordered = p1(1) <= p2(1);
     238             : 
     239      469841 :     const auto y1 = ordered ? p1(1) : p2(1);
     240      469841 :     const auto y2 = ordered ? p2(1) : p1(1);
     241             : 
     242      469841 :     if (y >= y1 && y <= y2)
     243             :     {
     244             :       // line segment is oriented parallel to the irradiation direction
     245       14790 :       if (std::abs(y2 - y1) < libMesh::TOLERANCE)
     246             :         return false;
     247             : 
     248             :       // segment is in line with the QP in radiation direction. Is it in front or behind?
     249       14637 :       const auto x1 = ordered ? p1(0) : p2(0);
     250       14637 :       const auto x2 = ordered ? p2(0) : p1(0);
     251             : 
     252             :       // compute intersection location
     253       14637 :       const auto xs = (x2 - x1) * (y - y1) / (y2 - y1) + x1;
     254       14637 :       if (x > xs)
     255             :         return false;
     256             :     }
     257             :   }
     258             : 
     259             :   return true;
     260             : }
     261             : 
     262             : bool
     263      532836 : SelfShadowSideUserObject::check3DIllumination(const Point & qp, const SideIDType & id)
     264             : {
     265      532836 :   const auto x = qp(0);
     266      532836 :   const auto y = qp(1);
     267      532836 :   const auto z = qp(2);
     268             : 
     269             :   // loop over all triangles until one is found that provides shade
     270  1535064876 :   for (const auto & triangle : _triangles)
     271             :   {
     272             :     const auto & [p1, p2, p3, triangle_id] = triangle;
     273             : 
     274             :     // make sure a side never shades itself
     275     1455509 :     if (triangle_id == id)
     276     1455509 :       continue;
     277             : 
     278  1533422671 :     const auto x1 = p1(0);
     279  1533422671 :     const auto x2 = p2(0);
     280  1533422671 :     const auto x3 = p3(0);
     281             : 
     282  1533422671 :     const auto y1 = p1(1);
     283  1533422671 :     const auto y2 = p2(1);
     284  1533422671 :     const auto y3 = p3(1);
     285             : 
     286             :     // compute barycentric coordinates
     287  1533422671 :     const auto a = ((y2 - y3) * (x - x3) + (x3 - x2) * (y - y3)) /
     288  1533422671 :                    ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3));
     289  1533422671 :     const auto b = ((y3 - y1) * (x - x3) + (x1 - x3) * (y - y3)) /
     290             :                    ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3));
     291  1533422671 :     const auto c = 1.0 - a - b;
     292             : 
     293             :     // are we inside of the projected triangle?
     294  1533422671 :     if (0 <= a && a <= 1 && 0 <= b && b <= 1 && 0 <= c && c <= 1)
     295             :     {
     296             :       // Is the intersection it in front or behind the QP? (interpolate z using the barycentric
     297             :       // coordinates)
     298      638039 :       const auto zs = a * p1(2) + b * p2(2) + c * p3(2);
     299      638039 :       if (z > zs)
     300             :         return false;
     301             :     }
     302             :   }
     303             : 
     304             :   return true;
     305             : }
     306             : 
     307             : void
     308       96048 : SelfShadowSideUserObject::rotate(MooseArray<Point> & points)
     309             : {
     310      639126 :   for (const auto i : index_range(points))
     311      543078 :     points[i] = _rotation * points[i];
     312       96048 : }
     313             : 
     314             : void
     315      313488 : SelfShadowSideUserObject::rotate(Triangle & triangle)
     316             : {
     317             :   auto & [p1, p2, p3, triangle_id] = triangle;
     318             : 
     319      313488 :   p1 = _rotation * p1;
     320      313488 :   p2 = _rotation * p2;
     321      313488 :   p3 = _rotation * p3;
     322             :   libmesh_ignore(triangle_id);
     323      313488 : }
     324             : 
     325             : void
     326        5796 : SelfShadowSideUserObject::rotate(LineSegment & line)
     327             : {
     328             :   auto & [p1, p2, line_id] = line;
     329        5796 :   p1 = _rotation * p1;
     330        5796 :   p2 = _rotation * p2;
     331             :   libmesh_ignore(line_id);
     332        5796 : }

Generated by: LCOV version 1.14