diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp index 81549c4039487f4175e920b62a7a54a77be2c6e6..1cdc1a23b016e6744c953d3e74a159f24499ea5d 100644 --- a/src/language/modules/SchemeModule.cpp +++ b/src/language/modules/SchemeModule.cpp @@ -18,6 +18,7 @@ #include <mesh/MeshRandomizer.hpp> #include <mesh/MeshSmoother.hpp> #include <mesh/MeshSmootherEscobar.hpp> +#include <mesh/MeshSmootherJun.hpp> #include <scheme/AcousticSolver.hpp> #include <scheme/AxisBoundaryConditionDescriptor.hpp> #include <scheme/DirichletBoundaryConditionDescriptor.hpp> @@ -307,6 +308,60 @@ SchemeModule::SchemeModule() )); + this->_addBuiltinFunction("smoothMeshJun", + std::function( + + [](std::shared_ptr<const IMesh> p_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list) -> std::shared_ptr<const IMesh> { + MeshSmootherJunHandler handler; + return handler.getSmoothedMesh(p_mesh, bc_descriptor_list); + } + + )); + + this->_addBuiltinFunction("smoothMeshJun", + std::function( + + [](std::shared_ptr<const IMesh> p_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const FunctionSymbolId& function_symbol_id) -> std::shared_ptr<const IMesh> { + MeshSmootherJunHandler handler; + return handler.getSmoothedMesh(p_mesh, bc_descriptor_list, function_symbol_id); + } + + )); + + this->_addBuiltinFunction("smoothMeshJun", + std::function( + + [](std::shared_ptr<const IMesh> p_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const std::vector<std::shared_ptr<const IZoneDescriptor>>& smoothing_zone_list) + -> std::shared_ptr<const IMesh> { + MeshSmootherJunHandler handler; + return handler.getSmoothedMesh(p_mesh, bc_descriptor_list, smoothing_zone_list); + } + + )); + + this->_addBuiltinFunction("smoothMeshJun", + std::function( + + [](std::shared_ptr<const IMesh> p_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& + discrete_function_variant_list) -> std::shared_ptr<const IMesh> { + MeshSmootherJunHandler handler; + return handler.getSmoothedMesh(p_mesh, bc_descriptor_list, + discrete_function_variant_list); + } + + )); + this->_addBuiltinFunction("smoothMeshEscobar", std::function( diff --git a/src/mesh/CMakeLists.txt b/src/mesh/CMakeLists.txt index 54c7afbf2f19799524faf0038e944d5463e1f7c1..63b1b000484f10031214f93d605db9d1c869a1a1 100644 --- a/src/mesh/CMakeLists.txt +++ b/src/mesh/CMakeLists.txt @@ -40,5 +40,6 @@ add_library( MeshRandomizer.cpp MeshSmoother.cpp MeshSmootherEscobar.cpp + MeshSmootherJun.cpp MeshTransformer.cpp SynchronizerManager.cpp) diff --git a/src/mesh/MeshSmootherEscobar.cpp b/src/mesh/MeshSmootherEscobar.cpp index eca67723dd72c733107cdd5b0caeef04ae24e117..0f32fa6417dae941b4480cd98bbf3c4ce43a1fef 100644 --- a/src/mesh/MeshSmootherEscobar.cpp +++ b/src/mesh/MeshSmootherEscobar.cpp @@ -369,7 +369,7 @@ class MeshSmootherEscobarHandler::MeshSmootherEscobar const NodeId node_id = node_list[i_node]; if (m_is_smoothed_node[node_id] and node_is_owned[node_id]) { smooth(node_id, new_xr[node_id]); - // new_xr[node_id] = 0.5 * (new_xr[node_id] + old_xr[node_id]); + new_xr[node_id] = 0.5 * (new_xr[node_id] + old_xr[node_id]); } }); @@ -495,22 +495,18 @@ class MeshSmootherEscobarHandler::MeshSmootherEscobar const double sin_alpha = std::sin(alpha); const double cos_alpha = std::cos(alpha); const double inv_sin_alpha = 1. / sin_alpha; - const double inv_tan_alpha = 1. / std::tan(alpha); + const double inv_tan_alpha = cos_alpha / sin_alpha; const TinyMatrix<Dimension> W{1, cos_alpha, // 0, sin_alpha}; const TinyMatrix<Dimension> inv_W = inverse(W); - std::array<TinyMatrix<Dimension>, Dimension> // - S_gradient = {TinyMatrix<Dimension>{-1, -1, // + std::array<TinyMatrix<Dimension>, Dimension> // + S_gradient = {TinyMatrix<Dimension>{-1, -inv_sin_alpha + inv_tan_alpha, // +0, +0}, - TinyMatrix<Dimension>{+inv_tan_alpha, +inv_tan_alpha, // - -inv_sin_alpha, -inv_sin_alpha}}; - - f = 0; - f_gradient = zero; - f_hessian = zero; + TinyMatrix<Dimension>{+0, +0, // + -1, -inv_sin_alpha + inv_tan_alpha}}; SmallArray<TinyMatrix<Dimension>> S_list(cell_list.size()); for (size_t i_cell = 0; i_cell < cell_list.size(); ++i_cell) { @@ -658,6 +654,10 @@ class MeshSmootherEscobarHandler::MeshSmootherEscobar auto escobar = escobarSSigma; + f = 0; + f_gradient = zero; + f_hessian = zero; + for (size_t i_cell = 0; i_cell < S_list.size(); ++i_cell) { const double sigma = sigma_list[i_cell]; const TinyMatrix<Dimension> S = S_list[i_cell]; @@ -674,12 +674,11 @@ class MeshSmootherEscobarHandler::MeshSmootherEscobar TinyVector<Dimension> sigma_gradient{trace(Sigma * S_gradient[0]), // trace(Sigma * S_gradient[1])}; - - const std::array<TinyMatrix<Dimension>, Dimension> // - Sigma_gradient{TinyMatrix<Dimension>{+0, +1, // + const std::array<TinyMatrix<Dimension>, Dimension> // + Sigma_gradient{TinyMatrix<Dimension>{+0, +inv_sin_alpha - inv_tan_alpha, // +0, -1}, - TinyMatrix<Dimension>{-inv_sin_alpha, -inv_tan_alpha, // - +inv_sin_alpha, +inv_tan_alpha}}; + TinyMatrix<Dimension>{-inv_sin_alpha + inv_tan_alpha, +0, // + +1, +0}}; escobar(sigma, S, sigma_gradient, S_gradient, Sigma_gradient, f, f_gradient, f_hessian); } @@ -698,10 +697,10 @@ class MeshSmootherEscobarHandler::MeshSmootherEscobar const double mr_alpha = 2 * std::acos(-1) / ((cell_nb_nodes - 2) * (cell_list.size())); - const double mr_sin_alpha = std::sin(mr_alpha); - const double mr_cos_alpha = std::cos(mr_alpha); - const double mr_inv_sin_alpha = 1. / mr_sin_alpha; - const double mr_inv_tan_alpha = 1. / std::tan(mr_alpha); + const double mr_sin_alpha = std::sin(mr_alpha); + const double mr_cos_alpha = std::cos(mr_alpha); + // const double mr_inv_sin_alpha = 1. / mr_sin_alpha; + // const double mr_inv_tan_alpha = 1. / std::tan(mr_alpha); const TinyMatrix<Dimension> mr_W{1, mr_cos_alpha, // 0, mr_sin_alpha}; @@ -791,96 +790,164 @@ class MeshSmootherEscobarHandler::MeshSmootherEscobar auto cell_to_node_matrix = m_given_mesh.connectivity().cellToNodeMatrix(); auto node_number_in_their_cells = m_given_mesh.connectivity().nodeLocalNumbersInTheirCells(); + constexpr bool q_sin = true; + for (NodeId node_id = 0; node_id < m_given_mesh.numberOfNodes(); ++node_id) { if (not m_is_fixed_node[node_id]) { auto node_cells = node_to_cell_matrix[node_id]; auto node_number = node_number_in_their_cells[node_id]; - double min_sin = std::numeric_limits<double>::max(); - double max_abs_sin = -std::numeric_limits<double>::max(); + if constexpr (q_sin) { // q_area + double min_sin = std::numeric_limits<double>::max(); + double max_abs_sin = -std::numeric_limits<double>::max(); - for (size_t i_cell = 0; i_cell < node_cells.size(); ++i_cell) { - const CellId cell_id = node_cells[i_cell]; + for (size_t i_cell = 0; i_cell < node_cells.size(); ++i_cell) { + const CellId cell_id = node_cells[i_cell]; - const auto cell_nodes = cell_to_node_matrix[cell_id]; + const auto cell_nodes = cell_to_node_matrix[cell_id]; - const size_t i_A = node_number[i_cell]; - const size_t i_B = (i_A + 1) % cell_nodes.size(); - const size_t i_C = (i_A + cell_nodes.size() - 1) % cell_nodes.size(); - const size_t i_D = (i_A + 2) % cell_nodes.size(); - const size_t i_E = (i_A + cell_nodes.size() - 2) % cell_nodes.size(); + const size_t i_A = node_number[i_cell]; + const size_t i_B = (i_A + 1) % cell_nodes.size(); + const size_t i_C = (i_A + cell_nodes.size() - 1) % cell_nodes.size(); + const size_t i_D = (i_A + 2) % cell_nodes.size(); + const size_t i_E = (i_A + cell_nodes.size() - 2) % cell_nodes.size(); - const Rd A = xr[cell_nodes[i_A]]; - const Rd B = xr[cell_nodes[i_B]]; - const Rd C = xr[cell_nodes[i_C]]; - const Rd D = xr[cell_nodes[i_D]]; - const Rd E = xr[cell_nodes[i_E]]; + const Rd A = xr[cell_nodes[i_A]]; + const Rd B = xr[cell_nodes[i_B]]; + const Rd C = xr[cell_nodes[i_C]]; + const Rd D = xr[cell_nodes[i_D]]; + const Rd E = xr[cell_nodes[i_E]]; - const Rd AB = B - A; - const Rd AC = C - A; + const Rd AB = B - A; + const Rd AC = C - A; - const Rd CA = A - C; - const Rd CE = E - C; + const Rd CA = A - C; + const Rd CE = E - C; - const Rd BD = D - B; - const Rd BA = A - B; + const Rd BD = D - B; + const Rd BA = A - B; - const double l_AB = l2Norm(AB); - const double l_AC = l2Norm(AC); + const double l_AB = l2Norm(AB); + const double l_AC = l2Norm(AC); - const double l_CA = l2Norm(CA); - const double l_CE = l2Norm(CE); + const double l_CA = l2Norm(CA); + const double l_CE = l2Norm(CE); - const double l_BD = l2Norm(BD); - const double l_BA = l2Norm(BA); + const double l_BD = l2Norm(BD); + const double l_BA = l2Norm(BA); - if ((l_AB == 0) or (l_AC == 0) or (l_CA == 0) or (l_CE == 0) or (l_BD == 0) or (l_BA == 0)) { - min_sin = -1; - max_abs_sin = 1; - break; - } + if ((l_AB == 0) or (l_AC == 0) or (l_CA == 0) or (l_CE == 0) or (l_BD == 0) or (l_BA == 0)) { + min_sin = -1; + max_abs_sin = 1; + break; + } - const Rd u_AB = 1. / l_AB * AB; - const Rd u_AC = 1. / l_AC * AC; - const Rd u_CA = 1. / l_CA * CA; - const Rd u_CE = 1. / l_CE * CE; - const Rd u_BD = 1. / l_BD * BD; - const Rd u_BA = 1. / l_BA * BA; + const Rd u_AB = 1. / l_AB * AB; + const Rd u_AC = 1. / l_AC * AC; + const Rd u_CA = 1. / l_CA * CA; + const Rd u_CE = 1. / l_CE * CE; + const Rd u_BD = 1. / l_BD * BD; + const Rd u_BA = 1. / l_BA * BA; - const double sin_CAB = det(TinyMatrix{u_AB, u_AC}); - const double sin_ECA = det(TinyMatrix{u_CA, u_CE}); - const double sin_ABD = det(TinyMatrix{u_BD, u_BA}); + const double sin_CAB = det(TinyMatrix{u_AB, u_AC}); + const double sin_ECA = det(TinyMatrix{u_CA, u_CE}); + const double sin_ABD = det(TinyMatrix{u_BD, u_BA}); - const double abs_sin_CAB = std::abs(sin_CAB); - const double abs_sin_ECA = std::abs(sin_ECA); - const double abs_sin_ABD = std::abs(sin_ABD); + const double abs_sin_CAB = std::abs(sin_CAB); + const double abs_sin_ECA = std::abs(sin_ECA); + const double abs_sin_ABD = std::abs(sin_ABD); - if (sin_CAB < min_sin) { - min_sin = sin_CAB; - } + if (sin_CAB < min_sin) { + min_sin = sin_CAB; + } - if (sin_ECA < min_sin) { - min_sin = sin_ECA; - } + if (sin_ECA < min_sin) { + min_sin = sin_ECA; + } - if (sin_ABD < min_sin) { - min_sin = sin_ABD; - } + if (sin_ABD < min_sin) { + min_sin = sin_ABD; + } - if (abs_sin_CAB > max_abs_sin) { - max_abs_sin = abs_sin_CAB; - } + if (abs_sin_CAB > max_abs_sin) { + max_abs_sin = abs_sin_CAB; + } - if (abs_sin_ECA > max_abs_sin) { - max_abs_sin = abs_sin_ECA; + if (abs_sin_ECA > max_abs_sin) { + max_abs_sin = abs_sin_ECA; + } + + if (abs_sin_ABD > max_abs_sin) { + max_abs_sin = abs_sin_ABD; + } } - if (abs_sin_ABD > max_abs_sin) { - max_abs_sin = abs_sin_ABD; + node_quality[node_id] = min_sin / max_abs_sin; + } else { + double min_area = std::numeric_limits<double>::max(); + double max_abs_area = -std::numeric_limits<double>::max(); + + for (size_t i_cell = 0; i_cell < node_cells.size(); ++i_cell) { + const CellId cell_id = node_cells[i_cell]; + + const auto cell_nodes = cell_to_node_matrix[cell_id]; + + const size_t i_A = node_number[i_cell]; + const size_t i_B = (i_A + 1) % cell_nodes.size(); + const size_t i_C = (i_A + cell_nodes.size() - 1) % cell_nodes.size(); + const size_t i_D = (i_A + 2) % cell_nodes.size(); + const size_t i_E = (i_A + cell_nodes.size() - 2) % cell_nodes.size(); + + const Rd A = xr[cell_nodes[i_A]]; + const Rd B = xr[cell_nodes[i_B]]; + const Rd C = xr[cell_nodes[i_C]]; + const Rd D = xr[cell_nodes[i_D]]; + const Rd E = xr[cell_nodes[i_E]]; + + const Rd AB = B - A; + const Rd AC = C - A; + + const Rd CA = A - C; + const Rd CE = E - C; + + const Rd BD = D - B; + const Rd BA = A - B; + + const double area_CAB = det(TinyMatrix{AB, AC}); + const double area_ECA = det(TinyMatrix{CA, CE}); + const double area_ABD = det(TinyMatrix{BD, BA}); + + const double abs_area_CAB = std::abs(area_CAB); + const double abs_area_ECA = std::abs(area_ECA); + const double abs_area_ABD = std::abs(area_ABD); + + if (area_CAB < min_area) { + min_area = area_CAB; + } + + if (area_ECA < min_area) { + min_area = area_ECA; + } + + if (area_ABD < min_area) { + min_area = area_ABD; + } + + if (abs_area_CAB > max_abs_area) { + max_abs_area = abs_area_CAB; + } + + if (abs_area_ECA > max_abs_area) { + max_abs_area = abs_area_ECA; + } + + if (abs_area_ABD > max_abs_area) { + max_abs_area = abs_area_ABD; + } } - } - node_quality[node_id] = min_sin / max_abs_sin; + node_quality[node_id] = min_area / max_abs_area; + } } else { node_quality[node_id] = 1; } @@ -920,11 +987,7 @@ class MeshSmootherEscobarHandler::MeshSmootherEscobar quality.fill(2); - size_t maxiter = 0; - - auto smooth = [this, &maxiter, &old_xr](const NodeId node_id, TinyVector<Dimension>& x) -> double { - // SmallArray<TinyMatrix<Dimension>> S_list(cell_list.size()); - + auto smooth = [this, &old_xr](const NodeId node_id, TinyVector<Dimension>& x) -> double { double delta = 0; double final_f = 0; @@ -933,7 +996,7 @@ class MeshSmootherEscobarHandler::MeshSmootherEscobar const ConnectivityType& connectivity = m_given_mesh.connectivity(); - for (size_t i_iter = 0; i_iter < 200; ++i_iter) { + for (size_t i_iter = 0; i_iter < 20; ++i_iter) { double sigma_min = std::numeric_limits<double>::max(); delta = sigma_min; @@ -962,17 +1025,18 @@ class MeshSmootherEscobarHandler::MeshSmootherEscobar return max_lenght; }(); - std::cout.precision(15); - if constexpr (use_newton) { const TinyVector delta_x_candidate = -inverse(f_hessian) * f_gradient; const double delta_x_norm = l2Norm(delta_x_candidate); + TinyVector<Dimension> old_x = x; + double factor = 1; - if (delta_x_norm > 0.2 * max_edge_lenght) { - factor *= (0.2 * max_edge_lenght / delta_x_norm); + if (delta_x_norm > 0.5 * max_edge_lenght) { + factor *= (0.5 * max_edge_lenght / delta_x_norm); } + x += factor * delta_x_candidate; if (delta_x_norm < 1E-8 * max_edge_lenght) { @@ -1146,25 +1210,20 @@ class MeshSmootherEscobarHandler::MeshSmootherEscobar return final_f; }; - // parallel_for( - // connectivity.numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { - // if (node_is_owned[node_id] and m_is_smoothed_node[node_id] and not is_boundary_node[node_id]) { - // quality[node_id] = smooth(node_id, new_xr[node_id]); - // } - // }); - - size_t count = 0; - for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) { - if (node_is_owned[node_id] and m_is_smoothed_node[node_id] and not is_boundary_node[node_id]) { - quality[node_id] = smooth(node_id, new_xr[node_id]); - new_xr[node_id] = 0.5 * (new_xr[node_id] + old_xr[node_id]); - // TinyVector<Dimension> x0{2, 3}; - // quality[node_id] = smooth(node_id, x0); - count++; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + if (node_is_owned[node_id] and m_is_smoothed_node[node_id] and not is_boundary_node[node_id]) { + quality[node_id] = smooth(node_id, new_xr[node_id]); + new_xr[node_id] = 0.5 * (new_xr[node_id] + old_xr[node_id]); + } + }); + std::cout << "nb smoothed = " << [&] { + size_t count = 0; + for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) { + count += (node_is_owned[node_id] and m_is_smoothed_node[node_id] and not is_boundary_node[node_id]); } - } - std::cout << "nb smoothed = " << count << '\n'; - std::cout << "maxiter = " << maxiter << '\n'; + return count; + }() << '\n'; } else { throw NotImplementedError("Dimension != 2"); } @@ -1176,43 +1235,48 @@ class MeshSmootherEscobarHandler::MeshSmootherEscobar { NodeValue<const Rd> xr = m_given_mesh.xr(); - size_t nb_non_convex_cells = 0; - size_t i_convexify = 1; + size_t i_convexify = 1; + bool need_convexify = false; + + std::cout << "== calling smoothMesh ==\n"; + + NodeValue<const double> old_node_quality; do { std::cout << "- convexfication step " << i_convexify++ << '\n'; NodeValue<Rd> new_xr = copy(xr); auto node_quality = this->_getNodeQuality(xr); - CellValue<const int> non_convex_cell_tag = [=, this] { - CellValue<int> cell_tag(m_given_mesh.connectivity()); - cell_tag.fill(0); + need_convexify = min(node_quality) <= 0; - auto cell_to_node_matrix = m_given_mesh.connectivity().cellToNodeMatrix(); + if (need_convexify) { + m_is_smoothed_node = _getSmoothedNode(node_quality, m_is_fixed_node); - for (CellId cell_id = 0; cell_id < m_given_mesh.numberOfCells(); ++cell_id) { - for (size_t i_node = 0; i_node < cell_to_node_matrix[cell_id].size(); ++i_node) { - const NodeId node_id = cell_to_node_matrix[cell_id][i_node]; - if (node_quality[node_id] <= m_min_quality) { - cell_tag[cell_id] = 1; - break; + if (old_node_quality.isBuilt()) { + NodeValue<bool> is_smoothed_first{m_given_mesh.connectivity()}; + is_smoothed_first.fill(false); + bool has_new_smoothed_node = false; + for (NodeId node_id = 0; node_id < m_given_mesh.numberOfNodes(); ++node_id) { + if ((m_is_smoothed_node[node_id]) and (old_node_quality[node_id] >= 0)) { + is_smoothed_first[node_id] = true; + has_new_smoothed_node = true; } } - } - - return cell_tag; - }(); - // auto non_convex_cell_tag = this->_getNonConvexCellTag(xr); - nb_non_convex_cells = sum(non_convex_cell_tag); + if (has_new_smoothed_node) { + std::cout << "has_new_smoothed_node = " << has_new_smoothed_node << '\n'; + m_is_smoothed_node = is_smoothed_first; + } + } - std::cout << " remaining non convex cells: " << nb_non_convex_cells << '\n'; - if (nb_non_convex_cells > 0) { - m_is_smoothed_node = _getSmoothedNode(node_quality, m_is_fixed_node); this->_getNewPositions(xr, new_xr); xr = new_xr; } - } while ((nb_non_convex_cells > 0) and (i_convexify < 20)); + + old_node_quality = node_quality; + std::cout << "need_convexify = " << std::boolalpha << need_convexify << '\n'; + } while ((need_convexify) and (i_convexify < 50)); + std::cout << " --> finished\n"; return std::make_shared<MeshType>(m_given_mesh.shared_connectivity(), xr); } diff --git a/src/mesh/MeshSmootherJun.cpp b/src/mesh/MeshSmootherJun.cpp new file mode 100644 index 0000000000000000000000000000000000000000..20821c5713b95b22f132ea3416170d01dd74b718 --- /dev/null +++ b/src/mesh/MeshSmootherJun.cpp @@ -0,0 +1,627 @@ +#include <mesh/MeshSmootherJun.hpp> + +#include <algebra/TinyMatrix.hpp> +#include <algebra/TinyVector.hpp> +#include <language/utils/InterpolateItemValue.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/ItemValueUtils.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshCellZone.hpp> +#include <mesh/MeshFlatNodeBoundary.hpp> +#include <mesh/MeshLineNodeBoundary.hpp> +#include <mesh/MeshNodeBoundary.hpp> +#include <scheme/AxisBoundaryConditionDescriptor.hpp> +#include <scheme/DiscreteFunctionUtils.hpp> +#include <scheme/DiscreteFunctionVariant.hpp> +#include <scheme/FixedBoundaryConditionDescriptor.hpp> +#include <scheme/SymmetryBoundaryConditionDescriptor.hpp> +#include <utils/RandomEngine.hpp> + +#include <variant> + +template <size_t Dimension> +class MeshSmootherJunHandler::MeshSmootherJun +{ + private: + using Rd = TinyVector<Dimension>; + using Rdxd = TinyMatrix<Dimension>; + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + + const MeshType& m_given_mesh; + + class AxisBoundaryCondition; + class FixedBoundaryCondition; + class SymmetryBoundaryCondition; + + using BoundaryCondition = std::variant<AxisBoundaryCondition, FixedBoundaryCondition, SymmetryBoundaryCondition>; + + using BoundaryConditionList = std::vector<BoundaryCondition>; + BoundaryConditionList m_boundary_condition_list; + + BoundaryConditionList + _getBCList(const MeshType& mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) + { + BoundaryConditionList bc_list; + + for (const auto& bc_descriptor : bc_descriptor_list) { + switch (bc_descriptor->type()) { + case IBoundaryConditionDescriptor::Type::axis: { + if constexpr (Dimension == 1) { + bc_list.emplace_back(FixedBoundaryCondition{getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor())}); + } else { + bc_list.emplace_back( + AxisBoundaryCondition{getMeshLineNodeBoundary(mesh, bc_descriptor->boundaryDescriptor())}); + } + break; + } + case IBoundaryConditionDescriptor::Type::symmetry: { + bc_list.emplace_back( + SymmetryBoundaryCondition{getMeshFlatNodeBoundary(mesh, bc_descriptor->boundaryDescriptor())}); + break; + } + case IBoundaryConditionDescriptor::Type::fixed: { + bc_list.emplace_back(FixedBoundaryCondition{getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor())}); + break; + } + default: { + std::ostringstream error_msg; + error_msg << *bc_descriptor << " is an invalid boundary condition for mesh smoother"; + throw NormalError(error_msg.str()); + } + } + } + + return bc_list; + } + + void + _applyBC(NodeValue<Rd>& shift) const + { + for (auto&& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using BCType = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<BCType, SymmetryBoundaryCondition>) { + const Rd& n = bc.outgoingNormal(); + + const Rdxd I = identity; + const Rdxd nxn = tensorProduct(n, n); + const Rdxd P = I - nxn; + + const Array<const NodeId>& node_list = bc.nodeList(); + parallel_for( + node_list.size(), PUGS_LAMBDA(const size_t i_node) { + const NodeId node_id = node_list[i_node]; + + shift[node_id] = P * shift[node_id]; + }); + + } else if constexpr (std::is_same_v<BCType, AxisBoundaryCondition>) { + if constexpr (Dimension > 1) { + const Rd& t = bc.direction(); + + const Rdxd txt = tensorProduct(t, t); + + const Array<const NodeId>& node_list = bc.nodeList(); + parallel_for( + node_list.size(), PUGS_LAMBDA(const size_t i_node) { + const NodeId node_id = node_list[i_node]; + + shift[node_id] = txt * shift[node_id]; + }); + } else { + throw UnexpectedError("AxisBoundaryCondition make no sense in dimension 1"); + } + + } else if constexpr (std::is_same_v<BCType, FixedBoundaryCondition>) { + const Array<const NodeId>& node_list = bc.nodeList(); + parallel_for( + node_list.size(), PUGS_LAMBDA(const size_t i_node) { + const NodeId node_id = node_list[i_node]; + shift[node_id] = zero; + }); + + } else { + throw UnexpectedError("invalid boundary condition type"); + } + }, + boundary_condition); + } + } + + NodeValue<Rd> + _getDisplacement() const + { + const ConnectivityType& connectivity = m_given_mesh.connectivity(); + NodeValue<const Rd> given_xr = m_given_mesh.xr(); + + auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); + auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); + auto node_number_in_their_cells = connectivity.nodeLocalNumbersInTheirCells(); + + NodeValue<double> max_delta_xr{connectivity}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + const Rd& x0 = given_xr[node_id]; + + const auto& node_cell_list = node_to_cell_matrix[node_id]; + double min_distance_2 = std::numeric_limits<double>::max(); + + for (size_t i_cell = 0; i_cell < node_cell_list.size(); ++i_cell) { + const size_t i_cell_node = node_number_in_their_cells(node_id, i_cell); + + const CellId cell_id = node_cell_list[i_cell]; + const auto& cell_node_list = cell_to_node_matrix[cell_id]; + + for (size_t i_node = 0; i_node < cell_node_list.size(); ++i_node) { + if (i_node != i_cell_node) { + const NodeId cell_node_id = cell_node_list[i_node]; + const Rd delta = x0 - given_xr[cell_node_id]; + min_distance_2 = std::min(min_distance_2, dot(delta, delta)); + } + } + } + double max_delta = std::sqrt(min_distance_2); + + max_delta_xr[node_id] = max_delta; + }); + + NodeValue<Rd> shift_r{connectivity}; + + if constexpr (Dimension == 2) { + parallel_for( + m_given_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + const auto node_cell_list = node_to_cell_matrix[node_id]; + + constexpr TinyMatrix<Dimension> R{0, 1, -1, 0}; + + double a_r = 0; + Rd b_r = zero; + + for (size_t i_cell = 0; i_cell < node_cell_list.size(); ++i_cell) { + const size_t i_cell_node = node_number_in_their_cells(node_id, i_cell); + + const CellId cell_id = node_cell_list[i_cell]; + + const auto cell_node_list = cell_to_node_matrix[cell_id]; + const size_t cell_nb_node = cell_node_list.size(); + + const size_t i_next_node = (i_cell_node + 1) % cell_nb_node; + const size_t i_prev_node = (i_cell_node + cell_nb_node - 1) % cell_nb_node; + + const NodeId next_node_id = cell_node_list[i_next_node]; + const NodeId prev_node_id = cell_node_list[i_prev_node]; + + const Rd& xrm1 = given_xr[prev_node_id]; + const Rd& xr = given_xr[node_id]; + const Rd& xrp1 = given_xr[next_node_id]; + + const Rd& a = given_xr[next_node_id]; + const Rd& b = given_xr[prev_node_id]; + + const Rd a_xr = a - xr; + const Rd b_xr = b - xr; + + const double a11 = dot(b_xr, b_xr); + const double a22 = dot(a_xr, a_xr); + const double a12 = -dot(a_xr, b_xr); //(a_xr[0] + a_xr[1]) * (b_xr[0] + b_xr[1]); + + TinyMatrix<Dimension + 1> M; + + M(0, 0) = 0.5 * (a11 + 2 * a12 + a22); + M(0, 1) = 0.5 * (-a11 - a12); + M(0, 2) = 0.5 * (-a12 - a22); + + M(1, 0) = 0.5 * (-a11 - a12); + M(1, 1) = 0.5 * (a11); + M(1, 2) = 0.5 * (a12); + + M(2, 0) = 0.5 * (-a12 - a22); + M(2, 1) = 0.5 * (a12); + M(2, 2) = 0.5 * (a22); + + // const Rd xrm1{1, 3}; + // const Rd xr(1, 1); + // const Rd xrp1(4, 1); + + // const Rd xr_xrp1_perp = R * (xrp1 - xr); + // const Rd xrp1_xrm1_perp = R * (xrm1 - xrp1); + // const Rd xrm1_xr_perp = R * (xr - xrm1); + + // std::cout << "xr_xrp1_perp = " << xr_xrp1_perp << '\n'; + // std::cout << "xrp1_xrm1_perp = " << xrp1_xrm1_perp << '\n'; + // std::cout << "xrm1_xr_perp = " << xrm1_xr_perp << '\n'; + // const Rd Cjr = 0.5 * (xr_xrp1_perp + xrm1_xr_perp); + // const Rd Cjrp1 = 0.5 * (xr_xrp1_perp + xrp1_xrm1_perp); + // const Rd Cjrm1 = 0.5 * (xrm1_xr_perp + xrp1_xrm1_perp); + + // std::cout << "Cjr = " << Cjr << '\n'; + // std::cout << "Cjrp1 = " << Cjrp1 << '\n'; + // std::cout << "Cjrm1 = " << Cjrm1 << '\n'; + + // const double Sj = 0.5 * (dot(Cjr, xr) + dot(Cjrm1, xrm1) + dot(Cjrp1, xrp1)); + + // std::cout << "grad Wjr = " << 1 / Sj * Cjr << '\n'; + // std::cout << "grad Wjrp1 = " << 1 / Sj * Cjrp1 << '\n'; + // std::cout << "grad Wjrm1 = " << 1 / Sj * Cjrm1 << '\n'; + // std::cout << "Sj = " << Sj << '\n'; + + // b_r[0] += M(i_cell_node, i_prev_node) * xrm1[0] + M(i_cell_node, i_next_node) * xrp1[0]; + // b_r[1] += M(i_cell_node, i_prev_node) * xrm1[1] + M(i_cell_node, i_next_node) * xrp1[1]; + // a_r += M(i_cell_node, i_cell_node); + + b_r[0] += M(0, 2) * xrm1[0] + M(0, 1) * xrp1[0]; + b_r[1] += M(0, 2) * xrm1[1] + M(0, 1) * xrp1[1]; + a_r += M(0, 0); + + // std::cout << " ===\n"; + } + + // std::cout << "ar = " << a_r << '\n'; + // std::cout << "br = " << b_r << '\n'; + // std::cout << "a_r = " << a_r << '\n'; + // std::cout << "b_r = " << b_r << '\n'; + if (a_r != 0) { + Rd new_x = -1. / a_r * b_r; + // std::cout << rang::fgB::yellow << "new_x = " << new_x << rang::fg::reset << '\n'; + shift_r[node_id] = new_x - given_xr[node_id]; //-1. / a_r * b_r; + } else { + shift_r[node_id] = zero; + } + // std::cout << " -1. / a_r * b_r = " << -1. / a_r * b_r << '\n'; + // std::cout << " given_xr[" << node_id << "] = " << given_xr[node_id] << '\n'; + // std::cout << "shift[" << node_id << "] = " << shift_r[node_id] << '\n'; + // std::cout << "-------------------\n"; + }); + } else { + throw NotImplementedError("Jun smoother in dimension != 2"); + } + + this->_applyBC(shift_r); + + synchronize(shift_r); + + return shift_r; + } + + public: + std::shared_ptr<const IMesh> + getSmoothedMesh() const + { + NodeValue<const Rd> given_xr = m_given_mesh.xr(); + + NodeValue<Rd> xr = this->_getDisplacement(); + + parallel_for( + m_given_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { xr[node_id] += given_xr[node_id]; }); + + return std::make_shared<MeshType>(m_given_mesh.shared_connectivity(), xr); + } + + std::shared_ptr<const IMesh> + getSmoothedMesh(const FunctionSymbolId& function_symbol_id) const + { + NodeValue<const Rd> given_xr = m_given_mesh.xr(); + + NodeValue<const bool> is_displaced = + InterpolateItemValue<bool(const Rd)>::interpolate(function_symbol_id, given_xr); + + NodeValue<Rd> xr = this->_getDisplacement(); + + parallel_for( + m_given_mesh.numberOfNodes(), + PUGS_LAMBDA(const NodeId node_id) { xr[node_id] = is_displaced[node_id] * xr[node_id] + given_xr[node_id]; }); + + return std::make_shared<MeshType>(m_given_mesh.shared_connectivity(), xr); + } + + std::shared_ptr<const IMesh> + getSmoothedMesh(const std::vector<std::shared_ptr<const IZoneDescriptor>>& zone_descriptor_list) const + { + NodeValue<const Rd> given_xr = m_given_mesh.xr(); + + auto node_to_cell_matrix = m_given_mesh.connectivity().nodeToCellMatrix(); + + NodeValue<bool> is_displaced{m_given_mesh.connectivity()}; + is_displaced.fill(false); + + for (size_t i_zone = 0; i_zone < zone_descriptor_list.size(); ++i_zone) { + MeshCellZone<Dimension> cell_zone = getMeshCellZone(m_given_mesh, *zone_descriptor_list[i_zone]); + const auto cell_list = cell_zone.cellList(); + CellValue<bool> is_zone_cell{m_given_mesh.connectivity()}; + is_zone_cell.fill(false); + parallel_for( + cell_list.size(), PUGS_LAMBDA(const size_t i_cell) { is_zone_cell[cell_list[i_cell]] = true; }); + parallel_for( + m_given_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + auto node_cell_list = node_to_cell_matrix[node_id]; + bool displace = true; + for (size_t i_node_cell = 0; i_node_cell < node_cell_list.size(); ++i_node_cell) { + const CellId cell_id = node_cell_list[i_node_cell]; + displace &= is_zone_cell[cell_id]; + } + if (displace) { + is_displaced[node_id] = true; + } + }); + } + synchronize(is_displaced); + NodeValue<Rd> xr = this->_getDisplacement(); + + parallel_for( + m_given_mesh.numberOfNodes(), + PUGS_LAMBDA(const NodeId node_id) { xr[node_id] = is_displaced[node_id] * xr[node_id] + given_xr[node_id]; }); + + return std::make_shared<MeshType>(m_given_mesh.shared_connectivity(), xr); + } + + std::shared_ptr<const IMesh> + getSmoothedMesh( + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const + { + NodeValue<const Rd> given_xr = m_given_mesh.xr(); + + auto node_to_cell_matrix = m_given_mesh.connectivity().nodeToCellMatrix(); + + NodeValue<bool> is_displaced{m_given_mesh.connectivity()}; + is_displaced.fill(false); + + for (size_t i_zone = 0; i_zone < discrete_function_variant_list.size(); ++i_zone) { + auto is_zone_cell = discrete_function_variant_list[i_zone]->get<DiscreteFunctionP0<Dimension, const double>>(); + + parallel_for( + m_given_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + auto node_cell_list = node_to_cell_matrix[node_id]; + bool displace = true; + for (size_t i_node_cell = 0; i_node_cell < node_cell_list.size(); ++i_node_cell) { + const CellId cell_id = node_cell_list[i_node_cell]; + displace &= (is_zone_cell[cell_id] != 0); + } + if (displace) { + is_displaced[node_id] = true; + } + }); + } + synchronize(is_displaced); + NodeValue<Rd> xr = this->_getDisplacement(); + + parallel_for( + m_given_mesh.numberOfNodes(), + PUGS_LAMBDA(const NodeId node_id) { xr[node_id] = is_displaced[node_id] * xr[node_id] + given_xr[node_id]; }); + + return std::make_shared<MeshType>(m_given_mesh.shared_connectivity(), xr); + } + + MeshSmootherJun(const MeshSmootherJun&) = delete; + MeshSmootherJun(MeshSmootherJun&&) = delete; + + MeshSmootherJun(const MeshType& given_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) + : m_given_mesh(given_mesh), m_boundary_condition_list(this->_getBCList(given_mesh, bc_descriptor_list)) + {} + + ~MeshSmootherJun() = default; +}; + +template <size_t Dimension> +class MeshSmootherJunHandler::MeshSmootherJun<Dimension>::AxisBoundaryCondition +{ + public: + using Rd = TinyVector<Dimension, double>; + + private: + const MeshLineNodeBoundary<Dimension> m_mesh_line_node_boundary; + + public: + const Rd& + direction() const + { + return m_mesh_line_node_boundary.direction(); + } + + const Array<const NodeId>& + nodeList() const + { + return m_mesh_line_node_boundary.nodeList(); + } + + AxisBoundaryCondition(MeshLineNodeBoundary<Dimension>&& mesh_line_node_boundary) + : m_mesh_line_node_boundary(mesh_line_node_boundary) + { + ; + } + + ~AxisBoundaryCondition() = default; +}; + +template <> +class MeshSmootherJunHandler::MeshSmootherJun<1>::AxisBoundaryCondition +{ + public: + AxisBoundaryCondition() = default; + ~AxisBoundaryCondition() = default; +}; + +template <size_t Dimension> +class MeshSmootherJunHandler::MeshSmootherJun<Dimension>::FixedBoundaryCondition +{ + private: + const MeshNodeBoundary<Dimension> m_mesh_node_boundary; + + public: + const Array<const NodeId>& + nodeList() const + { + return m_mesh_node_boundary.nodeList(); + } + + FixedBoundaryCondition(MeshNodeBoundary<Dimension>&& mesh_node_boundary) : m_mesh_node_boundary{mesh_node_boundary} {} + + ~FixedBoundaryCondition() = default; +}; + +template <size_t Dimension> +class MeshSmootherJunHandler::MeshSmootherJun<Dimension>::SymmetryBoundaryCondition +{ + public: + using Rd = TinyVector<Dimension, double>; + + private: + const MeshFlatNodeBoundary<Dimension> m_mesh_flat_node_boundary; + + public: + const Rd& + outgoingNormal() const + { + return m_mesh_flat_node_boundary.outgoingNormal(); + } + + const Array<const NodeId>& + nodeList() const + { + return m_mesh_flat_node_boundary.nodeList(); + } + + SymmetryBoundaryCondition(MeshFlatNodeBoundary<Dimension>&& mesh_flat_node_boundary) + : m_mesh_flat_node_boundary(mesh_flat_node_boundary) + { + ; + } + + ~SymmetryBoundaryCondition() = default; +}; + +std::shared_ptr<const IMesh> +MeshSmootherJunHandler::getSmoothedMesh( + const std::shared_ptr<const IMesh>& mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const +{ + switch (mesh->dimension()) { + case 1: { + constexpr size_t Dimension = 1; + using MeshType = Mesh<Connectivity<Dimension>>; + MeshSmootherJun smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list); + return smoother.getSmoothedMesh(); + } + case 2: { + constexpr size_t Dimension = 2; + using MeshType = Mesh<Connectivity<Dimension>>; + MeshSmootherJun smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list); + return smoother.getSmoothedMesh(); + } + case 3: { + constexpr size_t Dimension = 3; + using MeshType = Mesh<Connectivity<Dimension>>; + MeshSmootherJun smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list); + return smoother.getSmoothedMesh(); + } + default: { + throw UnexpectedError("invalid mesh dimension"); + } + } +} + +std::shared_ptr<const IMesh> +MeshSmootherJunHandler::getSmoothedMesh( + const std::shared_ptr<const IMesh>& mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const FunctionSymbolId& function_symbol_id) const +{ + switch (mesh->dimension()) { + case 1: { + constexpr size_t Dimension = 1; + using MeshType = Mesh<Connectivity<Dimension>>; + MeshSmootherJun smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list); + return smoother.getSmoothedMesh(function_symbol_id); + } + case 2: { + constexpr size_t Dimension = 2; + using MeshType = Mesh<Connectivity<Dimension>>; + MeshSmootherJun smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list); + return smoother.getSmoothedMesh(function_symbol_id); + } + case 3: { + constexpr size_t Dimension = 3; + using MeshType = Mesh<Connectivity<Dimension>>; + MeshSmootherJun smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list); + return smoother.getSmoothedMesh(function_symbol_id); + } + default: { + throw UnexpectedError("invalid mesh dimension"); + } + } +} + +std::shared_ptr<const IMesh> +MeshSmootherJunHandler::getSmoothedMesh( + const std::shared_ptr<const IMesh>& mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::vector<std::shared_ptr<const IZoneDescriptor>>& smoothing_zone_list) const +{ + switch (mesh->dimension()) { + case 1: { + constexpr size_t Dimension = 1; + using MeshType = Mesh<Connectivity<Dimension>>; + MeshSmootherJun smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list); + return smoother.getSmoothedMesh(smoothing_zone_list); + } + case 2: { + constexpr size_t Dimension = 2; + using MeshType = Mesh<Connectivity<Dimension>>; + MeshSmootherJun smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list); + return smoother.getSmoothedMesh(smoothing_zone_list); + } + case 3: { + constexpr size_t Dimension = 3; + using MeshType = Mesh<Connectivity<Dimension>>; + MeshSmootherJun smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list); + return smoother.getSmoothedMesh(smoothing_zone_list); + } + default: { + throw UnexpectedError("invalid mesh dimension"); + } + } +} + +std::shared_ptr<const IMesh> +MeshSmootherJunHandler::getSmoothedMesh( + const std::shared_ptr<const IMesh>& mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const +{ + if (not hasSameMesh(discrete_function_variant_list)) { + throw NormalError("discrete functions are not defined on the same mesh"); + } + + std::shared_ptr<const IMesh> common_mesh = getCommonMesh(discrete_function_variant_list); + + if (common_mesh != mesh) { + throw NormalError("discrete functions are not defined on the smoothed mesh"); + } + + switch (mesh->dimension()) { + case 1: { + constexpr size_t Dimension = 1; + using MeshType = Mesh<Connectivity<Dimension>>; + MeshSmootherJun smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list); + return smoother.getSmoothedMesh(discrete_function_variant_list); + } + case 2: { + constexpr size_t Dimension = 2; + using MeshType = Mesh<Connectivity<Dimension>>; + MeshSmootherJun smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list); + return smoother.getSmoothedMesh(discrete_function_variant_list); + } + case 3: { + constexpr size_t Dimension = 3; + using MeshType = Mesh<Connectivity<Dimension>>; + MeshSmootherJun smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list); + return smoother.getSmoothedMesh(discrete_function_variant_list); + } + default: { + throw UnexpectedError("invalid mesh dimension"); + } + } +} diff --git a/src/mesh/MeshSmootherJun.hpp b/src/mesh/MeshSmootherJun.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a9a34438fdeec2fee1108d41c319d7ca5c33acc3 --- /dev/null +++ b/src/mesh/MeshSmootherJun.hpp @@ -0,0 +1,45 @@ +#ifndef MESH_SMOOTHER_JUN_HPP +#define MESH_SMOOTHER_JUN_HPP + +#include <mesh/IMesh.hpp> +#include <scheme/IBoundaryConditionDescriptor.hpp> + +#include <memory> +#include <vector> + +class FunctionSymbolId; +class IZoneDescriptor; +class DiscreteFunctionVariant; + +class MeshSmootherJunHandler +{ + private: + template <size_t Dimension> + class MeshSmootherJun; + + public: + std::shared_ptr<const IMesh> getSmoothedMesh( + const std::shared_ptr<const IMesh>& mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const; + + std::shared_ptr<const IMesh> getSmoothedMesh( + const std::shared_ptr<const IMesh>& mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const FunctionSymbolId& function_symbol_id) const; + + std::shared_ptr<const IMesh> getSmoothedMesh( + const std::shared_ptr<const IMesh>& mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::vector<std::shared_ptr<const IZoneDescriptor>>& smoothing_zone_list) const; + + std::shared_ptr<const IMesh> getSmoothedMesh( + const std::shared_ptr<const IMesh>& mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& smoothing_zone_list) const; + + MeshSmootherJunHandler() = default; + MeshSmootherJunHandler(MeshSmootherJunHandler&&) = default; + ~MeshSmootherJunHandler() = default; +}; + +#endif // MESH_SMOOTHER_JUN_HPP