27 params.
addRequiredParam<MeshGeneratorName>(
"input",
"The input mesh to be modified.");
29 params.
addParam<
bool>(
"move_end_nodes_in_span_direction",
31 "Whether to move the end nodes in the span direction of the arc or not.");
33 params.
addRequiredParam<std::vector<BoundaryName>>(
"input_mesh_circular_boundaries",
34 "Names of the circular boundaries.");
37 "transition_layer_ratios",
38 "transition_layer_ratios>0&transition_layer_ratios<=1.0",
39 "Ratios to radii as the transition layers on both sides of the original radii (if this " 40 "parameter is not provided, 0.01 will be used).");
43 "custom_circular_tolerance",
44 "custom_circular_tolerance>0",
45 "Custom tolerances (L2 norm) used to verify whether the provided boundaries are circular " 46 "(default is 1.0e-6).");
49 "This CircularBoundaryCorrectionGenerator object is designed to correct full " 50 "or partial circular boundaries in a 2D mesh to preserve areas.");
58 _input_name(getParam<MeshGeneratorName>(
"input")),
59 _input_mesh_circular_boundaries(
60 getParam<
std::vector<BoundaryName>>(
"input_mesh_circular_boundaries")),
61 _transition_layer_ratios(isParamValid(
"transition_layer_ratios")
62 ? getParam<
std::vector<
Real>>(
"transition_layer_ratios")
63 :
std::vector<
Real>(_input_mesh_circular_boundaries.size(), 0.01)),
64 _custom_circular_tolerance(
65 isParamValid(
"custom_circular_tolerance")
66 ? (getParam<
std::vector<
Real>>(
"custom_circular_tolerance").size() == 1
68 _input_mesh_circular_boundaries.size(),
69 getParam<
std::vector<
Real>>(
"custom_circular_tolerance").front())
70 : getParam<
std::vector<
Real>>(
"custom_circular_tolerance"))
71 :
std::vector<
Real>(_input_mesh_circular_boundaries.size(), 1.0e-6)),
72 _move_end_nodes_in_span_direction(getParam<bool>(
"move_end_nodes_in_span_direction")),
73 _input(getMeshByName(_input_name))
77 "this parameter must have the same length as 'input_mesh_circular_boundaries'.");
80 "if provided, this parameter must have either a unity length or the same length as " 81 "'input_mesh_circular_boundaries'.");
84 std::unique_ptr<MeshBase>
87 auto input_mesh =
dynamic_cast<ReplicatedMesh *
>(
_input.get());
89 paramError(
"input",
"Input is not a replicated mesh, which is required");
96 "the boundary '" + bd +
"' does not exist in the input mesh.");
101 auto side_list = input_mesh->get_boundary_info().build_side_list();
102 input_mesh->get_boundary_info().build_node_list_from_side_list();
103 auto node_list = input_mesh->get_boundary_info().build_node_list();
108 for (
unsigned int i = 0; i < node_list.size(); ++i)
112 std::get<1>(node_list[i]));
116 input_mesh->point(std::get<0>(node_list[i])));
121 paramError(
"input_mesh_circular_boundaries",
"Only boundaries in XY plane are supported.");
124 std::vector<std::vector<std::pair<Point, Point>>> input_circ_bds_sds(
126 std::vector<std::vector<std::pair<dof_id_type, dof_id_type>>> input_circ_bds_sds_ids(
130 for (
unsigned int i = 0; i < side_list.size(); ++i)
134 std::get<2>(side_list[i]));
138 input_mesh->elem_ptr(std::get<0>(side_list[i]))->side_ptr(std::get<1>(side_list[i]));
146 std::make_pair(side->node_id(0), side->node_id(1))) ==
154 std::make_pair(side->node_id(1), side->node_id(0))) ==
159 .push_back(std::make_pair(side->point(0), side->point(1)));
161 .push_back(std::make_pair(side->node_id(0), side->node_id(1)));
168 std::set<dof_id_type> mod_node_list;
170 bool has_partial_circle =
false;
171 for (
unsigned int i = 0; i < input_circ_bds_pts.size(); i++)
173 auto & input_circ_bd_pts = input_circ_bds_pts[i];
174 if (input_circ_bd_pts.size() < 3)
176 "too few nodes in one of the provided boundaries.");
178 const Point boundary_origin =
184 std::vector<dof_id_type> ordered_node_list;
189 input_circ_bds_sds_ids[i],
195 has_partial_circle = has_partial_circle || !is_bdry_closed;
214 const Point pt_start = input_mesh->point(ordered_node_list.front());
215 const Point pt_end = input_mesh->point(ordered_node_list.back());
217 const Point span_direction = is_bdry_closed ? Point(0.0, 0.0, 0.0) : (pt_start - pt_end).unit();
222 Real center_bdry_azi;
228 const Point pt_second = input_mesh->point(ordered_node_list[1]);
229 const Real pt_start_azi =
230 std::atan2(pt_start(1) - boundary_origin(1), pt_start(0) - boundary_origin(0));
231 const Real pt_second_azi =
232 std::atan2(pt_second(1) - boundary_origin(1), pt_second(0) - boundary_origin(0));
234 const Real azi_direction =
235 (pt_second_azi - pt_start_azi > 0 or pt_second_azi - pt_start_azi < -M_PI) ? 1.0 : -1.0;
236 center_bdry_azi = pt_start_azi + azi_direction * M_PI / 2.0;
237 half_azi_range = M_PI / 2.0;
241 const Point center_bdry =
242 (c_coeff > 1.0 ? 1.0 : -1.0) * (pt_start + pt_end) / 2.0 - boundary_origin;
243 const Real pt_start_azi =
244 std::atan2(pt_start(1) - boundary_origin(1), pt_start(0) - boundary_origin(0));
245 center_bdry_azi = std::atan2(center_bdry(1), center_bdry(0));
246 half_azi_range =
std::abs(pt_start_azi - center_bdry_azi > M_PI
247 ? pt_start_azi - center_bdry_azi - 2 * M_PI
248 : (pt_start_azi - center_bdry_azi < -M_PI
249 ? pt_start_azi - center_bdry_azi + 2 * M_PI
250 : pt_start_azi - center_bdry_azi));
254 for (
auto & node : input_mesh->node_ptr_range())
258 end_node_disp >= 0.0)
260 if (!mod_node_list.emplace((*node).id()).second)
261 paramError(
"transition_layer_ratios",
"the transition layers are overlapped.");
262 (*node) = (*node) + end_node_disp * bdry_rad * span_direction;
266 end_node_disp >= 0.0)
268 if (!mod_node_list.emplace((*node).id()).second)
269 paramError(
"transition_layer_ratios",
"the transition layers are overlapped.");
270 (*node) = (*node) - end_node_disp * bdry_rad * span_direction;
273 const Point tmp_pt = (*node) - boundary_origin;
274 const Real tmp_pt_azi = std::atan2(tmp_pt(1), tmp_pt(0));
275 const Real angle_diff =
276 tmp_pt_azi - center_bdry_azi > M_PI
277 ? tmp_pt_azi - center_bdry_azi - 2.0 * M_PI
278 : (tmp_pt_azi - center_bdry_azi < -M_PI ? tmp_pt_azi - center_bdry_azi + 2.0 * M_PI
279 : tmp_pt_azi - center_bdry_azi);
280 const Real pt_rad = tmp_pt.norm();
284 pt_rad < bdry_rad - transition_layer_thickness ||
285 pt_rad > bdry_rad + transition_layer_thickness)
287 const Real tmp_pt_azi_new =
289 const Real mod_corr_factor =
291 ? (1.0 + (corr_factor - 1.0) * (pt_rad - bdry_rad + transition_layer_thickness) /
292 transition_layer_thickness)
293 : (1.0 + (corr_factor - 1.0) * (bdry_rad + transition_layer_thickness - pt_rad) /
294 transition_layer_thickness);
297 if (!mod_node_list.emplace((*node).id()).second)
298 paramError(
"transition_layer_ratios",
"the transition layers are overlapped.");
300 const Point tmp_pt_alt =
301 Point(pt_rad *
std::cos(tmp_pt_azi_new), pt_rad *
std::sin(tmp_pt_azi_new), tmp_pt(2)) *
303 (*node) = tmp_pt_alt + boundary_origin;
307 paramError(
"move_end_nodes_in_span_direction",
308 "all the boundaries are closed, this parameter should be be set as 'true'.");
310 for (
auto & elem : input_mesh->element_ptr_range())
311 if (elem->volume() < 0.0)
313 "some elements are inverted during circular boundary correction, consider tuning " 322 const Real tol)
const 329 unsigned int farthest_pt_id(0);
330 for (
unsigned int i = 1; i < pts_list.size(); i++)
331 if ((pts_list[i] - pts_list[0]).norm() > tmp_dis)
333 tmp_dis = (pts_list[i] - pts_list[0]).
norm();
337 unsigned int mid_pt_id(0);
338 for (
unsigned int i = 1; i < pts_list.size(); i++)
339 if (i != farthest_pt_id &&
std::abs((pts_list[farthest_pt_id] - pts_list[i]).
norm() -
340 (pts_list[0] - pts_list[i]).
norm()) < tmp_dis)
342 tmp_dis =
std::abs((pts_list[farthest_pt_id] - pts_list[i]).
norm() -
343 (pts_list[0] - pts_list[i]).
norm());
347 const Real x12 = pts_list[0](0) - pts_list[farthest_pt_id](0);
348 const Real x13 = pts_list[0](0) - pts_list[mid_pt_id](0);
350 const Real y12 = pts_list[0](1) - pts_list[farthest_pt_id](1);
351 const Real y13 = pts_list[0](1) - pts_list[mid_pt_id](1);
354 pts_list[0](0) * pts_list[0](0) - pts_list[mid_pt_id](0) * pts_list[mid_pt_id](0);
356 pts_list[0](1) * pts_list[0](1) - pts_list[mid_pt_id](1) * pts_list[mid_pt_id](1);
359 pts_list[farthest_pt_id](0) * pts_list[farthest_pt_id](0) - pts_list[0](0) * pts_list[0](0);
361 pts_list[farthest_pt_id](1) * pts_list[farthest_pt_id](1) - pts_list[0](1) * pts_list[0](1);
364 (sx13 * x12 + sy13 * x12 + sx21 * x13 + sy21 * x13) / (2.0 * (-y13 * x12 + y12 * x13));
366 (sx13 * y12 + sy13 * y12 + sx21 * y13 + sy21 * y13) / (2.0 * (-x13 * y12 + x12 * y13));
368 const Real c = -pts_list[0](0) * pts_list[0](0) - pts_list[0](1) * pts_list[0](1) -
369 2.0 * g * pts_list[0](0) - 2.0 * f * pts_list[0](1);
370 const Real r2 = f * f + g * g - c;
373 const Point circ_origin = Point(-g, -f, 0.0);
375 for (
const auto & pt : pts_list)
378 "the boundary provided is not circular. The generator found point " +
380 " to be the likely circle origin to the boundary, with a radius of " +
382 " is at a radius of " + std::to_string((pt - circ_origin).
norm()));
388 const std::vector<std::pair<Point, Point>> & bd_side_list,
389 const Point & circle_center,
390 const bool is_closed_loop,
391 const bool move_end_nodes_in_span_direction,
393 Real & end_node_disp)
const 395 if (bd_side_list.empty())
397 "CircularBoundaryCorrectionGenerator::generateRadialCorrectionFactor is empty.");
400 std::vector<Real> d_azi_list;
401 for (
const auto & bd_side : bd_side_list)
403 const Point v1 = bd_side.first - circle_center;
404 const Point v2 = bd_side.second - circle_center;
406 acc_area += v1.cross(v2).norm() / v1.norm() / v2.norm();
407 const Real azi1 = std::atan2(v1(1), v1(0));
408 const Real azi2 = std::atan2(v2(1), v2(0));
412 d_azi_list.push_back(d_azi);
416 move_end_nodes_in_span_direction)
430 const Real gcp = -k_2 * acc_azi *
std::sin(acc_azi * c_old) +
431 k_1 * acc_azi *
std::cos(acc_azi * c_old) -
433 c_new = c_old - gc / gcp;
439 mooseError(
"Newton-Raphson method did not converge for generating the radial correction " 440 "factor for circular area preservation.");
443 end_node_disp = norm_corr_factor *
std::sin(0.5 * acc_azi * c_new) -
std::sin(0.5 * acc_azi);
446 return norm_corr_factor;
448 end_node_disp = -1.0;
455 const Real coeff)
const 458 for (
const auto & th : th_list)
465 const Real coeff)
const 469 for (
const auto & th : th_list)
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
MetaPhysicL::DualNumber< V, D, asd > abs(const MetaPhysicL::DualNumber< V, D, asd > &a)
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sin(_arg) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tan
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether two variables are equal within an absolute tolerance.
bool hasBoundaryName(const MeshBase &input_mesh, const BoundaryName &name)
Whether a particular boundary name exists in the mesh.
registerMooseObject("MooseApp", CircularBoundaryCorrectionGenerator)
static constexpr Real TOLERANCE
const std::vector< Real > _custom_circular_tolerance
Customized tolerance used to verify that boundaries are circular.
std::unique_ptr< T_DEST, T_DELETER > dynamic_pointer_cast(std::unique_ptr< T_SRC, T_DELETER > &src)
These are reworked from https://stackoverflow.com/a/11003103.
This CircularBoundaryCorrectionGenerator object is designed to correct full or partial circular bound...
Point circularCenterCalculator(const std::vector< Point > &pts_list, Real &radius, const Real tol=1e-12) const
Calculates the center of the circle/partial circle formed by a vector of points.
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template cos(_arg) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(cos
Real sineSummation(const std::vector< Real > &th_list, const Real coeff) const
Calculates the summation of the sine values of a list of angles.
const std::vector< BoundaryName > _input_mesh_circular_boundaries
Names of the circular boundaries of the 2D input mesh to correct.
std::vector< BoundaryID > getBoundaryIDs(const libMesh::MeshBase &mesh, const std::vector< BoundaryName > &boundary_name, bool generate_unknown, const std::set< BoundaryID > &mesh_boundary_ids)
Gets the boundary IDs with their names.
void paramError(const std::string ¶m, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
Real generateRadialCorrectionFactor(const std::vector< std::pair< Point, Point >> &bd_side_list, const Point &circle_center, const bool is_closed_loop, const bool move_end_nodes_in_span_direction, Real &c_coeff, Real &end_node_disp) const
Calculates the radius correction factor based on a list of sides on a circular boundary.
static InputParameters validParams()
std::string stringify(const T &t)
conversion to string
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sqrt(_arg)) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tanh
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type.
static InputParameters validParams()
const std::vector< Real > _transition_layer_ratios
Ratio parameters used to customize the transition area sizes.
std::unique_ptr< MeshBase > & _input
Reference to input mesh pointer.
std::vector< boundary_id_type > _input_mesh_circular_bids
IDs of the circular boundary of the input mesh.
MeshGenerators are objects that can modify or add to an existing mesh.
const bool _move_end_nodes_in_span_direction
Whether to move the end nodes of the partial circular boundary in the span direction.
bool absoluteFuzzyGreaterThan(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether a variable is greater than another variable within an absolute tolerance...
CircularBoundaryCorrectionGenerator(const InputParameters ¶meters)
Real sinePrimeSummation(const std::vector< Real > &th_list, const Real coeff) const
Calculates the first derivative with regards to coeff of the summation of the sine values of a list o...