From a3b5c00ba38166fae7e19fa2f86c7532896adf17 Mon Sep 17 00:00:00 2001 From: Axelle <axelle.drouard@orange.fr> Date: Wed, 12 Mar 2025 17:04:28 +0100 Subject: [PATCH] Add local wavespeeds kinetic lagrangian scheme for multiD problems --- src/language/modules/KineticSchemeModule.cpp | 22 + src/scheme/CMakeLists.txt | 1 + .../EulerKineticSolverLagrangeMultiD.cpp | 252 +++- .../EulerKineticSolverLagrangeMultiDLocal.cpp | 1061 +++++++++++++++++ .../EulerKineticSolverLagrangeMultiDLocal.hpp | 28 + 5 files changed, 1358 insertions(+), 6 deletions(-) create mode 100644 src/scheme/EulerKineticSolverLagrangeMultiDLocal.cpp create mode 100644 src/scheme/EulerKineticSolverLagrangeMultiDLocal.hpp diff --git a/src/language/modules/KineticSchemeModule.cpp b/src/language/modules/KineticSchemeModule.cpp index 9c16e6122..e5e8b929f 100644 --- a/src/language/modules/KineticSchemeModule.cpp +++ b/src/language/modules/KineticSchemeModule.cpp @@ -10,6 +10,7 @@ #include <scheme/EulerKineticSolverFirstOrderFV.hpp> #include <scheme/EulerKineticSolverLagrangeFV.hpp> #include <scheme/EulerKineticSolverLagrangeMultiD.hpp> +#include <scheme/EulerKineticSolverLagrangeMultiDLocal.hpp> #include <scheme/EulerKineticSolverMeanFluxMood.hpp> #include <scheme/EulerKineticSolverMoodFD.hpp> #include <scheme/EulerKineticSolverMoodFV.hpp> @@ -860,6 +861,27 @@ KineticSchemeModule::KineticSchemeModule() )); + this->_addBuiltinFunction("euler_kinetic_lagrange_multiD_local", + std::function( + + [](const double& dt, const size_t& L, const size_t& M, const size_t& k, + const double& gamma, const double& eps, const size_t& space_order, + const size_t& time_order, const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return eulerKineticSolverLagrangeMultiDLocal(dt, L, M, k, gamma, eps, space_order, + time_order, rho, u, E, p, + bc_descriptor_list); + } + + )); + this->_addBuiltinFunction("euler_kinetic_acoustic_lagrange_FV", std::function( diff --git a/src/scheme/CMakeLists.txt b/src/scheme/CMakeLists.txt index d563dcd9f..c6c6dc068 100644 --- a/src/scheme/CMakeLists.txt +++ b/src/scheme/CMakeLists.txt @@ -18,6 +18,7 @@ add_library( WavesEquationNonUniformCellKineticSolver.cpp EulerKineticSolver.cpp EulerKineticSolverLagrangeMultiD.cpp + EulerKineticSolverLagrangeMultiDLocal.cpp EulerKineticSolverMeanFluxMood.cpp EulerKineticSolverOneFluxMood.cpp EulerKineticSolverMoodFD.cpp diff --git a/src/scheme/EulerKineticSolverLagrangeMultiD.cpp b/src/scheme/EulerKineticSolverLagrangeMultiD.cpp index 3e8eb0b8a..15761bece 100644 --- a/src/scheme/EulerKineticSolverLagrangeMultiD.cpp +++ b/src/scheme/EulerKineticSolverLagrangeMultiD.cpp @@ -185,7 +185,7 @@ class EulerKineticSolverLagrangeMultiD const size_t nb_waves = m_lambda_vector.size(); CellArray<TinyVector<Dimension>> M{p_mesh->connectivity(), nb_waves}; TinyVector<Dimension> inv_S = zero; - TinyMatrix<Dimension> I = identity; + // TinyMatrix<Dimension> I = identity; for (size_t d = 0; d < Dimension; ++d) { for (size_t i = 0; i < nb_waves; ++i) { inv_S[d] += m_lambda_vector[i][d] * m_lambda_vector[i][d]; @@ -197,10 +197,8 @@ class EulerKineticSolverLagrangeMultiD m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) { M[cell_id][i_wave] = 1. / nb_waves * u[cell_id]; - for (size_t d1 = 0; d1 < Dimension; ++d1) { - for (size_t d2 = 0; d2 < Dimension; ++d2) { - M[cell_id][i_wave][d1] += inv_S[d2] * m_lambda_vector[i_wave][d2] * p[cell_id] * I(d2, d1); - } + for (size_t d = 0; d < Dimension; ++d) { + M[cell_id][i_wave][d] += 1. / Dimension * inv_S[d] * m_lambda_vector[i_wave][d] * p[cell_id]; } } }); @@ -251,6 +249,158 @@ class EulerKineticSolverLagrangeMultiD return Fr; } + NodeArray<double> + compute_Flux_Fp_lambdaj(const CellValue<const double>& p, + const CellValue<const TinyVector<Dimension>>& u, + const CellValue<const double>& lambda_j, + const NodeValue<const bool>& is_boundary_node) + { + const NodeValuePerCell<const TinyVector<Dimension>> njr = MeshDataManager::instance().getMeshData(m_mesh).njr(); + const size_t nb_waves = m_lambda_vector.size(); + NodeArray<double> Fr(m_mesh.connectivity(), nb_waves); + const auto& node_local_numbers_in_their_cells = p_mesh->connectivity().nodeLocalNumbersInTheirCells(); + const auto& node_to_cell_matrix = p_mesh->connectivity().nodeToCellMatrix(); + + Fr.fill(0.); + + if (Dimension > 1) { + throw NotImplementedError("Non local lambda not implemented for dimension > 1"); + } + + // std::cout << lambda_j << "\n"; + + parallel_for( + p_mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { + if (not is_boundary_node[node_id]) { + const auto& node_local_number_in_its_cell = node_local_numbers_in_their_cells.itemArray(node_id); + const auto& node_to_cell = node_to_cell_matrix[node_id]; + + double lambda_r = 0.; + for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) { + const CellId cell_id = node_to_cell[i_cell]; + + lambda_r = std::max(lambda_r, lambda_j[cell_id]); + } + + // Only in 1D + std::vector<TinyVector<Dimension>> lambda_r_vector(2); + lambda_r_vector[0][0] = lambda_r; + lambda_r_vector[1][0] = -lambda_r; + // std::cout << lambda_r_vector << "\n"; + + TinyVector<Dimension> inv_S = zero; + for (size_t d = 0; d < Dimension; ++d) { + for (size_t i = 0; i < nb_waves; ++i) { + inv_S[d] += lambda_r_vector[i][d] * lambda_r_vector[i][d]; + } + inv_S[d] = 1. / inv_S[d]; + } + + for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) { + double sum_li_njr = 0; + + for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) { + const CellId cell_id = node_to_cell[i_cell]; + const unsigned int i_node = node_local_number_in_its_cell[i_cell]; + + double li_njr = dot(lambda_r_vector[i_wave], njr(cell_id, i_node)); + if (li_njr > 1e-14) { + double Fj = (1. / nb_waves) * p[cell_id]; + + for (size_t d = 0; d < Dimension; ++d) { + Fj += inv_S[d] * lambda_r_vector[i_wave][d] * (lambda_r * lambda_r * u[cell_id][d]); + } + + Fr[node_id][i_wave] += li_njr * Fj; + sum_li_njr += li_njr; + } + } + if (sum_li_njr != 0) { + Fr[node_id][i_wave] = 1. / sum_li_njr * Fr[node_id][i_wave]; + } + } + } + }); + + return Fr; + } + + NodeArray<TinyVector<Dimension>> + compute_Flux_Fu_lambdaj(const CellValue<const double>& p, + const CellValue<const TinyVector<Dimension>>& u, + const CellValue<const double>& lambda_j, + const NodeValue<const bool>& is_boundary_node) + { + const NodeValuePerCell<const TinyVector<Dimension>> njr = MeshDataManager::instance().getMeshData(m_mesh).njr(); + const size_t nb_waves = m_lambda_vector.size(); + NodeArray<TinyVector<Dimension>> Fr(m_mesh.connectivity(), nb_waves); + const auto& node_local_numbers_in_their_cells = p_mesh->connectivity().nodeLocalNumbersInTheirCells(); + const auto& node_to_cell_matrix = p_mesh->connectivity().nodeToCellMatrix(); + + Fr.fill(zero); + + if (Dimension > 1) { + throw NotImplementedError("Non local lambda not implemented for dimension > 1"); + } + + parallel_for( + p_mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { + if (not is_boundary_node[node_id]) { + const auto& node_local_number_in_its_cell = node_local_numbers_in_their_cells.itemArray(node_id); + const auto& node_to_cell = node_to_cell_matrix[node_id]; + + double lambda_r = 0.; + for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) { + const CellId cell_id = node_to_cell[i_cell]; + + lambda_r = std::max(lambda_r, lambda_j[cell_id]); + } + + // Only in 1D + std::vector<TinyVector<Dimension>> lambda_r_vector(2); + lambda_r_vector[0][0] = lambda_r; + lambda_r_vector[1][0] = -lambda_r; + + TinyVector<Dimension> inv_S = zero; + for (size_t d = 0; d < Dimension; ++d) { + for (size_t i = 0; i < nb_waves; ++i) { + inv_S[d] += lambda_r_vector[i][d] * lambda_r_vector[i][d]; + } + inv_S[d] = 1. / inv_S[d]; + } + + for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) { + double sum_li_njr = 0; + + for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) { + const CellId cell_id = node_to_cell[i_cell]; + const unsigned int i_node = node_local_number_in_its_cell[i_cell]; + + double li_njr = dot(lambda_r_vector[i_wave], njr(cell_id, i_node)); + if (li_njr > 1e-14) { + TinyVector<Dimension> Fj = (1. / nb_waves) * u[cell_id]; + + for (size_t d1 = 0; d1 < Dimension; ++d1) { + for (size_t d2 = 0; d2 < Dimension; ++d2) { + TinyMatrix<Dimension> I = identity; + Fj[d1] += inv_S[d2] * lambda_r_vector[i_wave][d2] * p[cell_id] * I(d2, d1); + } + } + + Fr[node_id][i_wave] += li_njr * Fj; + sum_li_njr += li_njr; + } + } + if (sum_li_njr != 0) { + Fr[node_id][i_wave] = 1. / sum_li_njr * Fr[node_id][i_wave]; + } + } + } + }); + + return Fr; + } + void apply_bc(NodeArray<double>& Fr_p, NodeArray<TinyVector<Dimension>>& Fr_u, @@ -503,6 +653,39 @@ class EulerKineticSolverLagrangeMultiD return u; } + const NodeValue<TinyVector<Dimension>> + compute_velocity_flux_lambdaj(NodeArray<double>& F, const CellValue<const double>& lambda_j) + { + const auto& node_to_cell_matrix = p_mesh->connectivity().nodeToCellMatrix(); + NodeValue<TinyVector<Dimension>> u{p_mesh->connectivity()}; + const size_t nb_waves = m_lambda_vector.size(); + u.fill(zero); + + parallel_for( + m_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + const auto& node_to_cell = node_to_cell_matrix[node_id]; + + double lambda_r = 0.; + for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) { + const CellId cell_id = node_to_cell[i_cell]; + + lambda_r = std::max(lambda_r, lambda_j[cell_id]); + } + + const double inv_lambda_squared = (1. / (lambda_r * lambda_r)); + + std::vector<TinyVector<Dimension>> lambda_r_vector(2); + lambda_r_vector[0][0] = lambda_r; + lambda_r_vector[1][0] = -lambda_r; + + for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) { + u[node_id] += F[node_id][i_wave] * lambda_r_vector[i_wave]; + } + u[node_id] = inv_lambda_squared * u[node_id]; + }); + return u; + } + const NodeValue<TinyVector<Dimension>> compute_pressure_flux(NodeArray<TinyVector<Dimension>>& F) { @@ -514,7 +697,41 @@ class EulerKineticSolverLagrangeMultiD m_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) { for (size_t dim = 0; dim < Dimension; ++dim) { - p[node_id][dim] += F[node_id][i_wave][dim] * m_lambda_vector[i_wave][dim]; + p[node_id][dim] += dot(F[node_id][i_wave], m_lambda_vector[i_wave]); + // p[node_id][0] += 0.5 * F[node_id][i_wave][dim] * m_lambda_vector[i_wave][dim]; + // p[node_id][1] += 0.5 * F[node_id][i_wave][dim] * m_lambda_vector[i_wave][dim]; + } + } + }); + return p; + } + + const NodeValue<TinyVector<Dimension>> + compute_pressure_flux_lambdaj(NodeArray<TinyVector<Dimension>>& F, const CellValue<const double>& lambda_j) + { + const auto& node_to_cell_matrix = p_mesh->connectivity().nodeToCellMatrix(); + NodeValue<TinyVector<Dimension>> p{p_mesh->connectivity()}; + const size_t nb_waves = m_lambda_vector.size(); + p.fill(zero); + + parallel_for( + m_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + const auto& node_to_cell = node_to_cell_matrix[node_id]; + + double lambda_r = 0.; + for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) { + const CellId cell_id = node_to_cell[i_cell]; + + lambda_r = std::max(lambda_r, lambda_j[cell_id]); + } + + std::vector<TinyVector<Dimension>> lambda_r_vector(2); + lambda_r_vector[0][0] = lambda_r; + lambda_r_vector[1][0] = -lambda_r; + + for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) { + for (size_t dim = 0; dim < Dimension; ++dim) { + p[node_id][dim] += F[node_id][i_wave][dim] * lambda_r_vector[i_wave][dim]; } } }); @@ -638,14 +855,37 @@ class EulerKineticSolverLagrangeMultiD CellValue<double> p_np1 = copy(p); CellValue<double> E_np1 = copy(E); + const CellValue<const double> cj = [&] { + CellValue<double> computed_cj{m_mesh.connectivity()}; + parallel_for( + m_mesh.numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { computed_cj[cell_id] = std::sqrt(m_gamma * p[cell_id] / rho[cell_id]); }); + return computed_cj; + }(); + + const CellValue<const double> lambda_j = [&] { + CellValue<double> computed_lambda_j{m_mesh.connectivity()}; + parallel_for( + m_mesh.numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { computed_lambda_j[cell_id] = std::abs(rho[cell_id] * cj[cell_id]); }); + return computed_lambda_j; + }(); + + // std::cout << "c = " << cj << ", \n lambda = " << lambda_j << ", \n rho = " << rho << "\n"; + const CellArray<const double> F_p = compute_M_p(p, u, lambda); const CellArray<const TinyVector<Dimension>> F_u = compute_M_u(u, p); NodeArray<double> Fr_p = compute_Flux<double>(F_p, is_boundary_node); NodeArray<TinyVector<Dimension>> Fr_u = compute_Flux<TinyVector<Dimension>>(F_u, is_boundary_node); + // NodeArray<double> Fr_p = compute_Flux_Fp_lambdaj(p, u, lambda_j, is_boundary_node); + // NodeArray<TinyVector<Dimension>> Fr_u = compute_Flux_Fu_lambdaj(p, u, lambda_j, is_boundary_node); + apply_bc(Fr_p, Fr_u, p, u, F_p, F_u, lambda, bc_list); + // const NodeValue<TinyVector<Dimension>> ur = compute_velocity_flux_lambdaj(Fr_p, lambda_j); + // const NodeValue<TinyVector<Dimension>> pr = compute_pressure_flux_lambdaj(Fr_u, lambda_j); const NodeValue<TinyVector<Dimension>> ur = compute_velocity_flux(Fr_p, lambda); const NodeValue<TinyVector<Dimension>> pr = compute_pressure_flux(Fr_u); diff --git a/src/scheme/EulerKineticSolverLagrangeMultiDLocal.cpp b/src/scheme/EulerKineticSolverLagrangeMultiDLocal.cpp new file mode 100644 index 000000000..5672a020a --- /dev/null +++ b/src/scheme/EulerKineticSolverLagrangeMultiDLocal.cpp @@ -0,0 +1,1061 @@ +#include <scheme/EulerKineticSolverLagrangeMultiDLocal.hpp> + +#include <language/utils/EvaluateAtPoints.hpp> +#include <language/utils/InterpolateItemArray.hpp> +#include <language/utils/InterpolateItemValue.hpp> //new +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshFaceBoundary.hpp> +#include <mesh/MeshFlatFaceBoundary.hpp> +#include <mesh/MeshFlatNodeBoundary.hpp> +#include <mesh/MeshNodeBoundary.hpp> +#include <mesh/MeshVariant.hpp> +#include <scheme/DiscreteFunctionDescriptorP0Vector.hpp> +#include <scheme/DiscreteFunctionP0.hpp> //new +#include <scheme/DiscreteFunctionP0Vector.hpp> +#include <scheme/DiscreteFunctionUtils.hpp> +#include <scheme/IDiscreteFunctionDescriptor.hpp> +#include <scheme/InflowListBoundaryConditionDescriptor.hpp> +#include <scheme/OutflowBoundaryConditionDescriptor.hpp> +#include <scheme/SymmetryBoundaryConditionDescriptor.hpp> +#include <scheme/WallBoundaryConditionDescriptor.hpp> + +#include <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <analysis/QuadratureManager.hpp> +#include <geometry/LineTransformation.hpp> +#include <tuple> + +template <MeshConcept MeshType> +class EulerKineticSolverLagrangeMultiDLocal +{ + private: + constexpr static size_t Dimension = MeshType::Dimension; + using Rd = TinyVector<Dimension>; + + class SymmetryBoundaryCondition; + class WallBoundaryCondition; + class InflowListBoundaryCondition; + class OutflowBoundaryCondition; + + using BoundaryCondition = std:: + variant<SymmetryBoundaryCondition, WallBoundaryCondition, InflowListBoundaryCondition, OutflowBoundaryCondition>; + using BoundaryConditionList = std::vector<BoundaryCondition>; + + const double m_dt; + const size_t m_L; + const size_t m_M; + const size_t m_k; + const double m_gamma; + const size_t m_spaceOrder; + const size_t m_timeOrder; + const CellValue<const double> m_rho; + const CellValue<const TinyVector<Dimension>> m_u; + const CellValue<const double> m_E; + const CellValue<const double> m_p; + const std::shared_ptr<const MeshType> p_mesh; + const MeshType& m_mesh; + const CellValue<const double> m_Vj = MeshDataManager::instance().getMeshData(m_mesh).Vj(); + CellValue<const double> m_inv_Mj; + CellValue<const double> m_inv_Vj; + CellValue<const double> m_Mj; + const CellValue<const TinyVector<Dimension>> m_xj = MeshDataManager::instance().getMeshData(m_mesh).xj(); + const NodeValue<const TinyVector<Dimension>> m_xr = m_mesh.xr(); + + BoundaryConditionList + _getBCList(const MeshType& mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const + { + BoundaryConditionList bc_list; + + for (const auto& bc_descriptor : bc_descriptor_list) { + bool is_valid_boundary_condition = true; + + switch (bc_descriptor->type()) { + case IBoundaryConditionDescriptor::Type::symmetry: { + bc_list.emplace_back( + SymmetryBoundaryCondition(getMeshFlatNodeBoundary(mesh, bc_descriptor->boundaryDescriptor()), + getMeshFlatFaceBoundary(mesh, bc_descriptor->boundaryDescriptor()))); + break; + } + case IBoundaryConditionDescriptor::Type::wall: { + bc_list.emplace_back(WallBoundaryCondition(getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor()), + getMeshFaceBoundary(mesh, bc_descriptor->boundaryDescriptor()))); + break; + } + case IBoundaryConditionDescriptor::Type::inflow_list: { + const InflowListBoundaryConditionDescriptor& inflow_list_bc_descriptor = + dynamic_cast<const InflowListBoundaryConditionDescriptor&>(*bc_descriptor); + if (inflow_list_bc_descriptor.functionSymbolIdList().size() != 1 + Dimension) { + std::ostringstream error_msg; + error_msg << "invalid number of functions for inflow boundary " + << inflow_list_bc_descriptor.boundaryDescriptor() << ", found " + << inflow_list_bc_descriptor.functionSymbolIdList().size() << ", expecting " << 1 + Dimension; + throw NormalError(error_msg.str()); + } + auto node_boundary = getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor()); + + // CellValue<size_t> is_boundary_cell{p_mesh->connectivity()}; + + // is_boundary_cell.fill(0); + + // auto node_to_cell_matrix = m_mesh.connectivity().nodeToCellMatrix(); + + auto node_list = node_boundary.nodeList(); + // for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + // auto cell_list = node_to_cell_matrix[node_list[i_node]]; + // for (size_t i_cell = 0; i_cell < cell_list.size(); ++i_cell) { + // const CellId cell_id = cell_list[i_cell]; + // is_boundary_cell[cell_id] = 1; + // } + // } + + // size_t nb_boundary_cells = sum(is_boundary_cell); + + // Array<CellId> cell_list{nb_boundary_cells}; + + // size_t index = 0; + // for (CellId cell_id = 0; cell_id < p_mesh->numberOfCells(); ++cell_id) { + // if (is_boundary_cell[cell_id]) { + // cell_list[index++] = cell_id; + // } + // } + // auto xj = MeshDataManager::instance().getMeshData(m_mesh).xj(); + auto xr = m_mesh.xr(); + + // Table<const double> cell_array_list = + // InterpolateItemArray<double(Rd)>::template interpolate<ItemType::cell>(inflow_list_bc_descriptor + // .functionSymbolIdList(), + // xj, cell_list); + Table<const double> node_array_list = + InterpolateItemArray<double(Rd)>::template interpolate<ItemType::node>(inflow_list_bc_descriptor + .functionSymbolIdList(), + xr, node_list); + // bc_list.emplace_back(InflowListBoundaryCondition(cell_array_list, cell_list)); + bc_list.emplace_back(InflowListBoundaryCondition(node_array_list, node_list)); + break; + } + case IBoundaryConditionDescriptor::Type::outflow: { + bc_list.emplace_back(OutflowBoundaryCondition(getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor()), + getMeshFaceBoundary(mesh, bc_descriptor->boundaryDescriptor()))); + 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 acoustic solver"; + throw NormalError(error_msg.str()); + } + } + + return bc_list; + } + + public: + NodeArray<double> + compute_Flux_Fp(const CellValue<const double>& p, + const CellValue<const TinyVector<Dimension>>& u, + const NodeArray<const TinyVector<Dimension>>& lambda_vector, + const NodeValue<const bool>& is_boundary_node) + { + const NodeValuePerCell<const TinyVector<Dimension>> njr = MeshDataManager::instance().getMeshData(m_mesh).njr(); + const size_t nb_waves = m_k; + NodeArray<double> Fr(m_mesh.connectivity(), nb_waves); + const auto& node_local_numbers_in_their_cells = p_mesh->connectivity().nodeLocalNumbersInTheirCells(); + const auto& node_to_cell_matrix = p_mesh->connectivity().nodeToCellMatrix(); + + Fr.fill(0.); + + parallel_for( + p_mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { + if (not is_boundary_node[node_id]) { + const auto& node_local_number_in_its_cell = node_local_numbers_in_their_cells.itemArray(node_id); + const auto& node_to_cell = node_to_cell_matrix[node_id]; + + TinyVector<Dimension> inv_S = zero; + for (size_t d = 0; d < Dimension; ++d) { + for (size_t i = 0; i < nb_waves; ++i) { + inv_S[d] += lambda_vector[node_id][i][d] * lambda_vector[node_id][i][d]; + } + inv_S[d] = 1. / inv_S[d]; + } + double lambda_r = std::sqrt(dot(lambda_vector[node_id][m_L], lambda_vector[node_id][m_L])); + + for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) { + double sum_li_njr = 0; + + for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) { + const CellId cell_id = node_to_cell[i_cell]; + const unsigned int i_node = node_local_number_in_its_cell[i_cell]; + + double li_njr = dot(lambda_vector[node_id][i_wave], njr(cell_id, i_node)); + if (li_njr > 1e-14) { + double Fj = (1. / nb_waves) * p[cell_id]; + + for (size_t d = 0; d < Dimension; ++d) { + Fj += inv_S[d] * lambda_vector[node_id][i_wave][d] * (lambda_r * lambda_r * u[cell_id][d]); + } + + Fr[node_id][i_wave] += li_njr * Fj; + sum_li_njr += li_njr; + } + } + if (sum_li_njr != 0) { + Fr[node_id][i_wave] = 1. / sum_li_njr * Fr[node_id][i_wave]; + } + } + } + }); + + return Fr; + } + + NodeArray<TinyVector<Dimension>> + compute_Flux_Fu(const CellValue<const double>& p, + const CellValue<const TinyVector<Dimension>>& u, + const NodeArray<const TinyVector<Dimension>>& lambda_vector, + const NodeValue<const bool>& is_boundary_node) + { + const NodeValuePerCell<const TinyVector<Dimension>> njr = MeshDataManager::instance().getMeshData(m_mesh).njr(); + const size_t nb_waves = m_k; + NodeArray<TinyVector<Dimension>> Fr(m_mesh.connectivity(), nb_waves); + const auto& node_local_numbers_in_their_cells = p_mesh->connectivity().nodeLocalNumbersInTheirCells(); + const auto& node_to_cell_matrix = p_mesh->connectivity().nodeToCellMatrix(); + + Fr.fill(zero); + + parallel_for( + p_mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { + if (not is_boundary_node[node_id]) { + const auto& node_local_number_in_its_cell = node_local_numbers_in_their_cells.itemArray(node_id); + const auto& node_to_cell = node_to_cell_matrix[node_id]; + + for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) { + TinyVector<Dimension> inv_S = zero; + for (size_t d = 0; d < Dimension; ++d) { + for (size_t i = 0; i < nb_waves; ++i) { + inv_S[d] += lambda_vector[node_id][i][d] * lambda_vector[node_id][i][d]; + } + inv_S[d] = 1. / inv_S[d]; + } + + double sum_li_njr = 0; + + for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) { + const CellId cell_id = node_to_cell[i_cell]; + const unsigned int i_node = node_local_number_in_its_cell[i_cell]; + + double li_njr = dot(lambda_vector[node_id][i_wave], njr(cell_id, i_node)); + if (li_njr > 1e-14) { + TinyVector<Dimension> Fj = (1. / nb_waves) * u[cell_id]; + + for (size_t d = 0; d < Dimension; ++d) { + Fj[d] += 1. / Dimension * inv_S[d] * lambda_vector[node_id][i_wave][d] * p[cell_id]; + } + + Fr[node_id][i_wave] += li_njr * Fj; + sum_li_njr += li_njr; + } + } + if (sum_li_njr != 0) { + Fr[node_id][i_wave] = 1. / sum_li_njr * Fr[node_id][i_wave]; + } + } + } + }); + + return Fr; + } + + void + apply_bc(NodeArray<double>& Fr_p, + NodeArray<TinyVector<Dimension>>& Fr_u, + const CellValue<const double>& p, + const CellValue<const TinyVector<Dimension>>& u, + const NodeArray<const TinyVector<Dimension>>& lambda_vector, + const BoundaryConditionList& bc_list) + { + const size_t nb_waves = m_k; + const auto& node_local_numbers_in_their_cells = p_mesh->connectivity().nodeLocalNumbersInTheirCells(); + const auto& node_to_cell_matrix = p_mesh->connectivity().nodeToCellMatrix(); + const NodeValuePerCell<const TinyVector<Dimension>> njr = MeshDataManager::instance().getMeshData(m_mesh).njr(); + const NodeValuePerCell<const TinyVector<Dimension>> Cjr = MeshDataManager::instance().getMeshData(m_mesh).Cjr(); + + for (auto&& bc_v : bc_list) { + std::visit( + [=, this](auto&& bc) { + using BCType = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<BCType, SymmetryBoundaryCondition>) { + auto node_list = bc.nodeList(); + + TinyMatrix<Dimension> I = identity; + + NodeValue<TinyVector<Dimension>> nr{p_mesh->connectivity()}; + nr.fill(zero); + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; + const auto& node_to_cell = node_to_cell_matrix[node_id]; + const auto& node_local_number_in_its_cell = node_local_numbers_in_their_cells.itemArray(node_id); + + TinyVector<MeshType::Dimension> inv_S = zero; + for (size_t d = 0; d < MeshType::Dimension; ++d) { + for (size_t i = 0; i < nb_waves; ++i) { + inv_S[d] += lambda_vector[node_id][i][d] * lambda_vector[node_id][i][d]; + } + inv_S[d] = 1. / inv_S[d]; + } + const double lambda_r = std::sqrt(dot(lambda_vector[node_id][m_L], lambda_vector[node_id][m_L])); + + for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) { + const CellId cell_id = node_to_cell[i_cell]; + const size_t i_node_cell = node_local_number_in_its_cell[i_cell]; + + nr[node_id] += Cjr(cell_id, i_node_cell); + } + nr[node_id] = 1. / sqrt(dot(nr[node_id], nr[node_id])) * nr[node_id]; + auto nxn = tensorProduct(nr[node_id], nr[node_id]); + auto txt = I - nxn; + + for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) { + double sum = 0; + Fr_p[node_id][i_wave] = 0; + Fr_u[node_id][i_wave] = zero; + + for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) { + const CellId cell_id = node_to_cell[i_cell]; + const size_t i_node_cell = node_local_number_in_its_cell[i_cell]; + + double li_njr = dot(lambda_vector[node_id][i_wave], njr(cell_id, i_node_cell)); + + if (li_njr > 1e-14) { + const TinyVector<Dimension> A_p = lambda_r * lambda_r * u[cell_id]; + const double A_u = p[cell_id]; + + double Fn_p = p[cell_id] / nb_waves; + TinyVector<Dimension> Fn_u = 1. / nb_waves * u[cell_id]; + + for (size_t d = 0; d < Dimension; ++d) { + Fn_p += inv_S[d] * lambda_vector[node_id][i_wave][d] * A_p[d]; + Fn_u[d] += 1. / Dimension * inv_S[d] * lambda_vector[node_id][i_wave][d] * A_u; + } + + Fr_p[node_id][i_wave] += Fn_p * li_njr; + Fr_u[node_id][i_wave] += li_njr * Fn_u; + + sum += li_njr; + } + + TinyVector<Dimension> njr_prime = txt * njr(cell_id, i_node_cell) - nxn * njr(cell_id, i_node_cell); + double li_njr_prime = dot(lambda_vector[node_id][i_wave], njr_prime); + + if (li_njr_prime > 1e-14) { + double p_prime = p[cell_id]; + TinyVector<Dimension> u_prime = txt * u[cell_id] - nxn * u[cell_id]; + + TinyVector<Dimension> A_p_prime = lambda_r * lambda_r * u_prime; + double A_u_prime = p_prime; + + double Fn_p_prime = p_prime / nb_waves; + TinyVector<Dimension> Fn_u_prime = 1. / nb_waves * u_prime; + + for (size_t d = 0; d < Dimension; ++d) { + Fn_p_prime += inv_S[d] * lambda_vector[node_id][i_wave][d] * A_p_prime[d]; + Fn_u_prime[d] += 1. / Dimension * inv_S[d] * lambda_vector[node_id][i_wave][d] * A_u_prime; + } + + Fr_p[node_id][i_wave] += Fn_p_prime * li_njr_prime; + Fr_u[node_id][i_wave] += li_njr_prime * Fn_u_prime; + + sum += li_njr_prime; + } + } + if (sum != 0) { + Fr_p[node_id][i_wave] /= sum; + Fr_u[node_id][i_wave] = 1. / sum * Fr_u[node_id][i_wave]; + } + } + } + } else if constexpr (std::is_same_v<BCType, WallBoundaryCondition>) { + auto node_list = bc.nodeList(); + + TinyMatrix<Dimension> I = identity; + + NodeValue<TinyVector<Dimension>> nr{p_mesh->connectivity()}; + nr.fill(zero); + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; + const auto& node_to_cell = node_to_cell_matrix[node_id]; + const auto& node_local_number_in_its_cell = node_local_numbers_in_their_cells.itemArray(node_id); + + TinyVector<MeshType::Dimension> inv_S = zero; + for (size_t d = 0; d < MeshType::Dimension; ++d) { + for (size_t i = 0; i < nb_waves; ++i) { + inv_S[d] += lambda_vector[node_id][i][d] * lambda_vector[node_id][i][d]; + } + inv_S[d] = 1. / inv_S[d]; + } + const double lambda_r = std::sqrt(dot(lambda_vector[node_id][m_L], lambda_vector[node_id][m_L])); + + for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) { + const CellId cell_id = node_to_cell[i_cell]; + const size_t i_node_cell = node_local_number_in_its_cell[i_cell]; + + nr[node_id] += Cjr(cell_id, i_node_cell); + } + nr[node_id] = 1. / sqrt(dot(nr[node_id], nr[node_id])) * nr[node_id]; + auto nxn = tensorProduct(nr[node_id], nr[node_id]); + auto txt = I - nxn; + + for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) { + double sum = 0; + Fr_p[node_id][i_wave] = 0; + Fr_u[node_id][i_wave] = zero; + + for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) { + const CellId cell_id = node_to_cell[i_cell]; + const size_t i_node_cell = node_local_number_in_its_cell[i_cell]; + + double li_njr = dot(lambda_vector[node_id][i_wave], njr(cell_id, i_node_cell)); + + if (li_njr > 1e-14) { + const TinyVector<Dimension> A_p = lambda_r * lambda_r * u[cell_id]; + const double A_u = p[cell_id]; + + double Fn_p = p[cell_id] / nb_waves; + TinyVector<Dimension> Fn_u = 1. / nb_waves * u[cell_id]; + + for (size_t d = 0; d < Dimension; ++d) { + Fn_p += inv_S[d] * lambda_vector[node_id][i_wave][d] * A_p[d]; + Fn_u[d] += 1. / Dimension * inv_S[d] * lambda_vector[node_id][i_wave][d] * A_u; + } + + Fr_p[node_id][i_wave] += Fn_p * li_njr; + Fr_u[node_id][i_wave] += li_njr * Fn_u; + + sum += li_njr; + } + + TinyVector<Dimension> njr_prime = txt * njr(cell_id, i_node_cell) - nxn * njr(cell_id, i_node_cell); + double li_njr_prime = dot(lambda_vector[node_id][i_wave], njr_prime); + + if (li_njr_prime > 1e-14) { + double p_prime = p[cell_id]; + TinyVector<Dimension> u_prime = txt * u[cell_id] - nxn * u[cell_id]; + + TinyVector<Dimension> A_p_prime = lambda_r * lambda_r * u_prime; + double A_u_prime = p_prime; + + double Fn_p_prime = p_prime / nb_waves; + TinyVector<Dimension> Fn_u_prime = 1. / nb_waves * u_prime; + + for (size_t d = 0; d < Dimension; ++d) { + Fn_p_prime += inv_S[d] * lambda_vector[node_id][i_wave][d] * A_p_prime[d]; + Fn_u_prime[d] += 1. / Dimension * inv_S[d] * lambda_vector[node_id][i_wave][d] * A_u_prime; + } + + Fr_p[node_id][i_wave] += Fn_p_prime * li_njr_prime; + Fr_u[node_id][i_wave] += li_njr_prime * Fn_u_prime; + + sum += li_njr_prime; + } + } + if (sum != 0) { + Fr_p[node_id][i_wave] /= sum; + Fr_u[node_id][i_wave] = 1. / sum * Fr_u[node_id][i_wave]; + } + } + } + } + }, + bc_v); + } + } + + const NodeValue<TinyVector<Dimension>> + compute_velocity_flux(NodeArray<double>& F, const NodeArray<const TinyVector<Dimension>>& lambda_vector) + { + NodeValue<TinyVector<Dimension>> u{p_mesh->connectivity()}; + const size_t nb_waves = m_k; + u.fill(zero); + + parallel_for( + m_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + const double inv_lambda_squared = 1. / dot(lambda_vector[node_id][m_L], lambda_vector[node_id][m_L]); + + for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) { + u[node_id] += F[node_id][i_wave] * lambda_vector[node_id][i_wave]; + } + u[node_id] = inv_lambda_squared * u[node_id]; + }); + return u; + } + + const NodeValue<TinyVector<Dimension>> + compute_pressure_flux(NodeArray<TinyVector<Dimension>>& F, + const NodeArray<const TinyVector<Dimension>>& lambda_vector) + { + NodeValue<TinyVector<Dimension>> p{p_mesh->connectivity()}; + const size_t nb_waves = m_k; + p.fill(zero); + + parallel_for( + m_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) { + for (size_t dim = 0; dim < Dimension; ++dim) { + p[node_id][dim] += dot(F[node_id][i_wave], lambda_vector[node_id][i_wave]); + } + } + }); + return p; + } + + const CellValue<const double> + compute_delta_velocity(const NodeValue<const TinyVector<Dimension>>& ur) + { + const NodeValuePerCell<const TinyVector<Dimension>> Cjr = MeshDataManager::instance().getMeshData(m_mesh).Cjr(); + CellValue<double> delta{p_mesh->connectivity()}; + + const auto& cell_to_node_matrix = p_mesh->connectivity().cellToNodeMatrix(); + + delta.fill(0.); + + parallel_for( + p_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const auto& cell_to_node = cell_to_node_matrix[cell_id]; + + for (size_t i_node = 0; i_node < cell_to_node.size(); ++i_node) { + const NodeId node_id = cell_to_node[i_node]; + delta[cell_id] += dot(Cjr(cell_id, i_node), ur[node_id]); + } + }); + return delta; + } + + const CellValue<const TinyVector<Dimension>> + compute_delta_pressure(const NodeValue<const TinyVector<Dimension>>& pr) + { + const NodeValuePerCell<const TinyVector<Dimension>> Cjr = MeshDataManager::instance().getMeshData(m_mesh).Cjr(); + CellValue<TinyVector<Dimension>> delta{p_mesh->connectivity()}; + + const auto& cell_to_node_matrix = p_mesh->connectivity().cellToNodeMatrix(); + + delta.fill(zero); + + parallel_for( + p_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const auto& cell_to_node = cell_to_node_matrix[cell_id]; + + for (size_t i_node = 0; i_node < cell_to_node.size(); ++i_node) { + for (size_t dim = 0; dim < Dimension; ++dim) { + const NodeId node_id = cell_to_node[i_node]; + delta[cell_id][dim] += Cjr(cell_id, i_node)[dim] * pr[node_id][dim]; + } + } + }); + return delta; + } + + const CellValue<const double> + compute_delta_pu(const NodeValue<const TinyVector<Dimension>>& ur, const NodeValue<const TinyVector<Dimension>>& pr) + { + const NodeValuePerCell<const TinyVector<Dimension>> Cjr = MeshDataManager::instance().getMeshData(m_mesh).Cjr(); + CellValue<double> delta{p_mesh->connectivity()}; + + const auto& cell_to_node_matrix = p_mesh->connectivity().cellToNodeMatrix(); + delta.fill(0.); + + parallel_for( + p_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const auto& cell_to_node = cell_to_node_matrix[cell_id]; + + for (size_t i_node = 0; i_node < cell_to_node.size(); ++i_node) { + const NodeId node_id = cell_to_node[i_node]; + TinyVector<Dimension> Fjr = zero; + for (size_t dim = 0; dim < Dimension; ++dim) { + Fjr[dim] = Cjr(cell_id, i_node)[dim] * pr[node_id][dim]; + } + delta[cell_id] += dot(Fjr, ur[node_id]); + } + }); + return delta; + } + + const NodeValue<const bool> + getBoundaryNode(const BoundaryConditionList& bc_list) + { + NodeValue<bool> is_boundary_node{p_mesh->connectivity()}; + is_boundary_node.fill(false); + for (auto&& bc_v : bc_list) { + std::visit( + [=](auto&& bc) { + using BCType = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<BCType, SymmetryBoundaryCondition>) { + auto node_list = bc.nodeList(); + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + is_boundary_node[node_list[i_node]] = 1; + } + } else if constexpr (std::is_same_v<BCType, WallBoundaryCondition>) { + auto node_list = bc.nodeList(); + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + is_boundary_node[node_list[i_node]] = 1; + } + } + }, + bc_v); + } + return is_boundary_node; + } + + const CellValue<const double> + build_lambda_j(const CellValue<double>& rho, const CellValue<double>& p) + { + CellValue<double> lambda_j{p_mesh->connectivity()}; + CellValue<double> cj{m_mesh.connectivity()}; + + if constexpr (Dimension == 1) { + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + cj[cell_id] = std::sqrt(m_gamma * p[cell_id] / rho[cell_id]); + lambda_j[cell_id] = std::sqrt((3. * (m_k - 1.)) / (m_k + 1.)) * std::abs(rho[cell_id] * cj[cell_id]); + }); + } else if constexpr (Dimension == 2) { + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + cj[cell_id] = std::sqrt(m_gamma * p[cell_id] / rho[cell_id]); + lambda_j[cell_id] = + (12. * m_L * m_L) / ((m_L + 1.) * (2. * m_L + 1.)) * 2. * std::abs(rho[cell_id] * cj[cell_id]); + }); + } else if constexpr (Dimension == 3) { + if (m_k != 6) { + throw NotImplementedError("3D Lagrangian scheme not implemented for other than 6 waves model"); + } + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + cj[cell_id] = std::sqrt(m_gamma * p[cell_id] / rho[cell_id]); + lambda_j[cell_id] = 3. * std::abs(rho[cell_id] * cj[cell_id]); + }); + } + return lambda_j; + } + + const NodeArray<const TinyVector<Dimension>> + build_nodal_wavespeeds(const CellValue<const double>& lambda_j) + { + const auto& node_to_cell_matrix = m_mesh.connectivity().nodeToCellMatrix(); + NodeArray<TinyVector<Dimension>> lambda_vector(p_mesh->connectivity(), m_k); + const double pi = std::acos(-1); + + parallel_for( + m_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + const auto& node_to_cell = node_to_cell_matrix[node_id]; + + double lambda_r = 0.; + for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) { + const CellId cell_id = node_to_cell[i_cell]; + + lambda_r = std::max(lambda_r, lambda_j[cell_id]); + + // std::cout << "node_id = " << node_id << ", lambda_r = " << lambda_r << "\n"; + } + + if constexpr (Dimension == 1) { + for (size_t i_wave = 0; i_wave < m_k; ++i_wave) { + lambda_vector[node_id][i_wave][0] = lambda_r * (1 - 2. * i_wave / (m_k - 1)); + } + } else if constexpr (Dimension == 2) { + for (size_t l = 1; l < m_L + 1; ++l) { + for (size_t m = 1; m < 4 * m_M + 1; ++m) { + size_t i_wave = (l - 1) * 4 * m_M + m - 1; + double ll = l; + lambda_vector[node_id][i_wave] = + TinyVector<Dimension>{(ll / m_L) * lambda_r * std::cos((m * pi) / (2 * m_M)), // + 0.2 * pi), + (ll / m_L) * lambda_r * std::sin((m * pi) / (2 * m_M))}; // + 0.2 * pi)}; + } + } + } else if constexpr (Dimension == 3) { + if (m_k != 6) { + throw NotImplementedError("3D Lagrangian scheme not implemented for other than 6 waves model"); + } + lambda_vector[node_id][0] = TinyVector<Dimension>{lambda_r, 0., 0.}; + lambda_vector[node_id][1] = TinyVector<Dimension>{-lambda_r, 0., 0.}; + lambda_vector[node_id][2] = TinyVector<Dimension>{0., lambda_r, 0.}; + lambda_vector[node_id][3] = TinyVector<Dimension>{0., -lambda_r, 0.}; + lambda_vector[node_id][4] = TinyVector<Dimension>{0., 0., lambda_r}; + lambda_vector[node_id][5] = TinyVector<Dimension>{0., 0., -lambda_r}; + } + }); + return lambda_vector; + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply(const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) + { + BoundaryConditionList bc_list = this->_getBCList(m_mesh, bc_descriptor_list); + // const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); + const NodeValue<const bool> is_boundary_node = getBoundaryNode(bc_list); + + const CellValue<double> rho = copy(m_rho); + const CellValue<TinyVector<Dimension>> u = copy(m_u); + const CellValue<double> p = copy(m_p); + const CellValue<double> E = copy(m_E); + + CellValue<TinyVector<Dimension>> u_np1 = copy(u); + CellValue<double> p_np1 = copy(p); + CellValue<double> E_np1 = copy(E); + + // std::cout << "test 1 \n"; + const CellValue<const double> lambda_j = build_lambda_j(rho, p); + // std::cout << "lambda_j = " << lambda_j << "\n"; + const NodeArray<const TinyVector<Dimension>> lambda_vector = build_nodal_wavespeeds(lambda_j); + // std::cout << "lambda_vector = " << lambda_vector << "\n"; + + NodeArray<double> Fr_p = compute_Flux_Fp(p, u, lambda_vector, is_boundary_node); + NodeArray<TinyVector<Dimension>> Fr_u = compute_Flux_Fu(p, u, lambda_vector, is_boundary_node); + + apply_bc(Fr_p, Fr_u, p, u, lambda_vector, bc_list); + + const NodeValue<TinyVector<Dimension>> ur = compute_velocity_flux(Fr_p, lambda_vector); + const NodeValue<TinyVector<Dimension>> pr = compute_pressure_flux(Fr_u, lambda_vector); + + for (auto&& bc_v : bc_list) { + std::visit( + [=](auto&& bc) { + using BCType = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<BCType, InflowListBoundaryCondition>) { + auto node_list = bc.nodeList(); + auto node_array_list = bc.nodeArrayList(); + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; + for (size_t dim = 0; dim < Dimension; ++dim) { + ur[node_id][dim] = node_array_list[i_node][dim]; + pr[node_id][dim] = node_array_list[i_node][Dimension]; + } + } + } + }, + bc_v); + } + + const CellValue<const double> delta_u = compute_delta_velocity(ur); + const CellValue<const TinyVector<Dimension>> delta_p = compute_delta_pressure(pr); + const CellValue<const double> delta_pu = compute_delta_pu(ur, pr); + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double dt_over_Mj = m_dt * m_inv_Mj[cell_id]; + u_np1[cell_id] -= dt_over_Mj * delta_p[cell_id]; + E_np1[cell_id] -= dt_over_Mj * delta_pu[cell_id]; + }); + + NodeValue<TinyVector<Dimension>> new_xr = copy(m_mesh.xr()); + parallel_for( + m_mesh.numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { new_xr[node_id] += m_dt * ur[node_id]; }); + + std::shared_ptr<const MeshType> new_mesh = std::make_shared<MeshType>(m_mesh.shared_connectivity(), new_xr); + + CellValue<const double> new_Vj = MeshDataManager::instance().getMeshData(*new_mesh).Vj(); + CellValue<double> rho_np1{p_mesh->connectivity()}; + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId cell_id) { rho_np1[cell_id] = m_Mj[cell_id] / new_Vj[cell_id]; }); + + return std::make_tuple(std::make_shared<MeshVariant>(new_mesh), + std::make_shared<DiscreteFunctionVariant>( + DiscreteFunctionP0<const double>(new_mesh, rho_np1)), + std::make_shared<DiscreteFunctionVariant>( + DiscreteFunctionP0<const TinyVector<Dimension>>(new_mesh, u_np1)), + std::make_shared<DiscreteFunctionVariant>( + DiscreteFunctionP0<const double>(new_mesh, E_np1))); + } + + EulerKineticSolverLagrangeMultiDLocal(std::shared_ptr<const MeshType> mesh, + const double& dt, + const size_t& L, + const size_t& M, + const size_t& k, + const double& gamma, + const size_t& space_order, + const size_t& time_order, + const CellValue<const double>& rho, + const CellValue<const TinyVector<MeshType::Dimension>>& u, + const CellValue<const double>& E, + const CellValue<const double>& p) + : m_dt{dt}, + m_L{L}, + m_M{M}, + m_k{k}, + m_gamma{gamma}, + m_spaceOrder{space_order}, + m_timeOrder{time_order}, + m_rho{rho}, + m_u{u}, + m_E{E}, + m_p{p}, + p_mesh{mesh}, + m_mesh{*mesh} + { + m_Mj = [&] { + CellValue<double> Mj{m_mesh.connectivity()}; + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Mj[cell_id] = m_rho[cell_id] * m_Vj[cell_id]; }); + return Mj; + }(); + + m_inv_Mj = [&] { + CellValue<double> inv_Mj{m_mesh.connectivity()}; + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { inv_Mj[cell_id] = 1. / m_Mj[cell_id]; }); + return inv_Mj; + }(); + + m_inv_Vj = [&] { + CellValue<double> inv_Vj{m_mesh.connectivity()}; + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { inv_Vj[cell_id] = 1. / m_Vj[cell_id]; }); + return inv_Vj; + }(); + } + + ~EulerKineticSolverLagrangeMultiDLocal() = default; +}; + +template <MeshConcept MeshType> +class EulerKineticSolverLagrangeMultiDLocal<MeshType>::SymmetryBoundaryCondition +{ + public: + static constexpr const size_t Dimension = MeshType::Dimension; + + using Rd = TinyVector<Dimension, double>; + + private: + const MeshFlatNodeBoundary<MeshType> m_mesh_flat_node_boundary; + const MeshFlatFaceBoundary<MeshType> m_mesh_flat_face_boundary; + + public: + const Rd& + outgoingNormal() const + { + return m_mesh_flat_node_boundary.outgoingNormal(); + } + + size_t + numberOfNodes() const + { + return m_mesh_flat_node_boundary.nodeList().size(); + } + + const Array<const NodeId>& + nodeList() const + { + return m_mesh_flat_node_boundary.nodeList(); + } + + size_t + numberOfFaces() const + { + return m_mesh_flat_face_boundary.faceList().size(); + } + + const Array<const FaceId>& + faceList() const + { + return m_mesh_flat_face_boundary.faceList(); + } + + SymmetryBoundaryCondition(const MeshFlatNodeBoundary<MeshType>& mesh_flat_node_boundary, + const MeshFlatFaceBoundary<MeshType>& mesh_flat_face_boundary) + : m_mesh_flat_node_boundary(mesh_flat_node_boundary), m_mesh_flat_face_boundary(mesh_flat_face_boundary) + { + ; + } + + ~SymmetryBoundaryCondition() = default; +}; + +template <MeshConcept MeshType> +class EulerKineticSolverLagrangeMultiDLocal<MeshType>::WallBoundaryCondition +{ + public: + static constexpr const size_t Dimension = MeshType::Dimension; + + using Rd = TinyVector<Dimension, double>; + + private: + const MeshNodeBoundary m_mesh_node_boundary; + const MeshFaceBoundary m_mesh_face_boundary; + + public: + size_t + numberOfNodes() const + { + return m_mesh_node_boundary.nodeList().size(); + } + + const Array<const NodeId>& + nodeList() const + { + return m_mesh_node_boundary.nodeList(); + } + + size_t + numberOfFaces() const + { + return m_mesh_face_boundary.faceList().size(); + } + + const Array<const FaceId>& + faceList() const + { + return m_mesh_face_boundary.faceList(); + } + + WallBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary, const MeshFaceBoundary& mesh_face_boundary) + : m_mesh_node_boundary(mesh_node_boundary), m_mesh_face_boundary(mesh_face_boundary) + { + ; + } + + ~WallBoundaryCondition() = default; +}; + +template <MeshConcept MeshType> +class EulerKineticSolverLagrangeMultiDLocal<MeshType>::InflowListBoundaryCondition +{ + public: + static constexpr const size_t Dimension = MeshType::Dimension; + + using Rd = TinyVector<Dimension, double>; + + private: + const Table<const double> m_node_array_list; + const Array<const NodeId> m_node_list; + + public: + size_t + numberOfNodes() const + { + return m_node_list.size(); + } + + const Array<const NodeId>& + nodeList() const + { + return m_node_list; + } + + const Table<const double>& + nodeArrayList() const + { + return m_node_array_list; + } + + InflowListBoundaryCondition(const Table<const double>& node_array_list, const Array<const NodeId> node_list) + : m_node_array_list(node_array_list), m_node_list(node_list) + { + ; + } + + ~InflowListBoundaryCondition() = default; +}; + +template <MeshConcept MeshType> +class EulerKineticSolverLagrangeMultiDLocal<MeshType>::OutflowBoundaryCondition +{ + public: + static constexpr const size_t Dimension = MeshType::Dimension; + + using Rd = TinyVector<Dimension, double>; + + private: + const MeshNodeBoundary m_mesh_node_boundary; + const MeshFaceBoundary m_mesh_face_boundary; + + public: + size_t + numberOfNodes() const + { + return m_mesh_node_boundary.nodeList().size(); + } + + const Array<const NodeId>& + nodeList() const + { + return m_mesh_node_boundary.nodeList(); + } + + size_t + numberOfFaces() const + { + return m_mesh_face_boundary.faceList().size(); + } + + const Array<const FaceId>& + faceList() const + { + return m_mesh_face_boundary.faceList(); + } + + OutflowBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary, const MeshFaceBoundary& mesh_face_boundary) + : m_mesh_node_boundary(mesh_node_boundary), m_mesh_face_boundary(mesh_face_boundary) + { + ; + } + + ~OutflowBoundaryCondition() = default; +}; + +std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> +eulerKineticSolverLagrangeMultiDLocal( + const double& dt, + const size_t& L, + const size_t& M, + const size_t& k, + const double& gamma, + const double& eps, + const size_t& space_order, + const size_t& time_order, + const std::shared_ptr<const DiscreteFunctionVariant>& rho_n, + const std::shared_ptr<const DiscreteFunctionVariant>& u_n, + const std::shared_ptr<const DiscreteFunctionVariant>& E_n, + const std::shared_ptr<const DiscreteFunctionVariant>& p_n, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) +{ + const std::shared_ptr<const MeshVariant> mesh_v = getCommonMesh({rho_n, u_n, E_n, p_n}); + if (mesh_v.use_count() == 0) { + throw NormalError("rho_n, u_n, E_n and p_n have to be defined on the same mesh"); + } + if (space_order > 1 or time_order > 1) { + throw NotImplementedError("Euler kinetic solver in Multi D not implemented at order > 1"); + } + double eps_bis = eps; // A retirer !! + eps_bis += eps_bis; // A retirer !! + return std::visit( + [&](auto&& p_mesh) + -> std::tuple<std::shared_ptr<const MeshVariant>, std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const DiscreteFunctionVariant>> { + using MeshType = mesh_type_t<decltype(p_mesh)>; + if constexpr (is_polygonal_mesh_v<MeshType>) { + auto rho = rho_n->get<DiscreteFunctionP0<const double>>(); + auto u = u_n->get<DiscreteFunctionP0<const TinyVector<MeshType::Dimension>>>(); + auto E = E_n->get<DiscreteFunctionP0<const double>>(); + auto p = p_n->get<DiscreteFunctionP0<const double>>(); + + EulerKineticSolverLagrangeMultiDLocal solver(p_mesh, dt, L, M, k, gamma, space_order, time_order, + rho.cellValues(), u.cellValues(), E.cellValues(), p.cellValues()); + + return solver.apply(bc_descriptor_list); + } else { + throw NotImplementedError("Invalid mesh type for multi-D Lagrangian Kinetic solver"); + } + }, + mesh_v->variant()); +} diff --git a/src/scheme/EulerKineticSolverLagrangeMultiDLocal.hpp b/src/scheme/EulerKineticSolverLagrangeMultiDLocal.hpp new file mode 100644 index 000000000..98989615a --- /dev/null +++ b/src/scheme/EulerKineticSolverLagrangeMultiDLocal.hpp @@ -0,0 +1,28 @@ +#ifndef EULER_KINETIC_SOLVER_LAGRANGE_MULTID_LOCAL_HPP +#define EULER_KINETIC_SOLVER_LAGRANGE_MULTID_LOCAL_HPP + +#include <language/utils/FunctionSymbolId.hpp> +#include <scheme/DiscreteFunctionVariant.hpp> + +class IBoundaryConditionDescriptor; + +std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> +eulerKineticSolverLagrangeMultiDLocal( + const double& dt, + const size_t& L, + const size_t& M, + const size_t& k, + const double& gamma, + const double& eps, + const size_t& space_order, + const size_t& time_order, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& E, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list); + +#endif // EULER_KINETIC_SOLVER_LAGRANGE_MULTID_LOCAL_HPP -- GitLab