https://mooseframework.inl.gov
Loading...
Searching...
No Matches
RayTracingAngularQuadrature.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
12// MOOSE includes
13#include "MooseUtils.h"
14
15// libMesh includes
16#include "libmesh/dense_vector.h"
17
19 const unsigned int polar_order,
20 const unsigned int azimuthal_order,
21 const Real mu_min,
22 const Real mu_max)
23 : _dim(dim),
24 _polar_order(polar_order),
25 _azimuthal_order(azimuthal_order),
26 _mu_min(mu_min),
27 _mu_max(mu_max)
28{
29 if (_polar_order == 0)
30 mooseError("polar_order must be positive in RayTracingAngularQuadrature");
31 if (_azimuthal_order == 0)
32 mooseError("azimuthal_order must be positive in RayTracingAngularQuadrature");
33 if (_mu_min >= _mu_max)
34 mooseError("mu_min must be < mu_max in RayTracingAngularQuadrature");
35 if (_mu_min < -1)
36 mooseError("mu_min must be >= -1 in RayTracingAngularQuadrature");
37 if (_mu_max > 1)
38 mooseError("mu_max must be <= 1 in RayTracingAngularQuadrature");
39 if (dim != 2 && dim != 3)
40 mooseError("RayTracingAngularQuadrature only supports dimensions 2 and 3");
41
42 // Build the quadrature
43 build();
44
45 // Default rotation is up
46 rotate(libMesh::Point(0, 0, 1));
47}
48
49void
51{
52 // Chebyshev quadrature on [0, 2\pi]
53 std::vector<Real> chebyshev_x(_azimuthal_order);
54 std::vector<Real> chebyshev_w(_azimuthal_order);
55 chebyshev(_azimuthal_order, chebyshev_x, chebyshev_w);
56
57 // Gauss-Legendre quadrature on [0, 1]
58 std::vector<Real> gauss_legendre_x;
59 std::vector<Real> gauss_legendre_w;
60 gaussLegendre(_polar_order, gauss_legendre_x, gauss_legendre_w);
61
62 _phi.resize(0);
63 _mu.resize(0);
64 _w.resize(0);
65
66 // Build the product quadrature
67 for (std::size_t i = 0; i < chebyshev_x.size(); ++i)
68 for (std::size_t j = 0; j < gauss_legendre_x.size(); ++j)
69 {
70 // If 2D, throw away the downward mu
71 if (_dim == 2 && gauss_legendre_x[j] < 0.5 - TOLERANCE * TOLERANCE)
72 continue;
73
74 _phi.push_back(chebyshev_x[i]);
75 _mu.push_back(gauss_legendre_x[j] * (_mu_max - _mu_min) + _mu_min);
76 _polar_sin.push_back(std::sin(std::acos(_mu.back())));
77
78 const Real weight_factor =
79 (_dim == 2 && !MooseUtils::absoluteFuzzyEqual(gauss_legendre_x[j], 0.5)) ? 2.0 : 1.0;
80 _w.push_back(weight_factor * chebyshev_w[i] * gauss_legendre_w[j]);
81 }
82}
83
84void
86 std::vector<Real> & x,
87 std::vector<Real> & w)
88{
89 if (order == 0)
90 mooseError("Order must be positive in gaussLegendre()");
91
92 x.resize(order);
93 w.resize(order);
94 libMesh::DenseMatrix<Real> mat(order, order);
95 libMesh::DenseVector<Real> lambda(order);
96 libMesh::DenseVector<Real> lambdai(order);
97 libMesh::DenseMatrix<Real> vec(order, order);
98
99 for (unsigned int i = 1; i < order; ++i)
100 {
101 Real ri = i;
102 mat(i, i - 1) = ri / std::sqrt(((2. * ri - 1.) * (2. * ri + 1.)));
103 mat(i - 1, i) = mat(i, i - 1);
104 }
105 mat.evd_right(lambda, lambdai, vec);
106
107 for (unsigned int i = 0; i < order; ++i)
108 {
109 x[i] = 0.5 * (lambda(i) + 1.0);
110 w[i] = vec(0, i) * vec(0, i);
111 }
112
113 // Sort based on the points
114 std::vector<std::size_t> sorted_indices(x.size());
115 std::iota(sorted_indices.begin(), sorted_indices.end(), 0);
116 std::stable_sort(sorted_indices.begin(),
117 sorted_indices.end(),
118 [&x](size_t i1, size_t i2) { return x[i1] < x[i2]; });
119 const auto x_copy = x;
120 const auto w_copy = w;
121 for (std::size_t i = 0; i < x.size(); ++i)
122 {
123 x[i] = x_copy[sorted_indices[i]];
124 w[i] = w_copy[sorted_indices[i]];
125 }
126}
127
128void
130{
131 _current_rotation_direction = rotation_direction;
132
133 libMesh::DenseMatrix<Real> rotation_matrix;
134 rotationMatrix(rotation_direction.unit(), rotation_matrix);
135
136 _current_directions.clear();
137 _current_directions.reserve(_phi.size());
138
139 _current_weights.clear();
140 _current_weights.reserve(_w.size());
141
142 _current_polar_sins.clear();
143 _current_polar_sins.reserve(_polar_sin.size());
144
147 Point direction;
148
149 for (std::size_t q = 0; q < _phi.size(); ++q)
150 {
151 // Get direction as a DenseVector for rotation
152 omega(0) = sqrt(1 - _mu[q] * _mu[q]) * cos(_phi[q]);
153 omega(1) = sqrt(1 - _mu[q] * _mu[q]) * sin(_phi[q]);
154 omega(2) = _mu[q];
155
156 // Rotate and create the direction
157 rotation_matrix.vector_mult(omega_p, omega);
158 direction(0) = omega_p(0);
159 direction(1) = omega_p(1);
160 direction(2) = omega_p(2);
161
162 // The index for the new direction in the "current" data
163 // structures. By default - we add a new one. However,
164 // in 2D we may have a duplicate projected direction
165 // so we need to keep track of this in the case
166 // that we are adding information to an already
167 // existant direction
168 std::size_t l = _current_directions.size();
169
170 // If 2D, project to the xy plane and see if any other
171 // projected directions are a duplicate
172 if (_dim == 2)
173 {
174 direction(2) = 0;
175 direction /= direction.norm();
176
177 // If we loop through all current directions and find
178 // no duplicates, this will set l back to _current_directions.size()
179 for (l = 0; l < _current_directions.size(); ++l)
180 if (_current_directions[l].absolute_fuzzy_equals(direction))
181 break;
182 }
183
184 // If l is the current size of _current_directions, it means
185 // that no duplicates were found and that we are inserting
186 // a new direction
187 if (l == _current_directions.size())
188 {
189 _current_directions.push_back(direction);
190 _current_weights.resize(l + 1);
191 _current_polar_sins.resize(l + 1);
192 }
193 else
194 mooseAssert(_dim == 2, "Should only have duplicates in 2D");
195
196 // Add to weights and sins for this direction
197 _current_weights[l].push_back(_w[q]);
198 _current_polar_sins[l].push_back(_polar_sin[q]);
199 }
200}
201
202const libMesh::Point &
204{
206 return _current_directions[l];
207}
208
209const std::vector<Real> &
211{
213 return _current_weights[l];
214}
215
216Real
218{
220 return std::accumulate(_current_weights[l].begin(), _current_weights[l].end(), (Real)0);
221}
222
223const std::vector<Real> &
225{
227 return _current_polar_sins[l];
228}
229
230std::size_t
231RayTracingAngularQuadrature::numPolar(const unsigned int l) const
232{
234 if (_dim == 3)
235 mooseAssert(_current_weights[l].size() == 1, "Should be 1 polar in 3D");
236 return _current_polar_sins[l].size();
237}
238
239void
241{
242 if (!hasDirection(l))
243 mooseError("RayTracingAngularQuadrature does not have direction ", l);
244}
245
246void
248 std::vector<Real> & x,
249 std::vector<Real> & w)
250{
251 x.resize(order);
252 w.resize(order);
253
254 for (std::size_t i = 0; i < order; ++i)
255 {
256 x[i] = 2 * (Real)i * M_PI / (Real)order;
257 w[i] = 2 * M_PI / (Real)order;
258 }
259}
260
261void
264{
265 matrix.resize(3, 3);
266 matrix.zero();
267
268 // Create a local coordinate system around direction
269 const libMesh::Point tx = orthonormalVector(direction);
270 const libMesh::Point ty = direction.cross(tx);
271
272 // Create the rotation matrix
273 for (unsigned int j = 0; j < 3; ++j)
274 {
275 matrix(j, 0) = tx(j);
276 matrix(j, 1) = ty(j);
277 matrix(j, 2) = direction(j);
278 }
279}
280
283{
284 if (MooseUtils::absoluteFuzzyLessEqual(v.norm(), 0))
285 ::mooseError("Vector v has norm close to 0 in orthonormalVector()");
286
287 if (MooseUtils::absoluteFuzzyEqual(v(0), 0))
288 return libMesh::Point(1, 0, 0);
289 if (MooseUtils::absoluteFuzzyEqual(v(1), 0))
290 return libMesh::Point(0, 1, 0);
291 if (MooseUtils::absoluteFuzzyEqual(v(2), 0))
292 return libMesh::Point(0, 0, 1);
293
294 return libMesh::Point(-v(1), v(0), 0).unit();
295}
const std::vector< double > x
const double v
void mooseError(Args &&... args)
unsigned int dim
std::vector< Real > _mu
Quadrature mu.
void checkDirection(const unsigned int l) const
Throws a MooseError if the angular quadrature does not have direction l.
std::vector< libMesh::Point > _current_directions
The current quadrature information.
const std::vector< Real > & getWeights(const unsigned int l) const
Get the weights associated with the direction l.
std::vector< Real > _w
Quadrature combined weights.
std::size_t numPolar(const unsigned int l) const
The number of polar directions associated with the given direction.
libMesh::Point _current_rotation_direction
The current rotation direction.
static void chebyshev(const unsigned int order, std::vector< Real > &x, std::vector< Real > &w)
Builds Chebyshev quadrature on [0, 2\pi] with weights that sum to 2\pi.
const unsigned int _azimuthal_order
The azimuthal order.
RayTracingAngularQuadrature(const unsigned int dim, const unsigned int polar_order, const unsigned int azimuthal_order, const Real mu_min, const Real mu_max)
Constructor.
static void gaussLegendre(const unsigned int order, std::vector< Real > &x, std::vector< Real > &w)
Builds Gauss-Legendre quadrature on [0, 1] (symmetric about 0.5), with weights that sum to 1.
Real getTotalWeight(const unsigned int l) const
Gets the total of the weights associated with the direction l.
bool hasDirection(const unsigned int l) const
Whether or not the angular quadrature has direction l.
std::vector< std::vector< Real > > _current_polar_sins
const libMesh::Point & getDirection(const unsigned int l) const
Get the direction associated with direction l.
void rotate(const libMesh::Point &rotation_direction)
Rotates the quadrature to a given direction.
const std::vector< Real > & getPolarSins(const unsigned int l) const
Gets the polar sins for the direction l.
unsigned int dim() const
Get the quadrature dimension.
const unsigned int _dim
The dimension.
void build()
Build the quadrature.
static libMesh::Point orthonormalVector(const libMesh::Point &v)
Gets the vector that is orthonormal to v.
std::vector< Real > _phi
Quadrature phi.
std::vector< Real > _polar_sin
Quadrature polar sin.
std::vector< std::vector< Real > > _current_weights
const unsigned int _polar_order
The polar order.
static void rotationMatrix(const libMesh::Point &direction, libMesh::DenseMatrix< Real > &matrix)
Builds the rotation matrix for direction direction into matrix.
void vector_mult(DenseVector< T > &dest, const DenseVector< T > &arg) const
void resize(const unsigned int new_m, const unsigned int new_n)
void evd_right(DenseVector< T > &lambda_real, DenseVector< T > &lambda_imag, DenseMatrix< T > &VR)
virtual void zero() override final
TypeVector< typename CompareTypes< Real, T2 >::supertype > cross(const TypeVector< T2 > &v) const
TypeVector< Real > unit() const