From 5ca816e4d31806b3b11cb82cc0eb7e7cc4a1e143 Mon Sep 17 00:00:00 2001 From: LABOURASSE Emmanuel <labourassee@gmail.com> Date: Mon, 28 Sep 2020 18:05:57 +0200 Subject: [PATCH] New boundary condition treatment for Neumann --- .../algorithms/HeatDiamondAlgorithm2.cpp | 906 ++++++++++++++++++ .../algorithms/HeatDiamondAlgorithm2.hpp | 29 + 2 files changed, 935 insertions(+) create mode 100644 src/language/algorithms/HeatDiamondAlgorithm2.cpp create mode 100644 src/language/algorithms/HeatDiamondAlgorithm2.hpp diff --git a/src/language/algorithms/HeatDiamondAlgorithm2.cpp b/src/language/algorithms/HeatDiamondAlgorithm2.cpp new file mode 100644 index 000000000..cfcfe8ef3 --- /dev/null +++ b/src/language/algorithms/HeatDiamondAlgorithm2.cpp @@ -0,0 +1,906 @@ +#include <language/algorithms/HeatDiamondAlgorithm2.hpp> + +#include <algebra/BiCGStab.hpp> +#include <algebra/CRSMatrix.hpp> +#include <algebra/LocalRectangularMatrix.hpp> +#include <algebra/PCG.hpp> +#include <algebra/SparseMatrixDescriptor.hpp> +#include <algebra/TinyVector.hpp> +#include <algebra/Vector.hpp> +#include <language/utils/InterpolateItemValue.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp> +#include <mesh/DiamondDualConnectivityManager.hpp> +#include <mesh/DiamondDualMeshManager.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/MeshNodeBoundary.hpp> +#include <output/VTKWriter.hpp> +#include <scheme/DirichletBoundaryConditionDescriptor.hpp> +#include <scheme/FourierBoundaryConditionDescriptor.hpp> +#include <scheme/NeumannBoundaryConditionDescriptor.hpp> +#include <scheme/SymmetryBoundaryConditionDescriptor.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>; + + constexpr ItemType FaceType = [] { + if constexpr (Dimension > 1) { + return ItemType::face; + } else { + return ItemType::node; + } + }(); + + 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: { + // const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor = + // dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor); + throw NotImplementedError("NIY"); + break; + } + case IBoundaryConditionDescriptor::Type::dirichlet: { + const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor = + dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor); + + for (size_t i_ref_face_list = 0; i_ref_face_list < mesh->connectivity().template numberOfRefItemList<FaceType>(); + ++i_ref_face_list) { + const auto& ref_face_list = mesh->connectivity().template refItemList<FaceType>(i_ref_face_list); + const RefId& ref = ref_face_list.refId(); + if (ref == dirichlet_bc_descriptor.boundaryDescriptor()) { + MeshNodeBoundary<Dimension> mesh_node_boundary{mesh, ref_face_list}; + + const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId(); + + const auto& node_list = mesh_node_boundary.nodeList(); + + Array<const double> value_list = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::node>(g_id, mesh->xr(), + node_list); + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + NodeId node_id = node_list[i_node]; + if (not is_dirichlet[node_id]) { + is_dirichlet[node_id] = true; + dirichlet_value[node_id] = value_list[i_node]; + } + } + + boundary_condition_list.push_back(DirichletBoundaryCondition{node_list, value_list}); + } + } + + if constexpr (Dimension > 1) { + for (size_t i_ref_node_list = 0; + i_ref_node_list < mesh->connectivity().template numberOfRefItemList<ItemType::node>(); ++i_ref_node_list) { + const auto& ref_node_list = mesh->connectivity().template refItemList<ItemType::node>(i_ref_node_list); + const RefId& ref = ref_node_list.refId(); + if (ref == dirichlet_bc_descriptor.boundaryDescriptor()) { + const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId(); + + const auto& node_list = ref_node_list.list(); + + Array<const double> value_list = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::node>(g_id, + mesh->xr(), + node_list); + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + NodeId node_id = node_list[i_node]; + if (not is_dirichlet[node_id]) { + is_dirichlet[node_id] = true; + dirichlet_value[node_id] = value_list[i_node]; + } + } + + boundary_condition_list.push_back(DirichletBoundaryCondition{node_list, value_list}); + } + } + } + + break; + } + case IBoundaryConditionDescriptor::Type::fourier: { + // const FourierBoundaryConditionDescriptor& fourier_bc_descriptor = + // dynamic_cast<const FourierBoundaryConditionDescriptor&>(*bc_descriptor); + throw NotImplementedError("NIY"); + break; + } + case IBoundaryConditionDescriptor::Type::neumann: { + const NeumannBoundaryConditionDescriptor& neumann_bc_descriptor = + dynamic_cast<const NeumannBoundaryConditionDescriptor&>(*bc_descriptor); + + for (size_t i_ref_face_list = 0; i_ref_face_list < mesh->connectivity().template numberOfRefItemList<FaceType>(); + ++i_ref_face_list) { + const auto& ref_face_list = mesh->connectivity().template refItemList<FaceType>(i_ref_face_list); + const RefId& ref = ref_face_list.refId(); + if (ref == neumann_bc_descriptor.boundaryDescriptor()) { + const FunctionSymbolId g_id = neumann_bc_descriptor.rhsSymbolId(); + + if constexpr (Dimension > 1) { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + Array<const FaceId> face_list = ref_face_list.list(); + Array<const double> value_list = + InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, + mesh_data.xl(), + face_list); + boundary_condition_list.push_back(NeumannBoundaryCondition{face_list, value_list}); + } else { + throw NotImplementedError("Neumann conditions are not supported 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 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_neumann(mesh->connectivity()); + if (parallel::size() > 1) { + throw NotImplementedError("Calculation of face_is_neumann is incorrect"); + } + + primal_face_is_neumann.fill(false); + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (primal_face_is_on_boundary[face_id]) { + 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]; + if (not is_dirichlet[node_id]) { + primal_face_is_neumann[face_id] = true; + } + } + } + } + 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 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) { + Vector<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]) { + LocalRectangularMatrix<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]; + } + } + Vector<double> x{node_to_cell.size()}; + x = 0; + + LocalRectangularMatrix B = transpose(A) * A; + Vector y = transpose(A) * b; + PCG<false>{y, B, B, x, 10, 1e-12}; + + 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++; + } + } + LocalRectangularMatrix<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++; + } + } + } + + Vector<double> x{node_to_cell.size() + nb_face_used}; + x = 0; + + LocalRectangularMatrix B = transpose(A) * A; + Vector y = transpose(A) * b; + PCG<false>{y, B, B, x, 10, 1e-12}; + + 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++]; + } + } + } + } + + { + VTKWriter vtk_writer("T_" + std::to_string(Dimension), 0.01); + + vtk_writer.write(mesh, + {NamedItemValue{"Tr", Tr}, NamedItemValue{"temperature", Tj}, + NamedItemValue{"coords", mesh->xr()}, NamedItemValue{"xj", mesh_data.xj()}, + NamedItemValue{"cell_owner", mesh->connectivity().cellOwner()}, + NamedItemValue{"node_owner", mesh->connectivity().nodeOwner()}}, + 0, true); // forces last output + } + + { + std::shared_ptr diamond_mesh = DiamondDualMeshManager::instance().getDiamondDualMesh(mesh); + + MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh); + + std::shared_ptr mapper = + DiamondDualConnectivityManager::instance().getConnectivityToDiamondDualConnectivityDataMapper( + 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()); + + VTKWriter vtk_writer("D_" + std::to_string(Dimension), 0.01); + + vtk_writer.write(diamond_mesh, + {NamedItemValue{"kappaj", dual_kappaj}, NamedItemValue{"coords", diamond_mesh->xr()}, + NamedItemValue{"Vj", diamond_mesh_data.Vj()}, NamedItemValue{"xj", diamond_mesh_data.xj()}}, + 0, true); // forces last output + + 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); + + VTKWriter vtk_writer("DD_" + std::to_string(Dimension), 0.01); + + vtk_writer.write(diamond_mesh, + {NamedItemValue{"alphaj", alpha_j}, NamedItemValue{"xj", diamond_mesh_data.xj()}, + NamedItemValue{"Vl", diamond_mesh_data.Vj()}, NamedItemValue{"|l|", dual_mes_l_j}}, + 0, + true); // forces last output + + return computed_alpha_l; + }(); + + SparseMatrixDescriptor S{number_of_dof}; + // A^{JJ} & A^{LJ} + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + // if (not primal_face_is_neumann[face_id]) { COMMENTE + 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]; + + 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; // AJOUT EL + } + } else { + S(cell_dof_number[cell_id1], cell_dof_number[cell_id2]) -= beta_l; + } + } + } + } + + 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; + }(); + + const auto& dual_Cjr = diamond_mesh_data.Cjr(); + + const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix(); + + const NodeValuePerFace<const TinyVector<Dimension>> primal_nlr = mesh_data.nlr(); + const auto& primal_face_cell_is_reversed = mesh->connectivity().cellFaceIsReversed(); + const auto& face_local_numbers_in_their_cells = mesh->connectivity().faceLocalNumbersInTheirCells(); + const auto& primal_node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); + // A^{JR}A^{RJ} & A^{JR}A^{RL} & A^{LR}A^{RL} + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + // if (face_to_cell_matrix[face_id].size() > 1) { COMMENTE + const double alpha_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 = + primal_face_cell_is_reversed(i_id, face_local_numbers_in_their_cells(face_id, i_face_cell)); + + 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]; + + if (not is_dirichlet[node_id]) { + const TinyVector<Dimension> nil = [&] { + if (is_face_reversed_for_cell_i) { + return -primal_nlr(face_id, i_node); + } else { + return primal_nlr(face_id, i_node); + } + }(); + + 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 * (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; // AJOUT + } + } + 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_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; // AJOUT + } + // S(face_dof_number[l_id], cell_dof_number[i_id]) += w_rl(node_id, l_face) * a_ir; + } + } + } + } + } + } + } + } + } + // COMMENTE POUR L INSTANT PAS DE FOURIER + // for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + // if (primal_face_is_neumann[face_id]) { + // S(face_dof_number[face_id], face_dof_number[face_id]) += mes_l[face_id] * delta[face_id] ; + // } + // } + + 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}; + Vector<double> b{number_of_dof}; + + const auto& primal_cell_to_face_matrix = mesh->connectivity().cellToFaceMatrix(); + 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 -A^{JR}b^R & -A^{LR}b^R + 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& node_list = bc.nodeList(); + const auto& value_list = bc.valueList(); + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; + if (not node_tag[node_id]) { + node_tag[node_id] = true; + + const auto& node_cells = primal_node_to_cell_matrix[node_id]; + for (size_t i_cell = 0; i_cell < node_cells.size(); ++i_cell) { + const CellId cell_id = node_cells[i_cell]; + + const auto& primal_cell_to_face = primal_cell_to_face_matrix[cell_id]; + + for (size_t i_cell_face = 0; i_cell_face < primal_cell_to_face.size(); ++i_cell_face) { + FaceId face_id = primal_cell_to_face_matrix[cell_id][i_cell_face]; + + const bool is_face_reversed_for_cell_i = [=] { + for (size_t i_face = 0; i_face < primal_cell_to_face.size(); ++i_face) { + FaceId primal_face_id = primal_cell_to_face[i_face]; + if (primal_face_id == face_id) { + return primal_face_cell_is_reversed(cell_id, i_face); + } + } + Assert(false, "cannot find cell's face"); + return false; + }(); + + const auto& primal_face_to_node = primal_face_to_node_matrix[face_id]; + + for (size_t i_face_node = 0; i_face_node < primal_face_to_node.size(); ++i_face_node) { + if (primal_face_to_node[i_face_node] == node_id) { + const TinyVector<Dimension> nil = [=] { + if (is_face_reversed_for_cell_i) { + return -primal_nlr(face_id, i_face_node); + } else { + return primal_nlr(face_id, i_face_node); + } + }(); + + 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); + b[cell_dof_number[cell_id]] += alpha_l[face_id] * value_list[i_node] * (nil, Clr); + if (primal_face_is_neumann[face_id]) { + b[face_dof_number[face_id]] += alpha_l[face_id] * value_list[i_node] * (nil, Clr); + } + } + } + } + } + } + } + } + } + } + }, + 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]; + } + } + }, + boundary_condition); + } + // EL COMMENTE + // 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& node_list = bc.nodeList(); + // const auto& value_list = bc.valueList(); + // for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + // NodeId node_id = node_list[i_node]; + // for (size_t i_face = 0; i_face < node_to_face_matrix[node_id].size(); ++i_face) { + // FaceId face_id = node_to_face_matrix[node_id][i_face]; + // if (primal_face_is_neumann[face_id]) { // HARD ajouter contribution dirchlet à face neumann + // A^{LR} b^R + // int nb_node_per_face = primal_face_to_node_matrix[face_id].size(); + // b[face_dof_number[face_id]] += (1. / nb_node_per_face) * value_list[i_node]; + // } + // } + // } + // } + // }, + // boundary_condition); + // } + + for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) { + if (primal_face_is_neumann[face_id]) { + CellId cell_id = face_to_cell_matrix[face_id][0]; + const bool is_face_reversed_for_cell_i = + primal_face_cell_is_reversed(cell_id, face_local_numbers_in_their_cells(face_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]; + if (is_dirichlet[node_id]) { + const TinyVector<Dimension> nil = [&] { + if (is_face_reversed_for_cell_i) { + return -primal_nlr(face_id, i_node); + } else { + return primal_nlr(face_id, i_node); + } + }(); + 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); + b[cell_dof_number[cell_id]] -= alpha_l[face_id] * (nil, Clr) * dirichlet_value[node_id]; /// + } + } + } + } + } + } + + Vector<double> T{number_of_dof}; + T = 0; + std::cout << " Matrix " << S << "\n"; + for (size_t k = 0; k < b.size(); ++k) { + std::cout << " b[" << k << "]=" << b[k] << ", "; + } + std::cout << "\n"; + BiCGStab{b, A, T, 1000, 1e-15}; + + Vector r = A * T - b; + + std::cout << "real residu = " << std::sqrt((r, r)) << '\n'; + + CellValue<double> Temperature{mesh->connectivity()}; + + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { Temperature[cell_id] = T[cell_dof_number[cell_id]]; }); + + Vector<double> error{mesh->numberOfCells()}; + CellValue<double> cell_error{mesh->connectivity()}; + 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]) * sqrt(primal_Vj[cell_id]); + }); + for (size_t cell_id = 0; cell_id < mesh->numberOfCells(); cell_id++) { + if (error_max < std::abs(error[cell_id])) { + error_max = std::abs(error[cell_id]); + cell_max = cell_id; + } + } + + std::cout << " ||Error||_max = " << error_max << " on cell " << cell_max << "\n"; + std::cout << "||Error||_2 = " << std::sqrt((error, error)) << "\n"; + + { + VTKWriter vtk_writer("Temperature_" + std::to_string(Dimension), 0.01); + + vtk_writer.write(mesh, + {NamedItemValue{"T", Temperature}, NamedItemValue{"Vj", primal_Vj}, + NamedItemValue{"Texact", Tj}, NamedItemValue{"error", cell_error}}, + 0, + true); // forces last output + } + } + } 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 NodeId> m_node_list; + + public: + const Array<const NodeId>& + nodeList() const + { + return m_node_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + DirichletBoundaryCondition(const Array<const NodeId>& node_list, const Array<const double>& value_list) + : m_value_list{value_list}, m_node_list{node_list} + { + Assert(m_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 000000000..2cf388ed1 --- /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 -- GitLab