Loading [MathJax]/extensions/tex2jax.js
https://mooseframework.inl.gov
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends
SelfShadowSideUserObject.C
Go to the documentation of this file.
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 
11 #include "RotationMatrix.h"
12 
13 #include "libmesh/parallel_algebra.h"
14 
16 
19 {
21  params.addClassDescription("Compute the illumination status for a self shadowing sideset");
22  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  return params;
29 }
30 
32  : SideUserObject(parameters),
33  _dim(_mesh.dimension()),
34  _raw_direction(coupledPostprocessors("illumination_flux")),
35  _illumination_status(
36  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  if (_dim != 2 && _dim != 3)
42  mooseError("SelfShadowSideUserObject works only for 2 and 3 dimensional problems.");
43 
44  // fetch raw direction
45  for (const auto i : index_range(_raw_direction))
46  _raw_direction[i] = &getPostprocessorValue("illumination_flux", i);
47 }
48 
49 void
51 {
52  // delete list of collected lines or triangles
53  _lines.clear();
54  _triangles.clear();
55 
56  // delete local qps
57  _local_qps.clear();
58 
59  // update direction and rotation matrix
60  RealVectorValue direction;
61  for (const auto i : index_range(_raw_direction))
62  direction(i) = *_raw_direction[i];
63  _rotation =
64  _dim == 2 ? RotationMatrix::rotVec2DToX(direction) : RotationMatrix::rotVecToZ(direction);
65 }
66 
67 void
69 {
70  const SideIDType id(_current_elem->id(), _current_side);
71 
72  // add triangulated sides to list
73  if (_dim == 2)
74  addLines(id);
75  else
76  addTriangles(id);
77 
78  // save off rotated QP coordinates got local sides
79  _local_qps.emplace_back(id, _q_point);
80  rotate(_local_qps.back().second);
81 }
82 
83 void
85 {
86  const auto & uo = static_cast<const SelfShadowSideUserObject &>(y);
87 
88  // merge lists
89  _lines.insert(_lines.end(), uo._lines.begin(), uo._lines.end());
90  _triangles.insert(_triangles.end(), uo._triangles.begin(), uo._triangles.end());
91  _local_qps.insert(_local_qps.end(), uo._local_qps.begin(), uo._local_qps.end());
92 }
93 
94 unsigned int
96 {
97  const auto it = _illumination_status.find(id);
98  if (it == _illumination_status.end())
99  mooseError("Illumination status was not calculated for current side.");
100  return it->second;
101 }
102 
103 void
105 {
106  // rotate triangulations
107  if (_dim == 2)
108  for (auto & line : _lines)
109  rotate(line);
110  else
111  for (auto & triangle : _triangles)
112  rotate(triangle);
113 
114  // [compute local projected bounding box (in x or xy)]
115 
116  // communicate
117  if (_dim == 2)
118  _communicator.allgather(_lines, /*identical_buffer_sizes=*/false);
119  else
120  _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  for (const auto & [id, qps] : _local_qps)
124  {
125  auto & illumination = _illumination_status[id];
126 
127  // start off assuming no illumination
128  illumination = 0;
129 
130  // current bit in the illumination mask
131  unsigned int bit = 1;
132 
133  // iterate over QPs
134  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  if (_dim == 2 && check2DIllumination(qps[i], id))
138  illumination |= bit;
139  if (_dim == 3 && check3DIllumination(qps[i], id))
140  illumination |= bit;
141 
142  // shift to next bit
143  bit <<= 1;
144  }
145  }
146 }
147 
148 void
150 {
151  const auto & cse = *_current_side_elem;
152  switch (cse.type())
153  {
154  case libMesh::EDGE2:
155  _lines.emplace_back(cse.node_ref(0), cse.node_ref(1), id);
156  break;
157 
158  case libMesh::EDGE3:
159  _lines.emplace_back(cse.node_ref(0), cse.node_ref(2), id);
160  _lines.emplace_back(cse.node_ref(2), cse.node_ref(1), id);
161  break;
162 
163  case libMesh::EDGE4:
164  _lines.emplace_back(cse.node_ref(0), cse.node_ref(2), id);
165  _lines.emplace_back(cse.node_ref(2), cse.node_ref(3), id);
166  _lines.emplace_back(cse.node_ref(3), cse.node_ref(1), id);
167  break;
168 
169  default:
170  mooseError("Unsupported EDGE type");
171  }
172 }
173 
174 void
176 {
177  const auto & cse = *_current_side_elem;
178  switch (cse.type())
179  {
180  case libMesh::TRI3:
181  _triangles.emplace_back(cse.node_ref(0), cse.node_ref(1), cse.node_ref(2), id);
182  break;
183 
184  case libMesh::TRI6:
185  case libMesh::TRI7:
186  _triangles.emplace_back(cse.node_ref(0), cse.node_ref(3), cse.node_ref(5), id);
187  _triangles.emplace_back(cse.node_ref(3), cse.node_ref(1), cse.node_ref(4), id);
188  _triangles.emplace_back(cse.node_ref(4), cse.node_ref(2), cse.node_ref(5), id);
189  _triangles.emplace_back(cse.node_ref(3), cse.node_ref(4), cse.node_ref(5), id);
190  break;
191 
192  case libMesh::QUAD4:
193  _triangles.emplace_back(cse.node_ref(0), cse.node_ref(1), cse.node_ref(2), id);
194  _triangles.emplace_back(cse.node_ref(2), cse.node_ref(3), cse.node_ref(0), id);
195  break;
196 
197  case libMesh::QUAD8:
198  _triangles.emplace_back(cse.node_ref(0), cse.node_ref(4), cse.node_ref(7), id);
199  _triangles.emplace_back(cse.node_ref(4), cse.node_ref(1), cse.node_ref(5), id);
200  _triangles.emplace_back(cse.node_ref(5), cse.node_ref(2), cse.node_ref(6), id);
201  _triangles.emplace_back(cse.node_ref(6), cse.node_ref(3), cse.node_ref(7), id);
202  _triangles.emplace_back(cse.node_ref(6), cse.node_ref(7), cse.node_ref(4), id);
203  _triangles.emplace_back(cse.node_ref(4), cse.node_ref(5), cse.node_ref(6), id);
204  break;
205 
206  case libMesh::QUAD9:
207  _triangles.emplace_back(cse.node_ref(0), cse.node_ref(4), cse.node_ref(7), id);
208  _triangles.emplace_back(cse.node_ref(4), cse.node_ref(1), cse.node_ref(5), id);
209  _triangles.emplace_back(cse.node_ref(5), cse.node_ref(2), cse.node_ref(6), id);
210  _triangles.emplace_back(cse.node_ref(6), cse.node_ref(3), cse.node_ref(7), id);
211  _triangles.emplace_back(cse.node_ref(8), cse.node_ref(6), cse.node_ref(7), id);
212  _triangles.emplace_back(cse.node_ref(8), cse.node_ref(5), cse.node_ref(6), id);
213  _triangles.emplace_back(cse.node_ref(8), cse.node_ref(4), cse.node_ref(5), id);
214  _triangles.emplace_back(cse.node_ref(8), cse.node_ref(7), cse.node_ref(4), id);
215  break;
216 
217  default:
218  mooseError("Unsupported FACE type");
219  }
220 }
221 
222 bool
224 {
225  const auto x = qp(0);
226  const auto y = qp(1);
227 
228  // loop over all line segments until one is found that provides shade
229  for (const auto & line : _lines)
230  {
231  const auto & [p1, p2, line_id] = line;
232 
233  // make sure a side never shades itself
234  if (line_id == id)
235  continue;
236 
237  const bool ordered = p1(1) <= p2(1);
238 
239  const auto y1 = ordered ? p1(1) : p2(1);
240  const auto y2 = ordered ? p2(1) : p1(1);
241 
242  if (y >= y1 && y <= y2)
243  {
244  // line segment is oriented parallel to the irradiation direction
245  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  const auto x1 = ordered ? p1(0) : p2(0);
250  const auto x2 = ordered ? p2(0) : p1(0);
251 
252  // compute intersection location
253  const auto xs = (x2 - x1) * (y - y1) / (y2 - y1) + x1;
254  if (x > xs)
255  return false;
256  }
257  }
258 
259  return true;
260 }
261 
262 bool
264 {
265  const auto x = qp(0);
266  const auto y = qp(1);
267  const auto z = qp(2);
268 
269  // loop over all triangles until one is found that provides shade
270  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  if (triangle_id == id)
276  continue;
277 
278  const auto x1 = p1(0);
279  const auto x2 = p2(0);
280  const auto x3 = p3(0);
281 
282  const auto y1 = p1(1);
283  const auto y2 = p2(1);
284  const auto y3 = p3(1);
285 
286  // compute barycentric coordinates
287  const auto a = ((y2 - y3) * (x - x3) + (x3 - x2) * (y - y3)) /
288  ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3));
289  const auto b = ((y3 - y1) * (x - x3) + (x1 - x3) * (y - y3)) /
290  ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3));
291  const auto c = 1.0 - a - b;
292 
293  // are we inside of the projected triangle?
294  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  const auto zs = a * p1(2) + b * p2(2) + c * p3(2);
299  if (z > zs)
300  return false;
301  }
302  }
303 
304  return true;
305 }
306 
307 void
309 {
310  for (const auto i : index_range(points))
311  points[i] = _rotation * points[i];
312 }
313 
314 void
316 {
317  auto & [p1, p2, p3, triangle_id] = triangle;
318 
319  p1 = _rotation * p1;
320  p2 = _rotation * p2;
321  p3 = _rotation * p3;
322  libmesh_ignore(triangle_id);
323 }
324 
325 void
327 {
328  auto & [p1, p2, line_id] = line;
329  p1 = _rotation * p1;
330  p2 = _rotation * p2;
331  libmesh_ignore(line_id);
332 }
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
static InputParameters validParams()
static InputParameters validParams()
void addRequiredRangeCheckedParam(const std::string &name, const std::string &parsed_function, const std::string &doc_string)
std::vector< const PostprocessorValue * > _raw_direction
raw illumination vector data (direction the radiation is propagating in)
GenericRealTensorValue< is_ad > rotVec2DToX(const GenericRealVectorValue< is_ad > &vec)
const unsigned int & _current_side
static constexpr Real TOLERANCE
unsigned int illumination(const SideIDType &id) const
const Elem *const & _current_side_elem
bool check2DIllumination(const Point &qp, const SideIDType &id)
const MooseArray< Point > & _q_point
std::tuple< Point, Point, Point, SideIDType > Triangle
const std::vector< double > y
const Parallel::Communicator & _communicator
std::map< SideIDType, unsigned int > & _illumination_status
illumination status data (partition local), we use a bit for each QP
const PostprocessorValue & getPostprocessorValue(const std::string &param_name, const unsigned int index=0) const
Given a radiation direction vector this user object computes the illumination state of each side QP o...
virtual void threadJoin(const UserObject &y) override
only needed for ElementUserObjects and NodalUseroObjects
virtual void initialize() override
void libmesh_ignore(const Args &...)
std::vector< Triangle > _triangles
global triange data (3D)
void addLines(const SideIDType &id)
const std::vector< double > x
RealTensorValue _rotation
matrix that rotates the direction onto the z-axis
virtual void finalize() override
std::pair< dof_id_type, unsigned int > SideIDType
GenericRealTensorValue< is_ad > rotVecToZ(GenericRealVectorValue< is_ad > vec)
bool check3DIllumination(const Point &qp, const SideIDType &id)
std::vector< LineSegment > _lines
global line segment data (2D)
SelfShadowSideUserObject(const InputParameters &parameters)
void mooseError(Args &&... args) const
void addClassDescription(const std::string &doc_string)
const Elem *const & _current_elem
const unsigned int _dim
problem dimension
std::vector< std::pair< SideIDType, MooseArray< Point > > > _local_qps
local QP data
registerMooseObject("HeatTransferApp", SelfShadowSideUserObject)
void rotate(MooseArray< Point > &points)
rotate all points in the given container
void addTriangles(const SideIDType &id)
virtual void execute() override
void ErrorVector unsigned int
auto index_range(const T &sizable)