https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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 "MooseUtils.h"
12#include "RotationMatrix.h"
13
14#include "libmesh/parallel_algebra.h"
15
17
20{
22 params.addClassDescription("Compute the illumination status for a self shadowing sideset");
23 params.addRequiredRangeCheckedParam<std::vector<PostprocessorName>>(
24 "illumination_flux",
25 "illumination_flux_size>0 && illumination_flux_size<=3",
26 "Radiation direction vector. Each component of the vector can be a constant number or a "
27 "postprocessor name (the latter enables time varying radiation directions - spatial "
28 "variation is not supported)");
29 return params;
30}
31
33 : SideUserObject(parameters),
34 _dim(_mesh.dimension()),
35 _raw_direction(coupledPostprocessors("illumination_flux")),
36 _illumination_status(
37 declareRestartableData<std::map<SideIDType, unsigned int>>("illumination_status"))
38{
39 // we should check the coordinate system (i.e. permit only 0,0,+-1 for RZ)
40
41 // check problem dimension
42 if (_dim != 2 && _dim != 3)
43 mooseError("SelfShadowSideUserObject works only for 2 and 3 dimensional problems.");
44
45 // fetch raw direction
46 for (const auto i : index_range(_raw_direction))
47 _raw_direction[i] = &getPostprocessorValue("illumination_flux", i);
48}
49
50void
52{
53 // delete list of collected lines or triangles
54 _lines.clear();
55 _triangles.clear();
56
57 // delete local qps
58 _local_qps.clear();
59
60 // update direction and rotation matrix
61 RealVectorValue direction;
62 for (const auto i : index_range(_raw_direction))
63 direction(i) = *_raw_direction[i];
64 _rotation =
65 _dim == 2 ? RotationMatrix::rotVec2DToX(direction) : RotationMatrix::rotVecToZ(direction);
66}
67
68void
70{
71 const SideIDType id(_current_elem->id(), _current_side);
72
73 // add triangulated sides to list
74 if (_dim == 2)
75 addLines(id);
76 else
77 addTriangles(id);
78
79 // save off rotated QP coordinates got local sides
80 _local_qps.emplace_back(id, _q_point);
81 rotate(_local_qps.back().second);
82}
83
84void
86{
87 const auto & uo = static_cast<const SelfShadowSideUserObject &>(y);
88
89 // merge lists
90 _lines.insert(_lines.end(), uo._lines.begin(), uo._lines.end());
91 _triangles.insert(_triangles.end(), uo._triangles.begin(), uo._triangles.end());
92 _local_qps.insert(_local_qps.end(), uo._local_qps.begin(), uo._local_qps.end());
93}
94
95unsigned int
97{
98 const auto it = _illumination_status.find(id);
99 if (it == _illumination_status.end())
100 mooseError("Illumination status was not calculated for current side.");
101 return it->second;
102}
103
104void
106{
107 // rotate triangulations
108 if (_dim == 2)
109 for (auto & line : _lines)
110 rotate(line);
111 else
112 for (auto & triangle : _triangles)
113 rotate(triangle);
114
115 // [compute local projected bounding box (in x or xy)]
116
117 // communicate
118 if (_dim == 2)
119 _communicator.allgather(_lines, /*identical_buffer_sizes=*/false);
120 else
121 _communicator.allgather(_triangles, /*identical_buffer_sizes=*/false);
122
123 // otherwise we iterate over QPs and check if any other side is in the way of the radiation
124 for (const auto & [id, qps] : _local_qps)
125 {
127
128 // start off assuming no illumination
129 illumination = 0;
130
131 // current bit in the illumination mask
132 unsigned int bit = 1;
133
134 // iterate over QPs
135 for (const auto i : index_range(qps))
136 {
137 // we set the bit at position _qp in the bitmask if the QP is illuminated
138 if (_dim == 2 && check2DIllumination(qps[i], id))
139 illumination |= bit;
140 if (_dim == 3 && check3DIllumination(qps[i], id))
141 illumination |= bit;
142
143 // shift to next bit
144 bit <<= 1;
145 }
146 }
147}
148
149void
151{
152 const auto & cse = *_current_side_elem;
153 switch (cse.type())
154 {
155 case libMesh::EDGE2:
156 _lines.emplace_back(cse.node_ref(0), cse.node_ref(1), id);
157 break;
158
159 case libMesh::EDGE3:
160 _lines.emplace_back(cse.node_ref(0), cse.node_ref(2), id);
161 _lines.emplace_back(cse.node_ref(2), cse.node_ref(1), id);
162 break;
163
164 case libMesh::EDGE4:
165 _lines.emplace_back(cse.node_ref(0), cse.node_ref(2), id);
166 _lines.emplace_back(cse.node_ref(2), cse.node_ref(3), id);
167 _lines.emplace_back(cse.node_ref(3), cse.node_ref(1), id);
168 break;
169
170 default:
171 mooseError("Unsupported EDGE type");
172 }
173}
174
175void
177{
178 const auto & cse = *_current_side_elem;
179 switch (cse.type())
180 {
181 case libMesh::TRI3:
182 _triangles.emplace_back(cse.node_ref(0), cse.node_ref(1), cse.node_ref(2), id);
183 break;
184
185 case libMesh::TRI6:
186 case libMesh::TRI7:
187 _triangles.emplace_back(cse.node_ref(0), cse.node_ref(3), cse.node_ref(5), id);
188 _triangles.emplace_back(cse.node_ref(3), cse.node_ref(1), cse.node_ref(4), id);
189 _triangles.emplace_back(cse.node_ref(4), cse.node_ref(2), cse.node_ref(5), id);
190 _triangles.emplace_back(cse.node_ref(3), cse.node_ref(4), cse.node_ref(5), id);
191 break;
192
193 case libMesh::QUAD4:
194 _triangles.emplace_back(cse.node_ref(0), cse.node_ref(1), cse.node_ref(2), id);
195 _triangles.emplace_back(cse.node_ref(2), cse.node_ref(3), cse.node_ref(0), id);
196 break;
197
198 case libMesh::QUAD8:
199 _triangles.emplace_back(cse.node_ref(0), cse.node_ref(4), cse.node_ref(7), id);
200 _triangles.emplace_back(cse.node_ref(4), cse.node_ref(1), cse.node_ref(5), id);
201 _triangles.emplace_back(cse.node_ref(5), cse.node_ref(2), cse.node_ref(6), id);
202 _triangles.emplace_back(cse.node_ref(6), cse.node_ref(3), cse.node_ref(7), id);
203 _triangles.emplace_back(cse.node_ref(6), cse.node_ref(7), cse.node_ref(4), id);
204 _triangles.emplace_back(cse.node_ref(4), cse.node_ref(5), cse.node_ref(6), id);
205 break;
206
207 case libMesh::QUAD9:
208 _triangles.emplace_back(cse.node_ref(0), cse.node_ref(4), cse.node_ref(7), id);
209 _triangles.emplace_back(cse.node_ref(4), cse.node_ref(1), cse.node_ref(5), id);
210 _triangles.emplace_back(cse.node_ref(5), cse.node_ref(2), cse.node_ref(6), id);
211 _triangles.emplace_back(cse.node_ref(6), cse.node_ref(3), cse.node_ref(7), id);
212 _triangles.emplace_back(cse.node_ref(8), cse.node_ref(6), cse.node_ref(7), id);
213 _triangles.emplace_back(cse.node_ref(8), cse.node_ref(5), cse.node_ref(6), id);
214 _triangles.emplace_back(cse.node_ref(8), cse.node_ref(4), cse.node_ref(5), id);
215 _triangles.emplace_back(cse.node_ref(8), cse.node_ref(7), cse.node_ref(4), id);
216 break;
217
218 default:
219 mooseError("Unsupported FACE type");
220 }
221}
222
223bool
225{
226 const auto x = qp(0);
227 const auto y = qp(1);
228
229 // loop over all line segments until one is found that provides shade
230 for (const auto & line : _lines)
231 {
232 const auto & [p1, p2, line_id] = line;
233
234 // make sure a side never shades itself
235 if (line_id == id)
236 continue;
237
238 const bool ordered = p1(1) <= p2(1);
239
240 const auto y1 = ordered ? p1(1) : p2(1);
241 const auto y2 = ordered ? p2(1) : p1(1);
242
243 if (y >= y1 && y <= y2)
244 {
245 // line segment is oriented parallel to the irradiation direction
246 if (std::abs(y2 - y1) < libMesh::TOLERANCE)
247 return false;
248
249 // segment is in line with the QP in radiation direction. Is it in front or behind?
250 const auto x1 = ordered ? p1(0) : p2(0);
251 const auto x2 = ordered ? p2(0) : p1(0);
252
253 // compute intersection location
254 const auto xs = (x2 - x1) * (y - y1) / (y2 - y1) + x1;
255 if (x > xs)
256 return false;
257 }
258 }
259
260 return true;
261}
262
263bool
265{
266 const auto x = qp(0);
267 const auto y = qp(1);
268 const auto z = qp(2);
269
270 // loop over all triangles until one is found that provides shade
271 for (const auto & triangle : _triangles)
272 {
273 const auto & [p1, p2, p3, triangle_id] = triangle;
274
275 // make sure a side never shades itself
276 if (triangle_id == id)
277 continue;
278
279 const auto x1 = p1(0);
280 const auto x2 = p2(0);
281 const auto x3 = p3(0);
282
283 const auto y1 = p1(1);
284 const auto y2 = p2(1);
285 const auto y3 = p3(1);
286
287 // Scale the degeneracy tolerance by the projected-area terms so the check is independent of
288 // mesh size.
289 const auto denominator_term_1 = (y2 - y3) * (x1 - x3);
290 const auto denominator_term_2 = (x3 - x2) * (y1 - y3);
291 const auto denominator = denominator_term_1 + denominator_term_2;
292 const auto denominator_tolerance =
293 libMesh::TOLERANCE * (std::abs(denominator_term_1) + std::abs(denominator_term_2));
294 if (MooseUtils::absoluteFuzzyEqual(denominator, 0.0, denominator_tolerance))
295 continue;
296
297 // compute barycentric coordinates
298 const auto a = ((y2 - y3) * (x - x3) + (x3 - x2) * (y - y3)) / denominator;
299 const auto b = ((y3 - y1) * (x - x3) + (x1 - x3) * (y - y3)) / denominator;
300 const auto c = 1.0 - a - b;
301
302 // Match the robust triangle-intersection predicate by treating barycentric coordinates within
303 // libMesh::TOLERANCE of an edge as on the triangle.
304 if (MooseUtils::absoluteFuzzyGreaterEqual(a, 0.0, libMesh::TOLERANCE) &&
305 MooseUtils::absoluteFuzzyLessEqual(a, 1.0, libMesh::TOLERANCE) &&
306 MooseUtils::absoluteFuzzyGreaterEqual(b, 0.0, libMesh::TOLERANCE) &&
307 MooseUtils::absoluteFuzzyLessEqual(b, 1.0, libMesh::TOLERANCE) &&
308 MooseUtils::absoluteFuzzyGreaterEqual(c, 0.0, libMesh::TOLERANCE) &&
309 MooseUtils::absoluteFuzzyLessEqual(c, 1.0, libMesh::TOLERANCE))
310 {
311 // Is the intersection it in front or behind the QP? (interpolate z using the barycentric
312 // coordinates)
313 const auto zs = a * p1(2) + b * p2(2) + c * p3(2);
314 if (MooseUtils::absoluteFuzzyGreaterThan(z, zs))
315 return false;
316 }
317 }
318
319 return true;
320}
321
322void
324{
325 for (const auto i : index_range(points))
326 points[i] = _rotation * points[i];
327}
328
329void
331{
332 auto & [p1, p2, p3, triangle_id] = triangle;
333
334 p1 = _rotation * p1;
335 p2 = _rotation * p2;
336 p3 = _rotation * p3;
337 libmesh_ignore(triangle_id);
338}
339
340void
342{
343 auto & [p1, p2, line_id] = line;
344 p1 = _rotation * p1;
345 p2 = _rotation * p2;
346 libmesh_ignore(line_id);
347}
const std::vector< double > y
const std::vector< double > x
registerMooseObject("HeatTransferApp", SelfShadowSideUserObject)
void ErrorVector unsigned int
void addRequiredRangeCheckedParam(const std::string &name, const std::string &parsed_function, const std::string &doc_string)
void addClassDescription(const std::string &doc_string)
void mooseError(Args &&... args) const
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...
std::map< SideIDType, unsigned int > & _illumination_status
illumination status data (partition local), we use a bit for each QP
void addTriangles(const SideIDType &id)
std::vector< LineSegment > _lines
global line segment data (2D)
unsigned int illumination(const SideIDType &id) const
virtual void threadJoin(const UserObject &y) override
only needed for ElementUserObjects and NodalUseroObjects
void rotate(MooseArray< Point > &points)
rotate all points in the given container
std::vector< Triangle > _triangles
global triange data (3D)
virtual void finalize() override
bool check2DIllumination(const Point &qp, const SideIDType &id)
bool check3DIllumination(const Point &qp, const SideIDType &id)
RealTensorValue _rotation
matrix that rotates the direction onto the z-axis
virtual void execute() override
static InputParameters validParams()
SelfShadowSideUserObject(const InputParameters &parameters)
void addLines(const SideIDType &id)
virtual void initialize() override
const unsigned int _dim
problem dimension
std::pair< dof_id_type, unsigned int > SideIDType
std::vector< const PostprocessorValue * > _raw_direction
raw illumination vector data (direction the radiation is propagating in)
std::vector< std::pair< SideIDType, MooseArray< Point > > > _local_qps
local QP data
const unsigned int & _current_side
static InputParameters validParams()
const Elem *const & _current_side_elem
const MooseArray< Point > & _q_point
const Elem *const & _current_elem
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
const Parallel::Communicator & _communicator
GenericRealTensorValue< is_ad > rotVecToZ(GenericRealVectorValue< is_ad > vec)
GenericRealTensorValue< is_ad > rotVec2DToX(const GenericRealVectorValue< is_ad > &vec)
static constexpr Real TOLERANCE