diff --git a/src/algebra/CMakeLists.txt b/src/algebra/CMakeLists.txt index 6310f48dcf69eb93e2c1178e05d32dbc7fe5afad..58b2548ffef0ec4da9e919697627929eb2b1e29e 100644 --- a/src/algebra/CMakeLists.txt +++ b/src/algebra/CMakeLists.txt @@ -2,6 +2,7 @@ add_library( PugsAlgebra + LeastSquareSolver.cpp EigenvalueSolver.cpp LinearSolver.cpp LinearSolverOptions.cpp diff --git a/src/algebra/LeastSquareSolver.cpp b/src/algebra/LeastSquareSolver.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d376881c629a66d6c2864d882956ee83cd4fc5de --- /dev/null +++ b/src/algebra/LeastSquareSolver.cpp @@ -0,0 +1,27 @@ +#include <algebra/LeastSquareSolver.hpp> + +#include <algebra/CG.hpp> + +template <typename T> +void +LeastSquareSolver::solveLocalSystem(const SmallMatrix<T>& A, SmallVector<T>& x, const SmallVector<T>& b) +{ + if (A.numberOfRows() >= A.numberOfColumns()) { + const SmallMatrix tA = transpose(A); + const SmallMatrix B = tA * A; + const SmallVector y = tA * b; + CG{B, x, y, 1e-12, 10, false}; + } else { + const SmallMatrix tA = transpose(A); + const SmallMatrix B = A * tA; + SmallVector<double> y{A.numberOfRows()}; + y = zero; + CG{B, y, b, 1e-12, 10, false}; + + x = tA * y; + } +} + +template void LeastSquareSolver::solveLocalSystem(const SmallMatrix<double>&, + SmallVector<double>&, + const SmallVector<double>&); diff --git a/src/algebra/LeastSquareSolver.hpp b/src/algebra/LeastSquareSolver.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b23ec9b7d160c43ee7a4f9e1b38251c8489a9f20 --- /dev/null +++ b/src/algebra/LeastSquareSolver.hpp @@ -0,0 +1,17 @@ +#ifndef LEAST_SQUARE_SOLVER_HPP +#define LEAST_SQUARE_SOLVER_HPP + +#include <algebra/SmallMatrix.hpp> +#include <algebra/SmallVector.hpp> + +class LeastSquareSolver +{ + public: + template <typename T> + void solveLocalSystem(const SmallMatrix<T>& A, SmallVector<T>& x, const SmallVector<T>& b); + + LeastSquareSolver() = default; + ~LeastSquareSolver() = default; +}; + +#endif // LEAST_SQUARE_SOLVER_HPP diff --git a/src/language/algorithms/CMakeLists.txt b/src/language/algorithms/CMakeLists.txt index 63c2374987289e7e5c7352dbc773d77ef4e675fb..50783e8ea26f53d4eb5e2b9df92447b1ddd134fc 100644 --- a/src/language/algorithms/CMakeLists.txt +++ b/src/language/algorithms/CMakeLists.txt @@ -1,7 +1,13 @@ # ------------------- Source files -------------------- add_library(PugsLanguageAlgorithms - INTERFACE # THIS SHOULD BE REMOVED IF FILES ARE FINALY PROVIDED + ElasticityDiamondAlgorithm.cpp + UnsteadyElasticity.cpp + Heat5PointsAlgorithm.cpp + HeatDiamondAlgorithm.cpp + HeatDiamondAlgorithm2.cpp + ParabolicHeat.cpp +# INTERFACE # THIS SHOULD BE REMOVED IF FILES ARE FINALY PROVIDED ) diff --git a/src/language/algorithms/ElasticityDiamondAlgorithm.cpp b/src/language/algorithms/ElasticityDiamondAlgorithm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b14557ebd7599b9375c12d8ebe6f03311a9342fd --- /dev/null +++ b/src/language/algorithms/ElasticityDiamondAlgorithm.cpp @@ -0,0 +1,874 @@ +#include <language/algorithms/ElasticityDiamondAlgorithm.hpp> + +#include <algebra/CRSMatrix.hpp> +#include <algebra/CRSMatrixDescriptor.hpp> +#include <algebra/LeastSquareSolver.hpp> +#include <algebra/LinearSolver.hpp> +#include <algebra/SmallMatrix.hpp> +#include <algebra/TinyVector.hpp> +#include <algebra/Vector.hpp> +#include <language/utils/InterpolateItemValue.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/DualConnectivityManager.hpp> +#include <mesh/DualMeshManager.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/MeshFaceBoundary.hpp> +#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp> +#include <scheme/DirichletBoundaryConditionDescriptor.hpp> +#include <scheme/NeumannBoundaryConditionDescriptor.hpp> +#include <scheme/SymmetryBoundaryConditionDescriptor.hpp> + +template <size_t Dimension> +ElasticityDiamondScheme<Dimension>::ElasticityDiamondScheme( + std::shared_ptr<const IMesh> i_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const FunctionSymbolId& lambda_id, + const FunctionSymbolId& mu_id, + const FunctionSymbolId& f_id, + const FunctionSymbolId& U_id) +{ + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + using MeshDataType = MeshData<Dimension>; + + using BoundaryCondition = + std::variant<DirichletBoundaryCondition, NormalStrainBoundaryCondition, SymmetryBoundaryCondition>; + + using BoundaryConditionList = std::vector<BoundaryCondition>; + + BoundaryConditionList boundary_condition_list; + + std::cout << "number of bc descr = " << bc_descriptor_list.size() << '\n'; + std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + + NodeValue<bool> is_dirichlet{mesh->connectivity()}; + is_dirichlet.fill(false); + NodeValue<TinyVector<Dimension>> dirichlet_value{mesh->connectivity()}; + { + TinyVector<Dimension> nan_tiny_vector; + for (size_t i = 0; i < Dimension; ++i) { + nan_tiny_vector[i] = std::numeric_limits<double>::signaling_NaN(); + } + dirichlet_value.fill(nan_tiny_vector); + } + + for (const auto& bc_descriptor : bc_descriptor_list) { + bool is_valid_boundary_condition = true; + + switch (bc_descriptor->type()) { + case IBoundaryConditionDescriptor::Type::symmetry: { + const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor = + dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor); + if constexpr (Dimension > 1) { + MeshFaceBoundary mesh_face_boundary = getMeshFaceBoundary(*mesh, sym_bc_descriptor.boundaryDescriptor()); + boundary_condition_list.push_back(SymmetryBoundaryCondition{mesh_face_boundary.faceList()}); + + } else { + throw NotImplementedError("Symmetry conditions are not supported in 1d"); + } + + break; + } + case IBoundaryConditionDescriptor::Type::dirichlet: { + const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor = + dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor); + if (dirichlet_bc_descriptor.name() == "dirichlet") { + if constexpr (Dimension > 1) { + MeshFaceBoundary mesh_face_boundary = + getMeshFaceBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId(); + + Array<const TinyVector<Dimension>> value_list = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, mesh_data.xl(), + mesh_face_boundary.faceList()); + + boundary_condition_list.push_back(DirichletBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + } else { + throw NotImplementedError("Dirichlet conditions are not supported in 1d"); + } + } else if (dirichlet_bc_descriptor.name() == "normal_strain") { + if constexpr (Dimension > 1) { + MeshFaceBoundary mesh_face_boundary = + getMeshFaceBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId(); + + Array<const TinyVector<Dimension>> value_list = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, mesh_data.xl(), + mesh_face_boundary.faceList()); + boundary_condition_list.push_back(NormalStrainBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + } else { + throw NotImplementedError("Normal strain conditions are not supported in 1d"); + } + + } else { + is_valid_boundary_condition = false; + } + break; + } + default: { + is_valid_boundary_condition = false; + } + } + if (not is_valid_boundary_condition) { + std::ostringstream error_msg; + error_msg << *bc_descriptor << " is an invalid boundary condition for elasticity equation"; + throw NormalError(error_msg.str()); + } + } + + if constexpr (Dimension > 1) { + const CellValue<const size_t> cell_dof_number = [&] { + CellValue<size_t> compute_cell_dof_number{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { compute_cell_dof_number[cell_id] = cell_id; }); + return compute_cell_dof_number; + }(); + size_t number_of_dof = mesh->numberOfCells(); + + const FaceValue<const size_t> face_dof_number = [&] { + FaceValue<size_t> compute_face_dof_number{mesh->connectivity()}; + compute_face_dof_number.fill(std::numeric_limits<size_t>::max()); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NormalStrainBoundaryCondition>) or + (std::is_same_v<T, SymmetryBoundaryCondition>) or + (std::is_same_v<T, DirichletBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + if (compute_face_dof_number[face_id] != std::numeric_limits<size_t>::max()) { + std::ostringstream os; + os << "The face " << face_id << " is used at least twice for boundary conditions"; + throw NormalError(os.str()); + } else { + compute_face_dof_number[face_id] = number_of_dof++; + } + } + } + }, + boundary_condition); + } + + return compute_face_dof_number; + }(); + + const auto& primal_face_to_node_matrix = mesh->connectivity().faceToNodeMatrix(); + const auto& face_to_cell_matrix = mesh->connectivity().faceToCellMatrix(); + const FaceValue<const bool> primal_face_is_neumann = [&] { + FaceValue<bool> face_is_neumann{mesh->connectivity()}; + face_is_neumann.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NormalStrainBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + face_is_neumann[face_id] = true; + } + } + }, + boundary_condition); + } + + return face_is_neumann; + }(); + + const FaceValue<const bool> primal_face_is_symmetry = [&] { + FaceValue<bool> face_is_symmetry{mesh->connectivity()}; + face_is_symmetry.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, SymmetryBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + face_is_symmetry[face_id] = true; + } + } + }, + boundary_condition); + } + + return face_is_symmetry; + }(); + + NodeValue<bool> primal_node_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of node_is_on_boundary is incorrect"); + } + + primal_node_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + primal_node_is_on_boundary[node_id] = true; + } + } + } + + FaceValue<bool> primal_face_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_on_boundary is incorrect"); + } + + primal_face_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + primal_face_is_on_boundary[face_id] = true; + } + } + + FaceValue<bool> primal_face_is_dirichlet(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_neumann is incorrect"); + } + + primal_face_is_dirichlet.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + primal_face_is_dirichlet[face_id] = (primal_face_is_on_boundary[face_id] && (!primal_face_is_neumann[face_id]) && + (!primal_face_is_symmetry[face_id])); + } + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + const NodeValue<const TinyVector<Dimension>>& xr = mesh->xr(); + + const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl(); + const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj(); + const auto& node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); + const auto& node_to_face_matrix = mesh->connectivity().nodeToFaceMatrix(); + CellValuePerNode<double> w_rj{mesh->connectivity()}; + FaceValuePerNode<double> w_rl{mesh->connectivity()}; + + const NodeValuePerFace<const TinyVector<Dimension>> primal_nlr = mesh_data.nlr(); + auto project_to_face = [&](const TinyVector<Dimension>& x, const FaceId face_id) -> const TinyVector<Dimension> { + TinyVector<Dimension> proj; + const TinyVector<Dimension> nil = primal_nlr(face_id, 0); + proj = x - dot((x - xl[face_id]), nil) * nil; + return proj; + }; + + for (size_t i = 0; i < w_rl.numberOfValues(); ++i) { + w_rl[i] = std::numeric_limits<double>::signaling_NaN(); + } + + for (NodeId i_node = 0; i_node < mesh->numberOfNodes(); ++i_node) { + SmallVector<double> b{Dimension + 1}; + b[0] = 1; + for (size_t i = 1; i < Dimension + 1; i++) { + b[i] = xr[i_node][i - 1]; + } + const auto& node_to_cell = node_to_cell_matrix[i_node]; + + if (not primal_node_is_on_boundary[i_node]) { + SmallMatrix<double> A{Dimension + 1, node_to_cell.size()}; + for (size_t j = 0; j < node_to_cell.size(); j++) { + A(0, j) = 1; + } + for (size_t i = 1; i < Dimension + 1; i++) { + for (size_t j = 0; j < node_to_cell.size(); j++) { + const CellId J = node_to_cell[j]; + A(i, j) = xj[J][i - 1]; + } + } + + SmallVector<double> x{node_to_cell.size()}; + x = zero; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + for (size_t j = 0; j < node_to_cell.size(); j++) { + w_rj(i_node, j) = x[j]; + } + } else { + int nb_face_used = 0; + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (primal_face_is_on_boundary[face_id]) { + nb_face_used++; + } + } + SmallMatrix<double> A{Dimension + 1, node_to_cell.size() + nb_face_used}; + for (size_t j = 0; j < node_to_cell.size() + nb_face_used; j++) { + A(0, j) = 1; + } + for (size_t i = 1; i < Dimension + 1; i++) { + for (size_t j = 0; j < node_to_cell.size(); j++) { + const CellId J = node_to_cell[j]; + A(i, j) = xj[J][i - 1]; + } + } + for (size_t i = 1; i < Dimension + 1; i++) { + int cpt_face = 0; + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (primal_face_is_on_boundary[face_id]) { + if (primal_face_is_symmetry[face_id]) { + for (size_t j = 0; j < face_to_cell_matrix[face_id].size(); ++j) { + const CellId cell_id = face_to_cell_matrix[face_id][j]; + TinyVector<Dimension> xproj = project_to_face(xj[cell_id], face_id); + A(i, node_to_cell.size() + cpt_face) = xproj[i - 1]; + } + } else { + A(i, node_to_cell.size() + cpt_face) = xl[face_id][i - 1]; + } + cpt_face++; + } + } + } + + SmallVector<double> x{node_to_cell.size() + nb_face_used}; + x = zero; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + for (size_t j = 0; j < node_to_cell.size(); j++) { + w_rj(i_node, j) = x[j]; + } + int cpt_face = node_to_cell.size(); + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (primal_face_is_on_boundary[face_id]) { + w_rl(i_node, i_face) = x[cpt_face++]; + } + } + } + } + + { + std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh); + + MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh); + + std::shared_ptr mapper = + DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity()); + + CellValue<double> dual_muj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(mu_id, + diamond_mesh_data + .xj()); + + CellValue<double> dual_lambdaj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(lambda_id, + diamond_mesh_data + .xj()); + + CellValue<TinyVector<Dimension>> Uj = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::cell>(U_id, mesh_data.xj()); + + CellValue<TinyVector<Dimension>> fj = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::cell>(f_id, mesh_data.xj()); + + const CellValue<const double> dual_Vj = diamond_mesh_data.Vj(); + + const FaceValue<const double> mes_l = [&] { + if constexpr (Dimension == 1) { + FaceValue<double> compute_mes_l{mesh->connectivity()}; + compute_mes_l.fill(1); + return compute_mes_l; + } else { + return mesh_data.ll(); + } + }(); + + const CellValue<const double> dual_mes_l_j = [=] { + CellValue<double> compute_mes_j{diamond_mesh->connectivity()}; + mapper->toDualCell(mes_l, compute_mes_j); + + return compute_mes_j; + }(); + + const CellValue<const double> primal_Vj = mesh_data.Vj(); + FaceValue<const CellId> face_dual_cell_id = [=]() { + FaceValue<CellId> computed_face_dual_cell_id{mesh->connectivity()}; + CellValue<CellId> dual_cell_id{diamond_mesh->connectivity()}; + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { dual_cell_id[cell_id] = cell_id; }); + + mapper->fromDualCell(dual_cell_id, computed_face_dual_cell_id); + + return computed_face_dual_cell_id; + }(); + + NodeValue<const NodeId> dual_node_primal_node_id = [=]() { + CellValue<NodeId> cell_ignored_id{mesh->connectivity()}; + cell_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()}); + + NodeValue<NodeId> node_primal_id{mesh->connectivity()}; + + parallel_for( + mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { node_primal_id[node_id] = node_id; }); + + NodeValue<NodeId> computed_dual_node_primal_node_id{diamond_mesh->connectivity()}; + + mapper->toDualNode(node_primal_id, cell_ignored_id, computed_dual_node_primal_node_id); + + return computed_dual_node_primal_node_id; + }(); + + CellValue<NodeId> primal_cell_dual_node_id = [=]() { + CellValue<NodeId> cell_id{mesh->connectivity()}; + NodeValue<NodeId> node_ignored_id{mesh->connectivity()}; + node_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()}); + + NodeValue<NodeId> dual_node_id{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { dual_node_id[node_id] = node_id; }); + + CellValue<NodeId> computed_primal_cell_dual_node_id{mesh->connectivity()}; + + mapper->fromDualNode(dual_node_id, node_ignored_id, cell_id); + + return cell_id; + }(); + const auto& dual_Cjr = diamond_mesh_data.Cjr(); + FaceValue<TinyVector<Dimension>> dualClj = [&] { + FaceValue<TinyVector<Dimension>> computedClj{mesh->connectivity()}; + const auto& dual_node_to_cell_matrix = diamond_mesh->connectivity().nodeToCellMatrix(); + const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix(); + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { + const auto& primal_face_to_cell = face_to_cell_matrix[face_id]; + for (size_t i = 0; i < primal_face_to_cell.size(); i++) { + CellId cell_id = primal_face_to_cell[i]; + const NodeId dual_node_id = primal_cell_dual_node_id[cell_id]; + for (size_t i_dual_cell = 0; i_dual_cell < dual_node_to_cell_matrix[dual_node_id].size(); i_dual_cell++) { + const CellId dual_cell_id = dual_node_to_cell_matrix[dual_node_id][i_dual_cell]; + if (face_dual_cell_id[face_id] == dual_cell_id) { + for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); + i_dual_node++) { + const NodeId final_dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node]; + if (final_dual_node_id == dual_node_id) { + computedClj[face_id] = dual_Cjr(dual_cell_id, i_dual_node); + } + } + } + } + } + }); + return computedClj; + }(); + + FaceValue<TinyVector<Dimension>> nlj = [&] { + FaceValue<TinyVector<Dimension>> computedNlj{mesh->connectivity()}; + parallel_for( + mesh->numberOfFaces(), + PUGS_LAMBDA(FaceId face_id) { computedNlj[face_id] = 1. / l2Norm(dualClj[face_id]) * dualClj[face_id]; }); + return computedNlj; + }(); + + FaceValue<const double> alpha_lambda_l = [&] { + CellValue<double> alpha_j{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + alpha_j[diamond_cell_id] = dual_lambdaj[diamond_cell_id] / dual_Vj[diamond_cell_id]; + }); + + FaceValue<double> computed_alpha_l{mesh->connectivity()}; + mapper->fromDualCell(alpha_j, computed_alpha_l); + return computed_alpha_l; + }(); + + FaceValue<const double> alpha_mu_l = [&] { + CellValue<double> alpha_j{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + alpha_j[diamond_cell_id] = dual_muj[diamond_cell_id] / dual_Vj[diamond_cell_id]; + }); + + FaceValue<double> computed_alpha_l{mesh->connectivity()}; + mapper->fromDualCell(alpha_j, computed_alpha_l); + return computed_alpha_l; + }(); + + const TinyMatrix<Dimension> I = identity; + + const Array<int> non_zeros{number_of_dof * Dimension}; + non_zeros.fill(Dimension * Dimension); + CRSMatrixDescriptor<double> S(number_of_dof * Dimension, number_of_dof * Dimension, non_zeros); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + const double beta_mu_l = l2Norm(dualClj[face_id]) * alpha_mu_l[face_id] * mes_l[face_id]; + const double beta_lambda_l = l2Norm(dualClj[face_id]) * alpha_lambda_l[face_id] * mes_l[face_id]; + const auto& primal_face_to_cell = face_to_cell_matrix[face_id]; + for (size_t i_cell = 0; i_cell < primal_face_to_cell.size(); ++i_cell) { + const CellId i_id = primal_face_to_cell[i_cell]; + const bool is_face_reversed_for_cell_i = (dot(dualClj[face_id], xl[face_id] - xj[i_id]) < 0); + + const TinyVector<Dimension> nil = [&] { + if (is_face_reversed_for_cell_i) { + return -nlj[face_id]; + } else { + return nlj[face_id]; + } + }(); + for (size_t j_cell = 0; j_cell < primal_face_to_cell.size(); ++j_cell) { + const CellId j_id = primal_face_to_cell[j_cell]; + TinyMatrix<Dimension> M = + beta_mu_l * I + beta_mu_l * tensorProduct(nil, nil) + beta_lambda_l * tensorProduct(nil, nil); + TinyMatrix<Dimension> N = tensorProduct(nil, nil); + + if (i_cell == j_cell) { + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + S((cell_dof_number[i_id] * Dimension) + i, (cell_dof_number[j_id] * Dimension) + j) += M(i, j); + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id] * Dimension + i, cell_dof_number[j_id] * Dimension + j) -= M(i, j); + } + if (primal_face_is_symmetry[face_id]) { + S(face_dof_number[face_id] * Dimension + i, cell_dof_number[j_id] * Dimension + j) += + -((i == j) ? 1 : 0) + N(i, j); + S(face_dof_number[face_id] * Dimension + i, face_dof_number[face_id] * Dimension + j) += + (i == j) ? 1 : 0; + } + } + } + } else { + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + S((cell_dof_number[i_id] * Dimension) + i, (cell_dof_number[j_id] * Dimension) + j) -= M(i, j); + } + } + } + } + } + } + + const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix(); + const auto& primal_node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + const double alpha_mu_face_id = mes_l[face_id] * alpha_mu_l[face_id]; + const double alpha_lambda_face_id = mes_l[face_id] * alpha_lambda_l[face_id]; + + for (size_t i_face_cell = 0; i_face_cell < face_to_cell_matrix[face_id].size(); ++i_face_cell) { + CellId i_id = face_to_cell_matrix[face_id][i_face_cell]; + const bool is_face_reversed_for_cell_i = (dot(dualClj[face_id], xl[face_id] - xj[i_id]) < 0); + + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + + const TinyVector<Dimension> nil = [&] { + if (is_face_reversed_for_cell_i) { + return -nlj[face_id]; + } else { + return nlj[face_id]; + } + }(); + + CellId dual_cell_id = face_dual_cell_id[face_id]; + + for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); ++i_dual_node) { + const NodeId dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node]; + if (dual_node_primal_node_id[dual_node_id] == node_id) { + const TinyVector<Dimension> Clr = dual_Cjr(dual_cell_id, i_dual_node); + + TinyMatrix<Dimension> M = alpha_mu_face_id * dot(Clr, nil) * I + + alpha_mu_face_id * tensorProduct(Clr, nil) + + alpha_lambda_face_id * tensorProduct(nil, Clr); + + for (size_t j_cell = 0; j_cell < primal_node_to_cell_matrix[node_id].size(); ++j_cell) { + CellId j_id = primal_node_to_cell_matrix[node_id][j_cell]; + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + S((cell_dof_number[i_id] * Dimension) + i, (cell_dof_number[j_id] * Dimension) + j) -= + w_rj(node_id, j_cell) * M(i, j); + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id] * Dimension + i, cell_dof_number[j_id] * Dimension + j) += + w_rj(node_id, j_cell) * M(i, j); + } + } + } + } + if (primal_node_is_on_boundary[node_id]) { + for (size_t l_face = 0; l_face < node_to_face_matrix[node_id].size(); ++l_face) { + FaceId l_id = node_to_face_matrix[node_id][l_face]; + if (primal_face_is_on_boundary[l_id]) { + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + S(cell_dof_number[i_id] * Dimension + i, face_dof_number[l_id] * Dimension + j) -= + w_rl(node_id, l_face) * M(i, j); + } + } + if (primal_face_is_neumann[face_id]) { + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + S(face_dof_number[face_id] * Dimension + i, face_dof_number[l_id] * Dimension + j) += + w_rl(node_id, l_face) * M(i, j); + } + } + } + } + } + } + } + } + // } + } + } + } + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (primal_face_is_dirichlet[face_id]) { + for (size_t i = 0; i < Dimension; ++i) { + S(face_dof_number[face_id] * Dimension + i, face_dof_number[face_id] * Dimension + i) += 1; + } + } + } + + CRSMatrix A{S.getCRSMatrix()}; + Vector<double> b{number_of_dof * Dimension}; + b = zero; + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + for (size_t i = 0; i < Dimension; ++i) { + b[(cell_dof_number[cell_id] * Dimension) + i] = primal_Vj[cell_id] * fj[cell_id][i]; + } + } + + // Dirichlet + NodeValue<bool> node_tag{mesh->connectivity()}; + node_tag.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<T, DirichletBoundaryCondition>) { + const auto& face_list = bc.faceList(); + const auto& value_list = bc.valueList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + + for (size_t i = 0; i < Dimension; ++i) { + b[(face_dof_number[face_id] * Dimension) + i] += value_list[i_face][i]; + } + } + } + }, + boundary_condition); + } + + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NormalStrainBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + const auto& value_list = bc.valueList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + FaceId face_id = face_list[i_face]; + for (size_t i = 0; i < Dimension; ++i) { + b[face_dof_number[face_id] * Dimension + i] += mes_l[face_id] * value_list[i_face][i]; // sign + } + } + } + }, + boundary_condition); + } + + Vector<double> U{number_of_dof * Dimension}; + U = zero; + CellValue<TinyVector<Dimension>> Speed{mesh->connectivity()}; + FaceValue<TinyVector<Dimension>> Ul = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::face>(U_id, mesh_data.xl()); + FaceValue<TinyVector<Dimension>> Speed_face{mesh->connectivity()}; + + Vector r = A * U - b; + std::cout << "initial (real) residu = " << std::sqrt(dot(r, r)) << '\n'; + + LinearSolver solver; + solver.solveLocalSystem(A, U, b); + + r = A * U - b; + + std::cout << "final (real) residu = " << std::sqrt(dot(r, r)) << '\n'; + + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + for (size_t i = 0; i < Dimension; ++i) { + Speed[cell_id][i] = U[(cell_dof_number[cell_id] * Dimension) + i]; + } + } + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + for (size_t i = 0; i < Dimension; ++i) { + if (primal_face_is_on_boundary[face_id]) { + Speed_face[face_id][i] = U[(face_dof_number[face_id] * Dimension) + i]; + } else { + Speed_face[face_id][i] = Ul[face_id][i]; + } + } + } + Vector<double> Uexacte{mesh->numberOfCells() * Dimension}; + for (CellId j = 0; j < mesh->numberOfCells(); ++j) { + for (size_t l = 0; l < Dimension; ++l) { + Uexacte[(cell_dof_number[j] * Dimension) + l] = Uj[j][l]; + } + } + + Vector<double> error{mesh->numberOfCells() * Dimension}; + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + for (size_t i = 0; i < Dimension; ++i) { + error[(cell_id * Dimension) + i] = (Speed[cell_id][i] - Uj[cell_id][i]) * sqrt(primal_Vj[cell_id]); + } + } + Vector<double> error_face{mesh->numberOfFaces() * Dimension}; + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { + if (primal_face_is_on_boundary[face_id]) { + for (size_t i = 0; i < Dimension; ++i) { + error_face[face_id * Dimension + i] = (Speed_face[face_id][i] - Ul[face_id][i]) * sqrt(mes_l[face_id]); + } + } else { + error_face[face_id] = 0; + } + }); + + std::cout << "||Error||_2 (cell)= " << std::sqrt(dot(error, error)) << "\n"; + std::cout << "||Error||_2 (face)= " << std::sqrt(dot(error_face, error_face)) << "\n"; + std::cout << "||Error||_2 (total)= " << std::sqrt(dot(error, error)) + std::sqrt(dot(error_face, error_face)) + << "\n"; + + NodeValue<TinyVector<3>> ur3d{mesh->connectivity()}; + ur3d.fill(zero); + + parallel_for( + mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { + TinyVector<Dimension> x = zero; + const auto node_cells = node_to_cell_matrix[node_id]; + for (size_t i_cell = 0; i_cell < node_cells.size(); ++i_cell) { + CellId cell_id = node_cells[i_cell]; + x += w_rj(node_id, i_cell) * Speed[cell_id]; + } + const auto node_faces = node_to_face_matrix[node_id]; + for (size_t i_face = 0; i_face < node_faces.size(); ++i_face) { + FaceId face_id = node_faces[i_face]; + if (primal_face_is_on_boundary[face_id]) { + x += w_rl(node_id, i_face) * Speed_face[face_id]; + } + } + for (size_t i = 0; i < Dimension; ++i) { + ur3d[node_id][i] = x[i]; + } + }); + } + } else { + throw NotImplementedError("not done in 1d"); + } +} + +template <size_t Dimension> +class ElasticityDiamondScheme<Dimension>::DirichletBoundaryCondition +{ + private: + const Array<const TinyVector<Dimension>> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const TinyVector<Dimension>>& + valueList() const + { + return m_value_list; + } + + DirichletBoundaryCondition(const Array<const FaceId>& face_list, const Array<const TinyVector<Dimension>>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~DirichletBoundaryCondition() = default; +}; + +template <size_t Dimension> +class ElasticityDiamondScheme<Dimension>::NormalStrainBoundaryCondition +{ + private: + const Array<const TinyVector<Dimension>> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const TinyVector<Dimension>>& + valueList() const + { + return m_value_list; + } + + NormalStrainBoundaryCondition(const Array<const FaceId>& face_list, + const Array<const TinyVector<Dimension>>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~NormalStrainBoundaryCondition() = default; +}; + +template <size_t Dimension> +class ElasticityDiamondScheme<Dimension>::SymmetryBoundaryCondition +{ + private: + const Array<const TinyVector<Dimension>> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + public: + SymmetryBoundaryCondition(const Array<const FaceId>& face_list) : m_face_list{face_list} {} + + ~SymmetryBoundaryCondition() = default; +}; + +template ElasticityDiamondScheme<1>::ElasticityDiamondScheme( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&); + +template ElasticityDiamondScheme<2>::ElasticityDiamondScheme( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&); + +template ElasticityDiamondScheme<3>::ElasticityDiamondScheme( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&); diff --git a/src/language/algorithms/ElasticityDiamondAlgorithm.hpp b/src/language/algorithms/ElasticityDiamondAlgorithm.hpp new file mode 100644 index 0000000000000000000000000000000000000000..24988916ec799ecaa3e227ebc39d54878bfef8d3 --- /dev/null +++ b/src/language/algorithms/ElasticityDiamondAlgorithm.hpp @@ -0,0 +1,32 @@ +#ifndef ELASTICITY_DIAMOND_ALGORITHM_HPP +#define ELASTICITY_DIAMOND_ALGORITHM_HPP + +#include <algebra/TinyVector.hpp> +#include <language/utils/FunctionSymbolId.hpp> +#include <memory> +#include <mesh/IMesh.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <scheme/IBoundaryConditionDescriptor.hpp> +#include <variant> +#include <vector> + +template <size_t Dimension> +class ElasticityDiamondScheme +{ + private: + class DirichletBoundaryCondition; + class NormalStrainBoundaryCondition; + class SymmetryBoundaryCondition; + + public: + ElasticityDiamondScheme(std::shared_ptr<const IMesh> i_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const FunctionSymbolId& lambda_id, + const FunctionSymbolId& mu_id, + const FunctionSymbolId& f_id, + const FunctionSymbolId& U_id); +}; + +#endif // ELASTICITY_DIAMOND_ALGORITHM2_HPP diff --git a/src/language/algorithms/Heat5PointsAlgorithm.cpp b/src/language/algorithms/Heat5PointsAlgorithm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ffd1e810277ca05104cc00dd183c283dcf81f9a0 --- /dev/null +++ b/src/language/algorithms/Heat5PointsAlgorithm.cpp @@ -0,0 +1,207 @@ +#include <language/algorithms/Heat5PointsAlgorithm.hpp> + +#include <algebra/CRSMatrix.hpp> +#include <algebra/CRSMatrixDescriptor.hpp> +#include <algebra/LeastSquareSolver.hpp> +#include <algebra/LinearSolver.hpp> +#include <algebra/LinearSolverOptions.hpp> +#include <algebra/SmallMatrix.hpp> +#include <algebra/TinyVector.hpp> +#include <algebra/Vector.hpp> +#include <language/utils/InterpolateItemValue.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/DualConnectivityManager.hpp> +#include <mesh/DualMeshManager.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp> + +template <size_t Dimension> +Heat5PointsAlgorithm<Dimension>::Heat5PointsAlgorithm( + std::shared_ptr<const IMesh> i_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const FunctionSymbolId& T_id, + const FunctionSymbolId& kappa_id, + const FunctionSymbolId& f_id) +{ + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + using MeshDataType = MeshData<Dimension>; + + std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + + std::cout << "number of bc descr = " << bc_descriptor_list.size() << '\n'; + + if constexpr (Dimension == 2) { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + CellValue<double> Tj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(T_id, mesh_data.xj()); + + NodeValue<double> Tr(mesh->connectivity()); + const NodeValue<const TinyVector<Dimension>>& xr = mesh->xr(); + const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj(); + const auto& node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); + CellValuePerNode<double> w_rj{mesh->connectivity()}; + + for (NodeId i_node = 0; i_node < mesh->numberOfNodes(); ++i_node) { + SmallVector<double> b{Dimension + 1}; + b[0] = 1; + for (size_t i = 1; i < Dimension + 1; i++) { + b[i] = xr[i_node][i - 1]; + } + const auto& node_to_cell = node_to_cell_matrix[i_node]; + + SmallMatrix<double> A{Dimension + 1, node_to_cell.size()}; + for (size_t j = 0; j < node_to_cell.size(); j++) { + A(0, j) = 1; + } + for (size_t i = 1; i < Dimension + 1; i++) { + for (size_t j = 0; j < node_to_cell.size(); j++) { + const CellId J = node_to_cell[j]; + A(i, j) = xj[J][i - 1]; + } + } + SmallVector<double> x{node_to_cell.size()}; + x = zero; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + Tr[i_node] = 0; + for (size_t j = 0; j < node_to_cell.size(); j++) { + Tr[i_node] += x[j] * Tj[node_to_cell[j]]; + w_rj(i_node, j) = x[j]; + } + } + + { + std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh); + + MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh); + + std::shared_ptr mapper = + DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity()); + + NodeValue<double> Trd{diamond_mesh->connectivity()}; + + mapper->toDualNode(Tr, Tj, Trd); + + CellValue<double> kappaj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(kappa_id, + mesh_data.xj()); + + CellValue<double> dual_kappaj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(kappa_id, + diamond_mesh_data + .xj()); + + const CellValue<const double> dual_Vj = diamond_mesh_data.Vj(); + + const FaceValue<const double> mes_l = [&] { + if constexpr (Dimension == 1) { + FaceValue<double> compute_mes_l{mesh->connectivity()}; + compute_mes_l.fill(1); + return compute_mes_l; + } else { + return mesh_data.ll(); + } + }(); + + const CellValue<const double> dual_mes_l_j = [=] { + CellValue<double> compute_mes_j{diamond_mesh->connectivity()}; + mapper->toDualCell(mes_l, compute_mes_j); + + return compute_mes_j; + }(); + + FaceValue<const double> alpha_l = [&] { + CellValue<double> alpha_j{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + alpha_j[diamond_cell_id] = + dual_mes_l_j[diamond_cell_id] * dual_kappaj[diamond_cell_id] / dual_Vj[diamond_cell_id]; + }); + + FaceValue<double> computed_alpha_l{mesh->connectivity()}; + mapper->fromDualCell(alpha_j, computed_alpha_l); + + return computed_alpha_l; + }(); + + const Array<int> non_zeros{mesh->numberOfCells()}; + non_zeros.fill(Dimension); + CRSMatrixDescriptor<double> S(mesh->numberOfCells(), mesh->numberOfCells(), non_zeros); + + const auto& face_to_cell_matrix = mesh->connectivity().faceToCellMatrix(); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + const auto& primal_face_to_cell = face_to_cell_matrix[face_id]; + + const double beta_l = 0.5 * alpha_l[face_id] * mes_l[face_id]; + + for (size_t i_cell = 0; i_cell < primal_face_to_cell.size(); ++i_cell) { + const CellId cell_id1 = primal_face_to_cell[i_cell]; + for (size_t j_cell = 0; j_cell < primal_face_to_cell.size(); ++j_cell) { + const CellId cell_id2 = primal_face_to_cell[j_cell]; + if (i_cell == j_cell) { + S(cell_id1, cell_id2) -= beta_l; + } else { + S(cell_id1, cell_id2) += beta_l; + } + } + } + } + CellValue<double> fj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(f_id, mesh_data.xj()); + + const CellValue<const double> primal_Vj = mesh_data.Vj(); + CRSMatrix A{S.getCRSMatrix()}; + Vector<double> b{mesh->numberOfCells()}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { b[cell_id] = fj[cell_id] * primal_Vj[cell_id]; }); + + Vector<double> T{mesh->numberOfCells()}; + T = zero; + + LinearSolver solver; + solver.solveLocalSystem(A, T, b); + + CellValue<double> Temperature{mesh->connectivity()}; + + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { Temperature[cell_id] = T[cell_id]; }); + + Vector<double> error{mesh->numberOfCells()}; + parallel_for( + mesh->numberOfCells(), + PUGS_LAMBDA(CellId cell_id) { error[cell_id] = (Temperature[cell_id] - Tj[cell_id]) * primal_Vj[cell_id]; }); + + std::cout << "||Error||_2 = " << std::sqrt(dot(error, error)) << "\n"; + } + } else { + throw NotImplementedError("not done in this dimension"); + } +} + +template Heat5PointsAlgorithm<1>::Heat5PointsAlgorithm( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&); + +template Heat5PointsAlgorithm<2>::Heat5PointsAlgorithm( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&); + +template Heat5PointsAlgorithm<3>::Heat5PointsAlgorithm( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&); diff --git a/src/language/algorithms/Heat5PointsAlgorithm.hpp b/src/language/algorithms/Heat5PointsAlgorithm.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a061972d6c3b33073a296ea9b8fd4d1a456701c5 --- /dev/null +++ b/src/language/algorithms/Heat5PointsAlgorithm.hpp @@ -0,0 +1,21 @@ +#ifndef HEAT_5POINTS_ALGORITHM_HPP +#define HEAT_5POINTS_ALGORITHM_HPP + +#include <language/utils/FunctionSymbolId.hpp> +#include <mesh/IMesh.hpp> +#include <scheme/IBoundaryConditionDescriptor.hpp> + +#include <memory> +#include <vector> + +template <size_t Dimension> +struct Heat5PointsAlgorithm +{ + Heat5PointsAlgorithm(std::shared_ptr<const IMesh> i_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const FunctionSymbolId& T_id, + const FunctionSymbolId& kappa_id, + const FunctionSymbolId& f_id); +}; + +#endif // HEAT_5POINTS_ALGORITHM_HPP diff --git a/src/language/algorithms/HeatDiamondAlgorithm.cpp b/src/language/algorithms/HeatDiamondAlgorithm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d21c25062dd24234a2e3d619ce1847a9bb1b310b --- /dev/null +++ b/src/language/algorithms/HeatDiamondAlgorithm.cpp @@ -0,0 +1,773 @@ +#include <language/algorithms/HeatDiamondAlgorithm.hpp> + +#include <algebra/CRSMatrix.hpp> +#include <algebra/CRSMatrixDescriptor.hpp> +#include <algebra/LeastSquareSolver.hpp> +#include <algebra/LinearSolver.hpp> +#include <algebra/SmallMatrix.hpp> +#include <algebra/TinyVector.hpp> +#include <algebra/Vector.hpp> +#include <language/utils/InterpolateItemValue.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/DualConnectivityManager.hpp> +#include <mesh/DualMeshManager.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/MeshFaceBoundary.hpp> +#include <mesh/MeshNodeBoundary.hpp> +#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp> +#include <mesh/SubItemValuePerItem.hpp> +#include <scheme/DirichletBoundaryConditionDescriptor.hpp> +#include <scheme/FourierBoundaryConditionDescriptor.hpp> +#include <scheme/NeumannBoundaryConditionDescriptor.hpp> +#include <scheme/SymmetryBoundaryConditionDescriptor.hpp> +#include <utils/Timer.hpp> + +template <size_t Dimension> +HeatDiamondScheme<Dimension>::HeatDiamondScheme( + std::shared_ptr<const IMesh> i_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const FunctionSymbolId& T_id, + const FunctionSymbolId& kappa_id, + const FunctionSymbolId& f_id) +{ + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + using MeshDataType = MeshData<Dimension>; + + using BoundaryCondition = std::variant<DirichletBoundaryCondition, FourierBoundaryCondition, NeumannBoundaryCondition, + SymmetryBoundaryCondition>; + + using BoundaryConditionList = std::vector<BoundaryCondition>; + + BoundaryConditionList boundary_condition_list; + + std::cout << "number of bc descr = " << bc_descriptor_list.size() << '\n'; + std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + + for (const auto& bc_descriptor : bc_descriptor_list) { + bool is_valid_boundary_condition = true; + + switch (bc_descriptor->type()) { + case IBoundaryConditionDescriptor::Type::symmetry: { + throw NotImplementedError("NIY"); + break; + } + case IBoundaryConditionDescriptor::Type::dirichlet: { + const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor = + dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor); + if constexpr (Dimension > 1) { + MeshFaceBoundary<Dimension> mesh_face_boundary = + getMeshFaceBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + + const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId(); + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + Array<const double> value_list = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, + mesh_data.xl(), + mesh_face_boundary + .faceList()); + boundary_condition_list.push_back(DirichletBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + } else { + throw NotImplementedError("not implemented in 1d"); + } + break; + } + case IBoundaryConditionDescriptor::Type::fourier: { + throw NotImplementedError("NIY"); + break; + } + case IBoundaryConditionDescriptor::Type::neumann: { + const NeumannBoundaryConditionDescriptor& neumann_bc_descriptor = + dynamic_cast<const NeumannBoundaryConditionDescriptor&>(*bc_descriptor); + + if constexpr (Dimension > 1) { + MeshFaceBoundary<Dimension> mesh_face_boundary = + getMeshFaceBoundary(*mesh, neumann_bc_descriptor.boundaryDescriptor()); + + const FunctionSymbolId g_id = neumann_bc_descriptor.rhsSymbolId(); + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + Array<const double> value_list = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, + mesh_data.xl(), + mesh_face_boundary + .faceList()); + boundary_condition_list.push_back(NeumannBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + } else { + throw NotImplementedError("not implemented in 1d"); + } + break; + } + default: { + is_valid_boundary_condition = false; + } + } + if (not is_valid_boundary_condition) { + std::ostringstream error_msg; + error_msg << *bc_descriptor << " is an invalid boundary condition for heat equation"; + throw NormalError(error_msg.str()); + } + } + + if constexpr (Dimension > 1) { + const CellValue<const size_t> cell_dof_number = [&] { + CellValue<size_t> compute_cell_dof_number{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { compute_cell_dof_number[cell_id] = cell_id; }); + return compute_cell_dof_number; + }(); + size_t number_of_dof = mesh->numberOfCells(); + + const FaceValue<const size_t> face_dof_number = [&] { + FaceValue<size_t> compute_face_dof_number{mesh->connectivity()}; + compute_face_dof_number.fill(std::numeric_limits<size_t>::max()); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NeumannBoundaryCondition>) or + (std::is_same_v<T, FourierBoundaryCondition>) or + (std::is_same_v<T, SymmetryBoundaryCondition>) or + (std::is_same_v<T, DirichletBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + if (compute_face_dof_number[face_id] != std::numeric_limits<size_t>::max()) { + std::ostringstream os; + os << "The face " << face_id << " is used at least twice for boundary conditions"; + throw NormalError(os.str()); + } else { + compute_face_dof_number[face_id] = number_of_dof++; + } + } + } + }, + boundary_condition); + } + + return compute_face_dof_number; + }(); + + const FaceValue<const bool> primal_face_is_neumann = [&] { + FaceValue<bool> face_is_neumann{mesh->connectivity()}; + face_is_neumann.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NeumannBoundaryCondition>) or + (std::is_same_v<T, FourierBoundaryCondition>) or + (std::is_same_v<T, SymmetryBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + face_is_neumann[face_id] = true; + } + } + }, + boundary_condition); + } + + return face_is_neumann; + }(); + + const auto& primal_face_to_node_matrix = mesh->connectivity().faceToNodeMatrix(); + const auto& face_to_cell_matrix = mesh->connectivity().faceToCellMatrix(); + NodeValue<bool> primal_node_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of node_is_on_boundary is incorrect"); + } + + primal_node_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + primal_node_is_on_boundary[node_id] = true; + } + } + } + + FaceValue<bool> primal_face_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_on_boundary is incorrect"); + } + + primal_face_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + primal_face_is_on_boundary[face_id] = true; + } + } + + FaceValue<bool> primal_face_is_dirichlet(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_neumann is incorrect"); + } + + primal_face_is_dirichlet.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + primal_face_is_dirichlet[face_id] = (primal_face_is_on_boundary[face_id] && (!primal_face_is_neumann[face_id])); + } + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + CellValue<double> Tj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(T_id, mesh_data.xj()); + FaceValue<double> Tl = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(T_id, mesh_data.xl()); + NodeValue<double> Tr(mesh->connectivity()); + const NodeValue<const TinyVector<Dimension>>& xr = mesh->xr(); + + const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl(); + const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj(); + const auto& node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); + const auto& node_to_face_matrix = mesh->connectivity().nodeToFaceMatrix(); + CellValuePerNode<double> w_rj{mesh->connectivity()}; + FaceValuePerNode<double> w_rl{mesh->connectivity()}; + + for (size_t i = 0; i < w_rl.numberOfValues(); ++i) { + w_rl[i] = std::numeric_limits<double>::signaling_NaN(); + } + + for (NodeId i_node = 0; i_node < mesh->numberOfNodes(); ++i_node) { + SmallVector<double> b{Dimension + 1}; + b[0] = 1; + for (size_t i = 1; i < Dimension + 1; i++) { + b[i] = xr[i_node][i - 1]; + } + const auto& node_to_cell = node_to_cell_matrix[i_node]; + + if (not primal_node_is_on_boundary[i_node]) { + SmallMatrix<double> A{Dimension + 1, node_to_cell.size()}; + for (size_t j = 0; j < node_to_cell.size(); j++) { + A(0, j) = 1; + } + for (size_t i = 1; i < Dimension + 1; i++) { + for (size_t j = 0; j < node_to_cell.size(); j++) { + const CellId J = node_to_cell[j]; + A(i, j) = xj[J][i - 1]; + } + } + SmallVector<double> x{node_to_cell.size()}; + x = zero; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + Tr[i_node] = 0; + for (size_t j = 0; j < node_to_cell.size(); j++) { + Tr[i_node] += x[j] * Tj[node_to_cell[j]]; + w_rj(i_node, j) = x[j]; + } + } else { + int nb_face_used = 0; + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (primal_face_is_on_boundary[face_id]) { + nb_face_used++; + } + } + SmallMatrix<double> A{Dimension + 1, node_to_cell.size() + nb_face_used}; + for (size_t j = 0; j < node_to_cell.size() + nb_face_used; j++) { + A(0, j) = 1; + } + for (size_t i = 1; i < Dimension + 1; i++) { + for (size_t j = 0; j < node_to_cell.size(); j++) { + const CellId J = node_to_cell[j]; + A(i, j) = xj[J][i - 1]; + } + } + for (size_t i = 1; i < Dimension + 1; i++) { + int cpt_face = 0; + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (primal_face_is_on_boundary[face_id]) { + A(i, node_to_cell.size() + cpt_face) = xl[face_id][i - 1]; + cpt_face++; + } + } + } + + SmallVector<double> x{node_to_cell.size() + nb_face_used}; + x = zero; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + Tr[i_node] = 0; + for (size_t j = 0; j < node_to_cell.size(); j++) { + Tr[i_node] += x[j] * Tj[node_to_cell[j]]; + w_rj(i_node, j) = x[j]; + } + int cpt_face = node_to_cell.size(); + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (primal_face_is_on_boundary[face_id]) { + w_rl(i_node, i_face) = x[cpt_face++]; + Tr[i_node] += w_rl(i_node, i_face) * Tl[face_id]; + } + } + } + } + + { + std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh); + + MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh); + + std::shared_ptr mapper = + DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity()); + + NodeValue<double> Trd{diamond_mesh->connectivity()}; + + mapper->toDualNode(Tr, Tj, Trd); + + CellValue<double> kappaj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(kappa_id, + mesh_data.xj()); + + CellValue<double> dual_kappaj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(kappa_id, + diamond_mesh_data + .xj()); + + const CellValue<const double> dual_Vj = diamond_mesh_data.Vj(); + + const FaceValue<const double> mes_l = [&] { + if constexpr (Dimension == 1) { + FaceValue<double> compute_mes_l{mesh->connectivity()}; + compute_mes_l.fill(1); + return compute_mes_l; + } else { + return mesh_data.ll(); + } + }(); + + const CellValue<const double> dual_mes_l_j = [=] { + CellValue<double> compute_mes_j{diamond_mesh->connectivity()}; + mapper->toDualCell(mes_l, compute_mes_j); + + return compute_mes_j; + }(); + + FaceValue<const CellId> face_dual_cell_id = [=]() { + FaceValue<CellId> computed_face_dual_cell_id{mesh->connectivity()}; + CellValue<CellId> dual_cell_id{diamond_mesh->connectivity()}; + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { dual_cell_id[cell_id] = cell_id; }); + + mapper->fromDualCell(dual_cell_id, computed_face_dual_cell_id); + + return computed_face_dual_cell_id; + }(); + + NodeValue<const NodeId> dual_node_primal_node_id = [=]() { + CellValue<NodeId> cell_ignored_id{mesh->connectivity()}; + cell_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()}); + + NodeValue<NodeId> node_primal_id{mesh->connectivity()}; + + parallel_for( + mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { node_primal_id[node_id] = node_id; }); + + NodeValue<NodeId> computed_dual_node_primal_node_id{diamond_mesh->connectivity()}; + + mapper->toDualNode(node_primal_id, cell_ignored_id, computed_dual_node_primal_node_id); + + return computed_dual_node_primal_node_id; + }(); + + CellValue<NodeId> primal_cell_dual_node_id = [=]() { + CellValue<NodeId> cell_id{mesh->connectivity()}; + NodeValue<NodeId> node_ignored_id{mesh->connectivity()}; + node_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()}); + + NodeValue<NodeId> dual_node_id{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { dual_node_id[node_id] = node_id; }); + + CellValue<NodeId> computed_primal_cell_dual_node_id{mesh->connectivity()}; + + mapper->fromDualNode(dual_node_id, node_ignored_id, cell_id); + + return cell_id; + }(); + Timer my_timer; + const auto& dual_Cjr = diamond_mesh_data.Cjr(); + FaceValue<TinyVector<Dimension>> dualClj = [&] { + FaceValue<TinyVector<Dimension>> computedClj{mesh->connectivity()}; + const auto& dual_node_to_cell_matrix = diamond_mesh->connectivity().nodeToCellMatrix(); + const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix(); + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { + const auto& primal_face_to_cell = face_to_cell_matrix[face_id]; + for (size_t i = 0; i < primal_face_to_cell.size(); i++) { + CellId cell_id = primal_face_to_cell[i]; + const NodeId dual_node_id = primal_cell_dual_node_id[cell_id]; + for (size_t i_dual_cell = 0; i_dual_cell < dual_node_to_cell_matrix[dual_node_id].size(); i_dual_cell++) { + const CellId dual_cell_id = dual_node_to_cell_matrix[dual_node_id][i_dual_cell]; + if (face_dual_cell_id[face_id] == dual_cell_id) { + for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); + i_dual_node++) { + const NodeId final_dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node]; + if (final_dual_node_id == dual_node_id) { + computedClj[face_id] = dual_Cjr(dual_cell_id, i_dual_node); + } + } + } + } + } + }); + return computedClj; + }(); + + FaceValue<TinyVector<Dimension>> nlj = [&] { + FaceValue<TinyVector<Dimension>> computedNlj{mesh->connectivity()}; + parallel_for( + mesh->numberOfFaces(), + PUGS_LAMBDA(FaceId face_id) { computedNlj[face_id] = 1. / l2Norm(dualClj[face_id]) * dualClj[face_id]; }); + return computedNlj; + }(); + + FaceValue<const double> alpha_l = [&] { + CellValue<double> alpha_j{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + alpha_j[diamond_cell_id] = dual_kappaj[diamond_cell_id] / dual_Vj[diamond_cell_id]; + }); + + FaceValue<double> computed_alpha_l{mesh->connectivity()}; + mapper->fromDualCell(alpha_j, computed_alpha_l); + + return computed_alpha_l; + }(); + + const Array<int> non_zeros{number_of_dof}; + non_zeros.fill(Dimension * Dimension); + CRSMatrixDescriptor<double> S(number_of_dof, number_of_dof, non_zeros); + + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + const auto& primal_face_to_cell = face_to_cell_matrix[face_id]; + // const double beta_l = 1. / Dimension * alpha_l[face_id] * mes_l[face_id]; + const double beta_l = l2Norm(dualClj[face_id]) * alpha_l[face_id] * mes_l[face_id]; + for (size_t i_cell = 0; i_cell < primal_face_to_cell.size(); ++i_cell) { + const CellId cell_id1 = primal_face_to_cell[i_cell]; + for (size_t j_cell = 0; j_cell < primal_face_to_cell.size(); ++j_cell) { + const CellId cell_id2 = primal_face_to_cell[j_cell]; + if (i_cell == j_cell) { + S(cell_dof_number[cell_id1], cell_dof_number[cell_id2]) += beta_l; + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id], cell_dof_number[cell_id2]) -= beta_l; + } + } else { + S(cell_dof_number[cell_id1], cell_dof_number[cell_id2]) -= beta_l; + } + } + } + } + + const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix(); + + const auto& primal_node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + const double alpha_face_id = mes_l[face_id] * alpha_l[face_id]; + + for (size_t i_face_cell = 0; i_face_cell < face_to_cell_matrix[face_id].size(); ++i_face_cell) { + CellId i_id = face_to_cell_matrix[face_id][i_face_cell]; + const bool is_face_reversed_for_cell_i = (dot(dualClj[face_id], xl[face_id] - xj[i_id]) < 0); + + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + + const TinyVector<Dimension> nil = [&] { + if (is_face_reversed_for_cell_i) { + return -nlj[face_id]; + } else { + return nlj[face_id]; + } + }(); + + CellId dual_cell_id = face_dual_cell_id[face_id]; + + for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); ++i_dual_node) { + const NodeId dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node]; + if (dual_node_primal_node_id[dual_node_id] == node_id) { + const TinyVector<Dimension> Clr = dual_Cjr(dual_cell_id, i_dual_node); + + const double a_ir = alpha_face_id * dot(nil, Clr); + + for (size_t j_cell = 0; j_cell < primal_node_to_cell_matrix[node_id].size(); ++j_cell) { + CellId j_id = primal_node_to_cell_matrix[node_id][j_cell]; + S(cell_dof_number[i_id], cell_dof_number[j_id]) -= w_rj(node_id, j_cell) * a_ir; + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id], cell_dof_number[j_id]) += w_rj(node_id, j_cell) * a_ir; + } + } + if (primal_node_is_on_boundary[node_id]) { + for (size_t l_face = 0; l_face < node_to_face_matrix[node_id].size(); ++l_face) { + FaceId l_id = node_to_face_matrix[node_id][l_face]; + if (primal_face_is_on_boundary[l_id]) { + S(cell_dof_number[i_id], face_dof_number[l_id]) -= w_rl(node_id, l_face) * a_ir; + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id], face_dof_number[l_id]) += w_rl(node_id, l_face) * a_ir; + } + } + } + } + } + } + } + } + } + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (primal_face_is_dirichlet[face_id]) { + S(face_dof_number[face_id], face_dof_number[face_id]) += 1; + } + } + + CellValue<double> fj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(f_id, mesh_data.xj()); + + const CellValue<const double> primal_Vj = mesh_data.Vj(); + CRSMatrix A{S.getCRSMatrix()}; + Vector<double> b{number_of_dof}; + b = zero; + + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + b[cell_dof_number[cell_id]] = fj[cell_id] * primal_Vj[cell_id]; + } + // Dirichlet on b^L_D + { + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<T, DirichletBoundaryCondition>) { + const auto& face_list = bc.faceList(); + const auto& value_list = bc.valueList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + b[face_dof_number[face_id]] += value_list[i_face]; // sign + } + } + }, + boundary_condition); + } + } + // EL b^L + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NeumannBoundaryCondition>) or + (std::is_same_v<T, FourierBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + const auto& value_list = bc.valueList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + FaceId face_id = face_list[i_face]; + b[face_dof_number[face_id]] += mes_l[face_id] * value_list[i_face]; // sign + } + } + }, + boundary_condition); + } + + Vector<double> T{number_of_dof}; + T = zero; + + LinearSolver solver; + solver.solveLocalSystem(A, T, b); + + CellValue<double> Temperature{mesh->connectivity()}; + FaceValue<double> Temperature_face{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { Temperature[cell_id] = T[cell_dof_number[cell_id]]; }); + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { + if (primal_face_is_neumann[face_id]) { + Temperature_face[face_id] = T[face_dof_number[face_id]]; + } else { + Temperature_face[face_id] = Tl[face_id]; + } + }); + Vector<double> error{mesh->numberOfCells()}; + CellValue<double> cell_error{mesh->connectivity()}; + Vector<double> face_error{mesh->numberOfFaces()}; + double error_max = 0.; + size_t cell_max = 0; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + error[cell_id] = (Temperature[cell_id] - Tj[cell_id]) * sqrt(primal_Vj[cell_id]); + cell_error[cell_id] = (Temperature[cell_id] - Tj[cell_id]); + }); + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { + if (primal_face_is_on_boundary[face_id]) { + face_error[face_id] = (Temperature_face[face_id] - Tl[face_id]) * sqrt(mes_l[face_id]); + } else { + face_error[face_id] = 0; + } + }); + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); cell_id++) { + if (error_max < std::abs(cell_error[cell_id])) { + error_max = std::abs(cell_error[cell_id]); + cell_max = cell_id; + } + } + + std::cout << " ||Error||_max (cell)= " << error_max << " on cell " << cell_max << "\n"; + std::cout << "||Error||_2 (cell)= " << std::sqrt(dot(error, error)) << "\n"; + std::cout << "||Error||_2 (face)= " << std::sqrt(dot(face_error, face_error)) << "\n"; + std::cout << "||Error||_2 (total)= " << std::sqrt(dot(error, error)) + std::sqrt(dot(face_error, face_error)) + << "\n"; + } + } else { + throw NotImplementedError("not implemented in 1d"); + } +} + +template <size_t Dimension> +class HeatDiamondScheme<Dimension>::DirichletBoundaryCondition +{ + private: + const Array<const double> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + DirichletBoundaryCondition(const Array<const FaceId>& face_list, const Array<const double>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~DirichletBoundaryCondition() = default; +}; + +template <size_t Dimension> +class HeatDiamondScheme<Dimension>::NeumannBoundaryCondition +{ + private: + const Array<const double> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + NeumannBoundaryCondition(const Array<const FaceId>& face_list, const Array<const double>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~NeumannBoundaryCondition() = default; +}; + +template <size_t Dimension> +class HeatDiamondScheme<Dimension>::FourierBoundaryCondition +{ + private: + const Array<const double> m_coef_list; + const Array<const double> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + const Array<const double>& + coefList() const + { + return m_coef_list; + } + + public: + FourierBoundaryCondition(const Array<const FaceId>& face_list, + const Array<const double>& coef_list, + const Array<const double>& value_list) + : m_coef_list{coef_list}, m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_coef_list.size() == m_face_list.size()); + Assert(m_value_list.size() == m_face_list.size()); + } + + ~FourierBoundaryCondition() = default; +}; + +template <size_t Dimension> +class HeatDiamondScheme<Dimension>::SymmetryBoundaryCondition +{ + private: + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + public: + SymmetryBoundaryCondition(const Array<const FaceId>& face_list) : m_face_list{face_list} {} + + ~SymmetryBoundaryCondition() = default; +}; + +template HeatDiamondScheme<1>::HeatDiamondScheme( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&); + +template HeatDiamondScheme<2>::HeatDiamondScheme( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&); + +template HeatDiamondScheme<3>::HeatDiamondScheme( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&); diff --git a/src/language/algorithms/HeatDiamondAlgorithm.hpp b/src/language/algorithms/HeatDiamondAlgorithm.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9e29d91bc1bc9f37842b8e993a698a4d26d9f68f --- /dev/null +++ b/src/language/algorithms/HeatDiamondAlgorithm.hpp @@ -0,0 +1,29 @@ +#ifndef HEAT_DIAMOND_ALGORITHM_HPP +#define HEAT_DIAMOND_ALGORITHM_HPP + +#include <language/utils/FunctionSymbolId.hpp> +#include <mesh/IMesh.hpp> +#include <scheme/IBoundaryConditionDescriptor.hpp> + +#include <memory> +#include <variant> +#include <vector> + +template <size_t Dimension> +class HeatDiamondScheme +{ + private: + class DirichletBoundaryCondition; + class FourierBoundaryCondition; + class NeumannBoundaryCondition; + class SymmetryBoundaryCondition; + + public: + HeatDiamondScheme(std::shared_ptr<const IMesh> i_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const FunctionSymbolId& T_id, + const FunctionSymbolId& kappa_id, + const FunctionSymbolId& f_id); +}; + +#endif // HEAT_DIAMOND_ALGORITHM_HPP diff --git a/src/language/algorithms/HeatDiamondAlgorithm2.cpp b/src/language/algorithms/HeatDiamondAlgorithm2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c36e796780f77ccd58c2fa7559a8c5cab9478d87 --- /dev/null +++ b/src/language/algorithms/HeatDiamondAlgorithm2.cpp @@ -0,0 +1,869 @@ +#include <language/algorithms/HeatDiamondAlgorithm2.hpp> + +#include <algebra/CRSMatrix.hpp> +#include <algebra/CRSMatrixDescriptor.hpp> +#include <algebra/LeastSquareSolver.hpp> +#include <algebra/LinearSolver.hpp> +#include <algebra/SmallMatrix.hpp> +#include <algebra/TinyVector.hpp> +#include <algebra/Vector.hpp> +#include <language/utils/InterpolateItemValue.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/DualConnectivityManager.hpp> +#include <mesh/DualMeshManager.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/MeshFaceBoundary.hpp> +#include <mesh/MeshNodeBoundary.hpp> +#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp> +#include <mesh/SubItemValuePerItem.hpp> +#include <scheme/DirichletBoundaryConditionDescriptor.hpp> +#include <scheme/FourierBoundaryConditionDescriptor.hpp> +#include <scheme/NeumannBoundaryConditionDescriptor.hpp> +#include <scheme/SymmetryBoundaryConditionDescriptor.hpp> +#include <utils/Timer.hpp> + +template <size_t Dimension> +HeatDiamondScheme2<Dimension>::HeatDiamondScheme2( + std::shared_ptr<const IMesh> i_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const FunctionSymbolId& T_id, + const FunctionSymbolId& kappa_id, + const FunctionSymbolId& f_id) +{ + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + using MeshDataType = MeshData<Dimension>; + + using BoundaryCondition = std::variant<DirichletBoundaryCondition, FourierBoundaryCondition, NeumannBoundaryCondition, + SymmetryBoundaryCondition>; + + using BoundaryConditionList = std::vector<BoundaryCondition>; + + BoundaryConditionList boundary_condition_list; + + std::cout << "number of bc descr = " << bc_descriptor_list.size() << '\n'; + std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + NodeValue<bool> is_dirichlet{mesh->connectivity()}; + is_dirichlet.fill(false); + NodeValue<double> dirichlet_value{mesh->connectivity()}; + dirichlet_value.fill(std::numeric_limits<double>::signaling_NaN()); + + for (const auto& bc_descriptor : bc_descriptor_list) { + bool is_valid_boundary_condition = true; + + switch (bc_descriptor->type()) { + case IBoundaryConditionDescriptor::Type::symmetry: { + throw NotImplementedError("NIY"); + break; + } + case IBoundaryConditionDescriptor::Type::dirichlet: { + const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor = + dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor); + if constexpr (Dimension > 1) { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId(); + + MeshNodeBoundary<Dimension> mesh_node_boundary = + getMeshNodeBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + + Array<const double> node_value_list = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::node>(g_id, mesh->xr(), + mesh_node_boundary + .nodeList()); + + MeshFaceBoundary<Dimension> mesh_face_boundary = + getMeshFaceBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + + Array<const double> face_value_list = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, + mesh_data.xl(), + mesh_face_boundary + .faceList()); + + for (size_t i_node = 0; i_node < mesh_node_boundary.nodeList().size(); ++i_node) { + NodeId node_id = mesh_node_boundary.nodeList()[i_node]; + + if (not is_dirichlet[node_id]) { + is_dirichlet[node_id] = true; + dirichlet_value[node_id] = node_value_list[i_node]; + } + } + + boundary_condition_list.push_back(DirichletBoundaryCondition{mesh_face_boundary.faceList(), face_value_list, + mesh_node_boundary.nodeList(), node_value_list}); + + } else { + throw NotImplementedError("not implemented in 1d"); + } + break; + } + case IBoundaryConditionDescriptor::Type::fourier: { + throw NotImplementedError("NIY"); + break; + } + case IBoundaryConditionDescriptor::Type::neumann: { + const NeumannBoundaryConditionDescriptor& neumann_bc_descriptor = + dynamic_cast<const NeumannBoundaryConditionDescriptor&>(*bc_descriptor); + + if constexpr (Dimension > 1) { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + const FunctionSymbolId g_id = neumann_bc_descriptor.rhsSymbolId(); + + MeshFaceBoundary<Dimension> mesh_face_boundary = + getMeshFaceBoundary(*mesh, neumann_bc_descriptor.boundaryDescriptor()); + + Array<const double> face_value_list = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, + mesh_data.xl(), + mesh_face_boundary + .faceList()); + + boundary_condition_list.push_back(NeumannBoundaryCondition{mesh_face_boundary.faceList(), face_value_list}); + } else { + throw NotImplementedError("not implemented in 1d"); + } + + break; + } + default: { + is_valid_boundary_condition = false; + } + } + if (not is_valid_boundary_condition) { + std::ostringstream error_msg; + error_msg << *bc_descriptor << " is an invalid boundary condition for heat equation"; + throw NormalError(error_msg.str()); + } + } + + if constexpr (Dimension > 1) { + const CellValue<const size_t> cell_dof_number = [&] { + CellValue<size_t> compute_cell_dof_number{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { compute_cell_dof_number[cell_id] = cell_id; }); + return compute_cell_dof_number; + }(); + size_t number_of_dof = mesh->numberOfCells(); + + const FaceValue<const size_t> face_dof_number = [&] { + FaceValue<size_t> compute_face_dof_number{mesh->connectivity()}; + compute_face_dof_number.fill(std::numeric_limits<size_t>::max()); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NeumannBoundaryCondition>) or + (std::is_same_v<T, FourierBoundaryCondition>) or + (std::is_same_v<T, SymmetryBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + if (compute_face_dof_number[face_id] != std::numeric_limits<size_t>::max()) { + std::ostringstream os; + os << "The face " << face_id << " is used at least twice for boundary conditions"; + throw NormalError(os.str()); + } else { + compute_face_dof_number[face_id] = number_of_dof++; + } + } + } + }, + boundary_condition); + } + + return compute_face_dof_number; + }(); + + const FaceValue<const bool> primal_face_is_neumann = [&] { + FaceValue<bool> face_is_neumann{mesh->connectivity()}; + face_is_neumann.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NeumannBoundaryCondition>) or + (std::is_same_v<T, FourierBoundaryCondition>) or + (std::is_same_v<T, SymmetryBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + face_is_neumann[face_id] = true; + } + } + }, + boundary_condition); + } + + return face_is_neumann; + }(); + + const auto& primal_face_to_node_matrix = mesh->connectivity().faceToNodeMatrix(); + const auto& face_to_cell_matrix = mesh->connectivity().faceToCellMatrix(); + NodeValue<bool> primal_node_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of node_is_on_boundary is incorrect"); + } + + primal_node_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + primal_node_is_on_boundary[node_id] = true; + } + } + } + + FaceValue<bool> primal_face_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_on_boundary is incorrect"); + } + + primal_face_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + primal_face_is_on_boundary[face_id] = true; + } + } + + FaceValue<bool> primal_face_is_dirichlet(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_neumann is incorrect"); + } + + primal_face_is_dirichlet.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + primal_face_is_dirichlet[face_id] = (primal_face_is_on_boundary[face_id] && (!primal_face_is_neumann[face_id])); + } + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + CellValue<double> Tj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(T_id, mesh_data.xj()); + FaceValue<double> Tl = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(T_id, mesh_data.xl()); + NodeValue<double> Tr(mesh->connectivity()); + const NodeValue<const TinyVector<Dimension>>& xr = mesh->xr(); + + const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl(); + const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj(); + const auto& node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); + const auto& node_to_face_matrix = mesh->connectivity().nodeToFaceMatrix(); + CellValuePerNode<double> w_rj{mesh->connectivity()}; + FaceValuePerNode<double> w_rl{mesh->connectivity()}; + + for (size_t i = 0; i < w_rl.numberOfValues(); ++i) { + w_rl[i] = std::numeric_limits<double>::signaling_NaN(); + } + + for (NodeId i_node = 0; i_node < mesh->numberOfNodes(); ++i_node) { + SmallVector<double> b{Dimension + 1}; + b[0] = 1; + for (size_t i = 1; i < Dimension + 1; i++) { + b[i] = xr[i_node][i - 1]; + } + const auto& node_to_cell = node_to_cell_matrix[i_node]; + + if (not primal_node_is_on_boundary[i_node]) { + SmallMatrix<double> A{Dimension + 1, node_to_cell.size()}; + for (size_t j = 0; j < node_to_cell.size(); j++) { + A(0, j) = 1; + } + for (size_t i = 1; i < Dimension + 1; i++) { + for (size_t j = 0; j < node_to_cell.size(); j++) { + const CellId J = node_to_cell[j]; + A(i, j) = xj[J][i - 1]; + } + } + SmallVector<double> x{node_to_cell.size()}; + x = zero; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + Tr[i_node] = 0; + for (size_t j = 0; j < node_to_cell.size(); j++) { + Tr[i_node] += x[j] * Tj[node_to_cell[j]]; + w_rj(i_node, j) = x[j]; + } + } else { + int nb_face_used = 0; + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (primal_face_is_on_boundary[face_id]) { + nb_face_used++; + } + } + SmallMatrix<double> A{Dimension + 1, node_to_cell.size() + nb_face_used}; + for (size_t j = 0; j < node_to_cell.size() + nb_face_used; j++) { + A(0, j) = 1; + } + for (size_t i = 1; i < Dimension + 1; i++) { + for (size_t j = 0; j < node_to_cell.size(); j++) { + const CellId J = node_to_cell[j]; + A(i, j) = xj[J][i - 1]; + } + } + for (size_t i = 1; i < Dimension + 1; i++) { + int cpt_face = 0; + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (primal_face_is_on_boundary[face_id]) { + A(i, node_to_cell.size() + cpt_face) = xl[face_id][i - 1]; + cpt_face++; + } + } + } + + SmallVector<double> x{node_to_cell.size() + nb_face_used}; + x = zero; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + Tr[i_node] = 0; + for (size_t j = 0; j < node_to_cell.size(); j++) { + Tr[i_node] += x[j] * Tj[node_to_cell[j]]; + w_rj(i_node, j) = x[j]; + } + int cpt_face = node_to_cell.size(); + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (primal_face_is_on_boundary[face_id]) { + w_rl(i_node, i_face) = x[cpt_face++]; + Tr[i_node] += w_rl(i_node, i_face) * Tl[face_id]; + } + } + } + } + + { + std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh); + + MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh); + + std::shared_ptr mapper = + DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity()); + + NodeValue<double> Trd{diamond_mesh->connectivity()}; + + mapper->toDualNode(Tr, Tj, Trd); + + CellValue<double> kappaj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(kappa_id, + mesh_data.xj()); + + CellValue<double> dual_kappaj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(kappa_id, + diamond_mesh_data + .xj()); + + const CellValue<const double> dual_Vj = diamond_mesh_data.Vj(); + + const FaceValue<const double> mes_l = [&] { + if constexpr (Dimension == 1) { + FaceValue<double> compute_mes_l{mesh->connectivity()}; + compute_mes_l.fill(1); + return compute_mes_l; + } else { + return mesh_data.ll(); + } + }(); + + const CellValue<const double> dual_mes_l_j = [=] { + CellValue<double> compute_mes_j{diamond_mesh->connectivity()}; + mapper->toDualCell(mes_l, compute_mes_j); + + return compute_mes_j; + }(); + + FaceValue<const CellId> face_dual_cell_id = [=]() { + FaceValue<CellId> computed_face_dual_cell_id{mesh->connectivity()}; + CellValue<CellId> dual_cell_id{diamond_mesh->connectivity()}; + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { dual_cell_id[cell_id] = cell_id; }); + + mapper->fromDualCell(dual_cell_id, computed_face_dual_cell_id); + + return computed_face_dual_cell_id; + }(); + + NodeValue<const NodeId> dual_node_primal_node_id = [=]() { + CellValue<NodeId> cell_ignored_id{mesh->connectivity()}; + cell_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()}); + + NodeValue<NodeId> node_primal_id{mesh->connectivity()}; + + parallel_for( + mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { node_primal_id[node_id] = node_id; }); + + NodeValue<NodeId> computed_dual_node_primal_node_id{diamond_mesh->connectivity()}; + + mapper->toDualNode(node_primal_id, cell_ignored_id, computed_dual_node_primal_node_id); + + return computed_dual_node_primal_node_id; + }(); + + CellValue<NodeId> primal_cell_dual_node_id = [=]() { + CellValue<NodeId> cell_id{mesh->connectivity()}; + NodeValue<NodeId> node_ignored_id{mesh->connectivity()}; + node_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()}); + + NodeValue<NodeId> dual_node_id{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { dual_node_id[node_id] = node_id; }); + + CellValue<NodeId> computed_primal_cell_dual_node_id{mesh->connectivity()}; + + mapper->fromDualNode(dual_node_id, node_ignored_id, cell_id); + + return cell_id; + }(); + Timer my_timer; + const auto& dual_Cjr = diamond_mesh_data.Cjr(); + FaceValue<TinyVector<Dimension>> dualClj = [&] { + FaceValue<TinyVector<Dimension>> computedClj{mesh->connectivity()}; + const auto& dual_node_to_cell_matrix = diamond_mesh->connectivity().nodeToCellMatrix(); + const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix(); + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { + const auto& primal_face_to_cell = face_to_cell_matrix[face_id]; + for (size_t i = 0; i < primal_face_to_cell.size(); i++) { + CellId cell_id = primal_face_to_cell[i]; + const NodeId dual_node_id = primal_cell_dual_node_id[cell_id]; + for (size_t i_dual_cell = 0; i_dual_cell < dual_node_to_cell_matrix[dual_node_id].size(); i_dual_cell++) { + const CellId dual_cell_id = dual_node_to_cell_matrix[dual_node_id][i_dual_cell]; + if (face_dual_cell_id[face_id] == dual_cell_id) { + for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); + i_dual_node++) { + const NodeId final_dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node]; + if (final_dual_node_id == dual_node_id) { + computedClj[face_id] = dual_Cjr(dual_cell_id, i_dual_node); + } + } + } + } + } + }); + return computedClj; + }(); + + FaceValue<TinyVector<Dimension>> nlj = [&] { + FaceValue<TinyVector<Dimension>> computedNlj{mesh->connectivity()}; + parallel_for( + mesh->numberOfFaces(), + PUGS_LAMBDA(FaceId face_id) { computedNlj[face_id] = 1. / l2Norm(dualClj[face_id]) * dualClj[face_id]; }); + return computedNlj; + }(); + + FaceValue<const double> alpha_l = [&] { + CellValue<double> alpha_j{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + alpha_j[diamond_cell_id] = dual_kappaj[diamond_cell_id] / dual_Vj[diamond_cell_id]; + }); + + FaceValue<double> computed_alpha_l{mesh->connectivity()}; + mapper->fromDualCell(alpha_j, computed_alpha_l); + + return computed_alpha_l; + }(); + + const Array<int> non_zeros{number_of_dof}; + non_zeros.fill(Dimension * Dimension); + CRSMatrixDescriptor<double> S(number_of_dof, number_of_dof, non_zeros); + + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + const auto& primal_face_to_cell = face_to_cell_matrix[face_id]; + // const double beta_l = 1. / Dimension * alpha_l[face_id] * mes_l[face_id]; + const double beta_l = l2Norm(dualClj[face_id]) * alpha_l[face_id] * mes_l[face_id]; + for (size_t i_cell = 0; i_cell < primal_face_to_cell.size(); ++i_cell) { + const CellId cell_id1 = primal_face_to_cell[i_cell]; + for (size_t j_cell = 0; j_cell < primal_face_to_cell.size(); ++j_cell) { + const CellId cell_id2 = primal_face_to_cell[j_cell]; + if (i_cell == j_cell) { + S(cell_dof_number[cell_id1], cell_dof_number[cell_id2]) += beta_l; + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id], cell_dof_number[cell_id2]) -= beta_l; + } + } else { + S(cell_dof_number[cell_id1], cell_dof_number[cell_id2]) -= beta_l; + } + } + } + } + + const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix(); + + const auto& primal_node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + const double alpha_face_id = mes_l[face_id] * alpha_l[face_id]; + + for (size_t i_face_cell = 0; i_face_cell < face_to_cell_matrix[face_id].size(); ++i_face_cell) { + CellId i_id = face_to_cell_matrix[face_id][i_face_cell]; + const bool is_face_reversed_for_cell_i = (dot(dualClj[face_id], xl[face_id] - xj[i_id]) < 0); + + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + + const TinyVector<Dimension> nil = [&] { + if (is_face_reversed_for_cell_i) { + return -nlj[face_id]; + } else { + return nlj[face_id]; + } + }(); + + CellId dual_cell_id = face_dual_cell_id[face_id]; + + for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); ++i_dual_node) { + const NodeId dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node]; + if (dual_node_primal_node_id[dual_node_id] == node_id) { + const TinyVector<Dimension> Clr = dual_Cjr(dual_cell_id, i_dual_node); + + const double a_ir = alpha_face_id * dot(nil, Clr); + + for (size_t j_cell = 0; j_cell < primal_node_to_cell_matrix[node_id].size(); ++j_cell) { + CellId j_id = primal_node_to_cell_matrix[node_id][j_cell]; + S(cell_dof_number[i_id], cell_dof_number[j_id]) -= w_rj(node_id, j_cell) * a_ir; + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id], cell_dof_number[j_id]) += w_rj(node_id, j_cell) * a_ir; + } + } + if (primal_node_is_on_boundary[node_id]) { + for (size_t l_face = 0; l_face < node_to_face_matrix[node_id].size(); ++l_face) { + FaceId l_id = node_to_face_matrix[node_id][l_face]; + // ELIMINATION METTRE AU SECOND MEMBRE + if (primal_face_is_neumann[l_id]) { + S(cell_dof_number[i_id], face_dof_number[l_id]) -= w_rl(node_id, l_face) * a_ir; + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id], face_dof_number[l_id]) += w_rl(node_id, l_face) * a_ir; + } + } + } + } + } + } + } + } + } + + CellValue<double> fj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(f_id, mesh_data.xj()); + + const CellValue<const double> primal_Vj = mesh_data.Vj(); + CRSMatrix A{S.getCRSMatrix()}; + Vector<double> b{number_of_dof}; + b = zero; + + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + b[cell_dof_number[cell_id]] = fj[cell_id] * primal_Vj[cell_id]; + } + // Dirichlet on b^L_D a Dirichlet face F contribute to the second member J via the node R thanks to A^{JR} A^{RF} + // for cell and A^{NR} A^{RF} for Neumann faces + { + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<T, DirichletBoundaryCondition>) { + const auto& value_list = bc.valueList(); + const auto& face_list = bc.faceList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + FaceId face_id = face_list[i_face]; + + // loop on Nodes of Dirichlet faces + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + // loop on faces of node + const double w_face_node = [&] { + for (size_t i_node_face = 0; i_node_face < node_to_face_matrix[node_id].size(); ++i_node_face) { + FaceId l_id = node_to_face_matrix[node_id][i_node_face]; + if (l_id == face_id) { + return w_rl(node_id, i_node_face); + } + } + throw UnexpectedError("unable to get face node weight"); + }(); + + for (size_t i_node_face = 0; i_node_face < node_to_face_matrix[node_id].size(); ++i_node_face) { + FaceId l_id = node_to_face_matrix[node_id][i_node_face]; + CellId dual_cell_id = face_dual_cell_id[l_id]; + + for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); + ++i_dual_node) { + const NodeId dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node]; + // find the dual node of interest + if (dual_node_primal_node_id[dual_node_id] == node_id) { + // get Cjr + const TinyVector<Dimension> Clr = dual_Cjr(dual_cell_id, i_dual_node); + const double alpha_face_id = mes_l[l_id] * alpha_l[l_id]; + for (size_t cell_id = 0; cell_id < face_to_cell_matrix[l_id].size(); ++cell_id) { + CellId i_id = face_to_cell_matrix[l_id][cell_id]; + const bool is_face_reversed_for_cell_i = (dot(dualClj[l_id], xl[l_id] - xj[i_id]) < 0); + + const TinyVector<Dimension> nil = [&] { + if (is_face_reversed_for_cell_i) { + return -nlj[l_id]; + } else { + return nlj[l_id]; + } + }(); + + const double a_ir = alpha_face_id * dot(nil, Clr); + // loop on faces of cells + b[cell_dof_number[i_id]] += w_face_node * a_ir * value_list[i_face]; + + // If l_id is Neumann do... + if (primal_face_is_neumann[l_id]) { + b[face_dof_number[l_id]] -= w_face_node * a_ir * value_list[i_face]; + } + } + } + } + } + } + } + } + }, + boundary_condition); + } + } + // EL b^L + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NeumannBoundaryCondition>) or + (std::is_same_v<T, FourierBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + const auto& value_list = bc.valueList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + FaceId face_id = face_list[i_face]; + Assert(face_to_cell_matrix[face_id].size() == 1); + b[face_dof_number[face_id]] += mes_l[face_id] * value_list[i_face]; // sign + } + } + }, + boundary_condition); + } + + Vector<double> T{number_of_dof}; + T = zero; + + LinearSolver solver; + solver.solveLocalSystem(A, T, b); + + CellValue<double> Temperature{mesh->connectivity()}; + FaceValue<double> Temperature_face{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { Temperature[cell_id] = T[cell_dof_number[cell_id]]; }); + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { + if (primal_face_is_neumann[face_id]) { + Temperature_face[face_id] = T[face_dof_number[face_id]]; + } else { + Temperature_face[face_id] = Tl[face_id]; + } + }); + Vector<double> error{mesh->numberOfCells()}; + CellValue<double> cell_error{mesh->connectivity()}; + Vector<double> face_error{mesh->numberOfFaces()}; + double error_max = 0.; + size_t cell_max = 0; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + error[cell_id] = (Temperature[cell_id] - Tj[cell_id]) * sqrt(primal_Vj[cell_id]); + cell_error[cell_id] = (Temperature[cell_id] - Tj[cell_id]); + }); + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { + // ELIMINATION ENLEVER DIRICHLET + if (primal_face_is_neumann[face_id]) { + face_error[face_id] = (Temperature_face[face_id] - Tl[face_id]) * sqrt(mes_l[face_id]); + } else { + face_error[face_id] = 0; + } + }); + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); cell_id++) { + if (error_max < std::abs(cell_error[cell_id])) { + error_max = std::abs(cell_error[cell_id]); + cell_max = cell_id; + } + } + + std::cout << " ||Error||_max (cell)= " << error_max << " on cell " << cell_max << "\n"; + std::cout << "||Error||_2 (cell)= " << std::sqrt(dot(error, error)) << "\n"; + std::cout << "||Error||_2 (face)= " << std::sqrt(dot(face_error, face_error)) << "\n"; + std::cout << "||Error||_2 (total)= " << std::sqrt(dot(error, error)) + std::sqrt(dot(face_error, face_error)) + << "\n"; + } + } else { + throw NotImplementedError("not done in 1d"); + } +} + +template <size_t Dimension> +class HeatDiamondScheme2<Dimension>::DirichletBoundaryCondition +{ + private: + const Array<const double> m_value_list; + const Array<const FaceId> m_face_list; + + const Array<const double> m_node_value_list; + const Array<const NodeId> m_node_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + const Array<const NodeId>& + nodeList() const + { + return m_node_list; + } + + const Array<const double>& + nodeValueList() const + { + return m_node_value_list; + } + + DirichletBoundaryCondition(const Array<const FaceId>& face_list, + const Array<const double>& value_list, + const Array<const NodeId>& node_list, + const Array<const double>& node_value_list) + : m_value_list{value_list}, m_face_list{face_list}, m_node_value_list(node_value_list), m_node_list(node_list) + { + Assert(m_value_list.size() == m_face_list.size()); + Assert(m_node_value_list.size() == m_node_list.size()); + } + + ~DirichletBoundaryCondition() = default; +}; + +template <size_t Dimension> +class HeatDiamondScheme2<Dimension>::NeumannBoundaryCondition +{ + private: + const Array<const double> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + NeumannBoundaryCondition(const Array<const FaceId>& face_list, const Array<const double>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~NeumannBoundaryCondition() = default; +}; + +template <size_t Dimension> +class HeatDiamondScheme2<Dimension>::FourierBoundaryCondition +{ + private: + const Array<const double> m_coef_list; + const Array<const double> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + const Array<const double>& + coefList() const + { + return m_coef_list; + } + + public: + FourierBoundaryCondition(const Array<const FaceId>& face_list, + const Array<const double>& coef_list, + const Array<const double>& value_list) + : m_coef_list{coef_list}, m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_coef_list.size() == m_face_list.size()); + Assert(m_value_list.size() == m_face_list.size()); + } + + ~FourierBoundaryCondition() = default; +}; + +template <size_t Dimension> +class HeatDiamondScheme2<Dimension>::SymmetryBoundaryCondition +{ + private: + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + public: + SymmetryBoundaryCondition(const Array<const FaceId>& face_list) : m_face_list{face_list} {} + + ~SymmetryBoundaryCondition() = default; +}; + +template HeatDiamondScheme2<1>::HeatDiamondScheme2( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&); + +template HeatDiamondScheme2<2>::HeatDiamondScheme2( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&); + +template HeatDiamondScheme2<3>::HeatDiamondScheme2( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&); diff --git a/src/language/algorithms/HeatDiamondAlgorithm2.hpp b/src/language/algorithms/HeatDiamondAlgorithm2.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2cf388ed134d443fbcdbc7d3402f4a917610d19c --- /dev/null +++ b/src/language/algorithms/HeatDiamondAlgorithm2.hpp @@ -0,0 +1,29 @@ +#ifndef HEAT_DIAMOND_ALGORITHM2_HPP +#define HEAT_DIAMOND_ALGORITHM2_HPP + +#include <language/utils/FunctionSymbolId.hpp> +#include <mesh/IMesh.hpp> +#include <scheme/IBoundaryConditionDescriptor.hpp> + +#include <memory> +#include <variant> +#include <vector> + +template <size_t Dimension> +class HeatDiamondScheme2 +{ + private: + class DirichletBoundaryCondition; + class FourierBoundaryCondition; + class NeumannBoundaryCondition; + class SymmetryBoundaryCondition; + + public: + HeatDiamondScheme2(std::shared_ptr<const IMesh> i_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const FunctionSymbolId& T_id, + const FunctionSymbolId& kappa_id, + const FunctionSymbolId& f_id); +}; + +#endif // HEAT_DIAMOND_ALGORITHM2_HPP diff --git a/src/language/algorithms/ParabolicHeat.cpp b/src/language/algorithms/ParabolicHeat.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d535751ae11c75f2cf08e20024435d82738010d2 --- /dev/null +++ b/src/language/algorithms/ParabolicHeat.cpp @@ -0,0 +1,568 @@ +#include <algebra/LeastSquareSolver.hpp> +#include <algebra/LinearSolver.hpp> +#include <algebra/SmallMatrix.hpp> +#include <algebra/TinyVector.hpp> +#include <algebra/Vector.hpp> +#include <language/algorithms/ParabolicHeat.hpp> +#include <language/utils/InterpolateItemValue.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/DualConnectivityManager.hpp> +#include <mesh/DualMeshManager.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/MeshNodeBoundary.hpp> +#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp> +#include <mesh/SubItemValuePerItem.hpp> +#include <scheme/DirichletBoundaryConditionDescriptor.hpp> +#include <scheme/FourierBoundaryConditionDescriptor.hpp> +#include <scheme/NeumannBoundaryConditionDescriptor.hpp> +#include <scheme/ScalarDiamondScheme.hpp> +#include <scheme/SymmetryBoundaryConditionDescriptor.hpp> +#include <utils/Timer.hpp> + +template <size_t Dimension> +ParabolicHeatScheme<Dimension>::ParabolicHeatScheme( + std::shared_ptr<const IMesh> i_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const FunctionSymbolId& T_id, + const FunctionSymbolId& T_init_id, + const FunctionSymbolId& kappa_id, + const FunctionSymbolId& f_id, + const double& Tf, + const double& dt) +{ + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + using MeshDataType = MeshData<Dimension>; + + using BoundaryCondition = std::variant<DirichletBoundaryCondition, FourierBoundaryCondition, NeumannBoundaryCondition, + SymmetryBoundaryCondition>; + + using BoundaryConditionList = std::vector<BoundaryCondition>; + + BoundaryConditionList boundary_condition_list; + + std::cout << "number of bc descr = " << bc_descriptor_list.size() << '\n'; + std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + + for (const auto& bc_descriptor : bc_descriptor_list) { + bool is_valid_boundary_condition = true; + + switch (bc_descriptor->type()) { + case IBoundaryConditionDescriptor::Type::symmetry: { + throw NotImplementedError("NIY"); + break; + } + case IBoundaryConditionDescriptor::Type::dirichlet: { + const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor = + dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor); + if constexpr (Dimension > 1) { + MeshFaceBoundary<Dimension> mesh_face_boundary = + getMeshFaceBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + + const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId(); + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + Array<const double> value_list = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, + mesh_data.xl(), + mesh_face_boundary + .faceList()); + boundary_condition_list.push_back(DirichletBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + } else { + throw NotImplementedError("not implemented in 1d"); + } + break; + } + case IBoundaryConditionDescriptor::Type::fourier: { + throw NotImplementedError("NIY"); + break; + } + case IBoundaryConditionDescriptor::Type::neumann: { + const NeumannBoundaryConditionDescriptor& neumann_bc_descriptor = + dynamic_cast<const NeumannBoundaryConditionDescriptor&>(*bc_descriptor); + + if constexpr (Dimension > 1) { + MeshFaceBoundary<Dimension> mesh_face_boundary = + getMeshFaceBoundary(*mesh, neumann_bc_descriptor.boundaryDescriptor()); + + const FunctionSymbolId g_id = neumann_bc_descriptor.rhsSymbolId(); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + Array<const double> value_list = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, + mesh_data.xl(), + mesh_face_boundary + .faceList()); + boundary_condition_list.push_back(NeumannBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + } else { + throw NotImplementedError("not implemented in 1d"); + } + + break; + } + default: { + is_valid_boundary_condition = false; + } + } + if (not is_valid_boundary_condition) { + std::ostringstream error_msg; + error_msg << *bc_descriptor << " is an invalid boundary condition for heat equation"; + throw NormalError(error_msg.str()); + } + } + + if constexpr (Dimension > 1) { + const CellValue<const size_t> cell_dof_number = [&] { + CellValue<size_t> compute_cell_dof_number{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { compute_cell_dof_number[cell_id] = cell_id; }); + return compute_cell_dof_number; + }(); + size_t number_of_dof = mesh->numberOfCells(); + + const FaceValue<const size_t> face_dof_number = [&] { + FaceValue<size_t> compute_face_dof_number{mesh->connectivity()}; + compute_face_dof_number.fill(std::numeric_limits<size_t>::max()); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NeumannBoundaryCondition>) or + (std::is_same_v<T, FourierBoundaryCondition>) or + (std::is_same_v<T, SymmetryBoundaryCondition>) or + (std::is_same_v<T, DirichletBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + if (compute_face_dof_number[face_id] != std::numeric_limits<size_t>::max()) { + std::ostringstream os; + os << "The face " << face_id << " is used at least twice for boundary conditions"; + throw NormalError(os.str()); + } else { + compute_face_dof_number[face_id] = number_of_dof++; + } + } + } + }, + boundary_condition); + } + + return compute_face_dof_number; + }(); + + const FaceValue<const bool> primal_face_is_neumann = [&] { + FaceValue<bool> face_is_neumann{mesh->connectivity()}; + face_is_neumann.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NeumannBoundaryCondition>) or + (std::is_same_v<T, FourierBoundaryCondition>) or + (std::is_same_v<T, SymmetryBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + face_is_neumann[face_id] = true; + } + } + }, + boundary_condition); + } + + return face_is_neumann; + }(); + + const auto& primal_face_to_node_matrix = mesh->connectivity().faceToNodeMatrix(); + const auto& face_to_cell_matrix = mesh->connectivity().faceToCellMatrix(); + NodeValue<bool> primal_node_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of node_is_on_boundary is incorrect"); + } + + primal_node_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + primal_node_is_on_boundary[node_id] = true; + } + } + } + + FaceValue<bool> primal_face_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_on_boundary is incorrect"); + } + + primal_face_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + primal_face_is_on_boundary[face_id] = true; + } + } + + FaceValue<bool> primal_face_is_dirichlet(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_neumann is incorrect"); + } + + primal_face_is_dirichlet.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + primal_face_is_dirichlet[face_id] = (primal_face_is_on_boundary[face_id] && (!primal_face_is_neumann[face_id])); + } + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + CellValue<double> Tj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(T_id, mesh_data.xj()); + CellValue<double> Temperature = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(T_init_id, + mesh_data.xj()); + //{mesh->connectivity()}; + FaceValue<double> Tl = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(T_id, mesh_data.xl()); + FaceValue<double> Temperature_face = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(T_init_id, + mesh_data.xl()); + + double time = 0; + Assert(dt > 0, "The time-step have to be positive!"); + const CellValue<const double> primal_Vj = mesh_data.Vj(); + + InterpolationWeightsManager iwm(mesh, primal_face_is_on_boundary, primal_node_is_on_boundary); + iwm.compute(); + CellValuePerNode<double> w_rj = iwm.wrj(); + FaceValuePerNode<double> w_rl = iwm.wrl(); + do { + double deltat = std::min(dt, Tf - time); + std::cout << "Current time = " << time << " time-step = " << deltat << " final time = " << Tf << "\n"; + LegacyScalarDiamondScheme<Dimension>(i_mesh, bc_descriptor_list, kappa_id, f_id, Temperature, Temperature_face, + Tf, deltat, w_rj, w_rl); + time += deltat; + } while (time < Tf && std::abs(time - Tf) > 1e-15); + { + Vector<double> error{mesh->numberOfCells()}; + CellValue<double> cell_error{mesh->connectivity()}; + Vector<double> face_error{mesh->numberOfFaces()}; + double error_max = 0.; + size_t cell_max = 0; + const FaceValue<const double> mes_l = [&] { + if constexpr (Dimension == 1) { + FaceValue<double> compute_mes_l{mesh->connectivity()}; + compute_mes_l.fill(1); + return compute_mes_l; + } else { + return mesh_data.ll(); + } + }(); + + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + error[cell_id] = (Temperature[cell_id] - Tj[cell_id]) * sqrt(primal_Vj[cell_id]); + cell_error[cell_id] = (Temperature[cell_id] - Tj[cell_id]); + }); + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { + if (primal_face_is_on_boundary[face_id]) { + face_error[face_id] = (Temperature_face[face_id] - Tl[face_id]) * sqrt(mes_l[face_id]); + } else { + face_error[face_id] = 0; + } + }); + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); cell_id++) { + if (error_max < std::abs(cell_error[cell_id])) { + error_max = std::abs(cell_error[cell_id]); + cell_max = cell_id; + } + } + + std::cout << " ||Error||_max (cell)= " << error_max << " on cell " << cell_max << "\n"; + std::cout << "||Error||_2 (cell)= " << std::sqrt(dot(error, error)) << "\n"; + std::cout << "||Error||_2 (face)= " << std::sqrt(dot(face_error, face_error)) << "\n"; + std::cout << "||Error||_2 (total)= " << std::sqrt(dot(error, error)) + std::sqrt(dot(face_error, face_error)) + << "\n"; + } + } else { + throw NotImplementedError("not done in 1d"); + } +} + +template <size_t Dimension> +class ParabolicHeatScheme<Dimension>::DirichletBoundaryCondition +{ + private: + const Array<const double> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + DirichletBoundaryCondition(const Array<const FaceId>& face_list, const Array<const double>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~DirichletBoundaryCondition() = default; +}; + +template <size_t Dimension> +class ParabolicHeatScheme<Dimension>::NeumannBoundaryCondition +{ + private: + const Array<const double> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + NeumannBoundaryCondition(const Array<const FaceId>& face_list, const Array<const double>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~NeumannBoundaryCondition() = default; +}; + +template <size_t Dimension> +class ParabolicHeatScheme<Dimension>::FourierBoundaryCondition +{ + private: + const Array<const double> m_coef_list; + const Array<const double> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + const Array<const double>& + coefList() const + { + return m_coef_list; + } + + public: + FourierBoundaryCondition(const Array<const FaceId>& face_list, + const Array<const double>& coef_list, + const Array<const double>& value_list) + : m_coef_list{coef_list}, m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_coef_list.size() == m_face_list.size()); + Assert(m_value_list.size() == m_face_list.size()); + } + + ~FourierBoundaryCondition() = default; +}; + +template <size_t Dimension> +class ParabolicHeatScheme<Dimension>::SymmetryBoundaryCondition +{ + private: + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + public: + SymmetryBoundaryCondition(const Array<const FaceId>& face_list) : m_face_list{face_list} {} + + ~SymmetryBoundaryCondition() = default; +}; + +template <size_t Dimension> +class ParabolicHeatScheme<Dimension>::InterpolationWeightsManager +{ + private: + std::shared_ptr<const Mesh<Connectivity<Dimension>>> m_mesh; + FaceValue<bool> m_primal_face_is_on_boundary; + NodeValue<bool> m_primal_node_is_on_boundary; + CellValuePerNode<double> m_w_rj; + FaceValuePerNode<double> m_w_rl; + + public: + InterpolationWeightsManager(std::shared_ptr<const Mesh<Connectivity<Dimension>>> mesh, + FaceValue<bool> primal_face_is_on_boundary, + NodeValue<bool> primal_node_is_on_boundary) + : m_mesh(mesh), + m_primal_face_is_on_boundary(primal_face_is_on_boundary), + m_primal_node_is_on_boundary(primal_node_is_on_boundary) + {} + ~InterpolationWeightsManager() = default; + CellValuePerNode<double>& + wrj() + { + return m_w_rj; + } + FaceValuePerNode<double>& + wrl() + { + return m_w_rl; + } + void + compute() + { + using MeshDataType = MeshData<Dimension>; + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*m_mesh); + + const NodeValue<const TinyVector<Dimension>>& xr = m_mesh->xr(); + + const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl(); + const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj(); + const auto& node_to_cell_matrix = m_mesh->connectivity().nodeToCellMatrix(); + const auto& node_to_face_matrix = m_mesh->connectivity().nodeToFaceMatrix(); + CellValuePerNode<double> w_rj{m_mesh->connectivity()}; + FaceValuePerNode<double> w_rl{m_mesh->connectivity()}; + + for (size_t i = 0; i < w_rl.numberOfValues(); ++i) { + w_rl[i] = std::numeric_limits<double>::signaling_NaN(); + } + + for (NodeId i_node = 0; i_node < m_mesh->numberOfNodes(); ++i_node) { + SmallVector<double> b{Dimension + 1}; + b[0] = 1; + for (size_t i = 1; i < Dimension + 1; i++) { + b[i] = xr[i_node][i - 1]; + } + const auto& node_to_cell = node_to_cell_matrix[i_node]; + + if (not m_primal_node_is_on_boundary[i_node]) { + SmallMatrix<double> A{Dimension + 1, node_to_cell.size()}; + for (size_t j = 0; j < node_to_cell.size(); j++) { + A(0, j) = 1; + } + for (size_t i = 1; i < Dimension + 1; i++) { + for (size_t j = 0; j < node_to_cell.size(); j++) { + const CellId J = node_to_cell[j]; + A(i, j) = xj[J][i - 1]; + } + } + SmallVector<double> x{node_to_cell.size()}; + x = zero; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + for (size_t j = 0; j < node_to_cell.size(); j++) { + w_rj(i_node, j) = x[j]; + } + } else { + int nb_face_used = 0; + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (m_primal_face_is_on_boundary[face_id]) { + nb_face_used++; + } + } + SmallMatrix<double> A{Dimension + 1, node_to_cell.size() + nb_face_used}; + for (size_t j = 0; j < node_to_cell.size() + nb_face_used; j++) { + A(0, j) = 1; + } + for (size_t i = 1; i < Dimension + 1; i++) { + for (size_t j = 0; j < node_to_cell.size(); j++) { + const CellId J = node_to_cell[j]; + A(i, j) = xj[J][i - 1]; + } + } + for (size_t i = 1; i < Dimension + 1; i++) { + int cpt_face = 0; + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (m_primal_face_is_on_boundary[face_id]) { + A(i, node_to_cell.size() + cpt_face) = xl[face_id][i - 1]; + cpt_face++; + } + } + } + + SmallVector<double> x{node_to_cell.size() + nb_face_used}; + x = zero; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + for (size_t j = 0; j < node_to_cell.size(); j++) { + w_rj(i_node, j) = x[j]; + } + int cpt_face = node_to_cell.size(); + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (m_primal_face_is_on_boundary[face_id]) { + w_rl(i_node, i_face) = x[cpt_face++]; + } + } + } + } + m_w_rj = w_rj; + m_w_rl = w_rl; + } +}; + +template ParabolicHeatScheme<1>::ParabolicHeatScheme( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const double&, + const double&); + +template ParabolicHeatScheme<2>::ParabolicHeatScheme( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const double&, + const double&); + +template ParabolicHeatScheme<3>::ParabolicHeatScheme( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const double&, + const double&); diff --git a/src/language/algorithms/ParabolicHeat.hpp b/src/language/algorithms/ParabolicHeat.hpp new file mode 100644 index 0000000000000000000000000000000000000000..84ee713eea6c4616d533ce93b1f34268aad060cc --- /dev/null +++ b/src/language/algorithms/ParabolicHeat.hpp @@ -0,0 +1,33 @@ +#ifndef PARABOLIC_HEAT_HPP +#define PARABOLIC_HEAT_HPP + +#include <language/utils/FunctionSymbolId.hpp> +#include <mesh/IMesh.hpp> +#include <scheme/IBoundaryConditionDescriptor.hpp> + +#include <memory> +#include <variant> +#include <vector> + +template <size_t Dimension> +class ParabolicHeatScheme +{ + private: + class DirichletBoundaryCondition; + class FourierBoundaryCondition; + class NeumannBoundaryCondition; + class SymmetryBoundaryCondition; + class InterpolationWeightsManager; + + public: + ParabolicHeatScheme(std::shared_ptr<const IMesh> i_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const FunctionSymbolId& T_id, + const FunctionSymbolId& T_init_id, + const FunctionSymbolId& kappa_id, + const FunctionSymbolId& f_id, + const double& final_time, + const double& dt); +}; + +#endif // HEAT_DIAMOND_ALGORITHM_HPP diff --git a/src/language/algorithms/UnsteadyElasticity.cpp b/src/language/algorithms/UnsteadyElasticity.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6eb2017c1286f73356ae126f8e19998ae8015de8 --- /dev/null +++ b/src/language/algorithms/UnsteadyElasticity.cpp @@ -0,0 +1,613 @@ +#include <language/algorithms/UnsteadyElasticity.hpp> + +#include <algebra/CRSMatrix.hpp> +#include <algebra/CRSMatrixDescriptor.hpp> +#include <algebra/LeastSquareSolver.hpp> +#include <algebra/LinearSolver.hpp> +#include <algebra/SmallMatrix.hpp> +#include <algebra/TinyVector.hpp> +#include <algebra/Vector.hpp> +#include <language/utils/InterpolateItemValue.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/DualConnectivityManager.hpp> +#include <mesh/DualMeshManager.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/MeshFaceBoundary.hpp> +#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp> +#include <scheme/DirichletBoundaryConditionDescriptor.hpp> +#include <scheme/NeumannBoundaryConditionDescriptor.hpp> +#include <scheme/SymmetryBoundaryConditionDescriptor.hpp> +#include <scheme/VectorDiamondScheme.hpp> + +template <size_t Dimension> +UnsteadyElasticity<Dimension>::UnsteadyElasticity( + std::shared_ptr<const IMesh> i_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const FunctionSymbolId& lambda_id, + const FunctionSymbolId& mu_id, + const FunctionSymbolId& f_id, + const FunctionSymbolId& U_id, + const FunctionSymbolId& U_init_id, + const double& Tf, + const double& dt) +{ + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + using MeshDataType = MeshData<Dimension>; + + using BoundaryCondition = + std::variant<DirichletBoundaryCondition, NormalStrainBoundaryCondition, SymmetryBoundaryCondition>; + + using BoundaryConditionList = std::vector<BoundaryCondition>; + + BoundaryConditionList boundary_condition_list; + + std::cout << "number of bc descr = " << bc_descriptor_list.size() << '\n'; + std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + + NodeValue<bool> is_dirichlet{mesh->connectivity()}; + is_dirichlet.fill(false); + NodeValue<TinyVector<Dimension>> dirichlet_value{mesh->connectivity()}; + { + TinyVector<Dimension> nan_tiny_vector; + for (size_t i = 0; i < Dimension; ++i) { + nan_tiny_vector[i] = std::numeric_limits<double>::signaling_NaN(); + } + dirichlet_value.fill(nan_tiny_vector); + } + + for (const auto& bc_descriptor : bc_descriptor_list) { + bool is_valid_boundary_condition = true; + + switch (bc_descriptor->type()) { + case IBoundaryConditionDescriptor::Type::symmetry: { + const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor = + dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor); + if constexpr (Dimension > 1) { + MeshFaceBoundary mesh_face_boundary = getMeshFaceBoundary(*mesh, sym_bc_descriptor.boundaryDescriptor()); + boundary_condition_list.push_back(SymmetryBoundaryCondition{mesh_face_boundary.faceList()}); + + } else { + throw NotImplementedError("Symmetry conditions are not supported in 1d"); + } + + break; + } + case IBoundaryConditionDescriptor::Type::dirichlet: { + const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor = + dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor); + if (dirichlet_bc_descriptor.name() == "dirichlet") { + if constexpr (Dimension > 1) { + MeshFaceBoundary mesh_face_boundary = + getMeshFaceBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId(); + + Array<const TinyVector<Dimension>> value_list = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, mesh_data.xl(), + mesh_face_boundary.faceList()); + + boundary_condition_list.push_back(DirichletBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + } else { + throw NotImplementedError("Dirichlet conditions are not supported in 1d"); + } + } else if (dirichlet_bc_descriptor.name() == "normal_strain") { + if constexpr (Dimension > 1) { + MeshFaceBoundary mesh_face_boundary = + getMeshFaceBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId(); + + Array<const TinyVector<Dimension>> value_list = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, mesh_data.xl(), + mesh_face_boundary.faceList()); + boundary_condition_list.push_back(NormalStrainBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + } else { + throw NotImplementedError("Normal strain conditions are not supported in 1d"); + } + + } else { + is_valid_boundary_condition = false; + } + break; + } + default: { + is_valid_boundary_condition = false; + } + } + if (not is_valid_boundary_condition) { + std::ostringstream error_msg; + error_msg << *bc_descriptor << " is an invalid boundary condition for elasticity equation"; + throw NormalError(error_msg.str()); + } + } + + if constexpr (Dimension > 1) { + const CellValue<const size_t> cell_dof_number = [&] { + CellValue<size_t> compute_cell_dof_number{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { compute_cell_dof_number[cell_id] = cell_id; }); + return compute_cell_dof_number; + }(); + size_t number_of_dof = mesh->numberOfCells(); + + const FaceValue<const size_t> face_dof_number = [&] { + FaceValue<size_t> compute_face_dof_number{mesh->connectivity()}; + compute_face_dof_number.fill(std::numeric_limits<size_t>::max()); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NormalStrainBoundaryCondition>) or + (std::is_same_v<T, SymmetryBoundaryCondition>) or + (std::is_same_v<T, DirichletBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + if (compute_face_dof_number[face_id] != std::numeric_limits<size_t>::max()) { + std::ostringstream os; + os << "The face " << face_id << " is used at least twice for boundary conditions"; + throw NormalError(os.str()); + } else { + compute_face_dof_number[face_id] = number_of_dof++; + } + } + } + }, + boundary_condition); + } + + return compute_face_dof_number; + }(); + + const auto& primal_face_to_node_matrix = mesh->connectivity().faceToNodeMatrix(); + const auto& face_to_cell_matrix = mesh->connectivity().faceToCellMatrix(); + const FaceValue<const bool> primal_face_is_neumann = [&] { + FaceValue<bool> face_is_neumann{mesh->connectivity()}; + face_is_neumann.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NormalStrainBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + face_is_neumann[face_id] = true; + } + } + }, + boundary_condition); + } + + return face_is_neumann; + }(); + + FaceValue<bool> primal_face_is_symmetry = [&] { + FaceValue<bool> face_is_symmetry{mesh->connectivity()}; + face_is_symmetry.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, SymmetryBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + face_is_symmetry[face_id] = true; + } + } + }, + boundary_condition); + } + + return face_is_symmetry; + }(); + + NodeValue<bool> primal_node_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of node_is_on_boundary is incorrect"); + } + + primal_node_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + primal_node_is_on_boundary[node_id] = true; + } + } + } + + FaceValue<bool> primal_face_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_on_boundary is incorrect"); + } + + primal_face_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + primal_face_is_on_boundary[face_id] = true; + } + } + + FaceValue<bool> primal_face_is_dirichlet(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_neumann is incorrect"); + } + + primal_face_is_dirichlet.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + primal_face_is_dirichlet[face_id] = (primal_face_is_on_boundary[face_id] && (!primal_face_is_neumann[face_id]) && + (!primal_face_is_symmetry[face_id])); + } + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + const auto& node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); + const auto& node_to_face_matrix = mesh->connectivity().nodeToFaceMatrix(); + + { + CellValue<TinyVector<Dimension>> velocity = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::cell>(U_id, mesh_data.xj()); + CellValue<TinyVector<Dimension>> Uj = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::cell>(U_init_id, mesh_data.xj()); + + CellValue<TinyVector<Dimension>> fj = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::cell>(f_id, mesh_data.xj()); + FaceValue<TinyVector<Dimension>> velocity_face = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::face>(U_id, mesh_data.xl()); + FaceValue<TinyVector<Dimension>> Ul = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::face>(U_init_id, mesh_data.xl()); + + double time = 0; + Assert(dt > 0, "The time-step have to be positive!"); + const CellValue<const double> primal_Vj = mesh_data.Vj(); + + InterpolationWeightsManager iwm(mesh, primal_face_is_on_boundary, primal_node_is_on_boundary, + primal_face_is_symmetry); + iwm.compute(); + CellValuePerNode<double> w_rj = iwm.wrj(); + FaceValuePerNode<double> w_rl = iwm.wrl(); + do { + double deltat = std::min(dt, Tf - time); + std::cout << "Current time = " << time << " time-step = " << deltat << " final time = " << Tf << "\n"; + LegacyVectorDiamondScheme<Dimension>(i_mesh, bc_descriptor_list, lambda_id, mu_id, f_id, Uj, Ul, Tf, deltat, + w_rj, w_rl); + time += deltat; + } while (time < Tf && std::abs(time - Tf) > 1e-15); + + const FaceValue<const double> mes_l = [&] { + if constexpr (Dimension == 1) { + FaceValue<double> compute_mes_l{mesh->connectivity()}; + compute_mes_l.fill(1); + return compute_mes_l; + } else { + return mesh_data.ll(); + } + }(); + + Vector<double> Uexacte{mesh->numberOfCells() * Dimension}; + for (CellId j = 0; j < mesh->numberOfCells(); ++j) { + for (size_t l = 0; l < Dimension; ++l) { + Uexacte[(cell_dof_number[j] * Dimension) + l] = velocity[j][l]; + } + } + + Vector<double> error{mesh->numberOfCells() * Dimension}; + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + for (size_t i = 0; i < Dimension; ++i) { + error[(cell_id * Dimension) + i] = (Uj[cell_id][i] - velocity[cell_id][i]) * sqrt(primal_Vj[cell_id]); + } + } + Vector<double> error_face{mesh->numberOfFaces() * Dimension}; + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { + if (primal_face_is_on_boundary[face_id]) { + for (size_t i = 0; i < Dimension; ++i) { + error_face[face_id * Dimension + i] = (Ul[face_id][i] - velocity_face[face_id][i]) * sqrt(mes_l[face_id]); + } + } else { + error_face[face_id] = 0; + } + }); + + std::cout << "||Error||_2 (cell)= " << std::sqrt(dot(error, error)) << "\n"; + std::cout << "||Error||_2 (face)= " << std::sqrt(dot(error_face, error_face)) << "\n"; + std::cout << "||Error||_2 (total)= " << std::sqrt(dot(error, error)) + std::sqrt(dot(error_face, error_face)) + << "\n"; + + NodeValue<TinyVector<3>> ur3d{mesh->connectivity()}; + ur3d.fill(zero); + + parallel_for( + mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { + TinyVector<Dimension> x = zero; + const auto node_cells = node_to_cell_matrix[node_id]; + for (size_t i_cell = 0; i_cell < node_cells.size(); ++i_cell) { + CellId cell_id = node_cells[i_cell]; + x += w_rj(node_id, i_cell) * Uj[cell_id]; + } + const auto node_faces = node_to_face_matrix[node_id]; + for (size_t i_face = 0; i_face < node_faces.size(); ++i_face) { + FaceId face_id = node_faces[i_face]; + if (primal_face_is_on_boundary[face_id]) { + x += w_rl(node_id, i_face) * Ul[face_id]; + } + } + for (size_t i = 0; i < Dimension; ++i) { + ur3d[node_id][i] = x[i]; + } + }); + } + } else { + throw NotImplementedError("not done in 1d"); + } +} + +template <size_t Dimension> +class UnsteadyElasticity<Dimension>::DirichletBoundaryCondition +{ + private: + const Array<const TinyVector<Dimension>> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const TinyVector<Dimension>>& + valueList() const + { + return m_value_list; + } + + DirichletBoundaryCondition(const Array<const FaceId>& face_list, const Array<const TinyVector<Dimension>>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~DirichletBoundaryCondition() = default; +}; + +template <size_t Dimension> +class UnsteadyElasticity<Dimension>::NormalStrainBoundaryCondition +{ + private: + const Array<const TinyVector<Dimension>> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const TinyVector<Dimension>>& + valueList() const + { + return m_value_list; + } + + NormalStrainBoundaryCondition(const Array<const FaceId>& face_list, + const Array<const TinyVector<Dimension>>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~NormalStrainBoundaryCondition() = default; +}; + +template <size_t Dimension> +class UnsteadyElasticity<Dimension>::SymmetryBoundaryCondition +{ + private: + const Array<const TinyVector<Dimension>> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + public: + SymmetryBoundaryCondition(const Array<const FaceId>& face_list) : m_face_list{face_list} {} + + ~SymmetryBoundaryCondition() = default; +}; + +template <size_t Dimension> +class UnsteadyElasticity<Dimension>::InterpolationWeightsManager +{ + private: + std::shared_ptr<const Mesh<Connectivity<Dimension>>> m_mesh; + FaceValue<bool> m_primal_face_is_on_boundary; + NodeValue<bool> m_primal_node_is_on_boundary; + FaceValue<bool> m_primal_face_is_symmetry; + CellValuePerNode<double> m_w_rj; + FaceValuePerNode<double> m_w_rl; + + public: + InterpolationWeightsManager(std::shared_ptr<const Mesh<Connectivity<Dimension>>> mesh, + FaceValue<bool> primal_face_is_on_boundary, + NodeValue<bool> primal_node_is_on_boundary, + FaceValue<bool> primal_face_is_symmetry) + : m_mesh(mesh), + m_primal_face_is_on_boundary(primal_face_is_on_boundary), + m_primal_node_is_on_boundary(primal_node_is_on_boundary), + m_primal_face_is_symmetry(primal_face_is_symmetry) + {} + ~InterpolationWeightsManager() = default; + CellValuePerNode<double>& + wrj() + { + return m_w_rj; + } + FaceValuePerNode<double>& + wrl() + { + return m_w_rl; + } + void + compute() + { + using MeshDataType = MeshData<Dimension>; + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*m_mesh); + + const NodeValue<const TinyVector<Dimension>>& xr = m_mesh->xr(); + + const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl(); + const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj(); + const auto& node_to_cell_matrix = m_mesh->connectivity().nodeToCellMatrix(); + const auto& node_to_face_matrix = m_mesh->connectivity().nodeToFaceMatrix(); + const auto& face_to_cell_matrix = m_mesh->connectivity().faceToCellMatrix(); + + CellValuePerNode<double> w_rj{m_mesh->connectivity()}; + FaceValuePerNode<double> w_rl{m_mesh->connectivity()}; + + const NodeValuePerFace<const TinyVector<Dimension>> primal_nlr = mesh_data.nlr(); + auto project_to_face = [&](const TinyVector<Dimension>& x, const FaceId face_id) -> const TinyVector<Dimension> { + TinyVector<Dimension> proj; + const TinyVector<Dimension> nil = primal_nlr(face_id, 0); + proj = x - dot((x - xl[face_id]), nil) * nil; + return proj; + }; + + for (size_t i = 0; i < w_rl.numberOfValues(); ++i) { + w_rl[i] = std::numeric_limits<double>::signaling_NaN(); + } + + for (NodeId i_node = 0; i_node < m_mesh->numberOfNodes(); ++i_node) { + SmallVector<double> b{Dimension + 1}; + b[0] = 1; + for (size_t i = 1; i < Dimension + 1; i++) { + b[i] = xr[i_node][i - 1]; + } + const auto& node_to_cell = node_to_cell_matrix[i_node]; + + if (not m_primal_node_is_on_boundary[i_node]) { + SmallMatrix<double> A{Dimension + 1, node_to_cell.size()}; + for (size_t j = 0; j < node_to_cell.size(); j++) { + A(0, j) = 1; + } + for (size_t i = 1; i < Dimension + 1; i++) { + for (size_t j = 0; j < node_to_cell.size(); j++) { + const CellId J = node_to_cell[j]; + A(i, j) = xj[J][i - 1]; + } + } + + SmallVector<double> x{node_to_cell.size()}; + x = zero; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + for (size_t j = 0; j < node_to_cell.size(); j++) { + w_rj(i_node, j) = x[j]; + } + } else { + int nb_face_used = 0; + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (m_primal_face_is_on_boundary[face_id]) { + nb_face_used++; + } + } + SmallMatrix<double> A{Dimension + 1, node_to_cell.size() + nb_face_used}; + for (size_t j = 0; j < node_to_cell.size() + nb_face_used; j++) { + A(0, j) = 1; + } + for (size_t i = 1; i < Dimension + 1; i++) { + for (size_t j = 0; j < node_to_cell.size(); j++) { + const CellId J = node_to_cell[j]; + A(i, j) = xj[J][i - 1]; + } + } + for (size_t i = 1; i < Dimension + 1; i++) { + int cpt_face = 0; + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (m_primal_face_is_on_boundary[face_id]) { + if (m_primal_face_is_symmetry[face_id]) { + for (size_t j = 0; j < face_to_cell_matrix[face_id].size(); ++j) { + const CellId cell_id = face_to_cell_matrix[face_id][j]; + TinyVector<Dimension> xproj = project_to_face(xj[cell_id], face_id); + A(i, node_to_cell.size() + cpt_face) = xproj[i - 1]; + } + } else { + A(i, node_to_cell.size() + cpt_face) = xl[face_id][i - 1]; + } + cpt_face++; + } + } + } + + SmallVector<double> x{node_to_cell.size() + nb_face_used}; + x = zero; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + for (size_t j = 0; j < node_to_cell.size(); j++) { + w_rj(i_node, j) = x[j]; + } + int cpt_face = node_to_cell.size(); + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (m_primal_face_is_on_boundary[face_id]) { + w_rl(i_node, i_face) = x[cpt_face++]; + } + } + } + } + m_w_rj = w_rj; + m_w_rl = w_rl; + } +}; + +template UnsteadyElasticity<1>::UnsteadyElasticity( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const double&, + const double&); + +template UnsteadyElasticity<2>::UnsteadyElasticity( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const double&, + const double&); + +template UnsteadyElasticity<3>::UnsteadyElasticity( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const double&, + const double&); diff --git a/src/language/algorithms/UnsteadyElasticity.hpp b/src/language/algorithms/UnsteadyElasticity.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f55ef9b32cd544bcc53d1aed05646df510f65c5b --- /dev/null +++ b/src/language/algorithms/UnsteadyElasticity.hpp @@ -0,0 +1,36 @@ +#ifndef UNSTEADY_ELASTICITY_HPP +#define UNSTEADY_ELASTICITY_HPP + +#include <algebra/TinyVector.hpp> +#include <language/utils/FunctionSymbolId.hpp> +#include <memory> +#include <mesh/IMesh.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <scheme/IBoundaryConditionDescriptor.hpp> +#include <variant> +#include <vector> + +template <size_t Dimension> +class UnsteadyElasticity +{ + private: + class DirichletBoundaryCondition; + class NormalStrainBoundaryCondition; + class SymmetryBoundaryCondition; + class InterpolationWeightsManager; + + public: + UnsteadyElasticity(std::shared_ptr<const IMesh> i_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const FunctionSymbolId& lambda_id, + const FunctionSymbolId& mu_id, + const FunctionSymbolId& f_id, + const FunctionSymbolId& U_id, + const FunctionSymbolId& U_init_id, + const double& Tf, + const double& dt); +}; + +#endif // ELASTICITY_DIAMOND_ALGORITHM2_HPP diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp index 5d397223996c592687707711150705056e57879c..90645124a5d3fbb12cef19c4f105eb3f08465661 100644 --- a/src/language/modules/SchemeModule.cpp +++ b/src/language/modules/SchemeModule.cpp @@ -3,6 +3,12 @@ #include <analysis/GaussLegendreQuadratureDescriptor.hpp> #include <analysis/GaussLobattoQuadratureDescriptor.hpp> #include <analysis/GaussQuadratureDescriptor.hpp> +#include <language/algorithms/ElasticityDiamondAlgorithm.hpp> +#include <language/algorithms/Heat5PointsAlgorithm.hpp> +#include <language/algorithms/HeatDiamondAlgorithm.hpp> +#include <language/algorithms/HeatDiamondAlgorithm2.hpp> +#include <language/algorithms/ParabolicHeat.hpp> +#include <language/algorithms/UnsteadyElasticity.hpp> #include <language/modules/BinaryOperatorRegisterForVh.hpp> #include <language/modules/MathFunctionRegisterForVh.hpp> #include <language/modules/UnaryOperatorRegisterForVh.hpp> @@ -36,6 +42,7 @@ #include <scheme/IDiscreteFunctionDescriptor.hpp> #include <scheme/NeumannBoundaryConditionDescriptor.hpp> #include <scheme/SymmetryBoundaryConditionDescriptor.hpp> +#include <scheme/VectorDiamondScheme.hpp> #include <utils/Socket.hpp> #include <memory> @@ -371,6 +378,57 @@ SchemeModule::SchemeModule() )); + this->_addBuiltinFunction("dirichlet", + std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr< + const IBoundaryConditionDescriptor>(std::shared_ptr<const IBoundaryDescriptor>, + const FunctionSymbolId&)>>( + + [](std::shared_ptr<const IBoundaryDescriptor> boundary, + const FunctionSymbolId& g_id) -> std::shared_ptr<const IBoundaryConditionDescriptor> { + return std::make_shared<DirichletBoundaryConditionDescriptor>("dirichlet", boundary, + g_id); + } + + )); + + this->_addBuiltinFunction("normal_strain", + std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr< + const IBoundaryConditionDescriptor>(std::shared_ptr<const IBoundaryDescriptor>, + const FunctionSymbolId&)>>( + + [](std::shared_ptr<const IBoundaryDescriptor> boundary, + const FunctionSymbolId& g_id) -> std::shared_ptr<const IBoundaryConditionDescriptor> { + return std::make_shared<DirichletBoundaryConditionDescriptor>("normal_strain", boundary, + g_id); + } + + )); + + this->_addBuiltinFunction("fourier", + std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr< + const IBoundaryConditionDescriptor>(std::shared_ptr<const IBoundaryDescriptor>, + const FunctionSymbolId&, const FunctionSymbolId&)>>( + + [](std::shared_ptr<const IBoundaryDescriptor> boundary, const FunctionSymbolId& alpha_id, + const FunctionSymbolId& g_id) -> std::shared_ptr<const IBoundaryConditionDescriptor> { + return std::make_shared<FourierBoundaryConditionDescriptor>("fourier", boundary, + alpha_id, g_id); + } + + )); + + this->_addBuiltinFunction("neumann", + std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr< + const IBoundaryConditionDescriptor>(std::shared_ptr<const IBoundaryDescriptor>, + const FunctionSymbolId&)>>( + + [](std::shared_ptr<const IBoundaryDescriptor> boundary, + const FunctionSymbolId& g_id) -> std::shared_ptr<const IBoundaryConditionDescriptor> { + return std::make_shared<NeumannBoundaryConditionDescriptor>("neumann", boundary, g_id); + } + + )); + this->_addBuiltinFunction("external_fsi_velocity", std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr< const IBoundaryConditionDescriptor>(std::shared_ptr<const IBoundaryDescriptor>, @@ -457,6 +515,305 @@ SchemeModule::SchemeModule() )); + this->_addBuiltinFunction("heat", std::make_shared<BuiltinFunctionEmbedder< + void(std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, const FunctionSymbolId&, const FunctionSymbolId&)>>( + + [](std::shared_ptr<const IMesh> p_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const FunctionSymbolId& T_id, const FunctionSymbolId& kappa_id, + const FunctionSymbolId& f_id) -> void { + switch (p_mesh->dimension()) { + case 1: { + HeatDiamondScheme<1>{p_mesh, bc_descriptor_list, T_id, kappa_id, f_id}; + break; + } + case 2: { + HeatDiamondScheme<2>{p_mesh, bc_descriptor_list, T_id, kappa_id, f_id}; + break; + } + case 3: { + HeatDiamondScheme<3>{p_mesh, bc_descriptor_list, T_id, kappa_id, f_id}; + break; + } + default: { + throw UnexpectedError("invalid mesh dimension"); + } + } + } + + )); + + this->_addBuiltinFunction("parabolicheat", + std::make_shared<BuiltinFunctionEmbedder< + void(std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, const FunctionSymbolId&, const FunctionSymbolId&, + const FunctionSymbolId&, const double&, const double&)>>( + + [](std::shared_ptr<const IMesh> p_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const FunctionSymbolId& T_id, const FunctionSymbolId& T_init_id, + const FunctionSymbolId& kappa_id, const FunctionSymbolId& f_id, + const double& final_time, const double& dt) -> void { + switch (p_mesh->dimension()) { + case 1: { + ParabolicHeatScheme<1>{p_mesh, bc_descriptor_list, T_id, T_init_id, kappa_id, + f_id, final_time, dt}; + break; + } + case 2: { + ParabolicHeatScheme<2>{p_mesh, bc_descriptor_list, T_id, T_init_id, kappa_id, + f_id, final_time, dt}; + break; + } + case 3: { + ParabolicHeatScheme<3>{p_mesh, bc_descriptor_list, T_id, T_init_id, kappa_id, + f_id, final_time, dt}; + break; + } + default: { + throw UnexpectedError("invalid mesh dimension"); + } + } + } + + )); + + this->_addBuiltinFunction( + "parabolicheat", + std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr< + const IDiscreteFunction>(const std::shared_ptr<const IDiscreteFunction>&, + const std::shared_ptr<const IDiscreteFunction>&, + const std::shared_ptr<const IDiscreteFunction>&, + const std::shared_ptr<const IDiscreteFunction>&, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&)>>( + + [](const std::shared_ptr<const IDiscreteFunction>& alpha, + const std::shared_ptr<const IDiscreteFunction>& mub_dual, + const std::shared_ptr<const IDiscreteFunction>& mu_dual, const std::shared_ptr<const IDiscreteFunction>& f, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) + -> const std::shared_ptr<const IDiscreteFunction> { + return ScalarDiamondSchemeHandler{alpha, mub_dual, mu_dual, f, bc_descriptor_list}.solution(); + } + + )); + + this->_addBuiltinFunction("unsteadyelasticity", + std::make_shared<BuiltinFunctionEmbedder< + void(std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, const FunctionSymbolId&, const FunctionSymbolId&, + const FunctionSymbolId&, const FunctionSymbolId&, const double&, const double&)>>( + + [](std::shared_ptr<const IMesh> p_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const FunctionSymbolId& lambda_id, const FunctionSymbolId& mu_id, + const FunctionSymbolId& f_id, const FunctionSymbolId& U_id, + const FunctionSymbolId& U_init_id, const double& final_time, + const double& dt) -> void { + switch (p_mesh->dimension()) { + case 1: { + UnsteadyElasticity<1>{p_mesh, bc_descriptor_list, lambda_id, mu_id, f_id, + U_id, U_init_id, final_time, dt}; + break; + } + case 2: { + UnsteadyElasticity<2>{p_mesh, bc_descriptor_list, lambda_id, mu_id, f_id, + U_id, U_init_id, final_time, dt}; + break; + } + case 3: { + UnsteadyElasticity<3>{p_mesh, bc_descriptor_list, lambda_id, mu_id, f_id, + U_id, U_init_id, final_time, dt}; + break; + } + default: { + throw UnexpectedError("invalid mesh dimension"); + } + } + } + + )); + + this->_addBuiltinFunction("unsteadyelasticity", + std::make_shared<BuiltinFunctionEmbedder< + std::shared_ptr<const IDiscreteFunction>(const std::shared_ptr<const IDiscreteFunction>&, + const std::shared_ptr<const IDiscreteFunction>&, + const std::shared_ptr<const IDiscreteFunction>&, + const std::shared_ptr<const IDiscreteFunction>&, + const std::shared_ptr<const IDiscreteFunction>&, + const std::shared_ptr<const IDiscreteFunction>&, + const std::vector<std::shared_ptr< + const IBoundaryConditionDescriptor>>&)>>( + + [](const std::shared_ptr<const IDiscreteFunction> alpha, + const std::shared_ptr<const IDiscreteFunction> lambdab, + const std::shared_ptr<const IDiscreteFunction> mub, + const std::shared_ptr<const IDiscreteFunction> lambda, + const std::shared_ptr<const IDiscreteFunction> mu, + const std::shared_ptr<const IDiscreteFunction> f, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list) -> const std::shared_ptr<const IDiscreteFunction> { + return VectorDiamondSchemeHandler{alpha, lambdab, mub, lambda, mu, + f, bc_descriptor_list} + .solution(); + } + + )); + + this->_addBuiltinFunction("moleculardiffusion", + std::make_shared<BuiltinFunctionEmbedder<std::tuple< + std::shared_ptr<const IDiscreteFunction>, + std::shared_ptr<const IDiscreteFunction>>(const std::shared_ptr<const IDiscreteFunction>&, + const std::shared_ptr<const IDiscreteFunction>&, + const std::shared_ptr<const IDiscreteFunction>&, + const std::shared_ptr<const IDiscreteFunction>&, + const std::shared_ptr<const IDiscreteFunction>&, + const std::shared_ptr<const IDiscreteFunction>&, + const std::vector<std::shared_ptr< + const IBoundaryConditionDescriptor>>&)>>( + [](const std::shared_ptr<const IDiscreteFunction> alpha, + const std::shared_ptr<const IDiscreteFunction> lambdab, + const std::shared_ptr<const IDiscreteFunction> mub, + const std::shared_ptr<const IDiscreteFunction> lambda, + const std::shared_ptr<const IDiscreteFunction> mu, + const std::shared_ptr<const IDiscreteFunction> f, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list) -> const std::tuple<std::shared_ptr<const IDiscreteFunction>, + std::shared_ptr<const IDiscreteFunction>> { + return VectorDiamondSchemeHandler{alpha, lambdab, mub, lambda, mu, + f, bc_descriptor_list} + .apply(); + } + + )); + + this->_addBuiltinFunction("energybalance", + std::make_shared<BuiltinFunctionEmbedder<std::tuple< + std::shared_ptr<const IDiscreteFunction>, + std::shared_ptr<const IDiscreteFunction>>(const std::shared_ptr<const IDiscreteFunction>&, + const std::shared_ptr<const IDiscreteFunction>&, + const std::shared_ptr<const IDiscreteFunction>&, + const std::shared_ptr<const IDiscreteFunction>&, + const std::shared_ptr<const IDiscreteFunction>&, + const std::vector<std::shared_ptr< + const IBoundaryConditionDescriptor>>&)>>( + [](const std::shared_ptr<const IDiscreteFunction> lambdab, + const std::shared_ptr<const IDiscreteFunction> mub, + const std::shared_ptr<const IDiscreteFunction> U, + const std::shared_ptr<const IDiscreteFunction> dual_U, + const std::shared_ptr<const IDiscreteFunction> source, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list) -> const std::tuple<std::shared_ptr<const IDiscreteFunction>, + std::shared_ptr<const IDiscreteFunction>> { + return EnergyComputerHandler{lambdab, mub, U, dual_U, source, bc_descriptor_list} + .computeEnergyUpdate(); + } + + )); + + this->_addBuiltinFunction("heat2", + std::make_shared<BuiltinFunctionEmbedder< + void(std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, const FunctionSymbolId&, const FunctionSymbolId&)>>( + + [](std::shared_ptr<const IMesh> p_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const FunctionSymbolId& T_id, const FunctionSymbolId& kappa_id, + const FunctionSymbolId& f_id) -> void { + switch (p_mesh->dimension()) { + case 1: { + HeatDiamondScheme2<1>{p_mesh, bc_descriptor_list, T_id, kappa_id, f_id}; + break; + } + case 2: { + HeatDiamondScheme2<2>{p_mesh, bc_descriptor_list, T_id, kappa_id, f_id}; + break; + } + case 3: { + HeatDiamondScheme2<3>{p_mesh, bc_descriptor_list, T_id, kappa_id, f_id}; + break; + } + default: { + throw UnexpectedError("invalid mesh dimension"); + } + } + } + + )); + + this->_addBuiltinFunction("elasticity", + std::make_shared<BuiltinFunctionEmbedder< + void(std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, const FunctionSymbolId&, const FunctionSymbolId&, + const FunctionSymbolId&)>>( + + [](std::shared_ptr<const IMesh> p_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const FunctionSymbolId& lambda_id, const FunctionSymbolId& mu_id, + const FunctionSymbolId& f_id, const FunctionSymbolId& U_id) -> void { + switch (p_mesh->dimension()) { + case 1: { + ElasticityDiamondScheme<1>{p_mesh, bc_descriptor_list, lambda_id, mu_id, f_id, U_id}; + break; + } + case 2: { + ElasticityDiamondScheme<2>{p_mesh, bc_descriptor_list, lambda_id, mu_id, f_id, U_id}; + break; + } + case 3: { + ElasticityDiamondScheme<3>{p_mesh, bc_descriptor_list, lambda_id, mu_id, f_id, U_id}; + break; + } + default: { + throw UnexpectedError("invalid mesh dimension"); + } + } + } + + )); + + this->_addBuiltinFunction("heat5points", + std::make_shared<BuiltinFunctionEmbedder< + void(std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, const FunctionSymbolId&, const FunctionSymbolId)>>( + + [](std::shared_ptr<const IMesh> p_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const FunctionSymbolId& T_id, const FunctionSymbolId& kappa_id, + const FunctionSymbolId& f_id) -> void { + switch (p_mesh->dimension()) { + case 1: { + Heat5PointsAlgorithm<1>{p_mesh, bc_descriptor_list, T_id, kappa_id, f_id}; + break; + } + case 2: { + Heat5PointsAlgorithm<2>{p_mesh, bc_descriptor_list, T_id, kappa_id, f_id}; + break; + } + case 3: { + Heat5PointsAlgorithm<3>{p_mesh, bc_descriptor_list, T_id, kappa_id, f_id}; + break; + } + default: { + throw UnexpectedError("invalid mesh dimension"); + } + } + } + + )); + this ->_addBuiltinFunction("lagrangian", std::make_shared<BuiltinFunctionEmbedder< diff --git a/src/scheme/CMakeLists.txt b/src/scheme/CMakeLists.txt index 115d7bc08ce9aa72dced807b9120431f17543864..25e1405a5e9d2f9603a5f50a6abf04350596c58b 100644 --- a/src/scheme/CMakeLists.txt +++ b/src/scheme/CMakeLists.txt @@ -7,4 +7,6 @@ add_library( DiscreteFunctionInterpoler.cpp DiscreteFunctionUtils.cpp DiscreteFunctionVectorIntegrator.cpp - DiscreteFunctionVectorInterpoler.cpp) + DiscreteFunctionVectorInterpoler.cpp + ScalarDiamondScheme.cpp + VectorDiamondScheme.cpp) diff --git a/src/scheme/ScalarDiamondScheme.cpp b/src/scheme/ScalarDiamondScheme.cpp new file mode 100644 index 0000000000000000000000000000000000000000..07561e6f5abfee4461cd72d2e634bce94f7f12e8 --- /dev/null +++ b/src/scheme/ScalarDiamondScheme.cpp @@ -0,0 +1,851 @@ +#include <scheme/ScalarDiamondScheme.hpp> + +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/DiscreteFunctionUtils.hpp> + +template <size_t Dimension> +class ScalarDiamondSchemeHandler::InterpolationWeightsManager +{ + private: + std::shared_ptr<const Mesh<Connectivity<Dimension>>> m_mesh; + FaceValue<bool> m_primal_face_is_on_boundary; + NodeValue<bool> m_primal_node_is_on_boundary; + CellValuePerNode<double> m_w_rj; + FaceValuePerNode<double> m_w_rl; + + public: + CellValuePerNode<double>& + wrj() + { + return m_w_rj; + } + + FaceValuePerNode<double>& + wrl() + { + return m_w_rl; + } + + void + compute() + { + using MeshDataType = MeshData<Dimension>; + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*m_mesh); + + const NodeValue<const TinyVector<Dimension>>& xr = m_mesh->xr(); + + const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl(); + const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj(); + const auto& node_to_cell_matrix = m_mesh->connectivity().nodeToCellMatrix(); + const auto& node_to_face_matrix = m_mesh->connectivity().nodeToFaceMatrix(); + CellValuePerNode<double> w_rj{m_mesh->connectivity()}; + FaceValuePerNode<double> w_rl{m_mesh->connectivity()}; + + for (size_t i = 0; i < w_rl.numberOfValues(); ++i) { + w_rl[i] = std::numeric_limits<double>::signaling_NaN(); + } + + for (NodeId i_node = 0; i_node < m_mesh->numberOfNodes(); ++i_node) { + SmallVector<double> b{Dimension + 1}; + b[0] = 1; + for (size_t i = 1; i < Dimension + 1; i++) { + b[i] = xr[i_node][i - 1]; + } + const auto& node_to_cell = node_to_cell_matrix[i_node]; + + if (not m_primal_node_is_on_boundary[i_node]) { + SmallMatrix<double> A{Dimension + 1, node_to_cell.size()}; + for (size_t j = 0; j < node_to_cell.size(); j++) { + A(0, j) = 1; + } + for (size_t i = 1; i < Dimension + 1; i++) { + for (size_t j = 0; j < node_to_cell.size(); j++) { + const CellId J = node_to_cell[j]; + A(i, j) = xj[J][i - 1]; + } + } + SmallVector<double> x{node_to_cell.size()}; + x = zero; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + for (size_t j = 0; j < node_to_cell.size(); j++) { + w_rj(i_node, j) = x[j]; + } + } else { + int nb_face_used = 0; + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (m_primal_face_is_on_boundary[face_id]) { + nb_face_used++; + } + } + SmallMatrix<double> A{Dimension + 1, node_to_cell.size() + nb_face_used}; + for (size_t j = 0; j < node_to_cell.size() + nb_face_used; j++) { + A(0, j) = 1; + } + for (size_t i = 1; i < Dimension + 1; i++) { + for (size_t j = 0; j < node_to_cell.size(); j++) { + const CellId J = node_to_cell[j]; + A(i, j) = xj[J][i - 1]; + } + } + for (size_t i = 1; i < Dimension + 1; i++) { + int cpt_face = 0; + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (m_primal_face_is_on_boundary[face_id]) { + A(i, node_to_cell.size() + cpt_face) = xl[face_id][i - 1]; + cpt_face++; + } + } + } + + SmallVector<double> x{node_to_cell.size() + nb_face_used}; + x = zero; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + for (size_t j = 0; j < node_to_cell.size(); j++) { + w_rj(i_node, j) = x[j]; + } + int cpt_face = node_to_cell.size(); + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (m_primal_face_is_on_boundary[face_id]) { + w_rl(i_node, i_face) = x[cpt_face++]; + } + } + } + } + m_w_rj = w_rj; + m_w_rl = w_rl; + } + + InterpolationWeightsManager(std::shared_ptr<const Mesh<Connectivity<Dimension>>> mesh, + FaceValue<bool> primal_face_is_on_boundary, + NodeValue<bool> primal_node_is_on_boundary) + : m_mesh(mesh), + m_primal_face_is_on_boundary(primal_face_is_on_boundary), + m_primal_node_is_on_boundary(primal_node_is_on_boundary) + {} + + ~InterpolationWeightsManager() = default; +}; + +class ScalarDiamondSchemeHandler::IScalarDiamondScheme +{ + public: + virtual std::shared_ptr<const IDiscreteFunction> getSolution() const = 0; + + IScalarDiamondScheme() = default; + virtual ~IScalarDiamondScheme() = default; +}; + +template <size_t Dimension> +class ScalarDiamondSchemeHandler::ScalarDiamondScheme : public ScalarDiamondSchemeHandler::IScalarDiamondScheme +{ + private: + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + using MeshDataType = MeshData<Dimension>; + + std::shared_ptr<const DiscreteFunctionP0<Dimension, double>> m_solution; + + class DirichletBoundaryCondition + { + private: + const Array<const double> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + DirichletBoundaryCondition(const Array<const FaceId>& face_list, const Array<const double>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~DirichletBoundaryCondition() = default; + }; + + class NeumannBoundaryCondition + { + private: + const Array<const double> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + NeumannBoundaryCondition(const Array<const FaceId>& face_list, const Array<const double>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~NeumannBoundaryCondition() = default; + }; + + class FourierBoundaryCondition + { + private: + const Array<const double> m_coef_list; + const Array<const double> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + const Array<const double>& + coefList() const + { + return m_coef_list; + } + + public: + FourierBoundaryCondition(const Array<const FaceId>& face_list, + const Array<const double>& coef_list, + const Array<const double>& value_list) + : m_coef_list{coef_list}, m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_coef_list.size() == m_face_list.size()); + Assert(m_value_list.size() == m_face_list.size()); + } + + ~FourierBoundaryCondition() = default; + }; + + class SymmetryBoundaryCondition + { + private: + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + public: + SymmetryBoundaryCondition(const Array<const FaceId>& face_list) : m_face_list{face_list} {} + + ~SymmetryBoundaryCondition() = default; + }; + + public: + std::shared_ptr<const IDiscreteFunction> + getSolution() const final + { + return m_solution; + } + + ScalarDiamondScheme(const std::shared_ptr<const MeshType>& mesh, + const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& alpha, + const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& dual_mub, + const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& dual_mu, + const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& f, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) + { + Assert(DualMeshManager::instance().getDiamondDualMesh(*mesh) == dual_mu->mesh(), + "diffusion coefficient is not defined on the dual mesh!"); + Assert(DualMeshManager::instance().getDiamondDualMesh(*mesh) == dual_mub->mesh(), + "boundary diffusion coefficient is not defined on the dual mesh!"); + + using BoundaryCondition = std::variant<DirichletBoundaryCondition, FourierBoundaryCondition, + NeumannBoundaryCondition, SymmetryBoundaryCondition>; + + using BoundaryConditionList = std::vector<BoundaryCondition>; + + BoundaryConditionList boundary_condition_list; + + for (const auto& bc_descriptor : bc_descriptor_list) { + bool is_valid_boundary_condition = true; + + switch (bc_descriptor->type()) { + case IBoundaryConditionDescriptor::Type::symmetry: { + throw NotImplementedError("NIY"); + break; + } + case IBoundaryConditionDescriptor::Type::dirichlet: { + const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor = + dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor); + if constexpr (Dimension > 1) { + MeshFaceBoundary<Dimension> mesh_face_boundary = + getMeshFaceBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + + const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId(); + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + Array<const double> value_list = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, + mesh_data.xl(), + mesh_face_boundary + .faceList()); + + boundary_condition_list.push_back(DirichletBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + + } else { + throw NotImplementedError("Dirichlet BC in 1d"); + } + break; + } + case IBoundaryConditionDescriptor::Type::fourier: { + throw NotImplementedError("NIY"); + break; + } + case IBoundaryConditionDescriptor::Type::neumann: { + const NeumannBoundaryConditionDescriptor& neumann_bc_descriptor = + dynamic_cast<const NeumannBoundaryConditionDescriptor&>(*bc_descriptor); + + if constexpr (Dimension > 1) { + MeshFaceBoundary<Dimension> mesh_face_boundary = + getMeshFaceBoundary(*mesh, neumann_bc_descriptor.boundaryDescriptor()); + + const FunctionSymbolId g_id = neumann_bc_descriptor.rhsSymbolId(); + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + Array<const double> value_list = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, + mesh_data.xl(), + mesh_face_boundary + .faceList()); + + boundary_condition_list.push_back(NeumannBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + + } else { + throw NotImplementedError("Dirichlet BC in 1d"); + } + break; + } + default: { + is_valid_boundary_condition = false; + } + } + if (not is_valid_boundary_condition) { + std::ostringstream error_msg; + error_msg << *bc_descriptor << " is an invalid boundary condition for heat equation"; + throw NormalError(error_msg.str()); + } + } + + if constexpr (Dimension > 1) { + const CellValue<const size_t> cell_dof_number = [&] { + CellValue<size_t> compute_cell_dof_number{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { compute_cell_dof_number[cell_id] = cell_id; }); + return compute_cell_dof_number; + }(); + size_t number_of_dof = mesh->numberOfCells(); + + const FaceValue<const size_t> face_dof_number = [&] { + FaceValue<size_t> compute_face_dof_number{mesh->connectivity()}; + compute_face_dof_number.fill(std::numeric_limits<size_t>::max()); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NeumannBoundaryCondition>) or + (std::is_same_v<T, FourierBoundaryCondition>) or + (std::is_same_v<T, SymmetryBoundaryCondition>) or + (std::is_same_v<T, DirichletBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + if (compute_face_dof_number[face_id] != std::numeric_limits<size_t>::max()) { + std::ostringstream os; + os << "The face " << face_id << " is used at least twice for boundary conditions"; + throw NormalError(os.str()); + } else { + compute_face_dof_number[face_id] = number_of_dof++; + } + } + } + }, + boundary_condition); + } + + return compute_face_dof_number; + }(); + + const FaceValue<const bool> primal_face_is_neumann = [&] { + FaceValue<bool> face_is_neumann{mesh->connectivity()}; + face_is_neumann.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NeumannBoundaryCondition>) or + (std::is_same_v<T, FourierBoundaryCondition>) or + (std::is_same_v<T, SymmetryBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + face_is_neumann[face_id] = true; + } + } + }, + boundary_condition); + } + + return face_is_neumann; + }(); + + const auto& primal_face_to_node_matrix = mesh->connectivity().faceToNodeMatrix(); + const auto& face_to_cell_matrix = mesh->connectivity().faceToCellMatrix(); + NodeValue<bool> primal_node_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of node_is_on_boundary is incorrect"); + } + + primal_node_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + primal_node_is_on_boundary[node_id] = true; + } + } + } + + FaceValue<bool> primal_face_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_on_boundary is incorrect"); + } + + primal_face_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + primal_face_is_on_boundary[face_id] = true; + } + } + + FaceValue<bool> primal_face_is_dirichlet(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_neumann is incorrect"); + } + + primal_face_is_dirichlet.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + primal_face_is_dirichlet[face_id] = (primal_face_is_on_boundary[face_id] && (!primal_face_is_neumann[face_id])); + } + + InterpolationWeightsManager iwm(mesh, primal_face_is_on_boundary, primal_node_is_on_boundary); + iwm.compute(); + CellValuePerNode<double> w_rj = iwm.wrj(); + FaceValuePerNode<double> w_rl = iwm.wrl(); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl(); + const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj(); + const auto& node_to_face_matrix = mesh->connectivity().nodeToFaceMatrix(); + + { + std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh); + + MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh); + + std::shared_ptr mapper = + DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity()); + + CellValue<const double> dual_kappaj = dual_mu->cellValues(); + CellValue<const double> dual_kappajb = dual_mub->cellValues(); + + const CellValue<const double> dual_Vj = diamond_mesh_data.Vj(); + + const FaceValue<const double> mes_l = [&] { + if constexpr (Dimension == 1) { + FaceValue<double> compute_mes_l{mesh->connectivity()}; + compute_mes_l.fill(1); + return compute_mes_l; + } else { + return mesh_data.ll(); + } + }(); + + const CellValue<const double> dual_mes_l_j = [=] { + CellValue<double> compute_mes_j{diamond_mesh->connectivity()}; + mapper->toDualCell(mes_l, compute_mes_j); + + return compute_mes_j; + }(); + + FaceValue<const CellId> face_dual_cell_id = [=]() { + FaceValue<CellId> computed_face_dual_cell_id{mesh->connectivity()}; + CellValue<CellId> dual_cell_id{diamond_mesh->connectivity()}; + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { dual_cell_id[cell_id] = cell_id; }); + + mapper->fromDualCell(dual_cell_id, computed_face_dual_cell_id); + + return computed_face_dual_cell_id; + }(); + + NodeValue<const NodeId> dual_node_primal_node_id = [=]() { + CellValue<NodeId> cell_ignored_id{mesh->connectivity()}; + cell_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()}); + + NodeValue<NodeId> node_primal_id{mesh->connectivity()}; + + parallel_for( + mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { node_primal_id[node_id] = node_id; }); + + NodeValue<NodeId> computed_dual_node_primal_node_id{diamond_mesh->connectivity()}; + + mapper->toDualNode(node_primal_id, cell_ignored_id, computed_dual_node_primal_node_id); + + return computed_dual_node_primal_node_id; + }(); + + CellValue<NodeId> primal_cell_dual_node_id = [=]() { + CellValue<NodeId> cell_id{mesh->connectivity()}; + NodeValue<NodeId> node_ignored_id{mesh->connectivity()}; + node_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()}); + + NodeValue<NodeId> dual_node_id{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { dual_node_id[node_id] = node_id; }); + + CellValue<NodeId> computed_primal_cell_dual_node_id{mesh->connectivity()}; + + mapper->fromDualNode(dual_node_id, node_ignored_id, cell_id); + + return cell_id; + }(); + const auto& dual_Cjr = diamond_mesh_data.Cjr(); + FaceValue<TinyVector<Dimension>> dualClj = [&] { + FaceValue<TinyVector<Dimension>> computedClj{mesh->connectivity()}; + const auto& dual_node_to_cell_matrix = diamond_mesh->connectivity().nodeToCellMatrix(); + const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix(); + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { + const auto& primal_face_to_cell = face_to_cell_matrix[face_id]; + for (size_t i = 0; i < primal_face_to_cell.size(); i++) { + CellId cell_id = primal_face_to_cell[i]; + const NodeId dual_node_id = primal_cell_dual_node_id[cell_id]; + for (size_t i_dual_cell = 0; i_dual_cell < dual_node_to_cell_matrix[dual_node_id].size(); + i_dual_cell++) { + const CellId dual_cell_id = dual_node_to_cell_matrix[dual_node_id][i_dual_cell]; + if (face_dual_cell_id[face_id] == dual_cell_id) { + for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); + i_dual_node++) { + const NodeId final_dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node]; + if (final_dual_node_id == dual_node_id) { + computedClj[face_id] = dual_Cjr(dual_cell_id, i_dual_node); + } + } + } + } + } + }); + return computedClj; + }(); + + FaceValue<TinyVector<Dimension>> nlj = [&] { + FaceValue<TinyVector<Dimension>> computedNlj{mesh->connectivity()}; + parallel_for( + mesh->numberOfFaces(), + PUGS_LAMBDA(FaceId face_id) { computedNlj[face_id] = 1. / l2Norm(dualClj[face_id]) * dualClj[face_id]; }); + return computedNlj; + }(); + + FaceValue<const double> alpha_l = [&] { + CellValue<double> alpha_j{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + alpha_j[diamond_cell_id] = dual_kappaj[diamond_cell_id] / dual_Vj[diamond_cell_id]; + }); + + FaceValue<double> computed_alpha_l{mesh->connectivity()}; + mapper->fromDualCell(alpha_j, computed_alpha_l); + return computed_alpha_l; + }(); + + FaceValue<const double> alphab_l = [&] { + CellValue<double> alpha_jb{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + alpha_jb[diamond_cell_id] = dual_kappajb[diamond_cell_id] / dual_Vj[diamond_cell_id]; + }); + + FaceValue<double> computed_alpha_lb{mesh->connectivity()}; + mapper->fromDualCell(alpha_jb, computed_alpha_lb); + return computed_alpha_lb; + }(); + + const CellValue<const double> primal_Vj = mesh_data.Vj(); + + const Array<int> non_zeros{number_of_dof}; + non_zeros.fill(Dimension); + CRSMatrixDescriptor<double> S(number_of_dof, number_of_dof, non_zeros); + + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + const auto& primal_face_to_cell = face_to_cell_matrix[face_id]; + const double beta_l = l2Norm(dualClj[face_id]) * alpha_l[face_id] * mes_l[face_id]; + const double betab_l = l2Norm(dualClj[face_id]) * alphab_l[face_id] * mes_l[face_id]; + for (size_t i_cell = 0; i_cell < primal_face_to_cell.size(); ++i_cell) { + const CellId cell_id1 = primal_face_to_cell[i_cell]; + for (size_t j_cell = 0; j_cell < primal_face_to_cell.size(); ++j_cell) { + const CellId cell_id2 = primal_face_to_cell[j_cell]; + if (i_cell == j_cell) { + S(cell_dof_number[cell_id1], cell_dof_number[cell_id2]) += beta_l; + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id], cell_dof_number[cell_id2]) -= betab_l; + } + } else { + S(cell_dof_number[cell_id1], cell_dof_number[cell_id2]) -= beta_l; + } + } + } + } + + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + const size_t j = cell_dof_number[cell_id]; + S(j, j) += (*alpha)[cell_id] * primal_Vj[cell_id]; + } + + const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix(); + + const auto& primal_node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + const double alpha_face_id = mes_l[face_id] * alpha_l[face_id]; + const double alphab_face_id = mes_l[face_id] * alphab_l[face_id]; + + for (size_t i_face_cell = 0; i_face_cell < face_to_cell_matrix[face_id].size(); ++i_face_cell) { + CellId i_id = face_to_cell_matrix[face_id][i_face_cell]; + const bool is_face_reversed_for_cell_i = (dot(dualClj[face_id], xl[face_id] - xj[i_id]) < 0); + + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + + const TinyVector<Dimension> nil = [&] { + if (is_face_reversed_for_cell_i) { + return -nlj[face_id]; + } else { + return nlj[face_id]; + } + }(); + + CellId dual_cell_id = face_dual_cell_id[face_id]; + + for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); ++i_dual_node) { + const NodeId dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node]; + if (dual_node_primal_node_id[dual_node_id] == node_id) { + const TinyVector<Dimension> Clr = dual_Cjr(dual_cell_id, i_dual_node); + + const double a_ir = alpha_face_id * dot(nil, Clr); + const double ab_ir = alphab_face_id * dot(nil, Clr); + + for (size_t j_cell = 0; j_cell < primal_node_to_cell_matrix[node_id].size(); ++j_cell) { + CellId j_id = primal_node_to_cell_matrix[node_id][j_cell]; + S(cell_dof_number[i_id], cell_dof_number[j_id]) -= w_rj(node_id, j_cell) * a_ir; + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id], cell_dof_number[j_id]) += w_rj(node_id, j_cell) * ab_ir; + } + } + if (primal_node_is_on_boundary[node_id]) { + for (size_t l_face = 0; l_face < node_to_face_matrix[node_id].size(); ++l_face) { + FaceId l_id = node_to_face_matrix[node_id][l_face]; + if (primal_face_is_on_boundary[l_id]) { + S(cell_dof_number[i_id], face_dof_number[l_id]) -= w_rl(node_id, l_face) * a_ir; + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id], face_dof_number[l_id]) += w_rl(node_id, l_face) * ab_ir; + } + } + } + } + } + } + } + } + } + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (primal_face_is_dirichlet[face_id]) { + S(face_dof_number[face_id], face_dof_number[face_id]) += 1; + } + } + + CellValue<const double> fj = f->cellValues(); + + CRSMatrix A{S.getCRSMatrix()}; + Vector<double> b{number_of_dof}; + b = zero; + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + b[cell_dof_number[cell_id]] = fj[cell_id] * primal_Vj[cell_id]; + } + // Dirichlet on b^L_D + { + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<T, DirichletBoundaryCondition>) { + const auto& face_list = bc.faceList(); + const auto& value_list = bc.valueList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + b[face_dof_number[face_id]] += value_list[i_face]; + } + } + }, + boundary_condition); + } + } + // EL b^L + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NeumannBoundaryCondition>) or + (std::is_same_v<T, FourierBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + const auto& value_list = bc.valueList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + FaceId face_id = face_list[i_face]; + b[face_dof_number[face_id]] += mes_l[face_id] * value_list[i_face]; + } + } + }, + boundary_condition); + } + + Vector<double> T{number_of_dof}; + T = zero; + + LinearSolver solver; + solver.solveLocalSystem(A, T, b); + + m_solution = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + auto& solution = *m_solution; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { solution[cell_id] = T[cell_dof_number[cell_id]]; }); + } + } + } +}; + +std::shared_ptr<const IDiscreteFunction> +ScalarDiamondSchemeHandler::solution() const +{ + return m_scheme->getSolution(); +} + +ScalarDiamondSchemeHandler::ScalarDiamondSchemeHandler( + const std::shared_ptr<const IDiscreteFunction>& alpha, + const std::shared_ptr<const IDiscreteFunction>& dual_mub, + const std::shared_ptr<const IDiscreteFunction>& dual_mu, + const std::shared_ptr<const IDiscreteFunction>& f, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) +{ + const std::shared_ptr i_mesh = getCommonMesh({alpha, f}); + if (not i_mesh) { + throw NormalError("primal discrete functions are not defined on the same mesh"); + } + const std::shared_ptr i_dual_mesh = getCommonMesh({dual_mub, dual_mu}); + if (not i_dual_mesh) { + throw NormalError("dual discrete functions are not defined on the same mesh"); + } + checkDiscretizationType({alpha, dual_mub, dual_mu, f}, DiscreteFunctionType::P0); + + switch (i_mesh->dimension()) { + case 1: { + using MeshType = Mesh<Connectivity<1>>; + using DiscreteScalarFunctionType = DiscreteFunctionP0<1, double>; + + std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + + if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != i_dual_mesh) { + throw NormalError("dual variables are is not defined on the diamond dual of the primal mesh"); + } + + m_scheme = + std::make_unique<ScalarDiamondScheme<1>>(mesh, std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(alpha), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_mub), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_mu), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(f), + bc_descriptor_list); + break; + } + case 2: { + using MeshType = Mesh<Connectivity<2>>; + using DiscreteScalarFunctionType = DiscreteFunctionP0<2, double>; + + std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + + if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != i_dual_mesh) { + throw NormalError("dual variables are is not defined on the diamond dual of the primal mesh"); + } + + m_scheme = + std::make_unique<ScalarDiamondScheme<2>>(mesh, std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(alpha), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_mub), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_mu), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(f), + bc_descriptor_list); + break; + } + case 3: { + using MeshType = Mesh<Connectivity<3>>; + using DiscreteScalarFunctionType = DiscreteFunctionP0<3, double>; + + std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + + if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != i_dual_mesh) { + throw NormalError("dual variables are is not defined on the diamond dual of the primal mesh"); + } + + m_scheme = + std::make_unique<ScalarDiamondScheme<3>>(mesh, std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(alpha), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_mub), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_mu), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(f), + bc_descriptor_list); + break; + } + default: { + throw UnexpectedError("invalid mesh dimension"); + } + } +} + +ScalarDiamondSchemeHandler::~ScalarDiamondSchemeHandler() = default; diff --git a/src/scheme/ScalarDiamondScheme.hpp b/src/scheme/ScalarDiamondScheme.hpp new file mode 100644 index 0000000000000000000000000000000000000000..47b66177b5628bb8b13a0ea2d92ede5e41a84950 --- /dev/null +++ b/src/scheme/ScalarDiamondScheme.hpp @@ -0,0 +1,688 @@ +#ifndef SCALAR_DIAMOND_SCHEME_HPP +#define SCALAR_DIAMOND_SCHEME_HPP + +#include <algebra/CRSMatrix.hpp> +#include <algebra/CRSMatrixDescriptor.hpp> +#include <algebra/LeastSquareSolver.hpp> +#include <algebra/LinearSolver.hpp> +#include <algebra/TinyVector.hpp> +#include <algebra/Vector.hpp> +#include <language/utils/InterpolateItemValue.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/DualConnectivityManager.hpp> +#include <mesh/DualMeshManager.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/MeshFaceBoundary.hpp> +#include <mesh/MeshNodeBoundary.hpp> +#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp> +#include <mesh/SubItemValuePerItem.hpp> +#include <scheme/DirichletBoundaryConditionDescriptor.hpp> +#include <scheme/FourierBoundaryConditionDescriptor.hpp> +#include <scheme/IDiscreteFunction.hpp> +#include <scheme/NeumannBoundaryConditionDescriptor.hpp> +#include <scheme/SymmetryBoundaryConditionDescriptor.hpp> + +class ScalarDiamondSchemeHandler +{ + private: + class IScalarDiamondScheme; + + template <size_t Dimension> + class ScalarDiamondScheme; + + template <size_t Dimension> + class InterpolationWeightsManager; + + public: + std::unique_ptr<IScalarDiamondScheme> m_scheme; + + std::shared_ptr<const IDiscreteFunction> solution() const; + + ScalarDiamondSchemeHandler( + const std::shared_ptr<const IDiscreteFunction>& alpha, + const std::shared_ptr<const IDiscreteFunction>& mu_dualb, + const std::shared_ptr<const IDiscreteFunction>& mu_dual, + const std::shared_ptr<const IDiscreteFunction>& f, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list); + + ~ScalarDiamondSchemeHandler(); +}; + +template <size_t Dimension> +class LegacyScalarDiamondScheme +{ + private: + class DirichletBoundaryCondition + { + private: + const Array<const double> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + DirichletBoundaryCondition(const Array<const FaceId>& face_list, const Array<const double>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~DirichletBoundaryCondition() = default; + }; + + class NeumannBoundaryCondition + { + private: + const Array<const double> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + NeumannBoundaryCondition(const Array<const FaceId>& face_list, const Array<const double>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~NeumannBoundaryCondition() = default; + }; + + class FourierBoundaryCondition + { + private: + const Array<const double> m_coef_list; + const Array<const double> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + const Array<const double>& + coefList() const + { + return m_coef_list; + } + + public: + FourierBoundaryCondition(const Array<const FaceId>& face_list, + const Array<const double>& coef_list, + const Array<const double>& value_list) + : m_coef_list{coef_list}, m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_coef_list.size() == m_face_list.size()); + Assert(m_value_list.size() == m_face_list.size()); + } + + ~FourierBoundaryCondition() = default; + }; + + class SymmetryBoundaryCondition + { + private: + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + public: + SymmetryBoundaryCondition(const Array<const FaceId>& face_list) : m_face_list{face_list} {} + + ~SymmetryBoundaryCondition() = default; + }; + + public: + LegacyScalarDiamondScheme(std::shared_ptr<const IMesh> i_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const FunctionSymbolId& kappa_id, + const FunctionSymbolId& f_id, + CellValue<double>& Temperature, + FaceValue<double>& Temperature_face, + const double& Tf, + const double& dt, + const CellValuePerNode<double>& w_rj, + const FaceValuePerNode<double>& w_rl) + { + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + using MeshDataType = MeshData<Dimension>; + + using BoundaryCondition = std::variant<DirichletBoundaryCondition, FourierBoundaryCondition, + NeumannBoundaryCondition, SymmetryBoundaryCondition>; + + using BoundaryConditionList = std::vector<BoundaryCondition>; + + BoundaryConditionList boundary_condition_list; + + std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + + for (const auto& bc_descriptor : bc_descriptor_list) { + bool is_valid_boundary_condition = true; + + switch (bc_descriptor->type()) { + case IBoundaryConditionDescriptor::Type::symmetry: { + throw NotImplementedError("NIY"); + break; + } + case IBoundaryConditionDescriptor::Type::dirichlet: { + const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor = + dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor); + if constexpr (Dimension > 1) { + MeshFaceBoundary<Dimension> mesh_face_boundary = + getMeshFaceBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + + const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId(); + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + Array<const double> value_list = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, + mesh_data.xl(), + mesh_face_boundary + .faceList()); + boundary_condition_list.push_back(DirichletBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + } + break; + } + case IBoundaryConditionDescriptor::Type::fourier: { + throw NotImplementedError("NIY"); + break; + } + case IBoundaryConditionDescriptor::Type::neumann: { + const NeumannBoundaryConditionDescriptor& neumann_bc_descriptor = + dynamic_cast<const NeumannBoundaryConditionDescriptor&>(*bc_descriptor); + + if constexpr (Dimension > 1) { + MeshFaceBoundary<Dimension> mesh_face_boundary = + getMeshFaceBoundary(*mesh, neumann_bc_descriptor.boundaryDescriptor()); + + const FunctionSymbolId g_id = neumann_bc_descriptor.rhsSymbolId(); + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + Array<const double> value_list = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, + mesh_data.xl(), + mesh_face_boundary + .faceList()); + + boundary_condition_list.push_back(NeumannBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + + } else { + throw NotImplementedError("Dirichlet BC in 1d"); + } + break; + } + default: { + is_valid_boundary_condition = false; + } + } + if (not is_valid_boundary_condition) { + std::ostringstream error_msg; + error_msg << *bc_descriptor << " is an invalid boundary condition for heat equation"; + throw NormalError(error_msg.str()); + } + } + + if constexpr (Dimension > 1) { + const CellValue<const size_t> cell_dof_number = [&] { + CellValue<size_t> compute_cell_dof_number{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { compute_cell_dof_number[cell_id] = cell_id; }); + return compute_cell_dof_number; + }(); + size_t number_of_dof = mesh->numberOfCells(); + + const FaceValue<const size_t> face_dof_number = [&] { + FaceValue<size_t> compute_face_dof_number{mesh->connectivity()}; + compute_face_dof_number.fill(std::numeric_limits<size_t>::max()); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NeumannBoundaryCondition>) or + (std::is_same_v<T, FourierBoundaryCondition>) or + (std::is_same_v<T, SymmetryBoundaryCondition>) or + (std::is_same_v<T, DirichletBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + if (compute_face_dof_number[face_id] != std::numeric_limits<size_t>::max()) { + std::ostringstream os; + os << "The face " << face_id << " is used at least twice for boundary conditions"; + throw NormalError(os.str()); + } else { + compute_face_dof_number[face_id] = number_of_dof++; + } + } + } + }, + boundary_condition); + } + + return compute_face_dof_number; + }(); + + const FaceValue<const bool> primal_face_is_neumann = [&] { + FaceValue<bool> face_is_neumann{mesh->connectivity()}; + face_is_neumann.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NeumannBoundaryCondition>) or + (std::is_same_v<T, FourierBoundaryCondition>) or + (std::is_same_v<T, SymmetryBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + face_is_neumann[face_id] = true; + } + } + }, + boundary_condition); + } + + return face_is_neumann; + }(); + + const auto& primal_face_to_node_matrix = mesh->connectivity().faceToNodeMatrix(); + const auto& face_to_cell_matrix = mesh->connectivity().faceToCellMatrix(); + NodeValue<bool> primal_node_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of node_is_on_boundary is incorrect"); + } + + primal_node_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + primal_node_is_on_boundary[node_id] = true; + } + } + } + + FaceValue<bool> primal_face_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_on_boundary is incorrect"); + } + + primal_face_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + primal_face_is_on_boundary[face_id] = true; + } + } + + FaceValue<bool> primal_face_is_dirichlet(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_neumann is incorrect"); + } + + primal_face_is_dirichlet.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + primal_face_is_dirichlet[face_id] = (primal_face_is_on_boundary[face_id] && (!primal_face_is_neumann[face_id])); + } + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl(); + const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj(); + const auto& node_to_face_matrix = mesh->connectivity().nodeToFaceMatrix(); + + { + std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh); + + MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh); + + std::shared_ptr mapper = + DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity()); + + CellValue<double> kappaj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(kappa_id, + mesh_data.xj()); + + CellValue<double> dual_kappaj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(kappa_id, + diamond_mesh_data + .xj()); + + const CellValue<const double> dual_Vj = diamond_mesh_data.Vj(); + + const FaceValue<const double> mes_l = [&] { + if constexpr (Dimension == 1) { + FaceValue<double> compute_mes_l{mesh->connectivity()}; + compute_mes_l.fill(1); + return compute_mes_l; + } else { + return mesh_data.ll(); + } + }(); + + const CellValue<const double> dual_mes_l_j = [=] { + CellValue<double> compute_mes_j{diamond_mesh->connectivity()}; + mapper->toDualCell(mes_l, compute_mes_j); + + return compute_mes_j; + }(); + + FaceValue<const CellId> face_dual_cell_id = [=]() { + FaceValue<CellId> computed_face_dual_cell_id{mesh->connectivity()}; + CellValue<CellId> dual_cell_id{diamond_mesh->connectivity()}; + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { dual_cell_id[cell_id] = cell_id; }); + + mapper->fromDualCell(dual_cell_id, computed_face_dual_cell_id); + + return computed_face_dual_cell_id; + }(); + + NodeValue<const NodeId> dual_node_primal_node_id = [=]() { + CellValue<NodeId> cell_ignored_id{mesh->connectivity()}; + cell_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()}); + + NodeValue<NodeId> node_primal_id{mesh->connectivity()}; + + parallel_for( + mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { node_primal_id[node_id] = node_id; }); + + NodeValue<NodeId> computed_dual_node_primal_node_id{diamond_mesh->connectivity()}; + + mapper->toDualNode(node_primal_id, cell_ignored_id, computed_dual_node_primal_node_id); + + return computed_dual_node_primal_node_id; + }(); + + CellValue<NodeId> primal_cell_dual_node_id = [=]() { + CellValue<NodeId> cell_id{mesh->connectivity()}; + NodeValue<NodeId> node_ignored_id{mesh->connectivity()}; + node_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()}); + + NodeValue<NodeId> dual_node_id{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { dual_node_id[node_id] = node_id; }); + + CellValue<NodeId> computed_primal_cell_dual_node_id{mesh->connectivity()}; + + mapper->fromDualNode(dual_node_id, node_ignored_id, cell_id); + + return cell_id; + }(); + const auto& dual_Cjr = diamond_mesh_data.Cjr(); + FaceValue<TinyVector<Dimension>> dualClj = [&] { + FaceValue<TinyVector<Dimension>> computedClj{mesh->connectivity()}; + const auto& dual_node_to_cell_matrix = diamond_mesh->connectivity().nodeToCellMatrix(); + const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix(); + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { + const auto& primal_face_to_cell = face_to_cell_matrix[face_id]; + for (size_t i = 0; i < primal_face_to_cell.size(); i++) { + CellId cell_id = primal_face_to_cell[i]; + const NodeId dual_node_id = primal_cell_dual_node_id[cell_id]; + for (size_t i_dual_cell = 0; i_dual_cell < dual_node_to_cell_matrix[dual_node_id].size(); + i_dual_cell++) { + const CellId dual_cell_id = dual_node_to_cell_matrix[dual_node_id][i_dual_cell]; + if (face_dual_cell_id[face_id] == dual_cell_id) { + for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); + i_dual_node++) { + const NodeId final_dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node]; + if (final_dual_node_id == dual_node_id) { + computedClj[face_id] = dual_Cjr(dual_cell_id, i_dual_node); + } + } + } + } + } + }); + return computedClj; + }(); + + FaceValue<TinyVector<Dimension>> nlj = [&] { + FaceValue<TinyVector<Dimension>> computedNlj{mesh->connectivity()}; + parallel_for( + mesh->numberOfFaces(), + PUGS_LAMBDA(FaceId face_id) { computedNlj[face_id] = 1. / l2Norm(dualClj[face_id]) * dualClj[face_id]; }); + return computedNlj; + }(); + + FaceValue<const double> alpha_l = [&] { + CellValue<double> alpha_j{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + alpha_j[diamond_cell_id] = dual_kappaj[diamond_cell_id] / dual_Vj[diamond_cell_id]; + }); + + FaceValue<double> computed_alpha_l{mesh->connectivity()}; + mapper->fromDualCell(alpha_j, computed_alpha_l); + return computed_alpha_l; + }(); + + double lambda = (Tf == 0) ? 0 : 1; + double time_factor = (lambda == 0) ? 1 : dt; + + const CellValue<const double> primal_Vj = mesh_data.Vj(); + Array<int> non_zero{number_of_dof}; + non_zero.fill(Dimension * Dimension); + CRSMatrixDescriptor<double> S(number_of_dof, number_of_dof, non_zero); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + const auto& primal_face_to_cell = face_to_cell_matrix[face_id]; + const double beta_l = l2Norm(dualClj[face_id]) * alpha_l[face_id] * mes_l[face_id]; + for (size_t i_cell = 0; i_cell < primal_face_to_cell.size(); ++i_cell) { + const CellId cell_id1 = primal_face_to_cell[i_cell]; + for (size_t j_cell = 0; j_cell < primal_face_to_cell.size(); ++j_cell) { + const CellId cell_id2 = primal_face_to_cell[j_cell]; + if (i_cell == j_cell) { + S(cell_dof_number[cell_id1], cell_dof_number[cell_id2]) += + (time_factor * beta_l + lambda * primal_Vj[cell_id1]); + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id], cell_dof_number[cell_id2]) -= beta_l; + } + } else { + S(cell_dof_number[cell_id1], cell_dof_number[cell_id2]) -= time_factor * beta_l; + } + } + } + } + + const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix(); + + const auto& primal_node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + const double alpha_face_id = mes_l[face_id] * alpha_l[face_id]; + + for (size_t i_face_cell = 0; i_face_cell < face_to_cell_matrix[face_id].size(); ++i_face_cell) { + CellId i_id = face_to_cell_matrix[face_id][i_face_cell]; + const bool is_face_reversed_for_cell_i = (dot(dualClj[face_id], xl[face_id] - xj[i_id]) < 0); + + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + + const TinyVector<Dimension> nil = [&] { + if (is_face_reversed_for_cell_i) { + return -nlj[face_id]; + } else { + return nlj[face_id]; + } + }(); + + CellId dual_cell_id = face_dual_cell_id[face_id]; + + for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); ++i_dual_node) { + const NodeId dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node]; + if (dual_node_primal_node_id[dual_node_id] == node_id) { + const TinyVector<Dimension> Clr = dual_Cjr(dual_cell_id, i_dual_node); + + const double a_ir = alpha_face_id * dot(nil, Clr); + + for (size_t j_cell = 0; j_cell < primal_node_to_cell_matrix[node_id].size(); ++j_cell) { + CellId j_id = primal_node_to_cell_matrix[node_id][j_cell]; + S(cell_dof_number[i_id], cell_dof_number[j_id]) -= time_factor * w_rj(node_id, j_cell) * a_ir; + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id], cell_dof_number[j_id]) += w_rj(node_id, j_cell) * a_ir; + } + } + if (primal_node_is_on_boundary[node_id]) { + for (size_t l_face = 0; l_face < node_to_face_matrix[node_id].size(); ++l_face) { + FaceId l_id = node_to_face_matrix[node_id][l_face]; + if (primal_face_is_on_boundary[l_id]) { + S(cell_dof_number[i_id], face_dof_number[l_id]) -= time_factor * w_rl(node_id, l_face) * a_ir; + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id], face_dof_number[l_id]) += w_rl(node_id, l_face) * a_ir; + } + } + } + } + } + } + } + } + } + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (primal_face_is_dirichlet[face_id]) { + S(face_dof_number[face_id], face_dof_number[face_id]) += 1; + } + } + + CellValue<double> fj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(f_id, + mesh_data.xj()); + + CRSMatrix A{S.getCRSMatrix()}; + Vector<double> b{number_of_dof}; + b = zero; + + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + b[cell_dof_number[cell_id]] = + (time_factor * fj[cell_id] + lambda * Temperature[cell_id]) * primal_Vj[cell_id]; + } + // Dirichlet on b^L_D + { + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<T, DirichletBoundaryCondition>) { + const auto& face_list = bc.faceList(); + const auto& value_list = bc.valueList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + b[face_dof_number[face_id]] += value_list[i_face]; + } + } + }, + boundary_condition); + } + } + // EL b^L + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NeumannBoundaryCondition>) or + (std::is_same_v<T, FourierBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + const auto& value_list = bc.valueList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + FaceId face_id = face_list[i_face]; + b[face_dof_number[face_id]] += mes_l[face_id] * value_list[i_face]; + } + } + }, + boundary_condition); + } + + Vector<double> T{number_of_dof}; + T = zero; + + LinearSolver solver; + solver.solveLocalSystem(A, T, b); + + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { Temperature[cell_id] = T[cell_dof_number[cell_id]]; }); + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { + if (primal_face_is_neumann[face_id]) { + Temperature_face[face_id] = T[face_dof_number[face_id]]; + } + }); + } + } + } +}; + +template LegacyScalarDiamondScheme<1>::LegacyScalarDiamondScheme( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + CellValue<double>&, + FaceValue<double>&, + const double&, + const double&, + const CellValuePerNode<double>& w_rj, + const FaceValuePerNode<double>& w_rl); + +template LegacyScalarDiamondScheme<2>::LegacyScalarDiamondScheme( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + CellValue<double>&, + FaceValue<double>&, + const double&, + const double&, + const CellValuePerNode<double>& w_rj, + const FaceValuePerNode<double>& w_rl); + +template LegacyScalarDiamondScheme<3>::LegacyScalarDiamondScheme( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + CellValue<double>&, + FaceValue<double>&, + const double&, + const double&, + const CellValuePerNode<double>& w_rj, + const FaceValuePerNode<double>& w_rl); + +#endif // SCALAR_DIAMOND_SCHEME_HPP diff --git a/src/scheme/SymmetryBoundaryConditionDescriptor.hpp b/src/scheme/SymmetryBoundaryConditionDescriptor.hpp index 9364090dde18490a4803662c7eb770d9f6354b1c..68ef6a55853e8a6665e582277c76a1c6c7d57f63 100644 --- a/src/scheme/SymmetryBoundaryConditionDescriptor.hpp +++ b/src/scheme/SymmetryBoundaryConditionDescriptor.hpp @@ -12,13 +12,20 @@ class SymmetryBoundaryConditionDescriptor : public IBoundaryConditionDescriptor std::ostream& _write(std::ostream& os) const final { - os << "symmetry(" << *m_boundary_descriptor << ")"; + os << "symmetry(" << m_name << ',' << *m_boundary_descriptor << ")"; return os; } + const std::string_view m_name; + std::shared_ptr<const IBoundaryDescriptor> m_boundary_descriptor; public: + std::string_view + name() const + { + return m_name; + } const IBoundaryDescriptor& boundaryDescriptor() const final { diff --git a/src/scheme/VectorDiamondScheme.cpp b/src/scheme/VectorDiamondScheme.cpp new file mode 100644 index 0000000000000000000000000000000000000000..368713589c323baa59201ea312348837293eba5d --- /dev/null +++ b/src/scheme/VectorDiamondScheme.cpp @@ -0,0 +1,2035 @@ +#include <scheme/VectorDiamondScheme.hpp> + +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/DiscreteFunctionUtils.hpp> + +template <size_t Dimension> +class VectorDiamondSchemeHandler::InterpolationWeightsManager +{ + private: + std::shared_ptr<const Mesh<Connectivity<Dimension>>> m_mesh; + FaceValue<const bool> m_primal_face_is_on_boundary; + NodeValue<const bool> m_primal_node_is_on_boundary; + FaceValue<const bool> m_primal_face_is_symmetry; + CellValuePerNode<double> m_w_rj; + FaceValuePerNode<double> m_w_rl; + + public: + InterpolationWeightsManager(std::shared_ptr<const Mesh<Connectivity<Dimension>>> mesh, + FaceValue<const bool> primal_face_is_on_boundary, + NodeValue<const bool> primal_node_is_on_boundary, + FaceValue<const bool> primal_face_is_symmetry) + : m_mesh(mesh), + m_primal_face_is_on_boundary(primal_face_is_on_boundary), + m_primal_node_is_on_boundary(primal_node_is_on_boundary), + m_primal_face_is_symmetry(primal_face_is_symmetry) + {} + ~InterpolationWeightsManager() = default; + CellValuePerNode<double>& + wrj() + { + return m_w_rj; + } + FaceValuePerNode<double>& + wrl() + { + return m_w_rl; + } + void + compute() + { + using MeshDataType = MeshData<Dimension>; + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*m_mesh); + + const NodeValue<const TinyVector<Dimension>>& xr = m_mesh->xr(); + + const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl(); + const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj(); + const auto& node_to_cell_matrix = m_mesh->connectivity().nodeToCellMatrix(); + const auto& node_to_face_matrix = m_mesh->connectivity().nodeToFaceMatrix(); + const auto& face_to_cell_matrix = m_mesh->connectivity().faceToCellMatrix(); + + CellValuePerNode<double> w_rj{m_mesh->connectivity()}; + FaceValuePerNode<double> w_rl{m_mesh->connectivity()}; + + const NodeValuePerFace<const TinyVector<Dimension>> primal_nlr = mesh_data.nlr(); + auto project_to_face = [&](const TinyVector<Dimension>& x, const FaceId face_id) -> const TinyVector<Dimension> { + TinyVector<Dimension> proj; + const TinyVector<Dimension> nil = primal_nlr(face_id, 0); + proj = x - dot((x - xl[face_id]), nil) * nil; + return proj; + }; + + for (size_t i = 0; i < w_rl.numberOfValues(); ++i) { + w_rl[i] = std::numeric_limits<double>::signaling_NaN(); + } + + for (NodeId i_node = 0; i_node < m_mesh->numberOfNodes(); ++i_node) { + SmallVector<double> b{Dimension + 1}; + b[0] = 1; + for (size_t i = 1; i < Dimension + 1; i++) { + b[i] = xr[i_node][i - 1]; + } + const auto& node_to_cell = node_to_cell_matrix[i_node]; + + if (not m_primal_node_is_on_boundary[i_node]) { + SmallMatrix<double> A{Dimension + 1, node_to_cell.size()}; + for (size_t j = 0; j < node_to_cell.size(); j++) { + A(0, j) = 1; + } + for (size_t i = 1; i < Dimension + 1; i++) { + for (size_t j = 0; j < node_to_cell.size(); j++) { + const CellId J = node_to_cell[j]; + A(i, j) = xj[J][i - 1]; + } + } + + SmallVector<double> x{node_to_cell.size()}; + x = zero; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + for (size_t j = 0; j < node_to_cell.size(); j++) { + w_rj(i_node, j) = x[j]; + } + } else { + int nb_face_used = 0; + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (m_primal_face_is_on_boundary[face_id]) { + nb_face_used++; + } + } + SmallMatrix<double> A{Dimension + 1, node_to_cell.size() + nb_face_used}; + for (size_t j = 0; j < node_to_cell.size() + nb_face_used; j++) { + A(0, j) = 1; + } + for (size_t i = 1; i < Dimension + 1; i++) { + for (size_t j = 0; j < node_to_cell.size(); j++) { + const CellId J = node_to_cell[j]; + A(i, j) = xj[J][i - 1]; + } + } + for (size_t i = 1; i < Dimension + 1; i++) { + int cpt_face = 0; + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (m_primal_face_is_on_boundary[face_id]) { + if (m_primal_face_is_symmetry[face_id]) { + for (size_t j = 0; j < face_to_cell_matrix[face_id].size(); ++j) { + const CellId cell_id = face_to_cell_matrix[face_id][j]; + TinyVector<Dimension> xproj = project_to_face(xj[cell_id], face_id); + A(i, node_to_cell.size() + cpt_face) = xproj[i - 1]; + } + } else { + A(i, node_to_cell.size() + cpt_face) = xl[face_id][i - 1]; + } + cpt_face++; + } + } + } + + SmallVector<double> x{node_to_cell.size() + nb_face_used}; + x = zero; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + for (size_t j = 0; j < node_to_cell.size(); j++) { + w_rj(i_node, j) = x[j]; + } + int cpt_face = node_to_cell.size(); + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (m_primal_face_is_on_boundary[face_id]) { + w_rl(i_node, i_face) = x[cpt_face++]; + } + } + } + } + m_w_rj = w_rj; + m_w_rl = w_rl; + } +}; + +class VectorDiamondSchemeHandler::IVectorDiamondScheme +{ + public: + virtual std::shared_ptr<const IDiscreteFunction> getSolution() const = 0; + virtual std::shared_ptr<const IDiscreteFunction> getDualSolution() const = 0; + virtual std::tuple<std::shared_ptr<const IDiscreteFunction>, std::shared_ptr<const IDiscreteFunction>> apply() + const = 0; + // virtual std::tuple<std::shared_ptr<const IDiscreteFunction>, std::shared_ptr<const IDiscreteFunction>> + // computeEnergyUpdate() const = 0; + + IVectorDiamondScheme() = default; + virtual ~IVectorDiamondScheme() = default; +}; + +template <size_t Dimension> +class VectorDiamondSchemeHandler::VectorDiamondScheme : public VectorDiamondSchemeHandler::IVectorDiamondScheme +{ + private: + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + using MeshDataType = MeshData<Dimension>; + + std::shared_ptr<const DiscreteFunctionP0<Dimension, TinyVector<Dimension>>> m_solution; + std::shared_ptr<const DiscreteFunctionP0<Dimension, TinyVector<Dimension>>> m_dual_solution; + // std::shared_ptr<const DiscreteFunctionP0<Dimension, double>> m_energy_delta; + + class DirichletBoundaryCondition + { + private: + const Array<const TinyVector<Dimension>> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const TinyVector<Dimension>>& + valueList() const + { + return m_value_list; + } + + DirichletBoundaryCondition(const Array<const FaceId>& face_list, + const Array<const TinyVector<Dimension>>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~DirichletBoundaryCondition() = default; + }; + + class NormalStrainBoundaryCondition + { + private: + const Array<const TinyVector<Dimension>> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const TinyVector<Dimension>>& + valueList() const + { + return m_value_list; + } + + NormalStrainBoundaryCondition(const Array<const FaceId>& face_list, + const Array<const TinyVector<Dimension>>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~NormalStrainBoundaryCondition() = default; + }; + + class SymmetryBoundaryCondition + { + private: + const Array<const TinyVector<Dimension>> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + public: + SymmetryBoundaryCondition(const Array<const FaceId>& face_list) : m_face_list{face_list} {} + + ~SymmetryBoundaryCondition() = default; + }; + + public: + std::shared_ptr<const IDiscreteFunction> + getSolution() const final + { + return m_solution; + } + + std::shared_ptr<const IDiscreteFunction> + getDualSolution() const final + { + return m_dual_solution; + } + + std::tuple<std::shared_ptr<const IDiscreteFunction>, std::shared_ptr<const IDiscreteFunction>> + apply() const final + { + return {m_solution, m_dual_solution}; + } + + VectorDiamondScheme(const std::shared_ptr<const MeshType>& mesh, + const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& alpha, + const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& dual_lambdab, + const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& dual_mub, + const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& dual_lambda, + const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& dual_mu, + const std::shared_ptr<const DiscreteFunctionP0<Dimension, TinyVector<Dimension>>>& source, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) + { + Assert(mesh == alpha->mesh()); + Assert(mesh == source->mesh()); + Assert(dual_lambda->mesh() == dual_mu->mesh()); + Assert(dual_lambdab->mesh() == dual_mu->mesh()); + Assert(dual_mub->mesh() == dual_mu->mesh()); + Assert(DualMeshManager::instance().getDiamondDualMesh(*mesh) == dual_mu->mesh(), + "diffusion coefficient is not defined on the dual mesh!"); + + using MeshDataType = MeshData<Dimension>; + + using BoundaryCondition = + std::variant<DirichletBoundaryCondition, NormalStrainBoundaryCondition, SymmetryBoundaryCondition>; + + using BoundaryConditionList = std::vector<BoundaryCondition>; + + BoundaryConditionList boundary_condition_list; + + NodeValue<bool> is_dirichlet{mesh->connectivity()}; + is_dirichlet.fill(false); + NodeValue<TinyVector<Dimension>> dirichlet_value{mesh->connectivity()}; + { + TinyVector<Dimension> nan_tiny_vector; + for (size_t i = 0; i < Dimension; ++i) { + nan_tiny_vector[i] = std::numeric_limits<double>::signaling_NaN(); + } + dirichlet_value.fill(nan_tiny_vector); + } + + for (const auto& bc_descriptor : bc_descriptor_list) { + bool is_valid_boundary_condition = true; + + switch (bc_descriptor->type()) { + case IBoundaryConditionDescriptor::Type::symmetry: { + const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor = + dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor); + + if constexpr (Dimension > 1) { + MeshFlatFaceBoundary<Dimension> mesh_face_boundary = + getMeshFlatFaceBoundary(*mesh, sym_bc_descriptor.boundaryDescriptor()); + boundary_condition_list.push_back(SymmetryBoundaryCondition{mesh_face_boundary.faceList()}); + } else { + throw NotImplementedError("Symmetry conditions are not supported in 1d"); + } + + break; + } + case IBoundaryConditionDescriptor::Type::dirichlet: { + const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor = + dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor); + if (dirichlet_bc_descriptor.name() == "dirichlet") { + if constexpr (Dimension > 1) { + MeshFaceBoundary<Dimension> mesh_face_boundary = + getMeshFaceBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId(); + Array<const TinyVector<Dimension>> value_list = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, mesh_data.xl(), + mesh_face_boundary.faceList()); + boundary_condition_list.push_back(DirichletBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + } else { + throw NotImplementedError("Neumann conditions are not supported in 1d"); + } + } else if (dirichlet_bc_descriptor.name() == "normal_strain") { + if constexpr (Dimension > 1) { + MeshFaceBoundary<Dimension> mesh_face_boundary = + getMeshFaceBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId(); + + Array<const TinyVector<Dimension>> value_list = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, mesh_data.xl(), + mesh_face_boundary.faceList()); + boundary_condition_list.push_back(NormalStrainBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + + } else { + throw NotImplementedError("Normal strain conditions are not supported in 1d"); + } + } else { + is_valid_boundary_condition = false; + } + break; + } + default: { + is_valid_boundary_condition = false; + } + } + if (not is_valid_boundary_condition) { + std::ostringstream error_msg; + error_msg << *bc_descriptor << " is an invalid boundary condition for elasticity equation"; + throw NormalError(error_msg.str()); + } + } + + if constexpr (Dimension > 1) { + const CellValue<const size_t> cell_dof_number = [&] { + CellValue<size_t> compute_cell_dof_number{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { compute_cell_dof_number[cell_id] = cell_id; }); + return compute_cell_dof_number; + }(); + size_t number_of_dof = mesh->numberOfCells(); + + const FaceValue<const size_t> face_dof_number = [&] { + FaceValue<size_t> compute_face_dof_number{mesh->connectivity()}; + compute_face_dof_number.fill(std::numeric_limits<size_t>::max()); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NormalStrainBoundaryCondition>) or + (std::is_same_v<T, SymmetryBoundaryCondition>) or + (std::is_same_v<T, DirichletBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + if (compute_face_dof_number[face_id] != std::numeric_limits<size_t>::max()) { + std::ostringstream os; + os << "The face " << face_id << " is used at least twice for boundary conditions"; + throw NormalError(os.str()); + } else { + compute_face_dof_number[face_id] = number_of_dof++; + } + } + } + }, + boundary_condition); + } + + return compute_face_dof_number; + }(); + + const auto& primal_face_to_node_matrix = mesh->connectivity().faceToNodeMatrix(); + const auto& face_to_cell_matrix = mesh->connectivity().faceToCellMatrix(); + const FaceValue<const bool> primal_face_is_neumann = [&] { + FaceValue<bool> face_is_neumann{mesh->connectivity()}; + face_is_neumann.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NormalStrainBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + face_is_neumann[face_id] = true; + } + } + }, + boundary_condition); + } + + return face_is_neumann; + }(); + + const FaceValue<const bool> primal_face_is_symmetry = [&] { + FaceValue<bool> face_is_symmetry{mesh->connectivity()}; + face_is_symmetry.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, SymmetryBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + face_is_symmetry[face_id] = true; + } + } + }, + boundary_condition); + } + + return face_is_symmetry; + }(); + + NodeValue<bool> primal_node_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of node_is_on_boundary is incorrect"); + } + + primal_node_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + primal_node_is_on_boundary[node_id] = true; + } + } + } + + FaceValue<bool> primal_face_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_on_boundary is incorrect"); + } + + primal_face_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + primal_face_is_on_boundary[face_id] = true; + } + } + + FaceValue<bool> primal_face_is_dirichlet(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_neumann is incorrect"); + } + + primal_face_is_dirichlet.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + primal_face_is_dirichlet[face_id] = (primal_face_is_on_boundary[face_id] && + (!primal_face_is_neumann[face_id]) && (!primal_face_is_symmetry[face_id])); + } + + InterpolationWeightsManager iwm(mesh, primal_face_is_on_boundary, primal_node_is_on_boundary, + primal_face_is_symmetry); + iwm.compute(); + CellValuePerNode<double> w_rj = iwm.wrj(); + FaceValuePerNode<double> w_rl = iwm.wrl(); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl(); + const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj(); + // const auto& node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); + const auto& node_to_face_matrix = mesh->connectivity().nodeToFaceMatrix(); + const NodeValuePerFace<const TinyVector<Dimension>> primal_nlr = mesh_data.nlr(); + + { + std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh); + + MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh); + + std::shared_ptr mapper = + DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity()); + + CellValue<const double> dual_muj = dual_mu->cellValues(); + CellValue<const double> dual_lambdaj = dual_lambda->cellValues(); + CellValue<const double> dual_mubj = dual_mub->cellValues(); + CellValue<const double> dual_lambdabj = dual_lambdab->cellValues(); + + CellValue<const TinyVector<Dimension>> fj = source->cellValues(); + // for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + // std::cout << xj[cell_id] << "-> fj[" << cell_id << "]=" << fj[cell_id] << '\n'; + // } + + const CellValue<const double> dual_Vj = diamond_mesh_data.Vj(); + + const FaceValue<const double> mes_l = [&] { + if constexpr (Dimension == 1) { + FaceValue<double> compute_mes_l{mesh->connectivity()}; + compute_mes_l.fill(1); + return compute_mes_l; + } else { + return mesh_data.ll(); + } + }(); + + const CellValue<const double> dual_mes_l_j = [=] { + CellValue<double> compute_mes_j{diamond_mesh->connectivity()}; + mapper->toDualCell(mes_l, compute_mes_j); + + return compute_mes_j; + }(); + + const CellValue<const double> primal_Vj = mesh_data.Vj(); + + CellValue<const FaceId> dual_cell_face_id = [=]() { + CellValue<FaceId> computed_dual_cell_face_id{diamond_mesh->connectivity()}; + FaceValue<FaceId> primal_face_id{mesh->connectivity()}; + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { primal_face_id[face_id] = face_id; }); + + mapper->toDualCell(primal_face_id, computed_dual_cell_face_id); + + return computed_dual_cell_face_id; + }(); + + FaceValue<const CellId> face_dual_cell_id = [=]() { + FaceValue<CellId> computed_face_dual_cell_id{mesh->connectivity()}; + CellValue<CellId> dual_cell_id{diamond_mesh->connectivity()}; + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { dual_cell_id[cell_id] = cell_id; }); + + mapper->fromDualCell(dual_cell_id, computed_face_dual_cell_id); + + return computed_face_dual_cell_id; + }(); + + NodeValue<const NodeId> dual_node_primal_node_id = [=]() { + CellValue<NodeId> cell_ignored_id{mesh->connectivity()}; + cell_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()}); + + NodeValue<NodeId> node_primal_id{mesh->connectivity()}; + + parallel_for( + mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { node_primal_id[node_id] = node_id; }); + + NodeValue<NodeId> computed_dual_node_primal_node_id{diamond_mesh->connectivity()}; + + mapper->toDualNode(node_primal_id, cell_ignored_id, computed_dual_node_primal_node_id); + + return computed_dual_node_primal_node_id; + }(); + + CellValue<NodeId> primal_cell_dual_node_id = [=]() { + CellValue<NodeId> cell_id{mesh->connectivity()}; + NodeValue<NodeId> node_ignored_id{mesh->connectivity()}; + node_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()}); + + NodeValue<NodeId> dual_node_id{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { dual_node_id[node_id] = node_id; }); + + CellValue<NodeId> computed_primal_cell_dual_node_id{mesh->connectivity()}; + + mapper->fromDualNode(dual_node_id, node_ignored_id, cell_id); + + return cell_id; + }(); + const auto& dual_Cjr = diamond_mesh_data.Cjr(); + FaceValue<TinyVector<Dimension>> dualClj = [&] { + FaceValue<TinyVector<Dimension>> computedClj{mesh->connectivity()}; + const auto& dual_node_to_cell_matrix = diamond_mesh->connectivity().nodeToCellMatrix(); + const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix(); + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { + const auto& primal_face_to_cell = face_to_cell_matrix[face_id]; + for (size_t i = 0; i < primal_face_to_cell.size(); i++) { + CellId cell_id = primal_face_to_cell[i]; + const NodeId dual_node_id = primal_cell_dual_node_id[cell_id]; + for (size_t i_dual_cell = 0; i_dual_cell < dual_node_to_cell_matrix[dual_node_id].size(); + i_dual_cell++) { + const CellId dual_cell_id = dual_node_to_cell_matrix[dual_node_id][i_dual_cell]; + if (face_dual_cell_id[face_id] == dual_cell_id) { + for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); + i_dual_node++) { + const NodeId final_dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node]; + if (final_dual_node_id == dual_node_id) { + computedClj[face_id] = dual_Cjr(dual_cell_id, i_dual_node); + } + } + } + } + } + }); + return computedClj; + }(); + + FaceValue<TinyVector<Dimension>> nlj = [&] { + FaceValue<TinyVector<Dimension>> computedNlj{mesh->connectivity()}; + parallel_for( + mesh->numberOfFaces(), + PUGS_LAMBDA(FaceId face_id) { computedNlj[face_id] = 1. / l2Norm(dualClj[face_id]) * dualClj[face_id]; }); + return computedNlj; + }(); + + FaceValue<const double> alpha_lambda_l = [&] { + CellValue<double> alpha_j{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + alpha_j[diamond_cell_id] = dual_lambdaj[diamond_cell_id] / dual_Vj[diamond_cell_id]; + }); + + FaceValue<double> computed_alpha_l{mesh->connectivity()}; + mapper->fromDualCell(alpha_j, computed_alpha_l); + return computed_alpha_l; + }(); + + FaceValue<const double> alpha_mu_l = [&] { + CellValue<double> alpha_j{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + alpha_j[diamond_cell_id] = dual_muj[diamond_cell_id] / dual_Vj[diamond_cell_id]; + }); + + FaceValue<double> computed_alpha_l{mesh->connectivity()}; + mapper->fromDualCell(alpha_j, computed_alpha_l); + return computed_alpha_l; + }(); + + FaceValue<const double> alpha_lambdab_l = [&] { + CellValue<double> alpha_j{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + alpha_j[diamond_cell_id] = dual_lambdabj[diamond_cell_id] / dual_Vj[diamond_cell_id]; + }); + + FaceValue<double> computed_alpha_l{mesh->connectivity()}; + mapper->fromDualCell(alpha_j, computed_alpha_l); + return computed_alpha_l; + }(); + + FaceValue<const double> alpha_mub_l = [&] { + CellValue<double> alpha_j{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + alpha_j[diamond_cell_id] = dual_mubj[diamond_cell_id] / dual_Vj[diamond_cell_id]; + }); + + FaceValue<double> computed_alpha_l{mesh->connectivity()}; + mapper->fromDualCell(alpha_j, computed_alpha_l); + return computed_alpha_l; + }(); + + const TinyMatrix<Dimension> I = identity; + + const Array<int> non_zeros{number_of_dof * Dimension}; + non_zeros.fill(Dimension * Dimension); + CRSMatrixDescriptor<double> S(number_of_dof * Dimension, number_of_dof * Dimension, non_zeros); + + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + const double beta_mu_l = l2Norm(dualClj[face_id]) * alpha_mu_l[face_id] * mes_l[face_id]; + const double beta_lambda_l = l2Norm(dualClj[face_id]) * alpha_lambda_l[face_id] * mes_l[face_id]; + const double beta_mub_l = l2Norm(dualClj[face_id]) * alpha_mub_l[face_id] * mes_l[face_id]; + const double beta_lambdab_l = l2Norm(dualClj[face_id]) * alpha_lambdab_l[face_id] * mes_l[face_id]; + const auto& primal_face_to_cell = face_to_cell_matrix[face_id]; + for (size_t i_cell = 0; i_cell < primal_face_to_cell.size(); ++i_cell) { + const CellId i_id = primal_face_to_cell[i_cell]; + const bool is_face_reversed_for_cell_i = (dot(dualClj[face_id], xl[face_id] - xj[i_id]) < 0); + + const TinyVector<Dimension> nil = [&] { + if (is_face_reversed_for_cell_i) { + return -nlj[face_id]; + } else { + return nlj[face_id]; + } + }(); + TinyMatrix<Dimension> M = + beta_mu_l * I + beta_mu_l * tensorProduct(nil, nil) + beta_lambda_l * tensorProduct(nil, nil); + TinyMatrix<Dimension> Mb = + beta_mub_l * I + beta_mub_l * tensorProduct(nil, nil) + beta_lambdab_l * tensorProduct(nil, nil); + TinyMatrix<Dimension> N = 1.e0 * tensorProduct(nil, nil); + double coef_adim = beta_mu_l + beta_lambdab_l; + for (size_t j_cell = 0; j_cell < primal_face_to_cell.size(); ++j_cell) { + const CellId j_id = primal_face_to_cell[j_cell]; + if (i_cell == j_cell) { + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + S((cell_dof_number[i_id] * Dimension) + i, (cell_dof_number[j_id] * Dimension) + j) += M(i, j); + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id] * Dimension + i, cell_dof_number[j_id] * Dimension + j) -= + 1.e0 * Mb(i, j); + // S(face_dof_number[face_id] * Dimension + i, face_dof_number[face_id] * Dimension + j) += + // 1.e-10 * Mb(i, j); + } + if (primal_face_is_symmetry[face_id]) { + S(face_dof_number[face_id] * Dimension + i, cell_dof_number[j_id] * Dimension + j) += + ((i == j) ? -coef_adim : 0) + coef_adim * N(i, j); + S(face_dof_number[face_id] * Dimension + i, face_dof_number[face_id] * Dimension + j) += + (i == j) ? coef_adim : 0; + } + } + } + } else { + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + S((cell_dof_number[i_id] * Dimension) + i, (cell_dof_number[j_id] * Dimension) + j) -= M(i, j); + } + } + } + } + } + } + + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + for (size_t i = 0; i < Dimension; ++i) { + const size_t j = cell_dof_number[cell_id] * Dimension + i; + S(j, j) += (*alpha)[cell_id] * primal_Vj[cell_id]; + } + } + + const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix(); + const auto& primal_node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + const double alpha_mu_face_id = mes_l[face_id] * alpha_mu_l[face_id]; + const double alpha_lambda_face_id = mes_l[face_id] * alpha_lambda_l[face_id]; + const double alpha_mub_face_id = mes_l[face_id] * alpha_mub_l[face_id]; + const double alpha_lambdab_face_id = mes_l[face_id] * alpha_lambdab_l[face_id]; + + for (size_t i_face_cell = 0; i_face_cell < face_to_cell_matrix[face_id].size(); ++i_face_cell) { + CellId i_id = face_to_cell_matrix[face_id][i_face_cell]; + const bool is_face_reversed_for_cell_i = (dot(dualClj[face_id], xl[face_id] - xj[i_id]) < 0); + + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + + const TinyVector<Dimension> nil = [&] { + if (is_face_reversed_for_cell_i) { + return -nlj[face_id]; + } else { + return nlj[face_id]; + } + }(); + + CellId dual_cell_id = face_dual_cell_id[face_id]; + + for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); ++i_dual_node) { + const NodeId dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node]; + if (dual_node_primal_node_id[dual_node_id] == node_id) { + const TinyVector<Dimension> Clr = dual_Cjr(dual_cell_id, i_dual_node); + + TinyMatrix<Dimension> M = alpha_mu_face_id * dot(Clr, nil) * I + + alpha_mu_face_id * tensorProduct(Clr, nil) + + alpha_lambda_face_id * tensorProduct(nil, Clr); + TinyMatrix<Dimension> Mb = alpha_mub_face_id * dot(Clr, nil) * I + + alpha_mub_face_id * tensorProduct(Clr, nil) + + alpha_lambdab_face_id * tensorProduct(nil, Clr); + + for (size_t j_cell = 0; j_cell < primal_node_to_cell_matrix[node_id].size(); ++j_cell) { + CellId j_id = primal_node_to_cell_matrix[node_id][j_cell]; + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + S((cell_dof_number[i_id] * Dimension) + i, (cell_dof_number[j_id] * Dimension) + j) -= + w_rj(node_id, j_cell) * M(i, j); + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id] * Dimension + i, cell_dof_number[j_id] * Dimension + j) += + 1.e0 * w_rj(node_id, j_cell) * Mb(i, j); + } + } + } + } + if (primal_node_is_on_boundary[node_id]) { + for (size_t l_face = 0; l_face < node_to_face_matrix[node_id].size(); ++l_face) { + FaceId l_id = node_to_face_matrix[node_id][l_face]; + if (primal_face_is_on_boundary[l_id]) { + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + // Mb? + S(cell_dof_number[i_id] * Dimension + i, face_dof_number[l_id] * Dimension + j) -= + w_rl(node_id, l_face) * M(i, j); + } + } + if (primal_face_is_neumann[face_id]) { + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + S(face_dof_number[face_id] * Dimension + i, face_dof_number[l_id] * Dimension + j) += + 1.e0 * w_rl(node_id, l_face) * Mb(i, j); + } + } + } + } + } + } + } + } + // } + } + } + } + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (primal_face_is_dirichlet[face_id]) { + for (size_t i = 0; i < Dimension; ++i) { + S(face_dof_number[face_id] * Dimension + i, face_dof_number[face_id] * Dimension + i) += 1.e0; + } + } + } + + Vector<double> b{number_of_dof * Dimension}; + b = zero; + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + for (size_t i = 0; i < Dimension; ++i) { + b[(cell_dof_number[cell_id] * Dimension) + i] = primal_Vj[cell_id] * fj[cell_id][i]; + } + } + + // Dirichlet + NodeValue<bool> node_tag{mesh->connectivity()}; + node_tag.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<T, DirichletBoundaryCondition>) { + const auto& face_list = bc.faceList(); + const auto& value_list = bc.valueList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + + for (size_t i = 0; i < Dimension; ++i) { + b[(face_dof_number[face_id] * Dimension) + i] += 1.e0 * value_list[i_face][i]; + } + } + } + }, + boundary_condition); + } + + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NormalStrainBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + const auto& value_list = bc.valueList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + FaceId face_id = face_list[i_face]; + for (size_t i = 0; i < Dimension; ++i) { + b[face_dof_number[face_id] * Dimension + i] += + 1.e0 * mes_l[face_id] * value_list[i_face][i]; // sign + } + } + } + }, + boundary_condition); + } + + CRSMatrix A{S.getCRSMatrix()}; + Vector<double> U{number_of_dof * Dimension}; + U = zero; + Vector r = A * U - b; + std::cout << "initial (real) residu = " << std::sqrt(dot(r, r)) << '\n'; + + LinearSolver solver; + solver.solveLocalSystem(A, U, b); + + r = A * U - b; + + std::cout << "final (real) residu = " << std::sqrt(dot(r, r)) << '\n'; + + m_solution = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<Dimension>>>(mesh); + auto& solution = *m_solution; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + for (size_t i = 0; i < Dimension; ++i) { + solution[cell_id][i] = U[(cell_dof_number[cell_id] * Dimension) + i]; + } + }); + + m_dual_solution = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<Dimension>>>(diamond_mesh); + auto& dual_solution = *m_dual_solution; + dual_solution.fill(zero); + const auto& face_to_cell_matrix = mesh->connectivity().faceToCellMatrix(); + for (CellId cell_id = 0; cell_id < diamond_mesh->numberOfCells(); ++cell_id) { + const FaceId face_id = dual_cell_face_id[cell_id]; + if (primal_face_is_on_boundary[face_id]) { + for (size_t i = 0; i < Dimension; ++i) { + dual_solution[cell_id][i] = U[(face_dof_number[face_id] * Dimension) + i]; + } + } else { + CellId cell_id1 = face_to_cell_matrix[face_id][0]; + CellId cell_id2 = face_to_cell_matrix[face_id][1]; + for (size_t i = 0; i < Dimension; ++i) { + dual_solution[cell_id][i] = + 0.5 * (U[(cell_dof_number[cell_id1] * Dimension) + i] + U[(cell_dof_number[cell_id2] * Dimension) + i]); + } + } + } + } + // provide a source for E? + // computeEnergyUpdate(mesh, dual_lambdab, dual_mub, m_solution, + // m_dual_solution, // f, + // bc_descriptor_list); + // computeEnergyUpdate(mesh, alpha, dual_lambdab, dual_mub, dual_lambda, dual_mu, m_solution, + // m_dual_solution, // f, + // bc_descriptor_list); + } else { + throw NotImplementedError("not done in 1d"); + } + } +}; +// NEW CLASS +template <size_t Dimension> +class EnergyComputerHandler::InterpolationWeightsManager +{ + private: + std::shared_ptr<const Mesh<Connectivity<Dimension>>> m_mesh; + FaceValue<const bool> m_primal_face_is_on_boundary; + NodeValue<const bool> m_primal_node_is_on_boundary; + FaceValue<const bool> m_primal_face_is_symmetry; + CellValuePerNode<double> m_w_rj; + FaceValuePerNode<double> m_w_rl; + + public: + InterpolationWeightsManager(std::shared_ptr<const Mesh<Connectivity<Dimension>>> mesh, + FaceValue<const bool> primal_face_is_on_boundary, + NodeValue<const bool> primal_node_is_on_boundary, + FaceValue<const bool> primal_face_is_symmetry) + : m_mesh(mesh), + m_primal_face_is_on_boundary(primal_face_is_on_boundary), + m_primal_node_is_on_boundary(primal_node_is_on_boundary), + m_primal_face_is_symmetry(primal_face_is_symmetry) + {} + ~InterpolationWeightsManager() = default; + CellValuePerNode<double>& + wrj() + { + return m_w_rj; + } + FaceValuePerNode<double>& + wrl() + { + return m_w_rl; + } + void + compute() + { + using MeshDataType = MeshData<Dimension>; + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*m_mesh); + + const NodeValue<const TinyVector<Dimension>>& xr = m_mesh->xr(); + + const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl(); + const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj(); + const auto& node_to_cell_matrix = m_mesh->connectivity().nodeToCellMatrix(); + const auto& node_to_face_matrix = m_mesh->connectivity().nodeToFaceMatrix(); + const auto& face_to_cell_matrix = m_mesh->connectivity().faceToCellMatrix(); + + CellValuePerNode<double> w_rj{m_mesh->connectivity()}; + FaceValuePerNode<double> w_rl{m_mesh->connectivity()}; + + const NodeValuePerFace<const TinyVector<Dimension>> primal_nlr = mesh_data.nlr(); + auto project_to_face = [&](const TinyVector<Dimension>& x, const FaceId face_id) -> const TinyVector<Dimension> { + TinyVector<Dimension> proj; + const TinyVector<Dimension> nil = primal_nlr(face_id, 0); + proj = x - dot((x - xl[face_id]), nil) * nil; + return proj; + }; + + for (size_t i = 0; i < w_rl.numberOfValues(); ++i) { + w_rl[i] = std::numeric_limits<double>::signaling_NaN(); + } + + for (NodeId i_node = 0; i_node < m_mesh->numberOfNodes(); ++i_node) { + SmallVector<double> b{Dimension + 1}; + b[0] = 1; + for (size_t i = 1; i < Dimension + 1; i++) { + b[i] = xr[i_node][i - 1]; + } + const auto& node_to_cell = node_to_cell_matrix[i_node]; + + if (not m_primal_node_is_on_boundary[i_node]) { + SmallMatrix<double> A{Dimension + 1, node_to_cell.size()}; + for (size_t j = 0; j < node_to_cell.size(); j++) { + A(0, j) = 1; + } + for (size_t i = 1; i < Dimension + 1; i++) { + for (size_t j = 0; j < node_to_cell.size(); j++) { + const CellId J = node_to_cell[j]; + A(i, j) = xj[J][i - 1]; + } + } + + SmallVector<double> x{node_to_cell.size()}; + x = zero; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + for (size_t j = 0; j < node_to_cell.size(); j++) { + w_rj(i_node, j) = x[j]; + } + } else { + int nb_face_used = 0; + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (m_primal_face_is_on_boundary[face_id]) { + nb_face_used++; + } + } + SmallMatrix<double> A{Dimension + 1, node_to_cell.size() + nb_face_used}; + for (size_t j = 0; j < node_to_cell.size() + nb_face_used; j++) { + A(0, j) = 1; + } + for (size_t i = 1; i < Dimension + 1; i++) { + for (size_t j = 0; j < node_to_cell.size(); j++) { + const CellId J = node_to_cell[j]; + A(i, j) = xj[J][i - 1]; + } + } + for (size_t i = 1; i < Dimension + 1; i++) { + int cpt_face = 0; + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (m_primal_face_is_on_boundary[face_id]) { + if (m_primal_face_is_symmetry[face_id]) { + for (size_t j = 0; j < face_to_cell_matrix[face_id].size(); ++j) { + const CellId cell_id = face_to_cell_matrix[face_id][j]; + TinyVector<Dimension> xproj = project_to_face(xj[cell_id], face_id); + A(i, node_to_cell.size() + cpt_face) = xproj[i - 1]; + } + } else { + A(i, node_to_cell.size() + cpt_face) = xl[face_id][i - 1]; + } + cpt_face++; + } + } + } + + SmallVector<double> x{node_to_cell.size() + nb_face_used}; + x = zero; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + for (size_t j = 0; j < node_to_cell.size(); j++) { + w_rj(i_node, j) = x[j]; + } + int cpt_face = node_to_cell.size(); + for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) { + FaceId face_id = node_to_face_matrix[i_node][i_face]; + if (m_primal_face_is_on_boundary[face_id]) { + w_rl(i_node, i_face) = x[cpt_face++]; + } + } + } + } + m_w_rj = w_rj; + m_w_rl = w_rl; + } +}; +class EnergyComputerHandler::IEnergyComputer +{ + public: + // virtual std::shared_ptr<const IDiscreteFunction> getSolution() const = 0; + // virtual std::shared_ptr<const IDiscreteFunction> getDualSolution() const = 0; + // virtual std::shared_ptr<const IDiscreteFunction> apply() const = 0; + virtual std::tuple<std::shared_ptr<const IDiscreteFunction>, std::shared_ptr<const IDiscreteFunction>> apply() + const = 0; + + IEnergyComputer() = default; + virtual ~IEnergyComputer() = default; +}; + +template <size_t Dimension> +class EnergyComputerHandler::EnergyComputer : public EnergyComputerHandler::IEnergyComputer +{ + private: + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + using MeshDataType = MeshData<Dimension>; + + std::shared_ptr<const DiscreteFunctionP0<Dimension, TinyVector<Dimension>>> m_solution; + std::shared_ptr<const DiscreteFunctionP0<Dimension, TinyVector<Dimension>>> m_dual_solution; + std::shared_ptr<const DiscreteFunctionP0<Dimension, double>> m_energy_delta; + + class DirichletBoundaryCondition + { + private: + const Array<const TinyVector<Dimension>> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const TinyVector<Dimension>>& + valueList() const + { + return m_value_list; + } + + DirichletBoundaryCondition(const Array<const FaceId>& face_list, + const Array<const TinyVector<Dimension>>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~DirichletBoundaryCondition() = default; + }; + + class NormalStrainBoundaryCondition + { + private: + const Array<const TinyVector<Dimension>> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const TinyVector<Dimension>>& + valueList() const + { + return m_value_list; + } + + NormalStrainBoundaryCondition(const Array<const FaceId>& face_list, + const Array<const TinyVector<Dimension>>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~NormalStrainBoundaryCondition() = default; + }; + + class SymmetryBoundaryCondition + { + private: + const Array<const TinyVector<Dimension>> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + public: + SymmetryBoundaryCondition(const Array<const FaceId>& face_list) : m_face_list{face_list} {} + + ~SymmetryBoundaryCondition() = default; + }; + + public: + std::tuple<std::shared_ptr<const IDiscreteFunction>, std::shared_ptr<const IDiscreteFunction>> + apply() const final + { + return {m_energy_delta, m_dual_solution}; + } + + // std::tuple<std::shared_ptr<const IDiscreteFunction>, std::shared_ptr<const IDiscreteFunction>> + // computeEnergyUpdate() const final + // { + // m_scheme->computeEnergyUpdate(mesh, dual_lambdab, dual_mub, m_solution, + // m_dual_solution, // f, + // bc_descriptor_list); + + // return {m_dual_solution, m_energy_delta}; + // } + + // compute the fluxes + // std::shared_ptr<const DiscreteFunctionP0<Dimension, double>> + EnergyComputer(const std::shared_ptr<const MeshType>& mesh, + // const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& alpha, + const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& dual_lambdab, + const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& dual_mub, + // const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& dual_lambda, + const std::shared_ptr<const DiscreteFunctionP0<Dimension, TinyVector<Dimension>>>& U, + const std::shared_ptr<const DiscreteFunctionP0<Dimension, TinyVector<Dimension>>>& dual_U, + const std::shared_ptr<const DiscreteFunctionP0<Dimension, TinyVector<Dimension>>>& source, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) + { + // Assert(mesh == alpha->mesh()); + Assert(mesh == U->mesh()); + // Assert(dual_lambda->mesh() == dual_mu->mesh()); + Assert(dual_lambdab->mesh() == dual_U->mesh()); + Assert(U->mesh() == source->mesh()); + Assert(dual_lambdab->mesh() == dual_mub->mesh()); + Assert(DualMeshManager::instance().getDiamondDualMesh(*mesh) == dual_mub->mesh(), + "diffusion coefficient is not defined on the dual mesh!"); + + using MeshDataType = MeshData<Dimension>; + + using BoundaryCondition = + std::variant<DirichletBoundaryCondition, NormalStrainBoundaryCondition, SymmetryBoundaryCondition>; + + using BoundaryConditionList = std::vector<BoundaryCondition>; + + BoundaryConditionList boundary_condition_list; + + NodeValue<bool> is_dirichlet{mesh->connectivity()}; + is_dirichlet.fill(false); + NodeValue<TinyVector<Dimension>> dirichlet_value{mesh->connectivity()}; + { + TinyVector<Dimension> nan_tiny_vector; + for (size_t i = 0; i < Dimension; ++i) { + nan_tiny_vector[i] = std::numeric_limits<double>::signaling_NaN(); + } + dirichlet_value.fill(nan_tiny_vector); + } + + for (const auto& bc_descriptor : bc_descriptor_list) { + bool is_valid_boundary_condition = true; + + switch (bc_descriptor->type()) { + case IBoundaryConditionDescriptor::Type::symmetry: { + const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor = + dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor); + + if constexpr (Dimension > 1) { + MeshFlatFaceBoundary<Dimension> mesh_face_boundary = + getMeshFlatFaceBoundary(*mesh, sym_bc_descriptor.boundaryDescriptor()); + boundary_condition_list.push_back(SymmetryBoundaryCondition{mesh_face_boundary.faceList()}); + } else { + throw NotImplementedError("Symmetry conditions are not supported in 1d"); + } + + break; + } + case IBoundaryConditionDescriptor::Type::dirichlet: { + const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor = + dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor); + if (dirichlet_bc_descriptor.name() == "dirichlet") { + if constexpr (Dimension > 1) { + MeshFaceBoundary<Dimension> mesh_face_boundary = + getMeshFaceBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId(); + Array<const TinyVector<Dimension>> value_list = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, mesh_data.xl(), + mesh_face_boundary.faceList()); + boundary_condition_list.push_back(DirichletBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + } else { + throw NotImplementedError("Neumann conditions are not supported in 1d"); + } + } else if (dirichlet_bc_descriptor.name() == "normal_strain") { + if constexpr (Dimension > 1) { + MeshFaceBoundary<Dimension> mesh_face_boundary = + getMeshFaceBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId(); + + Array<const TinyVector<Dimension>> value_list = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, mesh_data.xl(), + mesh_face_boundary.faceList()); + boundary_condition_list.push_back(NormalStrainBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + + } else { + throw NotImplementedError("Normal strain conditions are not supported in 1d"); + } + } else { + is_valid_boundary_condition = false; + } + break; + } + default: { + is_valid_boundary_condition = false; + } + } + if (not is_valid_boundary_condition) { + std::ostringstream error_msg; + error_msg << *bc_descriptor << " is an invalid boundary condition for elasticity equation"; + throw NormalError(error_msg.str()); + } + } + + if constexpr (Dimension > 1) { + const CellValue<const size_t> cell_dof_number = [&] { + CellValue<size_t> compute_cell_dof_number{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { compute_cell_dof_number[cell_id] = cell_id; }); + return compute_cell_dof_number; + }(); + size_t number_of_dof = mesh->numberOfCells(); + + const FaceValue<const size_t> face_dof_number = [&] { + FaceValue<size_t> compute_face_dof_number{mesh->connectivity()}; + compute_face_dof_number.fill(std::numeric_limits<size_t>::max()); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NormalStrainBoundaryCondition>) or + (std::is_same_v<T, SymmetryBoundaryCondition>) or + (std::is_same_v<T, DirichletBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + if (compute_face_dof_number[face_id] != std::numeric_limits<size_t>::max()) { + std::ostringstream os; + os << "The face " << face_id << " is used at least twice for boundary conditions"; + throw NormalError(os.str()); + } else { + compute_face_dof_number[face_id] = number_of_dof++; + } + } + } + }, + boundary_condition); + } + + return compute_face_dof_number; + }(); + + const auto& primal_face_to_node_matrix = mesh->connectivity().faceToNodeMatrix(); + const auto& face_to_cell_matrix = mesh->connectivity().faceToCellMatrix(); + const FaceValue<const bool> primal_face_is_neumann = [&] { + FaceValue<bool> face_is_neumann{mesh->connectivity()}; + face_is_neumann.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NormalStrainBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + face_is_neumann[face_id] = true; + } + } + }, + boundary_condition); + } + + return face_is_neumann; + }(); + + const FaceValue<const bool> primal_face_is_symmetry = [&] { + FaceValue<bool> face_is_symmetry{mesh->connectivity()}; + face_is_symmetry.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, SymmetryBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + face_is_symmetry[face_id] = true; + } + } + }, + boundary_condition); + } + + return face_is_symmetry; + }(); + + NodeValue<bool> primal_node_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of node_is_on_boundary is incorrect"); + } + + primal_node_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + primal_node_is_on_boundary[node_id] = true; + } + } + } + + FaceValue<bool> primal_face_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_on_boundary is incorrect"); + } + + primal_face_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + primal_face_is_on_boundary[face_id] = true; + } + } + + FaceValue<bool> primal_face_is_dirichlet(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_neumann is incorrect"); + } + + primal_face_is_dirichlet.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + primal_face_is_dirichlet[face_id] = (primal_face_is_on_boundary[face_id] && + (!primal_face_is_neumann[face_id]) && (!primal_face_is_symmetry[face_id])); + } + + InterpolationWeightsManager iwm(mesh, primal_face_is_on_boundary, primal_node_is_on_boundary, + primal_face_is_symmetry); + iwm.compute(); + CellValuePerNode<double> w_rj = iwm.wrj(); + FaceValuePerNode<double> w_rl = iwm.wrl(); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl(); + const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj(); + // const auto& node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); + const auto& node_to_face_matrix = mesh->connectivity().nodeToFaceMatrix(); + const NodeValuePerFace<const TinyVector<Dimension>> primal_nlr = mesh_data.nlr(); + + { + std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh); + + MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh); + + std::shared_ptr mapper = + DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity()); + + CellValue<const double> dual_mubj = dual_mub->cellValues(); + CellValue<const double> dual_lambdabj = dual_lambdab->cellValues(); + // attention, fj not in this context + CellValue<const TinyVector<Dimension>> velocity = U->cellValues(); + // CellValue<const TinyVector<Dimension>> dual_velocity = dual_U->cellValues(); + + const CellValue<const double> dual_Vj = diamond_mesh_data.Vj(); + + const FaceValue<const double> mes_l = [&] { + if constexpr (Dimension == 1) { + FaceValue<double> compute_mes_l{mesh->connectivity()}; + compute_mes_l.fill(1); + return compute_mes_l; + } else { + return mesh_data.ll(); + } + }(); + + const CellValue<const double> dual_mes_l_j = [=] { + CellValue<double> compute_mes_j{diamond_mesh->connectivity()}; + mapper->toDualCell(mes_l, compute_mes_j); + + return compute_mes_j; + }(); + + const CellValue<const double> primal_Vj = mesh_data.Vj(); + FaceValue<const CellId> face_dual_cell_id = [=]() { + FaceValue<CellId> computed_face_dual_cell_id{mesh->connectivity()}; + CellValue<CellId> dual_cell_id{diamond_mesh->connectivity()}; + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { dual_cell_id[cell_id] = cell_id; }); + + mapper->fromDualCell(dual_cell_id, computed_face_dual_cell_id); + + return computed_face_dual_cell_id; + }(); + + NodeValue<const NodeId> dual_node_primal_node_id = [=]() { + CellValue<NodeId> cell_ignored_id{mesh->connectivity()}; + cell_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()}); + + NodeValue<NodeId> node_primal_id{mesh->connectivity()}; + + parallel_for( + mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { node_primal_id[node_id] = node_id; }); + + NodeValue<NodeId> computed_dual_node_primal_node_id{diamond_mesh->connectivity()}; + + mapper->toDualNode(node_primal_id, cell_ignored_id, computed_dual_node_primal_node_id); + + return computed_dual_node_primal_node_id; + }(); + + CellValue<NodeId> primal_cell_dual_node_id = [=]() { + CellValue<NodeId> cell_id{mesh->connectivity()}; + NodeValue<NodeId> node_ignored_id{mesh->connectivity()}; + node_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()}); + + NodeValue<NodeId> dual_node_id{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { dual_node_id[node_id] = node_id; }); + + CellValue<NodeId> computed_primal_cell_dual_node_id{mesh->connectivity()}; + + mapper->fromDualNode(dual_node_id, node_ignored_id, cell_id); + + return cell_id; + }(); + const auto& dual_Cjr = diamond_mesh_data.Cjr(); + FaceValue<TinyVector<Dimension>> dualClj = [&] { + FaceValue<TinyVector<Dimension>> computedClj{mesh->connectivity()}; + const auto& dual_node_to_cell_matrix = diamond_mesh->connectivity().nodeToCellMatrix(); + const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix(); + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { + const auto& primal_face_to_cell = face_to_cell_matrix[face_id]; + for (size_t i = 0; i < primal_face_to_cell.size(); i++) { + CellId cell_id = primal_face_to_cell[i]; + const NodeId dual_node_id = primal_cell_dual_node_id[cell_id]; + for (size_t i_dual_cell = 0; i_dual_cell < dual_node_to_cell_matrix[dual_node_id].size(); + i_dual_cell++) { + const CellId dual_cell_id = dual_node_to_cell_matrix[dual_node_id][i_dual_cell]; + if (face_dual_cell_id[face_id] == dual_cell_id) { + for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); + i_dual_node++) { + const NodeId final_dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node]; + if (final_dual_node_id == dual_node_id) { + computedClj[face_id] = dual_Cjr(dual_cell_id, i_dual_node); + } + } + } + } + } + }); + return computedClj; + }(); + + FaceValue<TinyVector<Dimension>> nlj = [&] { + FaceValue<TinyVector<Dimension>> computedNlj{mesh->connectivity()}; + parallel_for( + mesh->numberOfFaces(), + PUGS_LAMBDA(FaceId face_id) { computedNlj[face_id] = 1. / l2Norm(dualClj[face_id]) * dualClj[face_id]; }); + return computedNlj; + }(); + + // FaceValue<const double> alpha_lambda_l = [&] { + // CellValue<double> alpha_j{diamond_mesh->connectivity()}; + + // parallel_for( + // diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + // alpha_j[diamond_cell_id] = dual_lambdaj[diamond_cell_id] / dual_Vj[diamond_cell_id]; + // }); + + // FaceValue<double> computed_alpha_l{mesh->connectivity()}; + // mapper->fromDualCell(alpha_j, computed_alpha_l); + // return computed_alpha_l; + // }(); + + // FaceValue<const double> alpha_mu_l = [&] { + // CellValue<double> alpha_j{diamond_mesh->connectivity()}; + + // parallel_for( + // diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + // alpha_j[diamond_cell_id] = dual_muj[diamond_cell_id] / dual_Vj[diamond_cell_id]; + // }); + + // FaceValue<double> computed_alpha_l{mesh->connectivity()}; + // mapper->fromDualCell(alpha_j, computed_alpha_l); + // return computed_alpha_l; + // }(); + + FaceValue<const double> alpha_lambdab_l = [&] { + CellValue<double> alpha_j{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + alpha_j[diamond_cell_id] = dual_lambdabj[diamond_cell_id] / dual_Vj[diamond_cell_id]; + }); + + FaceValue<double> computed_alpha_l{mesh->connectivity()}; + mapper->fromDualCell(alpha_j, computed_alpha_l); + return computed_alpha_l; + }(); + + FaceValue<const double> alpha_mub_l = [&] { + CellValue<double> alpha_j{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + alpha_j[diamond_cell_id] = dual_mubj[diamond_cell_id] / dual_Vj[diamond_cell_id]; + }); + + FaceValue<double> computed_alpha_l{mesh->connectivity()}; + mapper->fromDualCell(alpha_j, computed_alpha_l); + return computed_alpha_l; + }(); + + const TinyMatrix<Dimension> I = identity; + CellValue<const FaceId> dual_cell_face_id = [=]() { + CellValue<FaceId> computed_dual_cell_face_id{diamond_mesh->connectivity()}; + FaceValue<FaceId> primal_face_id{mesh->connectivity()}; + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { primal_face_id[face_id] = face_id; }); + + mapper->toDualCell(primal_face_id, computed_dual_cell_face_id); + + return computed_dual_cell_face_id; + }(); + + m_dual_solution = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<Dimension>>>(diamond_mesh); + // m_solution = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<Dimension>>>(mesh); + // const auto& solution = *U; + auto& dual_solution = *dual_U; + // dual_solution.fill(zero); + // const auto& face_to_cell_matrix = mesh->connectivity().faceToCellMatrix(); + // for (CellId cell_id = 0; cell_id < diamond_mesh->numberOfCells(); ++cell_id) { + // const FaceId face_id = dual_cell_face_id[cell_id]; + // CellId cell_id1 = face_to_cell_matrix[face_id][0]; + // if (primal_face_is_on_boundary[face_id]) { + // for (size_t i = 0; i < Dimension; ++i) { + // // A revoir!! + // dual_solution[cell_id][i] = solution[cell_id1][i]; + // } + // } else { + // CellId cell_id1 = face_to_cell_matrix[face_id][0]; + // CellId cell_id2 = face_to_cell_matrix[face_id][1]; + // for (size_t i = 0; i < Dimension; ++i) { + // dual_solution[cell_id][i] = 0.5 * (solution[cell_id1][i] + solution[cell_id2][i]); + // } + // } + // } + + // const Array<int> non_zeros{number_of_dof * Dimension}; + // non_zeros.fill(Dimension * Dimension); + // CRSMatrixDescriptor<double> S(number_of_dof * Dimension, number_of_dof * Dimension, non_zeros); + // Begining of main + CellValuePerFace<double> flux{mesh->connectivity()}; + parallel_for( + flux.numberOfValues(), PUGS_LAMBDA(size_t jl) { flux[jl] = 0; }); + + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + // const double beta_mu_l = l2Norm(dualClj[face_id]) * alpha_mu_l[face_id] * mes_l[face_id]; + // const double beta_lambda_l = l2Norm(dualClj[face_id]) * alpha_lambda_l[face_id] * mes_l[face_id]; + const double beta_mub_l = l2Norm(dualClj[face_id]) * alpha_mub_l[face_id] * mes_l[face_id]; + const double beta_lambdab_l = l2Norm(dualClj[face_id]) * alpha_lambdab_l[face_id] * mes_l[face_id]; + const auto& primal_face_to_cell = face_to_cell_matrix[face_id]; + for (size_t i_cell = 0; i_cell < primal_face_to_cell.size(); ++i_cell) { + const CellId i_id = primal_face_to_cell[i_cell]; + const bool is_face_reversed_for_cell_i = (dot(dualClj[face_id], xl[face_id] - xj[i_id]) < 0); + + const TinyVector<Dimension> nil = [&] { + if (is_face_reversed_for_cell_i) { + return -nlj[face_id]; + } else { + return nlj[face_id]; + } + }(); + TinyMatrix<Dimension> M = + beta_mub_l * I + beta_mub_l * tensorProduct(nil, nil) + beta_lambdab_l * tensorProduct(nil, nil); + // TinyMatrix<Dimension, double> Mb = + // beta_mub_l * I + beta_mub_l * tensorProduct(nil, nil) + beta_lambdab_l * tensorProduct(nil, nil); + // TinyMatrix<Dimension, double> N = 1.e0 * tensorProduct(nil, nil); + + for (size_t j_cell = 0; j_cell < primal_face_to_cell.size(); ++j_cell) { + const CellId j_id = primal_face_to_cell[j_cell]; + if (i_cell == j_cell) { + flux(face_id, i_cell) += dot(M * velocity[i_id], dual_solution[face_dual_cell_id[face_id]]); + } else { + flux(face_id, i_cell) -= dot(M * velocity[j_id], dual_solution[face_dual_cell_id[face_id]]); + } + } + } + } + + const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix(); + const auto& primal_node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + // const double alpha_mu_face_id = mes_l[face_id] * alpha_mu_l[face_id]; + // const double alpha_lambda_face_id = mes_l[face_id] * alpha_lambda_l[face_id]; + const double alpha_mub_face_id = mes_l[face_id] * alpha_mub_l[face_id]; + const double alpha_lambdab_face_id = mes_l[face_id] * alpha_lambdab_l[face_id]; + + for (size_t i_face_cell = 0; i_face_cell < face_to_cell_matrix[face_id].size(); ++i_face_cell) { + CellId i_id = face_to_cell_matrix[face_id][i_face_cell]; + const bool is_face_reversed_for_cell_i = (dot(dualClj[face_id], xl[face_id] - xj[i_id]) < 0); + + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + + const TinyVector<Dimension> nil = [&] { + if (is_face_reversed_for_cell_i) { + return -nlj[face_id]; + } else { + return nlj[face_id]; + } + }(); + + CellId dual_cell_id = face_dual_cell_id[face_id]; + + for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); ++i_dual_node) { + const NodeId dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node]; + if (dual_node_primal_node_id[dual_node_id] == node_id) { + const TinyVector<Dimension> Clr = dual_Cjr(dual_cell_id, i_dual_node); + + TinyMatrix<Dimension> M = alpha_mub_face_id * dot(Clr, nil) * I + + alpha_mub_face_id * tensorProduct(Clr, nil) + + alpha_lambdab_face_id * tensorProduct(nil, Clr); + // TinyMatrix<Dimension, double> Mb = alpha_mub_face_id * dot(Clr, nil) * I + + // alpha_mub_face_id * tensorProduct(Clr, nil) + + // alpha_lambdab_face_id * tensorProduct(nil, Clr); + + for (size_t j_cell = 0; j_cell < primal_node_to_cell_matrix[node_id].size(); ++j_cell) { + CellId j_id = primal_node_to_cell_matrix[node_id][j_cell]; + flux(face_id, i_face_cell) -= + w_rj(node_id, j_cell) * dot(M * velocity[j_id], dual_solution[dual_cell_id]); + } + if (primal_node_is_on_boundary[node_id]) { + for (size_t l_face = 0; l_face < node_to_face_matrix[node_id].size(); ++l_face) { + FaceId l_id = node_to_face_matrix[node_id][l_face]; + if (primal_face_is_on_boundary[l_id]) { + flux(face_id, i_face_cell) -= + w_rl(node_id, l_face) * + dot(M * dual_solution[face_dual_cell_id[l_id]], dual_solution[dual_cell_id]); + } + } + } + } + } + // } + } + } + } + // for (const auto& boundary_condition : boundary_condition_list) { + // std::visit( + // [&](auto&& bc) { + // using T = std::decay_t<decltype(bc)>; + // if constexpr ((std::is_same_v<T, NormalStrainBoundaryCondition>)) { + // const auto& face_list = bc.faceList(); + // const auto& value_list = bc.valueList(); + // for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + // FaceId face_id = face_list[i_face]; + // CellId dual_cell_id = face_dual_cell_id[face_id]; + // flux(face_id, 0) = mes_l[face_id] * dot(value_list[i_face], dual_solution[dual_cell_id]); // + // sign + // } + // } + // }, + // boundary_condition); + // } + // for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + // if (face_to_cell_matrix[face_id].size() == 2) { + // CellId i_id = face_to_cell_matrix[face_id][0]; + // CellId j_id = face_to_cell_matrix[face_id][1]; + // if (flux(face_id, 0) != -flux(face_id, 1)) { + // std::cout << "flux(" << i_id << "," << face_id << ")=" << flux(face_id, 0) << " not equal to -flux(" + // << j_id << "," << face_id << ")=" << -flux(face_id, 1) << "\n"; + // } + // } + // // exit(0); + // } + // Assemble + m_energy_delta = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + auto& energy_delta = *m_energy_delta; + // CellValue<const TinyVector<Dimension>> fj = source->cellValues(); + + double sum_deltae = 0.; + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + energy_delta[cell_id] = 0.; // dot(fj[cell_id], velocity[cell_id]); + sum_deltae += energy_delta[cell_id]; + } + // CellValue<double>& deltae = m_energy_delta->cellValues(); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + for (size_t j = 0; j < face_to_cell_matrix[face_id].size(); j++) { + CellId i_id = face_to_cell_matrix[face_id][j]; + energy_delta[i_id] -= flux(face_id, j) / primal_Vj[i_id]; + sum_deltae -= flux(face_id, j); + } + // exit(0); + } + + std::cout << "sum deltaej " << sum_deltae << "\n"; + } + } else { + throw NotImplementedError("not done in 1d"); + } + // return m_energy_delta; + } +}; + +std::tuple<std::shared_ptr<const IDiscreteFunction>, std::shared_ptr<const IDiscreteFunction>> +VectorDiamondSchemeHandler::apply() const +{ + return m_scheme->apply(); +} + +std::tuple<std::shared_ptr<const IDiscreteFunction>, std::shared_ptr<const IDiscreteFunction>> +EnergyComputerHandler::computeEnergyUpdate() const +{ + return m_energy_computer->apply(); +} + +std::shared_ptr<const IDiscreteFunction> +VectorDiamondSchemeHandler::solution() const +{ + return m_scheme->getSolution(); +} + +std::shared_ptr<const IDiscreteFunction> +VectorDiamondSchemeHandler::dual_solution() const +{ + return m_scheme->getDualSolution(); +} + +VectorDiamondSchemeHandler::VectorDiamondSchemeHandler( + const std::shared_ptr<const IDiscreteFunction>& alpha, + const std::shared_ptr<const IDiscreteFunction>& dual_lambdab, + const std::shared_ptr<const IDiscreteFunction>& dual_mub, + const std::shared_ptr<const IDiscreteFunction>& dual_lambda, + const std::shared_ptr<const IDiscreteFunction>& dual_mu, + const std::shared_ptr<const IDiscreteFunction>& f, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) +{ + const std::shared_ptr i_mesh = getCommonMesh({alpha, f}); + if (not i_mesh) { + throw NormalError("primal discrete functions are not defined on the same mesh"); + } + const std::shared_ptr i_dual_mesh = getCommonMesh({dual_lambda, dual_lambdab, dual_mu, dual_mub}); + if (not i_dual_mesh) { + throw NormalError("dual discrete functions are not defined on the same mesh"); + } + checkDiscretizationType({alpha, dual_lambdab, dual_mub, dual_lambda, dual_mu, f}, DiscreteFunctionType::P0); + + switch (i_mesh->dimension()) { + case 1: { + using MeshType = Mesh<Connectivity<1>>; + using DiscreteScalarFunctionType = DiscreteFunctionP0<1, double>; + using DiscreteVectorFunctionType = DiscreteFunctionP0<1, TinyVector<1>>; + + std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + + m_scheme = + std::make_unique<VectorDiamondScheme<1>>(mesh, std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(alpha), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>( + dual_lambdab), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_mub), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_lambda), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_mu), + std::dynamic_pointer_cast<const DiscreteVectorFunctionType>(f), + bc_descriptor_list); + break; + } + case 2: { + using MeshType = Mesh<Connectivity<2>>; + using DiscreteScalarFunctionType = DiscreteFunctionP0<2, double>; + using DiscreteVectorFunctionType = DiscreteFunctionP0<2, TinyVector<2>>; + + std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + + if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != i_dual_mesh) { + throw NormalError("dual variables are is not defined on the diamond dual of the primal mesh"); + } + + m_scheme = + std::make_unique<VectorDiamondScheme<2>>(mesh, std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(alpha), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>( + dual_lambdab), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_mub), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_lambda), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_mu), + std::dynamic_pointer_cast<const DiscreteVectorFunctionType>(f), + bc_descriptor_list); + break; + } + case 3: { + using MeshType = Mesh<Connectivity<3>>; + using DiscreteScalarFunctionType = DiscreteFunctionP0<3, double>; + using DiscreteVectorFunctionType = DiscreteFunctionP0<3, TinyVector<3>>; + + std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + + if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != i_dual_mesh) { + throw NormalError("dual variables are is not defined on the diamond dual of the primal mesh"); + } + + m_scheme = + std::make_unique<VectorDiamondScheme<3>>(mesh, std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(alpha), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>( + dual_lambdab), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_mub), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_lambda), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_mu), + std::dynamic_pointer_cast<const DiscreteVectorFunctionType>(f), + bc_descriptor_list); + break; + } + default: { + throw UnexpectedError("invalid mesh dimension"); + } + } +} + +VectorDiamondSchemeHandler::~VectorDiamondSchemeHandler() = default; + +EnergyComputerHandler::EnergyComputerHandler( + const std::shared_ptr<const IDiscreteFunction>& dual_lambdab, + const std::shared_ptr<const IDiscreteFunction>& dual_mub, + const std::shared_ptr<const IDiscreteFunction>& U, + const std::shared_ptr<const IDiscreteFunction>& dual_U, + const std::shared_ptr<const IDiscreteFunction>& source, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) +{ + const std::shared_ptr i_mesh = getCommonMesh({U, source}); + if (not i_mesh) { + throw NormalError("primal discrete functions are not defined on the same mesh"); + } + const std::shared_ptr i_dual_mesh = getCommonMesh({dual_lambdab, dual_mub, dual_U}); + if (not i_dual_mesh) { + throw NormalError("dual discrete functions are not defined on the same mesh"); + } + checkDiscretizationType({dual_lambdab, dual_mub, dual_U, U, source}, DiscreteFunctionType::P0); + + switch (i_mesh->dimension()) { + case 1: { + using MeshType = Mesh<Connectivity<1>>; + using DiscreteScalarFunctionType = DiscreteFunctionP0<1, double>; + using DiscreteVectorFunctionType = DiscreteFunctionP0<1, TinyVector<1>>; + + std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + + if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != i_dual_mesh) { + throw NormalError("dual variables are is not defined on the diamond dual of the primal mesh"); + } + + m_energy_computer = + std::make_unique<EnergyComputer<1>>(mesh, + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_lambdab), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_mub), + std::dynamic_pointer_cast<const DiscreteVectorFunctionType>(U), + std::dynamic_pointer_cast<const DiscreteVectorFunctionType>(dual_U), + std::dynamic_pointer_cast<const DiscreteVectorFunctionType>(source), + bc_descriptor_list); + break; + } + case 2: { + using MeshType = Mesh<Connectivity<2>>; + using DiscreteScalarFunctionType = DiscreteFunctionP0<2, double>; + using DiscreteVectorFunctionType = DiscreteFunctionP0<2, TinyVector<2>>; + + std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + + if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != i_dual_mesh) { + throw NormalError("dual variables are is not defined on the diamond dual of the primal mesh"); + } + + m_energy_computer = + std::make_unique<EnergyComputer<2>>(mesh, + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_lambdab), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_mub), + std::dynamic_pointer_cast<const DiscreteVectorFunctionType>(U), + std::dynamic_pointer_cast<const DiscreteVectorFunctionType>(dual_U), + std::dynamic_pointer_cast<const DiscreteVectorFunctionType>(source), + bc_descriptor_list); + break; + } + case 3: { + using MeshType = Mesh<Connectivity<3>>; + using DiscreteScalarFunctionType = DiscreteFunctionP0<3, double>; + using DiscreteVectorFunctionType = DiscreteFunctionP0<3, TinyVector<3>>; + + std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + + if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != i_dual_mesh) { + throw NormalError("dual variables are is not defined on the diamond dual of the primal mesh"); + } + + m_energy_computer = + std::make_unique<EnergyComputer<3>>(mesh, + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_lambdab), + std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(dual_mub), + std::dynamic_pointer_cast<const DiscreteVectorFunctionType>(U), + std::dynamic_pointer_cast<const DiscreteVectorFunctionType>(dual_U), + std::dynamic_pointer_cast<const DiscreteVectorFunctionType>(source), + bc_descriptor_list); + break; + } + default: { + throw UnexpectedError("invalid mesh dimension"); + } + } +} + +EnergyComputerHandler::~EnergyComputerHandler() = default; diff --git a/src/scheme/VectorDiamondScheme.hpp b/src/scheme/VectorDiamondScheme.hpp new file mode 100644 index 0000000000000000000000000000000000000000..20bbadfa3934cabf213d201e47397c97d56047e0 --- /dev/null +++ b/src/scheme/VectorDiamondScheme.hpp @@ -0,0 +1,827 @@ +#ifndef VECTOR_DIAMOND_SCHEME_HPP +#define VECTOR_DIAMOND_SCHEME_HPP +#include <algebra/CRSMatrix.hpp> +#include <algebra/CRSMatrixDescriptor.hpp> +#include <algebra/LeastSquareSolver.hpp> +#include <algebra/LinearSolver.hpp> +#include <algebra/TinyVector.hpp> +#include <algebra/Vector.hpp> +#include <language/utils/InterpolateItemValue.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/DualConnectivityManager.hpp> +#include <mesh/DualMeshManager.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/MeshFaceBoundary.hpp> +#include <mesh/MeshFlatFaceBoundary.hpp> +#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp> +#include <output/VTKWriter.hpp> +#include <scheme/DirichletBoundaryConditionDescriptor.hpp> +#include <scheme/NeumannBoundaryConditionDescriptor.hpp> +#include <scheme/SymmetryBoundaryConditionDescriptor.hpp> + +class VectorDiamondSchemeHandler +{ + private: + class IVectorDiamondScheme; + template <size_t Dimension> + class VectorDiamondScheme; + + template <size_t Dimension> + class InterpolationWeightsManager; + + public: + std::unique_ptr<IVectorDiamondScheme> m_scheme; + + std::shared_ptr<const IDiscreteFunction> solution() const; + + std::shared_ptr<const IDiscreteFunction> dual_solution() const; + + std::tuple<std::shared_ptr<const IDiscreteFunction>, std::shared_ptr<const IDiscreteFunction>> apply() const; + + VectorDiamondSchemeHandler( + const std::shared_ptr<const IDiscreteFunction>& alphab, + const std::shared_ptr<const IDiscreteFunction>& lambdab, + const std::shared_ptr<const IDiscreteFunction>& alpha, + const std::shared_ptr<const IDiscreteFunction>& lambda, + const std::shared_ptr<const IDiscreteFunction>& mu, + const std::shared_ptr<const IDiscreteFunction>& f, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list); + + ~VectorDiamondSchemeHandler(); +}; + +class EnergyComputerHandler +{ + private: + class IEnergyComputer; + template <size_t Dimension> + class EnergyComputer; + + template <size_t Dimension> + class InterpolationWeightsManager; + + public: + std::unique_ptr<IEnergyComputer> m_energy_computer; + std::shared_ptr<const IDiscreteFunction> dual_solution() const; + + std::tuple<std::shared_ptr<const IDiscreteFunction>, std::shared_ptr<const IDiscreteFunction>> computeEnergyUpdate() + const; + + EnergyComputerHandler(const std::shared_ptr<const IDiscreteFunction>& lambdab, + const std::shared_ptr<const IDiscreteFunction>& mub, + const std::shared_ptr<const IDiscreteFunction>& U, + const std::shared_ptr<const IDiscreteFunction>& dual_U, + const std::shared_ptr<const IDiscreteFunction>& source, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list); + + ~EnergyComputerHandler(); +}; + +template <size_t Dimension> +class LegacyVectorDiamondScheme +{ + private: + class DirichletBoundaryCondition + { + private: + const Array<const TinyVector<Dimension>> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const TinyVector<Dimension>>& + valueList() const + { + return m_value_list; + } + + DirichletBoundaryCondition(const Array<const FaceId>& face_list, + const Array<const TinyVector<Dimension>>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~DirichletBoundaryCondition() = default; + }; + + class NormalStrainBoundaryCondition + { + private: + const Array<const TinyVector<Dimension>> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + const Array<const TinyVector<Dimension>>& + valueList() const + { + return m_value_list; + } + + NormalStrainBoundaryCondition(const Array<const FaceId>& face_list, + const Array<const TinyVector<Dimension>>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + { + Assert(m_value_list.size() == m_face_list.size()); + } + + ~NormalStrainBoundaryCondition() = default; + }; + + class SymmetryBoundaryCondition + { + private: + const Array<const TinyVector<Dimension>> m_value_list; + const Array<const FaceId> m_face_list; + + public: + const Array<const FaceId>& + faceList() const + { + return m_face_list; + } + + public: + SymmetryBoundaryCondition(const Array<const FaceId>& face_list) : m_face_list{face_list} {} + + ~SymmetryBoundaryCondition() = default; + }; + + public: + LegacyVectorDiamondScheme(std::shared_ptr<const IMesh> i_mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const FunctionSymbolId& lambda_id, + const FunctionSymbolId& mu_id, + const FunctionSymbolId& f_id, + CellValue<TinyVector<Dimension>>& Uj, + FaceValue<TinyVector<Dimension>>& Ul, + const double& Tf, + const double& dt, + CellValuePerNode<double>& w_rj, + FaceValuePerNode<double>& w_rl) + { + using ConnectivityType = Connectivity<Dimension>; + using MeshType = Mesh<ConnectivityType>; + using MeshDataType = MeshData<Dimension>; + + using BoundaryCondition = + std::variant<DirichletBoundaryCondition, NormalStrainBoundaryCondition, SymmetryBoundaryCondition>; + + using BoundaryConditionList = std::vector<BoundaryCondition>; + + BoundaryConditionList boundary_condition_list; + + std::cout << "number of bc descr = " << bc_descriptor_list.size() << '\n'; + std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh); + + NodeValue<bool> is_dirichlet{mesh->connectivity()}; + is_dirichlet.fill(false); + NodeValue<TinyVector<Dimension>> dirichlet_value{mesh->connectivity()}; + { + TinyVector<Dimension> nan_tiny_vector; + for (size_t i = 0; i < Dimension; ++i) { + nan_tiny_vector[i] = std::numeric_limits<double>::signaling_NaN(); + } + dirichlet_value.fill(nan_tiny_vector); + } + + for (const auto& bc_descriptor : bc_descriptor_list) { + bool is_valid_boundary_condition = true; + + switch (bc_descriptor->type()) { + case IBoundaryConditionDescriptor::Type::symmetry: { + const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor = + dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor); + + if constexpr (Dimension > 1) { + MeshFlatFaceBoundary<Dimension> mesh_face_boundary = + getMeshFlatFaceBoundary(*mesh, sym_bc_descriptor.boundaryDescriptor()); + boundary_condition_list.push_back(SymmetryBoundaryCondition{mesh_face_boundary.faceList()}); + } else { + throw NotImplementedError("Symmetry conditions are not supported in 1d"); + } + + break; + } + case IBoundaryConditionDescriptor::Type::dirichlet: { + const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor = + dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor); + if (dirichlet_bc_descriptor.name() == "dirichlet") { + if constexpr (Dimension > 1) { + MeshFaceBoundary<Dimension> mesh_face_boundary = + getMeshFaceBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId(); + Array<const TinyVector<Dimension>> value_list = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, mesh_data.xl(), + mesh_face_boundary.faceList()); + boundary_condition_list.push_back(DirichletBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + } else { + throw NotImplementedError("Neumann conditions are not supported in 1d"); + } + + } else if (dirichlet_bc_descriptor.name() == "normal_strain") { + if constexpr (Dimension > 1) { + MeshFaceBoundary<Dimension> mesh_face_boundary = + getMeshFaceBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId(); + + Array<const TinyVector<Dimension>> value_list = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, mesh_data.xl(), + mesh_face_boundary.faceList()); + boundary_condition_list.push_back(NormalStrainBoundaryCondition{mesh_face_boundary.faceList(), value_list}); + + } else { + throw NotImplementedError("Normal strain conditions are not supported in 1d"); + } + + } else { + is_valid_boundary_condition = false; + } + break; + } + default: { + is_valid_boundary_condition = false; + } + } + if (not is_valid_boundary_condition) { + std::ostringstream error_msg; + error_msg << *bc_descriptor << " is an invalid boundary condition for elasticity equation"; + throw NormalError(error_msg.str()); + } + } + + if constexpr (Dimension > 1) { + const CellValue<const size_t> cell_dof_number = [&] { + CellValue<size_t> compute_cell_dof_number{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { compute_cell_dof_number[cell_id] = cell_id; }); + return compute_cell_dof_number; + }(); + size_t number_of_dof = mesh->numberOfCells(); + + const FaceValue<const size_t> face_dof_number = [&] { + FaceValue<size_t> compute_face_dof_number{mesh->connectivity()}; + compute_face_dof_number.fill(std::numeric_limits<size_t>::max()); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NormalStrainBoundaryCondition>) or + (std::is_same_v<T, SymmetryBoundaryCondition>) or + (std::is_same_v<T, DirichletBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + if (compute_face_dof_number[face_id] != std::numeric_limits<size_t>::max()) { + std::ostringstream os; + os << "The face " << face_id << " is used at least twice for boundary conditions"; + throw NormalError(os.str()); + } else { + compute_face_dof_number[face_id] = number_of_dof++; + } + } + } + }, + boundary_condition); + } + + return compute_face_dof_number; + }(); + + const auto& primal_face_to_node_matrix = mesh->connectivity().faceToNodeMatrix(); + const auto& face_to_cell_matrix = mesh->connectivity().faceToCellMatrix(); + const FaceValue<const bool> primal_face_is_neumann = [&] { + FaceValue<bool> face_is_neumann{mesh->connectivity()}; + face_is_neumann.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NormalStrainBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + face_is_neumann[face_id] = true; + } + } + }, + boundary_condition); + } + + return face_is_neumann; + }(); + + const FaceValue<const bool> primal_face_is_symmetry = [&] { + FaceValue<bool> face_is_symmetry{mesh->connectivity()}; + face_is_symmetry.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, SymmetryBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + face_is_symmetry[face_id] = true; + } + } + }, + boundary_condition); + } + + return face_is_symmetry; + }(); + + NodeValue<bool> primal_node_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of node_is_on_boundary is incorrect"); + } + + primal_node_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + primal_node_is_on_boundary[node_id] = true; + } + } + } + + FaceValue<bool> primal_face_is_on_boundary(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_on_boundary is incorrect"); + } + + primal_face_is_on_boundary.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (face_to_cell_matrix[face_id].size() == 1) { + primal_face_is_on_boundary[face_id] = true; + } + } + + FaceValue<bool> primal_face_is_dirichlet(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_neumann is incorrect"); + } + + primal_face_is_dirichlet.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + primal_face_is_dirichlet[face_id] = (primal_face_is_on_boundary[face_id] && + (!primal_face_is_neumann[face_id]) && (!primal_face_is_symmetry[face_id])); + } + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl(); + const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj(); + const auto& node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); + const auto& node_to_face_matrix = mesh->connectivity().nodeToFaceMatrix(); + const NodeValuePerFace<const TinyVector<Dimension>> primal_nlr = mesh_data.nlr(); + + { + std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh); + + MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh); + + std::shared_ptr mapper = + DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity()); + + CellValue<double> dual_muj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(mu_id, + diamond_mesh_data + .xj()); + + CellValue<double> dual_lambdaj = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(lambda_id, + diamond_mesh_data + .xj()); + + CellValue<TinyVector<Dimension>> fj = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::cell>(f_id, mesh_data.xj()); + + const CellValue<const double> dual_Vj = diamond_mesh_data.Vj(); + + const FaceValue<const double> mes_l = [&] { + if constexpr (Dimension == 1) { + FaceValue<double> compute_mes_l{mesh->connectivity()}; + compute_mes_l.fill(1); + return compute_mes_l; + } else { + return mesh_data.ll(); + } + }(); + + const CellValue<const double> dual_mes_l_j = [=] { + CellValue<double> compute_mes_j{diamond_mesh->connectivity()}; + mapper->toDualCell(mes_l, compute_mes_j); + + return compute_mes_j; + }(); + + const CellValue<const double> primal_Vj = mesh_data.Vj(); + FaceValue<const CellId> face_dual_cell_id = [=]() { + FaceValue<CellId> computed_face_dual_cell_id{mesh->connectivity()}; + CellValue<CellId> dual_cell_id{diamond_mesh->connectivity()}; + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { dual_cell_id[cell_id] = cell_id; }); + + mapper->fromDualCell(dual_cell_id, computed_face_dual_cell_id); + + return computed_face_dual_cell_id; + }(); + + NodeValue<const NodeId> dual_node_primal_node_id = [=]() { + CellValue<NodeId> cell_ignored_id{mesh->connectivity()}; + cell_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()}); + + NodeValue<NodeId> node_primal_id{mesh->connectivity()}; + + parallel_for( + mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { node_primal_id[node_id] = node_id; }); + + NodeValue<NodeId> computed_dual_node_primal_node_id{diamond_mesh->connectivity()}; + + mapper->toDualNode(node_primal_id, cell_ignored_id, computed_dual_node_primal_node_id); + + return computed_dual_node_primal_node_id; + }(); + + CellValue<NodeId> primal_cell_dual_node_id = [=]() { + CellValue<NodeId> cell_id{mesh->connectivity()}; + NodeValue<NodeId> node_ignored_id{mesh->connectivity()}; + node_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()}); + + NodeValue<NodeId> dual_node_id{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { dual_node_id[node_id] = node_id; }); + + CellValue<NodeId> computed_primal_cell_dual_node_id{mesh->connectivity()}; + + mapper->fromDualNode(dual_node_id, node_ignored_id, cell_id); + + return cell_id; + }(); + const auto& dual_Cjr = diamond_mesh_data.Cjr(); + FaceValue<TinyVector<Dimension>> dualClj = [&] { + FaceValue<TinyVector<Dimension>> computedClj{mesh->connectivity()}; + const auto& dual_node_to_cell_matrix = diamond_mesh->connectivity().nodeToCellMatrix(); + const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix(); + parallel_for( + mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { + const auto& primal_face_to_cell = face_to_cell_matrix[face_id]; + for (size_t i = 0; i < primal_face_to_cell.size(); i++) { + CellId cell_id = primal_face_to_cell[i]; + const NodeId dual_node_id = primal_cell_dual_node_id[cell_id]; + for (size_t i_dual_cell = 0; i_dual_cell < dual_node_to_cell_matrix[dual_node_id].size(); + i_dual_cell++) { + const CellId dual_cell_id = dual_node_to_cell_matrix[dual_node_id][i_dual_cell]; + if (face_dual_cell_id[face_id] == dual_cell_id) { + for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); + i_dual_node++) { + const NodeId final_dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node]; + if (final_dual_node_id == dual_node_id) { + computedClj[face_id] = dual_Cjr(dual_cell_id, i_dual_node); + } + } + } + } + } + }); + return computedClj; + }(); + + FaceValue<TinyVector<Dimension>> nlj = [&] { + FaceValue<TinyVector<Dimension>> computedNlj{mesh->connectivity()}; + parallel_for( + mesh->numberOfFaces(), + PUGS_LAMBDA(FaceId face_id) { computedNlj[face_id] = 1. / l2Norm(dualClj[face_id]) * dualClj[face_id]; }); + return computedNlj; + }(); + + FaceValue<const double> alpha_lambda_l = [&] { + CellValue<double> alpha_j{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + alpha_j[diamond_cell_id] = dual_lambdaj[diamond_cell_id] / dual_Vj[diamond_cell_id]; + }); + + FaceValue<double> computed_alpha_l{mesh->connectivity()}; + mapper->fromDualCell(alpha_j, computed_alpha_l); + return computed_alpha_l; + }(); + + FaceValue<const double> alpha_mu_l = [&] { + CellValue<double> alpha_j{diamond_mesh->connectivity()}; + + parallel_for( + diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) { + alpha_j[diamond_cell_id] = dual_muj[diamond_cell_id] / dual_Vj[diamond_cell_id]; + }); + + FaceValue<double> computed_alpha_l{mesh->connectivity()}; + mapper->fromDualCell(alpha_j, computed_alpha_l); + return computed_alpha_l; + }(); + + double unsteady = (Tf == 0) ? 0 : 1; + double time_factor = (unsteady == 0) ? 1 : dt; + + TinyMatrix<Dimension> I = identity; + + const Array<int> non_zeros{number_of_dof * Dimension}; + non_zeros.fill(Dimension * Dimension); + CRSMatrixDescriptor<double> S(number_of_dof * Dimension, number_of_dof * Dimension, non_zeros); + + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + const double beta_mu_l = l2Norm(dualClj[face_id]) * alpha_mu_l[face_id] * mes_l[face_id]; + const double beta_lambda_l = l2Norm(dualClj[face_id]) * alpha_lambda_l[face_id] * mes_l[face_id]; + const auto& primal_face_to_cell = face_to_cell_matrix[face_id]; + for (size_t i_cell = 0; i_cell < primal_face_to_cell.size(); ++i_cell) { + const CellId i_id = primal_face_to_cell[i_cell]; + const bool is_face_reversed_for_cell_i = (dot(dualClj[face_id], xl[face_id] - xj[i_id]) < 0); + + const TinyVector<Dimension> nil = [&] { + if (is_face_reversed_for_cell_i) { + return -nlj[face_id]; + } else { + return nlj[face_id]; + } + }(); + for (size_t j_cell = 0; j_cell < primal_face_to_cell.size(); ++j_cell) { + const CellId j_id = primal_face_to_cell[j_cell]; + TinyMatrix<Dimension> M = + beta_mu_l * I + beta_mu_l * tensorProduct(nil, nil) + beta_lambda_l * tensorProduct(nil, nil); + TinyMatrix<Dimension> N = tensorProduct(nil, nil); + + if (i_cell == j_cell) { + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + S((cell_dof_number[i_id] * Dimension) + i, (cell_dof_number[j_id] * Dimension) + j) += + (time_factor * M(i, j) + unsteady * primal_Vj[i_id]); + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id] * Dimension + i, cell_dof_number[j_id] * Dimension + j) -= M(i, j); + } + if (primal_face_is_symmetry[face_id]) { + S(face_dof_number[face_id] * Dimension + i, cell_dof_number[j_id] * Dimension + j) += + -((i == j) ? 1 : 0) + N(i, j); + S(face_dof_number[face_id] * Dimension + i, face_dof_number[face_id] * Dimension + j) += + (i == j) ? 1 : 0; + } + } + } + } else { + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + S((cell_dof_number[i_id] * Dimension) + i, (cell_dof_number[j_id] * Dimension) + j) -= + time_factor * M(i, j); + } + } + } + } + } + } + + const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix(); + const auto& primal_node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + const double alpha_mu_face_id = mes_l[face_id] * alpha_mu_l[face_id]; + const double alpha_lambda_face_id = mes_l[face_id] * alpha_lambda_l[face_id]; + + for (size_t i_face_cell = 0; i_face_cell < face_to_cell_matrix[face_id].size(); ++i_face_cell) { + CellId i_id = face_to_cell_matrix[face_id][i_face_cell]; + const bool is_face_reversed_for_cell_i = (dot(dualClj[face_id], xl[face_id] - xj[i_id]) < 0); + + for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) { + NodeId node_id = primal_face_to_node_matrix[face_id][i_node]; + + const TinyVector<Dimension> nil = [&] { + if (is_face_reversed_for_cell_i) { + return -nlj[face_id]; + } else { + return nlj[face_id]; + } + }(); + + CellId dual_cell_id = face_dual_cell_id[face_id]; + + for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); ++i_dual_node) { + const NodeId dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node]; + if (dual_node_primal_node_id[dual_node_id] == node_id) { + const TinyVector<Dimension> Clr = dual_Cjr(dual_cell_id, i_dual_node); + + TinyMatrix<Dimension> M = alpha_mu_face_id * dot(Clr, nil) * I + + alpha_mu_face_id * tensorProduct(Clr, nil) + + alpha_lambda_face_id * tensorProduct(nil, Clr); + + for (size_t j_cell = 0; j_cell < primal_node_to_cell_matrix[node_id].size(); ++j_cell) { + CellId j_id = primal_node_to_cell_matrix[node_id][j_cell]; + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + S((cell_dof_number[i_id] * Dimension) + i, (cell_dof_number[j_id] * Dimension) + j) -= + time_factor * w_rj(node_id, j_cell) * M(i, j); + if (primal_face_is_neumann[face_id]) { + S(face_dof_number[face_id] * Dimension + i, cell_dof_number[j_id] * Dimension + j) += + w_rj(node_id, j_cell) * M(i, j); + } + } + } + } + if (primal_node_is_on_boundary[node_id]) { + for (size_t l_face = 0; l_face < node_to_face_matrix[node_id].size(); ++l_face) { + FaceId l_id = node_to_face_matrix[node_id][l_face]; + if (primal_face_is_on_boundary[l_id]) { + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + S(cell_dof_number[i_id] * Dimension + i, face_dof_number[l_id] * Dimension + j) -= + time_factor * w_rl(node_id, l_face) * M(i, j); + } + } + if (primal_face_is_neumann[face_id]) { + for (size_t i = 0; i < Dimension; ++i) { + for (size_t j = 0; j < Dimension; ++j) { + S(face_dof_number[face_id] * Dimension + i, face_dof_number[l_id] * Dimension + j) += + w_rl(node_id, l_face) * M(i, j); + } + } + } + } + } + } + } + } + // } + } + } + } + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (primal_face_is_dirichlet[face_id]) { + for (size_t i = 0; i < Dimension; ++i) { + S(face_dof_number[face_id] * Dimension + i, face_dof_number[face_id] * Dimension + i) += 1; + } + } + } + + Vector<double> b{number_of_dof * Dimension}; + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + for (size_t i = 0; i < Dimension; ++i) { + b[(cell_dof_number[cell_id] * Dimension) + i] = + primal_Vj[cell_id] * (time_factor * fj[cell_id][i] + unsteady * Uj[cell_id][i]); + } + } + + // Dirichlet + NodeValue<bool> node_tag{mesh->connectivity()}; + node_tag.fill(false); + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<T, DirichletBoundaryCondition>) { + const auto& face_list = bc.faceList(); + const auto& value_list = bc.valueList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + const FaceId face_id = face_list[i_face]; + + for (size_t i = 0; i < Dimension; ++i) { + b[(face_dof_number[face_id] * Dimension) + i] += value_list[i_face][i]; + } + } + } + }, + boundary_condition); + } + + for (const auto& boundary_condition : boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr ((std::is_same_v<T, NormalStrainBoundaryCondition>)) { + const auto& face_list = bc.faceList(); + const auto& value_list = bc.valueList(); + for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + FaceId face_id = face_list[i_face]; + for (size_t i = 0; i < Dimension; ++i) { + b[face_dof_number[face_id] * Dimension + i] += mes_l[face_id] * value_list[i_face][i]; // sign + } + } + } + }, + boundary_condition); + } + + CRSMatrix A{S.getCRSMatrix()}; + Vector<double> U{number_of_dof * Dimension}; + U = zero; + Vector r = A * U - b; + std::cout << "initial (real) residu = " << std::sqrt(dot(r, r)) << '\n'; + + LinearSolver solver; + solver.solveLocalSystem(A, U, b); + + r = A * U - b; + + std::cout << "final (real) residu = " << std::sqrt(dot(r, r)) << '\n'; + + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + for (size_t i = 0; i < Dimension; ++i) { + Uj[cell_id][i] = U[(cell_dof_number[cell_id] * Dimension) + i]; + } + } + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + for (size_t i = 0; i < Dimension; ++i) { + if (primal_face_is_on_boundary[face_id]) { + Ul[face_id][i] = U[(face_dof_number[face_id] * Dimension) + i]; + } + } + } + NodeValue<TinyVector<3>> ur3d{mesh->connectivity()}; + ur3d.fill(zero); + + parallel_for( + mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { + TinyVector<Dimension> x = zero; + const auto node_cells = node_to_cell_matrix[node_id]; + for (size_t i_cell = 0; i_cell < node_cells.size(); ++i_cell) { + CellId cell_id = node_cells[i_cell]; + x += w_rj(node_id, i_cell) * Uj[cell_id]; + } + const auto node_faces = node_to_face_matrix[node_id]; + for (size_t i_face = 0; i_face < node_faces.size(); ++i_face) { + FaceId face_id = node_faces[i_face]; + if (primal_face_is_on_boundary[face_id]) { + x += w_rl(node_id, i_face) * Ul[face_id]; + } + } + for (size_t i = 0; i < Dimension; ++i) { + ur3d[node_id][i] = x[i]; + } + }); + } + } else { + throw NotImplementedError("not done in 1d"); + } + } +}; +template LegacyVectorDiamondScheme<1>::LegacyVectorDiamondScheme( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + CellValue<TinyVector<1>>&, + FaceValue<TinyVector<1>>&, + const double&, + const double&, + CellValuePerNode<double>&, + FaceValuePerNode<double>&); + +template LegacyVectorDiamondScheme<2>::LegacyVectorDiamondScheme( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + CellValue<TinyVector<2>>&, + FaceValue<TinyVector<2>>&, + const double&, + const double&, + CellValuePerNode<double>&, + FaceValuePerNode<double>&); + +template LegacyVectorDiamondScheme<3>::LegacyVectorDiamondScheme( + std::shared_ptr<const IMesh>, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&, + const FunctionSymbolId&, + const FunctionSymbolId&, + const FunctionSymbolId&, + CellValue<TinyVector<3>>&, + FaceValue<TinyVector<3>>&, + const double&, + const double&, + CellValuePerNode<double>&, + FaceValuePerNode<double>&); + +#endif // VECTOR_DIAMOND_SCHEME_HPP diff --git a/tests/test_CG.cpp b/tests/test_CG.cpp index f0c14f24f9c416048aa05905123621deb011219f..5afd80e7c39250ac76080cc611f6fe6a19afc595 100644 --- a/tests/test_CG.cpp +++ b/tests/test_CG.cpp @@ -4,6 +4,7 @@ #include <algebra/CG.hpp> #include <algebra/CRSMatrix.hpp> #include <algebra/CRSMatrixDescriptor.hpp> +#include <algebra/Vector.hpp> // clazy:excludeall=non-pod-global-static diff --git a/tests/test_LeastSquareSolver.cpp b/tests/test_LeastSquareSolver.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6022bbe151ff2c261dc21b015692b9045edcc9ca --- /dev/null +++ b/tests/test_LeastSquareSolver.cpp @@ -0,0 +1,77 @@ +#include <catch2/catch.hpp> + +#include <algebra/LeastSquareSolver.hpp> +#include <algebra/LocalRectangularMatrix.hpp> +#include <algebra/Vector.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("LeastSquareSolver", "[algebra]") +{ + SECTION("Least Squares [under-determined]") + { + Vector<double> b{3}; + b[0] = 1; + b[1] = 0; + b[2] = 0; + + LocalRectangularMatrix<double> A{3, 4}; + A(0, 0) = 1; + A(0, 1) = 1; + A(0, 2) = 1; + A(0, 3) = 1; + A(1, 0) = 1; + A(1, 1) = -1; + A(1, 2) = 0; + A(1, 3) = 0; + A(2, 0) = 0; + A(2, 1) = 0; + A(2, 2) = 1; + A(2, 3) = -1; + + Vector<double> x_exact{4}; + + x_exact[0] = 0.25; + x_exact[1] = 0.25; + x_exact[2] = 0.25; + x_exact[3] = 0.25; + + Vector<double> x{4}; + x = 0; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + Vector error = x - x_exact; + REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x, x))); + } + + SECTION("Least Squares [over-determined]") + { + LocalRectangularMatrix<double> A{3, 2}; + A(0, 0) = 0; + A(0, 1) = 1; + A(1, 0) = 1; + A(1, 1) = 1; + A(2, 0) = 2; + A(2, 1) = 1; + + Vector<double> x_exact{2}; + x_exact[0] = -3; + x_exact[1] = 5; + + Vector b{3}; + b[0] = 6; + b[1] = 0; + b[2] = 0; + + Vector<double> x{2}; + x = 0; + + LeastSquareSolver ls_solver; + ls_solver.solveLocalSystem(A, x, b); + + Vector error = x - x_exact; + REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x_exact, x_exact))); + } +}