include/meltpooldg/utilities/distance_functions.hpp Source File

Developer Documentation: include/meltpooldg/utilities/distance_functions.hpp Source File
Developer Documentation
distance_functions.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <deal.II/base/exceptions.h>
4#include <deal.II/base/function.h>
5#include <deal.II/base/point.h>
6#include <deal.II/base/symmetric_tensor.h>
7#include <deal.II/base/tensor.h>
8
9#include <algorithm>
10#include <array>
11#include <cmath>
12
13
19{
28 template <int dim, typename number>
29 class Plane : public dealii::Function<dim, number>
30 {
31 public:
41 Plane(const dealii::Point<dim, number> &center_of_wall,
42 const dealii::Tensor<1, dim, number> &normal,
43 const std::array<number, dim - 1> &half_extents,
44 const dealii::Tensor<1, dim, number> &first_tangent_direction =
45 dealii::Tensor<1, dim, number>())
47 , normal(normal)
49 {
50 AssertThrow(std::abs(normal.norm() - 1.) < tolerance,
51 dealii::ExcMessage("The normal of the wall plane must be a unit vector."));
52
53 // Building tangent basis
54 if constexpr (dim == 2)
55 {
56 tangents[0][0] = -normal[1];
57 tangents[0][1] = normal[0];
58 }
59 else if constexpr (dim == 3)
60 {
61 const number first_tangent_norm = first_tangent_direction.norm();
62 AssertThrow(first_tangent_norm > tolerance,
63 dealii::ExcMessage("The first tangent direction must be nonzero in 3D."));
64
65 tangents[0] = first_tangent_direction / first_tangent_norm;
66 tangents[1] = dealii::cross_product_3d(normal, tangents[0]);
67 AssertThrow(std::abs(normal * tangents[0]) < tolerance,
68 dealii::ExcMessage(
69 "The first tangent direction must be perpendicular to the normal."));
70 }
71 }
72
77 dealii::Tensor<1, dim, number>
78 gradient(const dealii::Point<dim, number> &x, const unsigned int component = 0) const override
79 {
80 Assert(component == 0,
81 dealii::ExcMessage("The distance function is scalar; component must be 0."));
82
83 const dealii::Tensor<1, dim, number> r = offset_from_wall(x);
84 const number r_norm = r.norm();
85
86 // At the wall surface, r vanishes and r/r_norm is undefined. Fall back to the wall's unit
87 // normal as the gradient direction.
88 if (r_norm < tolerance)
89 return normal;
90
91 return r / r_norm;
92 }
93
101 number
102 value(const dealii::Point<dim, number> &x, const unsigned int component = 0) const override
103 {
104 Assert(component == 0,
105 dealii::ExcMessage("The distance function is scalar; component must be 0."));
106
107 return offset_from_wall(x).norm();
108 }
109
110 private:
114 dealii::Tensor<1, dim, number>
115 offset_from_wall(const dealii::Point<dim, number> &x) const
116 {
117 const dealii::Tensor<1, dim, number> position_relative_to_center = x - center_of_wall;
118 const number normal_coord = position_relative_to_center * normal;
119
120 dealii::Tensor<1, dim, number> offset = normal_coord * normal;
121 for (unsigned int i = 0; i < dim - 1; ++i)
122 {
123 const number tangential_coord = position_relative_to_center * tangents[i];
124 const number clamped_tangential_coord =
125 std::clamp(tangential_coord, -half_extents[i], half_extents[i]);
126 offset += (tangential_coord - clamped_tangential_coord) * tangents[i];
127 }
128
129 return offset;
130 }
131
133 dealii::Point<dim, number> center_of_wall;
134
136 dealii::Tensor<1, dim, number> normal;
137
139 std::array<dealii::Tensor<1, dim, number>, dim - 1> tangents;
140
142 std::array<number, dim - 1> half_extents;
143
145 const number tolerance = 1e-12;
146 };
147
156 template <int dim, typename number>
157 class TruncatedCone : public dealii::Function<dim, number>
158 {
159 public:
169 TruncatedCone(const dealii::Point<dim, number> &axis_center,
170 const dealii::Tensor<1, dim, number> &axis_direction,
171 const number radius_bottom,
172 const number radius_top,
173 const number half_length)
178 {
179 AssertThrow(axis_direction.norm() > tolerance,
180 dealii::ExcMessage("The axis direction of the truncated cone must not be zero."));
181 AssertThrow(half_length > 0.,
182 dealii::ExcMessage("The half length of the truncated cone must be positive."));
183 AssertThrow(radius_bottom >= 0. && radius_top >= 0.,
184 dealii::ExcMessage("The radii of the truncated cone must not be negative."));
185 AssertThrow(radius_bottom > 0. || radius_top > 0.,
186 dealii::ExcMessage(
187 "At least one radius of the truncated cone must be greater than zero."));
188
189 axis = axis_direction / axis_direction.norm(); // unit vector
190 }
191
199 dealii::Tensor<1, dim, number>
200 gradient(const dealii::Point<dim, number> &x, const unsigned int component = 0) const override
201 {
202 Assert(component == 0,
203 dealii::ExcMessage("The distance function is scalar; component must be 0."));
204
205 const dealii::Tensor<1, dim, number> d =
206 x - axis_center; // Vector from the axis center to the point
207 const dealii::Tensor<1, dim, number> d_radial =
208 d - (d * axis) * axis; // Vector from the axis (perpendicular) to the point.
209 const number radial_coord = d_radial.norm();
210
211 dealii::Tensor<1, dim, number> radial_direction;
212 if (radial_coord >= tolerance)
213 radial_direction = d_radial / radial_coord;
214 else
215 {
216 // The Cartesian unit vector least aligned with the axis, made perpendicular to it is
217 // used, so the gradient stays well-defined.
218 unsigned int least_aligned = 0;
219 for (unsigned int i = 1; i < dim; ++i)
220 if (std::abs(axis[i]) < std::abs(axis[least_aligned]))
221 least_aligned = i;
222
223 dealii::Tensor<1, dim, number> unit_vector;
224 unit_vector[least_aligned] = 1.;
225 radial_direction = unit_vector - (unit_vector * axis) * axis;
226 radial_direction /= radial_direction.norm();
227 }
228
229 const auto [axial_offset, radial_offset] = meridian_offset_from_wall(x);
230 const number distance = std::hypot(axial_offset, radial_offset);
231
232 // On the surface, fall back to the inward surface normal.
233 if (distance < tolerance)
234 {
235 const number axial_span = 2. * half_length;
236 const number radial_span = radius_top - radius_bottom;
237 const number slant_length = std::hypot(axial_span, radial_span);
238
239 return (radial_span * axis - axial_span * radial_direction) / slant_length;
240 }
241
242 return (radial_offset * radial_direction + axial_offset * axis) / distance;
243 }
244
250 number
251 value(const dealii::Point<dim, number> &x, const unsigned int component = 0) const override
252 {
253 Assert(component == 0,
254 dealii::ExcMessage("The distance function is scalar; component must be 0."));
255
256 const auto [axial_offset, radial_offset] = meridian_offset_from_wall(x);
257 return std::hypot(axial_offset, radial_offset);
258 }
259
260 private:
272 std::array<number, 2>
273 meridian_offset_from_wall(const dealii::Point<dim, number> &x) const
274 {
275 const dealii::Tensor<1, dim, number> d =
276 x - axis_center; // Vector from the axis center to the point
277 const number axial_coord = d * axis; // Coordinate of the point along the axis.
278 const number radial_coord =
279 (d - axial_coord * axis).norm(); // Coordinate of the point from the axis.
280
281 const number axial_span = 2. * half_length;
282 const number radial_span = radius_top - radius_bottom;
283
284 // Project (axial_coord, radial_coord) onto the meridian segment, clamped to its end points.
285 const number segment_fraction =
286 std::clamp(((axial_coord + half_length) * axial_span +
287 (radial_coord - radius_bottom) * radial_span) /
288 (axial_span * axial_span + radial_span * radial_span),
289 number(0.),
290 number(1.));
291
292 const number axial_coord_closest = -half_length + segment_fraction * axial_span;
293 const number radial_coord_closest = radius_bottom + segment_fraction * radial_span;
294
295 return {{axial_coord - axial_coord_closest, radial_coord - radial_coord_closest}};
296 }
297
299 const dealii::Point<dim, number> axis_center;
300
302 dealii::Tensor<1, dim, number> axis;
303
305 const number radius_bottom;
306
308 const number radius_top;
309
311 const number half_length;
312
315 const number tolerance = 1e-12;
316 };
317} // namespace MeltPoolDG::Functions::Distance
Definition distance_functions.hpp:30
number value(const dealii::Point< dim, number > &x, const unsigned int component=0) const override
Definition distance_functions.hpp:102
Plane(const dealii::Point< dim, number > &center_of_wall, const dealii::Tensor< 1, dim, number > &normal, const std::array< number, dim - 1 > &half_extents, const dealii::Tensor< 1, dim, number > &first_tangent_direction=dealii::Tensor< 1, dim, number >())
Definition distance_functions.hpp:41
std::array< dealii::Tensor< 1, dim, number >, dim - 1 > tangents
The unit tangent directions spanning the wall plane (one in 2D, two in 3D).
Definition distance_functions.hpp:139
dealii::Tensor< 1, dim, number > normal
The normal of the wall plane.
Definition distance_functions.hpp:136
dealii::Point< dim, number > center_of_wall
The center of the rectangular wall plane.
Definition distance_functions.hpp:133
const number tolerance
Tolerance for the geometric consistency checks and for detecting points on the wall.
Definition distance_functions.hpp:145
dealii::Tensor< 1, dim, number > gradient(const dealii::Point< dim, number > &x, const unsigned int component=0) const override
Definition distance_functions.hpp:78
std::array< number, dim - 1 > half_extents
The half-widths of the wall along each tangent direction.
Definition distance_functions.hpp:142
dealii::Tensor< 1, dim, number > offset_from_wall(const dealii::Point< dim, number > &x) const
Definition distance_functions.hpp:115
Definition distance_functions.hpp:158
const number tolerance
Definition distance_functions.hpp:315
const number half_length
Half of the truncated cone height, measured from the axis center along the axis.
Definition distance_functions.hpp:311
TruncatedCone(const dealii::Point< dim, number > &axis_center, const dealii::Tensor< 1, dim, number > &axis_direction, const number radius_bottom, const number radius_top, const number half_length)
Definition distance_functions.hpp:169
std::array< number, 2 > meridian_offset_from_wall(const dealii::Point< dim, number > &x) const
Definition distance_functions.hpp:273
dealii::Tensor< 1, dim, number > axis
The unit vector along the axis, pointing from the bottom to the top end.
Definition distance_functions.hpp:302
const number radius_top
The radius at the top end, i.e. at +half_length along the axis.
Definition distance_functions.hpp:308
const number radius_bottom
The radius at the bottom end, i.e. at -half_length along the axis.
Definition distance_functions.hpp:305
number value(const dealii::Point< dim, number > &x, const unsigned int component=0) const override
Definition distance_functions.hpp:251
dealii::Tensor< 1, dim, number > gradient(const dealii::Point< dim, number > &x, const unsigned int component=0) const override
Definition distance_functions.hpp:200
const dealii::Point< dim, number > axis_center
The midpoint on its axis of the truncated cone.
Definition distance_functions.hpp:299
Definition distance_functions.hpp:19