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])));
117 if (!MooseUtils::absoluteFuzzyEqual(
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 =
183 Real dummy_max_rad = 0;
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;
224 if (MooseUtils::absoluteFuzzyEqual(c_coeff, 1.0))
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();
282 if ((!is_bdry_closed && MooseUtils::absoluteFuzzyGreaterThan(
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);
295 if (!MooseUtils::absoluteFuzzyEqual(mod_corr_factor, 1.0))
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 "
316 return dynamic_pointer_cast<MeshBase>(
_input);
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)
376 if (!MooseUtils::absoluteFuzzyEqual((pt - circ_origin).norm(),
radius, tol))
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));
410 std::abs(azi1 - azi2) > M_PI ? 2 * M_PI - std::abs(azi1 - azi2) : std::abs(azi1 - azi2);
412 d_azi_list.push_back(d_azi);
415 if (!MooseUtils::absoluteFuzzyEqual(acc_azi, M_PI) && !is_closed_loop &&
416 move_end_nodes_in_span_direction)
418 const Real k_1 = 1.0 + std::cos(acc_azi);
419 const Real k_2 = acc_azi - std::sin(acc_azi);
428 const Real gc = k_2 + k_2 * std::cos(acc_azi * c_old) + k_1 * std::sin(acc_azi * c_old) -
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;
434 diff = std::abs(c_new - c_old);
439 mooseError(
"Newton-Raphson method did not converge for generating the radial correction "
440 "factor for circular area preservation.");
442 const Real norm_corr_factor = std::cos(0.5 * acc_azi) / std::cos(0.5 * acc_azi * c_new);
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;
450 return std::sqrt(acc_azi / acc_area);