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.
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 ...
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.
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 and optionally a file path to the top-level block p...
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...