diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp index 504033a1f57ec2160752f5cfacdd3e83cf289a83..8e917c929d5216c31411775985720637efe4d526 100644 --- a/src/language/modules/SchemeModule.cpp +++ b/src/language/modules/SchemeModule.cpp @@ -61,6 +61,9 @@ #include <scheme/ImplicitAcousticO2Solver2steps.hpp> #include <scheme/ImplicitAcousticO2Solver2stepsModified.hpp> #include <scheme/ImplicitAcousticO2SolverModified.hpp> +#include <scheme/ImplicitAcousticO2SolverRhombus.hpp> +#include <scheme/ImplicitAcousticO2SolverRhombusModified.hpp> + // retour SchemeModule::SchemeModule() { @@ -588,6 +591,84 @@ SchemeModule::SchemeModule() )); + this + ->_addBuiltinFunction("implicit_2_states_euler_o2_rhombus", + std::function( + + [](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>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> { + return ImplicitAcousticO2SolverHandlerRhombus{ImplicitAcousticO2SolverHandlerRhombus:: + SolverType::Glace2States, + rho, + c, + u, + p, + pi, + gamma, + Cv, + entropy, + bc_descriptor_list, + std::vector< + std::shared_ptr<const IZoneDescriptor>>{}, + dt} + .solver() + .apply(dt, rho, u, E, c, p, pi, gamma, Cv, entropy); + } + + )); + + this->_addBuiltinFunction("implicit_2_states_euler_o2_rhombus_modified", + std::function( + + [](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>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& + bc_descriptor_list, + const double& dt) + -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, double> { + return ImplicitAcousticO2SolverHandlerModified{ImplicitAcousticO2SolverHandlerModified:: + SolverType::Glace2States, + rho, + c, + u, + p, + pi, + gamma, + Cv, + entropy, + bc_descriptor_list, + std::vector<std::shared_ptr< + const IZoneDescriptor>>{}, + dt} + .solver() + .apply(dt, rho, u, E, c, p, pi, gamma, Cv, entropy); + } + + )); + this->_addBuiltinFunction( "implicit_2_states_euler_o2", std::function( @@ -621,6 +702,7 @@ SchemeModule::SchemeModule() } )); + this->_addBuiltinFunction("implicit_2_states_euler_o2_modified", std::function( @@ -636,10 +718,10 @@ SchemeModule::SchemeModule() const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, const double& dt) - -> std::tuple< - std::shared_ptr<const MeshVariant>, std::shared_ptr<const DiscreteFunctionVariant>, - std::shared_ptr<const DiscreteFunctionVariant>, - std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const double>> { + -> std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, double> { return ImplicitAcousticO2SolverHandlerModified{ImplicitAcousticO2SolverHandlerModified:: SolverType::Glace2States, rho, @@ -712,7 +794,8 @@ SchemeModule::SchemeModule() const std::shared_ptr<const DiscreteFunctionVariant>& entropy, const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>, std::shared_ptr<const DiscreteFunctionVariant>, - std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const DiscreteFunctionVariant>> { + std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const DiscreteFunctionVariant>, + double> { return ImplicitAcousticO2SolverHandler2stepsModified{ImplicitAcousticO2SolverHandler2stepsModified::SolverType:: Glace2States, rho, diff --git a/src/scheme/CMakeLists.txt b/src/scheme/CMakeLists.txt index d7b2c66c883ea42719b0931de08f868939c7914f..948dcef643e5ba7ed268235cef6a04bb490f6507 100644 --- a/src/scheme/CMakeLists.txt +++ b/src/scheme/CMakeLists.txt @@ -21,6 +21,8 @@ add_library( FluxingAdvectionSolver.cpp ImplicitAcousticO2Solver2steps.cpp ImplicitAcousticO2Solver2stepsModified.cpp + ImplicitAcousticO2SolverRhombus.cpp + ImplicitAcousticO2SolverRhombusModified.cpp ) target_link_libraries( diff --git a/src/scheme/ImplicitAcousticO2Solver2steps.cpp b/src/scheme/ImplicitAcousticO2Solver2steps.cpp index fb9cf391e84a7a74f3eeacf7562c33b1a3f41e97..4a47ef50c9dc75868df0e416a3faf4a8e14e57c3 100644 --- a/src/scheme/ImplicitAcousticO2Solver2steps.cpp +++ b/src/scheme/ImplicitAcousticO2Solver2steps.cpp @@ -1806,7 +1806,8 @@ class ImplicitAcousticO2SolverHandler2steps::ImplicitAcousticO2Solver final double m = min(new_Vj); if (m < 0) { - std::cout << "negative volume\n"; + std::cout << "negative volume" << '\n'; + throw NormalError("Negative Volume"); parallel_for( mesh->numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { new_xr[node_id] = 0.5 * (new_xr[node_id] + mesh->xr()[node_id]); }); diff --git a/src/scheme/ImplicitAcousticO2Solver2stepsModified.cpp b/src/scheme/ImplicitAcousticO2Solver2stepsModified.cpp index 4b1407c089330cddf7be4b98112ef4e893816080..2b0798b2dfed2a3022440000319d2c2523755115 100644 --- a/src/scheme/ImplicitAcousticO2Solver2stepsModified.cpp +++ b/src/scheme/ImplicitAcousticO2Solver2stepsModified.cpp @@ -1626,10 +1626,12 @@ class ImplicitAcousticO2SolverHandler2stepsModified::ImplicitAcousticO2Solver fi } public: + double dt_f = 0; std::tuple<std::shared_ptr<const MeshVariant>, std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const DiscreteFunctionVariant>, - std::shared_ptr<const DiscreteFunctionVariant>> + std::shared_ptr<const DiscreteFunctionVariant>, + const double> apply(const double& dt, const std::shared_ptr<const MeshType>& mesh, const DiscreteScalarFunction& rho, @@ -1702,274 +1704,323 @@ class ImplicitAcousticO2SolverHandler2stepsModified::ImplicitAcousticO2Solver fi }); return computed_g_1_exp_S_Cv_inv_g; }(); - - double max_tau_error; - int number_iter = 0; + bool CFL = false; + double dt_interm = dt; + int number_iter_CFL = 0; do { - number_iter++; - // first step flux - this->_computeGradJU_AU(u, p, pi, 0.25 * dt); + double max_tau_error; + bool test = true; + bool minder = false; + int number_iter = 0; + do { + number_iter++; + minder = false; + bool question1 = false; + bool question2 = false; + bool question3 = false; + bool question4 = false; + // first step flux + this->_computeGradJU_AU(u, p, pi, 0.25 * dt_interm); - NodeValue<const Rd> ur_1 = this->_getUr(); - NodeValuePerCell<const Rd> Fjr_1 = this->_getFjr(ur_1); + NodeValue<const Rd> ur_1 = this->_getUr(); + NodeValuePerCell<const Rd> Fjr_1 = this->_getFjr(ur_1); - // second step + // second step - const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); + const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); - auto u_inter = copy(u); - CellValue<double> tau_inter(m_mesh.connectivity()); - auto p_inter = copy(p); + auto u_inter = copy(u); + CellValue<double> tau_inter(m_mesh.connectivity()); + auto p_inter = copy(p); - parallel_for( - mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { - const auto& cell_nodes = cell_to_node_matrix[j]; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; - Rd momentum_fluxes = zero; + Rd momentum_fluxes = zero; + for (size_t R = 0; R < cell_nodes.size(); ++R) { + const NodeId r = cell_nodes[R]; + momentum_fluxes += Fjr_1(j, R); + } + const double dt_interm_over_Mj = dt_interm / m_Mj[j]; + u_inter[j] -= 0.5 * dt_interm_over_Mj * momentum_fluxes; + }); + + for (CellId j = 0; j < mesh->numberOfCells(); ++j) { + double tau_fluxes_1 = 0; + const auto& cell_nodes = cell_to_node_matrix[j]; for (size_t R = 0; R < cell_nodes.size(); ++R) { const NodeId r = cell_nodes[R]; - momentum_fluxes += Fjr_1(j, R); + tau_fluxes_1 += dot(m_Djr(j, R), ur_1[r]); + } + tau_inter[j] = m_tau[j] + 0.5 * (dt_interm / m_Mj[j]) * tau_fluxes_1; + p_inter[j] = + std::pow((gamma[j] - 1) * exp(entropy[j] / Cv[j]), m_inv_gamma[j]) * + pow(tau_inter[j], gamma[j]); // sert à convertir tau en p par "p = (gamma-1)exp(S/C_v)tau^{-gamma}" + if (-tau_fluxes_1 > 0) { + question1 = true; + if (dt_interm >= 2 * m_Mj[j] * m_tau[j] / (-tau_fluxes_1)) { + question3 = true; + test = false; + } } - const double dt_over_Mj = dt / m_Mj[j]; - u_inter[j] -= 0.5 * dt_over_Mj * momentum_fluxes; - }); - - for (CellId j = 0; j < mesh->numberOfCells(); ++j) { - double tau_fluxes_1 = 0; - const auto& cell_nodes = cell_to_node_matrix[j]; - for (size_t R = 0; R < cell_nodes.size(); ++R) { - const NodeId r = cell_nodes[R]; - tau_fluxes_1 += dot(m_Djr(j, R), ur_1[r]); } - tau_inter[j] = m_tau[j] + 0.5 * (dt / m_Mj[j]) * tau_fluxes_1; - p_inter[j] = std::pow((gamma[j] - 1) * exp(entropy[j] / Cv[j]), m_inv_gamma[j]) * - pow(tau_inter[j], gamma[j]); // convertir tau en p par "p = (gamma-1)exp(S/C_v)tau^{-gamma}" - } - - this->_computeGradJU_AU(u_inter, p_inter, pi, 0.25 * dt); - - NodeValue<const Rd> ur_2 = this->_getUr(); - NodeValuePerCell<const Rd> Fjr_2 = this->_getFjr(ur_2); + this->_computeGradJU_AU(u_inter, p_inter, pi, 0.25 * dt_interm); - // time n+1 + NodeValue<const Rd> ur_2 = this->_getUr(); - // const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); + NodeValuePerCell<const Rd> Fjr_2 = this->_getFjr(ur_2); - NodeValue<Rd> new_xr = copy(mesh->xr()); - parallel_for( - mesh->numberOfNodes(), PUGS_LAMBDA(NodeId r) { new_xr[r] += 0.5 * dt * (ur_2[r] + ur_1[r]); }); - - new_mesh = std::make_shared<MeshType>(mesh->shared_connectivity(), new_xr); + // time n+1 - CellValue<const double> Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + // const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); - new_rho = copy(rho.cellValues()); - new_u = copy(u.cellValues()); - new_E = copy(E.cellValues()); - - parallel_for( - mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { - const auto& cell_nodes = cell_to_node_matrix[j]; + NodeValue<Rd> new_xr = copy(mesh->xr()); + parallel_for( + mesh->numberOfNodes(), PUGS_LAMBDA(NodeId r) { new_xr[r] += 0.5 * dt_interm * (ur_2[r] + ur_1[r]); }); - Rd momentum_fluxes = zero; - double energy_fluxes = 0; - for (size_t R = 0; R < cell_nodes.size(); ++R) { - const NodeId r = cell_nodes[R]; - momentum_fluxes += Fjr_2(j, R) + Fjr_1(j, R); - energy_fluxes += dot(Fjr_2(j, R), ur_2[r]) + dot(Fjr_1(j, R), ur_1[r]); - } - const double dt_over_Mj = dt / m_Mj[j]; - new_u[j] -= 0.5 * dt_over_Mj * momentum_fluxes; - new_E[j] -= 0.5 * dt_over_Mj * energy_fluxes; - }); + new_mesh = std::make_shared<MeshType>(mesh->shared_connectivity(), new_xr); - // update Djr - const NodeValuePerCell<const Rd> new_Cjr = MeshDataManager::instance().getMeshData(*new_mesh).Cjr(); + CellValue<const double> Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); - CellValue<double> new_Vj{m_mesh.connectivity()}; + new_rho = copy(rho.cellValues()); + new_u = copy(u.cellValues()); + new_E = copy(E.cellValues()); - bool is_positive = true; - do { - is_positive = true; parallel_for( mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { - new_Vj[j] = 0; - auto cell_nodes = cell_to_node_matrix[j]; + const auto& cell_nodes = cell_to_node_matrix[j]; - for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { - const NodeId node_id = cell_nodes[i_node]; - new_Vj[j] += dot(new_Cjr[j][i_node], new_xr[node_id]); + Rd momentum_fluxes = zero; + double energy_fluxes = 0; + for (size_t R = 0; R < cell_nodes.size(); ++R) { + const NodeId r = cell_nodes[R]; + momentum_fluxes += Fjr_2(j, R) + Fjr_1(j, R); + energy_fluxes += dot(Fjr_2(j, R), ur_2[r]) + dot(Fjr_1(j, R), ur_1[r]); } - new_Vj[j] *= 1. / Dimension; + const double dt_interm_over_Mj = dt_interm / m_Mj[j]; + new_u[j] -= 0.5 * dt_interm_over_Mj * momentum_fluxes; + new_E[j] -= 0.5 * dt_interm_over_Mj * energy_fluxes; }); - double m = min(new_Vj); - if (m < 0) { - std::cout << "negative volume\n"; + // update Djr + const NodeValuePerCell<const Rd> new_Cjr = MeshDataManager::instance().getMeshData(*new_mesh).Cjr(); + + CellValue<double> new_Vj{m_mesh.connectivity()}; + + bool is_positive = true; + do { + is_positive = true; parallel_for( - mesh->numberOfNodes(), - PUGS_LAMBDA(const NodeId node_id) { new_xr[node_id] = 0.5 * (new_xr[node_id] + mesh->xr()[node_id]); }); - is_positive = false; - } - } while (not is_positive); + mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { + new_Vj[j] = 0; + auto cell_nodes = cell_to_node_matrix[j]; - parallel_for( - mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { new_rho[j] *= Vj[j] / new_Vj[j]; }); + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + const NodeId node_id = cell_nodes[i_node]; + new_Vj[j] += dot(new_Cjr[j][i_node], new_xr[node_id]); + } + new_Vj[j] *= 1. / Dimension; + }); - CellValue<double> new_tau(m_mesh.connectivity()); + double m = min(new_Vj); + if (m < 0) { + std::cout << "negative volume" << '\n'; + minder = true; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { + if (new_Vj[j] == m) { + std::cout << "la c'est la " << j << " en plus elle vaut " << new_Vj[j] << '\n'; + } + }); + }; + } while (not is_positive); - for (CellId j = 0; j < mesh->numberOfCells(); ++j) { - double new_tau_fluxes = 0; - const auto& cell_nodes = cell_to_node_matrix[j]; - for (size_t R = 0; R < cell_nodes.size(); ++R) { - const NodeId r = cell_nodes[R]; - new_tau_fluxes += dot(m_Djr(j, R), ur_2[r] + ur_1[r]); - } - new_tau[j] = m_tau[j] + 0.5 * (dt / m_Mj[j]) * new_tau_fluxes; - // std::cout << "new_tau(" << j << ")=" << new_tau[j] << '\n'; - } + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { new_rho[j] *= Vj[j] / new_Vj[j]; }); - // double sum_tau_rho = 0; - max_tau_error = 0; + CellValue<double> new_tau(m_mesh.connectivity()); - for (CellId j = 0; j < mesh->numberOfCells(); ++j) { - // const auto& cell_nodes = cell_to_node_matrix[j]; - // V_j^n+1/tau_j^n+1 - M_j - // sum_tau_rho += std::abs(new_tau[j] - 1. / new_rho[j]); - if (m_is_implicit_cell[j]) { - max_tau_error = std::max(max_tau_error, std::abs(1 - new_tau[j] * new_rho[j])); + for (CellId j = 0; j < mesh->numberOfCells(); ++j) { + double fluxe_tau2 = 0; + double fluxe_tau1 = 0; + const auto& cell_nodes = cell_to_node_matrix[j]; + for (size_t R = 0; R < cell_nodes.size(); ++R) { + const NodeId r = cell_nodes[R]; + fluxe_tau1 += dot(m_Djr(j, R), ur_1[r]); + fluxe_tau2 += dot(m_Djr(j, R), ur_2[r]); + } + new_tau[j] = m_tau[j] + 0.5 * (dt_interm / m_Mj[j]) * (fluxe_tau1 + fluxe_tau2); + double tau_2 = m_tau[j] + (dt_interm / m_Mj[j]) * (0.5 * fluxe_tau1 + 0.25 * fluxe_tau2); + if (j == 50) { + std::cout << "tau2 = " << tau_2 << '\n'; + // throw NormalError("lalala"); + }; + if (-fluxe_tau2 > 0) { + question2 = true; + if (dt_interm >= 4 * m_Mj[j] * tau_2 / (-fluxe_tau2)) { + question4 = true; + test = false; + }; + }; + // std::cout << "new_tau(" << j << ")=" << new_tau[j] << '\n'; + }; + + // double sum_tau_rho = 0; + max_tau_error = 0; + + for (CellId j = 0; j < mesh->numberOfCells(); ++j) { + // const auto& cell_nodes = cell_to_node_matrix[j]; + // V_j^n+1/tau_j^n+1 - M_j + // sum_tau_rho += std::abs(new_tau[j] - 1. / new_rho[j]); + if (m_is_implicit_cell[j]) { + max_tau_error = std::max(max_tau_error, std::abs(1 - new_tau[j] * new_rho[j])); + } } - } - // std::cout << "sum_tau_rho =" << sum_tau_rho << '\n'; - // std::cout << "max_tau_error=" << max_tau_error << '\n'; + // std::cout << "sum_tau_rho =" << sum_tau_rho << '\n'; + // std::cout << "max_tau_error=" << max_tau_error << '\n'; - if constexpr (Dimension == 2) { - NodeValuePerCell<Rd> Djr{m_mesh.connectivity()}; + if constexpr (Dimension == 2) { + NodeValuePerCell<Rd> Djr{m_mesh.connectivity()}; - parallel_for( - mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { - const auto& cell_nodes = cell_to_node_matrix[cell_id]; - if (m_is_implicit_cell[cell_id]) { - for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { - Djr(cell_id, i_node) = 0.5 * (Cjr_n(cell_id, i_node) + new_Cjr(cell_id, i_node)); - } - } else { - for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { - Djr(cell_id, i_node) = Cjr_n(cell_id, i_node); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const auto& cell_nodes = cell_to_node_matrix[cell_id]; + if (m_is_implicit_cell[cell_id]) { + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + Djr(cell_id, i_node) = 0.5 * (Cjr_n(cell_id, i_node) + new_Cjr(cell_id, i_node)); + } + } else { + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + Djr(cell_id, i_node) = Cjr_n(cell_id, i_node); + } } - } - }); + }); - m_Djr = Djr; - } else if constexpr (Dimension == 3) { - NodeValuePerFace<Rd> Nlr_delta(m_mesh.connectivity()); - const auto& face_to_node_matrix = m_mesh.connectivity().faceToNodeMatrix(); + m_Djr = Djr; + } else if constexpr (Dimension == 3) { + NodeValuePerFace<Rd> Nlr_delta(m_mesh.connectivity()); + const auto& face_to_node_matrix = m_mesh.connectivity().faceToNodeMatrix(); - const auto& xr_n = m_mesh.xr(); - const auto& xr_np1 = new_xr; + const auto& xr_n = m_mesh.xr(); + const auto& xr_np1 = new_xr; - parallel_for( - m_mesh.numberOfFaces(), PUGS_LAMBDA(FaceId l) { - const auto& face_nodes = face_to_node_matrix[l]; - const size_t nb_nodes = face_nodes.size(); - std::vector<Rd> dxr_n(nb_nodes); - std::vector<Rd> dxr_np1(nb_nodes); - for (size_t r = 0; r < nb_nodes; ++r) { - dxr_n[r] = xr_n[face_nodes[(r + 1) % nb_nodes]] - xr_n[face_nodes[(r + nb_nodes - 1) % nb_nodes]]; - dxr_np1[r] = xr_np1[face_nodes[(r + 1) % nb_nodes]] - xr_np1[face_nodes[(r + nb_nodes - 1) % nb_nodes]]; - } - const double inv_12_nb_nodes = 1. / (12. * nb_nodes); - for (size_t r = 0; r < nb_nodes; ++r) { - Rd Nr = zero; - for (size_t s = 0; s < nb_nodes; ++s) { - Nr -= crossProduct((1. / 6) * (2 * (dxr_np1[r] - dxr_n[r]) - (dxr_np1[s] - dxr_n[s])), - xr_np1[face_nodes[s]] - xr_n[face_nodes[s]]); + parallel_for( + m_mesh.numberOfFaces(), PUGS_LAMBDA(FaceId l) { + const auto& face_nodes = face_to_node_matrix[l]; + const size_t nb_nodes = face_nodes.size(); + std::vector<Rd> dxr_n(nb_nodes); + std::vector<Rd> dxr_np1(nb_nodes); + for (size_t r = 0; r < nb_nodes; ++r) { + dxr_n[r] = xr_n[face_nodes[(r + 1) % nb_nodes]] - xr_n[face_nodes[(r + nb_nodes - 1) % nb_nodes]]; + dxr_np1[r] = xr_np1[face_nodes[(r + 1) % nb_nodes]] - xr_np1[face_nodes[(r + nb_nodes - 1) % nb_nodes]]; } - Nr *= inv_12_nb_nodes; - Nlr_delta(l, r) = Nr; - } - }); + const double inv_12_nb_nodes = 1. / (12. * nb_nodes); + for (size_t r = 0; r < nb_nodes; ++r) { + Rd Nr = zero; + for (size_t s = 0; s < nb_nodes; ++s) { + Nr -= crossProduct((1. / 6) * (2 * (dxr_np1[r] - dxr_n[r]) - (dxr_np1[s] - dxr_n[s])), + xr_np1[face_nodes[s]] - xr_n[face_nodes[s]]); + } + Nr *= inv_12_nb_nodes; + Nlr_delta(l, r) = Nr; + } + }); - const auto& cell_to_face_matrix = m_mesh.connectivity().cellToFaceMatrix(); - const auto& cell_face_is_reversed = m_mesh.connectivity().cellFaceIsReversed(); + const auto& cell_to_face_matrix = m_mesh.connectivity().cellToFaceMatrix(); + const auto& cell_face_is_reversed = m_mesh.connectivity().cellFaceIsReversed(); - NodeValuePerCell<Rd> Djr{m_mesh.connectivity()}; - Djr.fill(zero); + NodeValuePerCell<Rd> Djr{m_mesh.connectivity()}; + Djr.fill(zero); - parallel_for( - m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { - const auto& cell_nodes = cell_to_node_matrix[j]; + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; - const auto& cell_faces = cell_to_face_matrix[j]; - const auto& face_is_reversed = cell_face_is_reversed.itemArray(j); + const auto& cell_faces = cell_to_face_matrix[j]; + const auto& face_is_reversed = cell_face_is_reversed.itemArray(j); - for (size_t L = 0; L < cell_faces.size(); ++L) { - const FaceId& l = cell_faces[L]; - const auto& face_nodes = face_to_node_matrix[l]; + for (size_t L = 0; L < cell_faces.size(); ++L) { + const FaceId& l = cell_faces[L]; + const auto& face_nodes = face_to_node_matrix[l]; - auto local_node_number_in_cell = [&](NodeId node_number) { - for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { - if (node_number == cell_nodes[i_node]) { - return i_node; + auto local_node_number_in_cell = [&](NodeId node_number) { + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + if (node_number == cell_nodes[i_node]) { + return i_node; + } } - } - return std::numeric_limits<size_t>::max(); - }; + return std::numeric_limits<size_t>::max(); + }; - if (face_is_reversed[L]) { - for (size_t rl = 0; rl < face_nodes.size(); ++rl) { - const size_t R = local_node_number_in_cell(face_nodes[rl]); - Djr(j, R) -= Nlr_delta(l, rl); - } - } else { - for (size_t rl = 0; rl < face_nodes.size(); ++rl) { - const size_t R = local_node_number_in_cell(face_nodes[rl]); - Djr(j, R) += Nlr_delta(l, rl); + if (face_is_reversed[L]) { + for (size_t rl = 0; rl < face_nodes.size(); ++rl) { + const size_t R = local_node_number_in_cell(face_nodes[rl]); + Djr(j, R) -= Nlr_delta(l, rl); + } + } else { + for (size_t rl = 0; rl < face_nodes.size(); ++rl) { + const size_t R = local_node_number_in_cell(face_nodes[rl]); + Djr(j, R) += Nlr_delta(l, rl); + } } } - } - }); + }); - parallel_for( - Djr.numberOfValues(), PUGS_LAMBDA(const size_t i) { Djr[i] += 0.5 * (Cjr_n[i] + new_Cjr[i]); }); + parallel_for( + Djr.numberOfValues(), PUGS_LAMBDA(const size_t i) { Djr[i] += 0.5 * (Cjr_n[i] + new_Cjr[i]); }); - parallel_for( - m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - if (not m_is_implicit_cell[cell_id]) { - const auto& cell_nodes = cell_to_node_matrix[cell_id]; - for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { - Djr(cell_id, i_node) = Cjr_n(cell_id, i_node); + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + if (not m_is_implicit_cell[cell_id]) { + const auto& cell_nodes = cell_to_node_matrix[cell_id]; + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + Djr(cell_id, i_node) = Cjr_n(cell_id, i_node); + } } - } - }); + }); - m_Djr = Djr; + m_Djr = Djr; - double max_err = 0; + double max_err = 0; - for (CellId cell_id = 0; cell_id < m_mesh.numberOfCells(); ++cell_id) { - if (m_is_implicit_cell[cell_id]) { - double delta_V = new_Vj[cell_id] - Vj[cell_id]; - const auto& node_list = cell_to_node_matrix[cell_id]; - for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { - const NodeId node_id = node_list[i_node]; - delta_V -= 0.5 * dt * dot(ur_1[node_id] + ur_2[node_id], Djr[cell_id][i_node]); + for (CellId cell_id = 0; cell_id < m_mesh.numberOfCells(); ++cell_id) { + if (m_is_implicit_cell[cell_id]) { + double delta_V = new_Vj[cell_id] - Vj[cell_id]; + const auto& node_list = cell_to_node_matrix[cell_id]; + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; + delta_V -= 0.5 * dt_interm * dot(ur_1[node_id] + ur_2[node_id], Djr[cell_id][i_node]); + } + max_err = std::max(max_err, std::abs(delta_V)); } - max_err = std::max(max_err, std::abs(delta_V)); } + + std::cout << rang::fgB::yellow << "Max volume err = " << max_err << rang::fg::reset << '\n'; } - std::cout << rang::fgB::yellow << "Max volume err = " << max_err << rang::fg::reset << '\n'; - } + if constexpr (Dimension > 1) { + std::cout << rang::fgB::magenta << "number_iter_Djr=" << number_iter << " max_tau_error=" << max_tau_error + << rang::fg::reset << '\n'; + } - if constexpr (Dimension > 1) { - std::cout << rang::fgB::magenta << "number_iter_Djr=" << number_iter << " max_tau_error=" << max_tau_error - << rang::fg::reset << '\n'; + // std::cout << "new rho=" << new_rho << '\n'; + std::cout << "test is " << test << " la reponse de question 1 puis 3 est " << question1 << " " << question3 + << " et la reponse de question 2 puis 4 est " << question2 << " " << question4 << '\n'; + } while ((Dimension > 1) and (max_tau_error > 1e-4) and (number_iter < 100)); + if (test) { + if (minder) { + throw NormalError("Negative volume"); + }; + dt_f = dt_interm; + CFL = true; + } else { + dt_interm = 0.5 * dt_interm; } - - // std::cout << "new rho=" << new_rho << '\n'; - } while ((Dimension > 1) and (max_tau_error > 1e-4) and (number_iter < 100)); + number_iter_CFL++; + std::cout << " nombre d'iterations pour la CFL est " << number_iter_CFL << '\n'; + } while (CFL == false); // std::cout << "prochaine boucle" << '\n'; implicit_t.pause(); // std::cout << "getA=" << get_A_t << "(" << count_getA << ")" @@ -1981,13 +2032,14 @@ class ImplicitAcousticO2SolverHandler2stepsModified::ImplicitAcousticO2Solver fi return {std::make_shared<MeshVariant>(new_mesh), std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction{new_mesh, new_rho}), std::make_shared<DiscreteFunctionVariant>(DiscreteVectorFunction{new_mesh, new_u}), - std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction{new_mesh, new_E})}; + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction{new_mesh, new_E}), dt_f}; } std::tuple<std::shared_ptr<const MeshVariant>, std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const DiscreteFunctionVariant>, - std::shared_ptr<const DiscreteFunctionVariant>> + std::shared_ptr<const DiscreteFunctionVariant>, + double> apply(const double& dt, const std::shared_ptr<const DiscreteFunctionVariant>& rho, const std::shared_ptr<const DiscreteFunctionVariant>& u, diff --git a/src/scheme/ImplicitAcousticO2Solver2stepsModified.hpp b/src/scheme/ImplicitAcousticO2Solver2stepsModified.hpp index 996934b218a2b71898ba9552231850635d96ef74..1ffaaeab3904090330589ff9465cbbb6d8c2ad10 100644 --- a/src/scheme/ImplicitAcousticO2Solver2stepsModified.hpp +++ b/src/scheme/ImplicitAcousticO2Solver2stepsModified.hpp @@ -30,7 +30,8 @@ class ImplicitAcousticO2SolverHandler2stepsModified virtual std::tuple<std::shared_ptr<const MeshVariant>, std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const DiscreteFunctionVariant>, - std::shared_ptr<const DiscreteFunctionVariant>> + std::shared_ptr<const DiscreteFunctionVariant>, + double> apply(const double& dt, const std::shared_ptr<const DiscreteFunctionVariant>& rho, const std::shared_ptr<const DiscreteFunctionVariant>& u, diff --git a/src/scheme/ImplicitAcousticO2SolverModified.cpp b/src/scheme/ImplicitAcousticO2SolverModified.cpp index c1674e2aced4bd09283dc2bcca8b9b5672250b92..1752e16f865b3bfea7772e70cc80bbe0dd6dde4c 100644 --- a/src/scheme/ImplicitAcousticO2SolverModified.cpp +++ b/src/scheme/ImplicitAcousticO2SolverModified.cpp @@ -1631,7 +1631,7 @@ class ImplicitAcousticO2SolverHandlerModified::ImplicitAcousticO2Solver final std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const DiscreteFunctionVariant>, - std::shared_ptr<const double>> + double> apply(const double& dt, const std::shared_ptr<const MeshType>& mesh, const DiscreteScalarFunction& rho, @@ -1710,11 +1710,14 @@ class ImplicitAcousticO2SolverHandlerModified::ImplicitAcousticO2Solver final bool minder = false; double dt_interm = dt; do { - minder = false; - int number_iter = 0; - bool test = true; + minder = false; + int number_iter = 0; + bool test = true; + double tau_est_erreur = 0; do { number_iter++; + bool question1 = false; + bool question2 = false; this->_computeGradJU_AU(u, p, pi, 0.5 * dt_interm); NodeValue<const Rd> ur = this->_getUr(); @@ -1775,10 +1778,6 @@ class ImplicitAcousticO2SolverHandlerModified::ImplicitAcousticO2Solver final if (m < 0) { // throw NormalError("Negative volume"); minder = true; - parallel_for( - mesh->numberOfNodes(), - PUGS_LAMBDA(const NodeId node_id) { new_xr[node_id] = 0.5 * (new_xr[node_id] + mesh->xr()[node_id]); }); - is_positive = false; } } while (not is_positive); @@ -1794,7 +1793,8 @@ class ImplicitAcousticO2SolverHandlerModified::ImplicitAcousticO2Solver final const NodeId r = cell_nodes[R]; new_tau_fluxes += dot(m_Djr(j, R), ur[r]); } - new_tau[j] = m_tau[j] + (dt_interm / m_Mj[j]) * new_tau_fluxes; + new_tau[j] = m_tau[j] + (dt_interm / m_Mj[j]) * new_tau_fluxes; + tau_est_erreur = std::min(tau_est_erreur, new_tau[j]); } // std::cout << "new_tau(" << j << ")=" << new_tau[j] << '\n'; for (CellId j = 0; j < mesh->numberOfCells(); ++j) { @@ -1806,8 +1806,10 @@ class ImplicitAcousticO2SolverHandlerModified::ImplicitAcousticO2Solver final } double tau_1_j = m_tau[j] + 0.5 * (dt_interm / m_Mj[j]) * new_tau_fluxes_1; if (-new_tau_fluxes_1 > 0) { + question1 = true; if (dt_interm >= (2 * (m_Mj[j] * tau_1_j) / (-new_tau_fluxes_1))) { - test = false; + question2 = true; + test = false; } } } @@ -1951,7 +1953,9 @@ class ImplicitAcousticO2SolverHandlerModified::ImplicitAcousticO2Solver final } // std::cout << "new rho=" << new_rho << '\n'; + std::cout << "la question 1 donne " << question1 << " et la question 2 donne " << question2 << '\n'; } while ((Dimension > 1) and (max_tau_error > 1e-4) and (number_iter < 100)); + std::cout << "La plus petite valeur négative de tau est " << tau_est_erreur << "\n"; if (test) { if (minder) { throw NormalError("Negative volume"); @@ -1973,15 +1977,14 @@ class ImplicitAcousticO2SolverHandlerModified::ImplicitAcousticO2Solver final return {std::make_shared<MeshVariant>(new_mesh), std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction{new_mesh, new_rho}), std::make_shared<DiscreteFunctionVariant>(DiscreteVectorFunction{new_mesh, new_u}), - std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction{new_mesh, new_E}), - std::make_shared<double>(dt_f)}; + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction{new_mesh, new_E}), dt_f}; } std::tuple<std::shared_ptr<const MeshVariant>, std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const DiscreteFunctionVariant>, - std::shared_ptr<const double>> + double> apply(const double& dt, const std::shared_ptr<const DiscreteFunctionVariant>& rho, const std::shared_ptr<const DiscreteFunctionVariant>& u, diff --git a/src/scheme/ImplicitAcousticO2SolverModified.hpp b/src/scheme/ImplicitAcousticO2SolverModified.hpp index 070472b85aebe44758691ff1020b8d07dfb3e2f3..a375a7dae9df7e1a292a6e37425de6db8e78e8f2 100644 --- a/src/scheme/ImplicitAcousticO2SolverModified.hpp +++ b/src/scheme/ImplicitAcousticO2SolverModified.hpp @@ -31,7 +31,7 @@ class ImplicitAcousticO2SolverHandlerModified std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const DiscreteFunctionVariant>, - std::shared_ptr<const double>> + double> apply(const double& dt, const std::shared_ptr<const DiscreteFunctionVariant>& rho, const std::shared_ptr<const DiscreteFunctionVariant>& u, diff --git a/src/scheme/ImplicitAcousticO2SolverRhombus.cpp b/src/scheme/ImplicitAcousticO2SolverRhombus.cpp new file mode 100644 index 0000000000000000000000000000000000000000..99e4e867f52fdd4d54f70350ff7d5c7a075cd724 --- /dev/null +++ b/src/scheme/ImplicitAcousticO2SolverRhombus.cpp @@ -0,0 +1,2276 @@ +#include <algebra/CRSMatrix.hpp> +#include <algebra/CRSMatrixDescriptor.hpp> +#include <algebra/LinearSolver.hpp> +#include <language/utils/InterpolateItemValue.hpp> +#include <mesh/IZoneDescriptor.hpp> +#include <mesh/MeshCellZone.hpp> +#include <mesh/MeshFaceBoundary.hpp> +#include <mesh/MeshFlatNodeBoundary.hpp> +#include <mesh/MeshLineNodeBoundary.hpp> +#include <mesh/MeshNodeBoundary.hpp> +#include <scheme/AxisBoundaryConditionDescriptor.hpp> +#include <scheme/DirichletBoundaryConditionDescriptor.hpp> +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/DiscreteFunctionUtils.hpp> +#include <scheme/IBoundaryConditionDescriptor.hpp> +#include <scheme/IDiscreteFunctionDescriptor.hpp> +#include <scheme/ImplicitAcousticO2SolverRhombus.hpp> +#include <scheme/SymmetryBoundaryConditionDescriptor.hpp> + +#include <mesh/ItemValueVariant.hpp> +#include <output/NamedItemValueVariant.hpp> +#include <output/VTKWriter.hpp> +#include <utils/Timer.hpp> + +#include <variant> +#include <vector> + +// VTKWriter vtk_writer("imp/debug", 0); + +template <MeshConcept MeshType> +class ImplicitAcousticO2SolverHandlerRhombus::ImplicitAcousticO2Solver final + : public ImplicitAcousticO2SolverHandlerRhombus::IImplicitAcousticO2Solver +{ + private: + constexpr static size_t Dimension = MeshType::Dimension; + using Rdxd = TinyMatrix<Dimension>; + using Rd = TinyVector<Dimension>; + + using MeshDataType = MeshData<MeshType>; + + using DiscreteScalarFunction = DiscreteFunctionP0<const double>; + using DiscreteVectorFunction = DiscreteFunctionP0<const Rd>; + + class AxisBoundaryCondition; + class PressureBoundaryCondition; + class SymmetryBoundaryCondition; + class VelocityBoundaryCondition; + + using BoundaryCondition = std:: + variant<AxisBoundaryCondition, PressureBoundaryCondition, SymmetryBoundaryCondition, VelocityBoundaryCondition>; + + using BoundaryConditionList = std::vector<BoundaryCondition>; + + const SolverType m_solver_type; + BoundaryConditionList m_boundary_condition_list; + const MeshType& m_mesh; + + CellValue<const double> m_inv_gamma; + CellValue<const double> m_g_1_exp_S_Cv_inv_g; + CellValue<const double> m_tau_iter; + + CellValue<const Rd> m_u; + CellValue<const double> m_tau; + CellValue<const double> m_Mj; + + NodeValuePerCell<const Rdxd> m_Ajr; + NodeValue<const Rdxd> m_inv_Ar; + + NodeValuePerCell<const Rd> m_Djr; + + CellValue<const Rd> m_predicted_u; + CellValue<const double> m_predicted_p; + + CellValue<const bool> m_is_implicit_cell; + CellValue<const int> m_implicit_cell_index; + size_t m_number_of_implicit_cells; + + NodeValue<const bool> m_is_implicit_node; + NodeValue<const int> m_implicit_node_index; + size_t m_number_of_implicit_nodes; + + NodeValue<const double> + _getRhoCr(const DiscreteScalarFunction& rho, const DiscreteScalarFunction& c) const + { + Assert(rho.meshVariant()->id() == c.meshVariant()->id()); + NodeValue<double> rhocr{m_mesh.connectivity()}; + + const auto& node_to_cell_matrix = m_mesh.connectivity().nodeToCellMatrix(); + + parallel_for( + m_mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { + const auto& node_cells = node_to_cell_matrix[r]; + const size_t nb_cells = node_cells.size(); + double rhoc = 0; + + for (size_t J = 0; J < nb_cells; ++J) { + CellId j = node_cells[J]; + rhoc += rho[j] * c[j]; + } + rhocr[r] = rhoc * (1. / nb_cells); + }); + + return rhocr; + } + + NodeValuePerCell<const Rdxd> + _computeAjr(const DiscreteScalarFunction& rho, const DiscreteScalarFunction& c) const + { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(m_mesh); + + const NodeValuePerCell<const Rd> Cjr_n = mesh_data.Cjr(); + const NodeValuePerCell<const Rd> njr_n = mesh_data.njr(); + + NodeValuePerCell<Rdxd> Ajr{m_mesh.connectivity()}; + const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); + + switch (m_solver_type) { + case SolverType::Glace1State: { + NodeValue<const double> rhocr = _getRhoCr(rho, c); + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const size_t& nb_nodes = Ajr.numberOfSubValues(j); + for (size_t r = 0; r < nb_nodes; ++r) { + const NodeId node_id = cell_to_node_matrix[j][r]; + Ajr(j, r) = tensorProduct(rhocr[node_id] * Cjr_n(j, r), njr_n(j, r)); + } + }); + break; + } + case SolverType::Glace2States: { + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const size_t& nb_nodes = Ajr.numberOfSubValues(j); + for (size_t r = 0; r < nb_nodes; ++r) { + Ajr(j, r) = tensorProduct(rho[j] * c[j] * Cjr_n(j, r), njr_n(j, r)); + } + }); + break; + } + case SolverType::Eucclhyd: { + if constexpr (Dimension > 1) { + const NodeValuePerFace<const Rd> Nlr = mesh_data.Nlr(); + const NodeValuePerFace<const Rd> nlr = mesh_data.nlr(); + const auto& face_to_node_matrix = m_mesh.connectivity().faceToNodeMatrix(); + const auto& cell_to_face_matrix = m_mesh.connectivity().cellToFaceMatrix(); + parallel_for( + Ajr.numberOfValues(), PUGS_LAMBDA(size_t jr) { Ajr[jr] = zero; }); + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + const auto& cell_faces = cell_to_face_matrix[j]; + + const double& rho_c = rho[j] * c[j]; + + for (size_t L = 0; L < cell_faces.size(); ++L) { + const FaceId& l = cell_faces[L]; + const auto& face_nodes = face_to_node_matrix[l]; + + auto local_node_number_in_cell = [&](NodeId node_number) { + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + if (node_number == cell_nodes[i_node]) { + return i_node; + } + } + return std::numeric_limits<size_t>::max(); + }; + + for (size_t rl = 0; rl < face_nodes.size(); ++rl) { + const size_t R = local_node_number_in_cell(face_nodes[rl]); + Ajr(j, R) += tensorProduct(rho_c * Nlr(l, rl), nlr(l, rl)); + } + } + }); + + break; + } else { + throw NotImplementedError("Eucclhyd switch is not implemented yet in 1d"); + } + } + } + + return Ajr; + } + + NodeValue<const Rdxd> + _computeAr(const NodeValuePerCell<const Rdxd>& Ajr) const + { + const auto& node_to_cell_matrix = m_mesh.connectivity().nodeToCellMatrix(); + const auto& node_local_numbers_in_their_cells = m_mesh.connectivity().nodeLocalNumbersInTheirCells(); + + NodeValue<Rdxd> Ar{m_mesh.connectivity()}; + + parallel_for( + m_mesh.numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { + Rdxd sum = zero; + const auto& node_to_cell = node_to_cell_matrix[node_id]; + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + + for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) { + const unsigned int i_node = node_local_number_in_its_cells[i_cell]; + const CellId cell_id = node_to_cell[i_cell]; + sum += Ajr(cell_id, i_node); + } + Ar[node_id] = sum; + }); + + NodeValue<bool> has_boundary_condition{m_mesh.connectivity()}; + has_boundary_condition.fill(false); + + // velocity bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<VelocityBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + + parallel_for( + node_list.size(), PUGS_LAMBDA(const size_t i_node) { + const NodeId node_id = node_list[i_node]; + + if (not has_boundary_condition[node_id]) { + Ar[node_id] = identity; + + has_boundary_condition[node_id] = true; + } + }); + } + }, + boundary_condition); + } + + if constexpr (Dimension > 1) { + // axis bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<AxisBoundaryCondition, T>) { + const Rd& t = bc.direction(); + + const Rdxd I = identity; + const Rdxd txt = tensorProduct(t, t); + + const Array<const NodeId>& node_list = bc.nodeList(); + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId& node_id = node_list[i_node]; + if (not has_boundary_condition[node_id]) { + Ar[node_id] = txt * Ar[node_id] * txt + (I - txt); + + has_boundary_condition[node_id] = true; + } + } + } + }, + boundary_condition); + } + } + + // symmetry bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<SymmetryBoundaryCondition, T>) { + const Rd& n = bc.outgoingNormal(); + + const Rdxd I = identity; + const Rdxd nxn = tensorProduct(n, n); + const Rdxd P = I - nxn; + + const Array<const NodeId>& node_list = bc.nodeList(); + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId& node_id = node_list[i_node]; + if (not has_boundary_condition[node_id]) { + Ar[node_id] = P * Ar[node_id] * P + nxn; + + has_boundary_condition[node_id] = true; + } + } + } + }, + boundary_condition); + } + + return Ar; + } + + NodeValue<const Rdxd> + _computeInvAr(const NodeValue<const Rdxd>& Ar) const + { + NodeValue<Rdxd> inv_Ar{m_mesh.connectivity()}; + + for (NodeId node_id = 0; node_id < m_mesh.numberOfNodes(); ++node_id) { + inv_Ar[node_id] = inverse(Ar[node_id]); + } + + return inv_Ar; + } + + int + mapP(CellId j) const + { + // return (1 + Dimension) * m_implicit_cell_index[j]; + return m_implicit_cell_index[j]; + } + + int + mapU(size_t k, CellId j) const + { + // return (1 + Dimension) * m_implicit_cell_index[j] + k + 1; + return m_number_of_implicit_cells + k + m_implicit_cell_index[j] * Dimension; + } + + CRSMatrixDescriptor<double> + _getA() const + { + Array<int> non_zeros{(Dimension + 1) * m_number_of_implicit_cells}; + + const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); + const auto& node_to_cell_matrix = m_mesh.connectivity().nodeToCellMatrix(); + + auto is_boundary_node = m_mesh.connectivity().isBoundaryNode(); + + non_zeros.fill(0); + for (CellId cell_j_id = 0; cell_j_id < m_mesh.numberOfCells(); ++cell_j_id) { + if (m_is_implicit_cell[cell_j_id]) { + const auto& cell_j_nodes = cell_to_node_matrix[cell_j_id]; + std::vector<size_t> nb_neighbors; + nb_neighbors.reserve(30); + + for (size_t id_node = 0; id_node < cell_j_nodes.size(); ++id_node) { + if (not is_boundary_node[cell_j_nodes[id_node]]) { + NodeId node_id = cell_j_nodes[id_node]; + const auto& node_cell = node_to_cell_matrix[node_id]; + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + nb_neighbors.push_back(m_implicit_cell_index[id_cell_i]); + } + } + } + } + std::sort(nb_neighbors.begin(), nb_neighbors.end()); + auto last = std::unique(nb_neighbors.begin(), nb_neighbors.end()); + nb_neighbors.resize(std::distance(nb_neighbors.begin(), last)); + + size_t line_index_p = mapP(cell_j_id); + non_zeros[line_index_p] = Dimension * nb_neighbors.size() + 1; + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + size_t line_index_u = mapU(i_dimension, cell_j_id); + non_zeros[line_index_u] = nb_neighbors.size() + 1; + } + + nb_neighbors.clear(); + } + } + + CRSMatrixDescriptor A{(Dimension + 1) * m_number_of_implicit_cells, non_zeros}; + const auto& node_local_numbers_in_their_cells = m_mesh.connectivity().nodeLocalNumbersInTheirCells(); + + for (NodeId node_id = 0; node_id < m_mesh.numberOfNodes(); ++node_id) { + if (not is_boundary_node[node_id]) { + const auto& node_cell = node_to_cell_matrix[node_id]; + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + const CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + const CellId id_cell_j = node_cell[cell_j]; + if (m_is_implicit_cell[id_cell_j]) { + const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + const int j_index_p = mapP(id_cell_j); + + const Rd Bji = m_Ajr(id_cell_i, node_nb_in_i) * m_inv_Ar[node_id] * m_Djr(id_cell_j, node_nb_in_j); + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + const int i_index_u = mapU(i_dimension, id_cell_i); + + A(j_index_p, i_index_u) += Bji[i_dimension]; + A(i_index_u, j_index_p) -= Bji[i_dimension]; + } + } + } + } + } + } + } + + // pressure bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<PressureBoundaryCondition, T>) { + // const auto& face_list = bc.faceList(); + + // const auto& face_local_numbers_in_their_cells = m_mesh.connectivity().faceLocalNumbersInTheirCells(); + + // const auto& face_to_cell_matrix = m_mesh.connectivity().faceToCellMatrix(); + + // for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + // for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + // const FaceId& face_id = face_list[i_face]; + // // if (m_is_implicit_node[node_id]) { + // const auto& face_cell = face_to_cell_matrix[face_id]; + // for (size_t cell_i = 0; cell_i < face_cell.size(); ++cell_i) { + // const CellId id_cell_i = face_cell[cell_i]; + // if (m_is_implicit_cell[id_cell_i]) { + // // const auto& face_local_number_in_its_cells = + // // face_local_numbers_in_their_cells.itemArray(face_id); const size_t node_nb_in_i = + // // face_local_number_in_its_cells[cell_i]; + // int index_p = mapP(id_cell_i); + // int index_u = mapU(i_dimension, id_cell_i); + + // // const Rd coef = m_Ajr(id_cell_i, face_local_number_in_its_cells[node_nb_in_i]) * + // // m_inv_Ar[face_id] * + // // m_Djr(id_cell_i, face_local_number_in_its_cells[node_nb_in_i]); + + // const Rd coef = zero; + // A(index_p, index_u) += coef[i_dimension]; + // A(index_u, index_p) -= coef[i_dimension]; + // //} + // } + // } + // } + // } + } + }, + boundary_condition); + } + + NodeValue<bool> has_boundary_condition{m_mesh.connectivity()}; + has_boundary_condition.fill(false); + + // velocity bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<VelocityBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + + parallel_for( + node_list.size(), PUGS_LAMBDA(const size_t i_node) { + const NodeId node_id = node_list[i_node]; + if (not has_boundary_condition[node_id]) { + has_boundary_condition[node_id] = true; + } + }); + } + }, + boundary_condition); + } + + if constexpr (Dimension > 1) { + // axis bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<AxisBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + + const Rd& t = bc.direction(); + const Rdxd txt = tensorProduct(t, t); + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId& node_id = node_list[i_node]; + if (not has_boundary_condition[node_id]) { + const Rdxd inverse_Ar_times_txt = m_inv_Ar[node_id] * txt; + const auto& node_cell = node_to_cell_matrix[node_id]; + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + const CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + const int i_index_u = mapU(i_dimension, id_cell_i); + for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + const CellId id_cell_j = node_cell[cell_j]; + if (m_is_implicit_cell[id_cell_j]) { + const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + const int j_index_p = mapP(id_cell_j); + + const Rd coef = + m_Ajr(id_cell_i, node_nb_in_i) * inverse_Ar_times_txt * m_Djr(id_cell_j, node_nb_in_j); + A(j_index_p, i_index_u) += coef[i_dimension]; + A(i_index_u, j_index_p) -= coef[i_dimension]; + } + } + } + } + } + has_boundary_condition[node_id] = true; + } + } + } + }, + boundary_condition); + } + } + + // symmetry bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<SymmetryBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + + const Rd& n = bc.outgoingNormal(); + const Rdxd I = identity; + const Rdxd nxn = tensorProduct(n, n); + const Rdxd P = I - nxn; + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId& node_id = node_list[i_node]; + if (not has_boundary_condition[node_id]) { + const Rdxd inverse_ArxP = m_inv_Ar[node_id] * P; + const auto& node_cell = node_to_cell_matrix[node_id]; + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + const CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + const int i_index_u = mapU(i_dimension, id_cell_i); + for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + const CellId id_cell_j = node_cell[cell_j]; + if (m_is_implicit_cell[id_cell_j]) { + const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + const int j_index_p = mapP(id_cell_j); + + const Rd coef = + m_Ajr(id_cell_i, node_nb_in_i) * inverse_ArxP * m_Djr(id_cell_j, node_nb_in_j); + A(j_index_p, i_index_u) += coef[i_dimension]; + A(i_index_u, j_index_p) -= coef[i_dimension]; + } + } + } + } + } + has_boundary_condition[node_id] = true; + } + } + } + }, + boundary_condition); + } + + return A; + } + + Vector<double> + _getU(const CellValue<const double>& p, const CellValue<const Rd>& u) + { + Vector<double> Un{(Dimension + 1) * m_number_of_implicit_cells}; + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + if (m_is_implicit_cell[j]) { + size_t i = mapP(j); + Un[i] = -p[j]; + } + }); + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + if (m_is_implicit_cell[j]) { + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + size_t i = mapU(i_dimension, j); + Un[i] = u[j][i_dimension]; + } + } + }); + + return Un; + } + + Vector<double> + _getF(const Vector<double>& Un, const Vector<double>& Uk, const DiscreteScalarFunction& pi, const double dt) + { + const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); + + NodeValue<const Rd> ur = this->_getUr(); + NodeValuePerCell<const Rd> Fjr = this->_getFjr(ur); + + m_tau_iter = [&]() { + CellValue<double> computed_tau_iter(m_mesh.connectivity()); + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + if (m_is_implicit_cell[j]) { + size_t k = mapP(j); + computed_tau_iter[j] = m_g_1_exp_S_Cv_inv_g[j] * std::pow(-Uk[k] + pi[j], -m_inv_gamma[j]); + } + }); + return computed_tau_iter; + }(); + + CellValue<double> volume_fluxes_sum{m_mesh.connectivity()}; + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + double sum = 0; + const auto& cell_nodes = cell_to_node_matrix[cell_id]; + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + const NodeId node_id = cell_nodes[i_node]; + sum += dot(m_Djr(cell_id, i_node), ur[node_id]); + } + volume_fluxes_sum[cell_id] = sum; + }); + + CellValue<Rd> momentum_fluxes_sum{m_mesh.connectivity()}; + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + Rd sum = zero; + const auto& cell_nodes = cell_to_node_matrix[cell_id]; + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + sum += Fjr(cell_id, i_node); + } + momentum_fluxes_sum[cell_id] = sum; + }); + + Vector<double> F{Un.size()}; + F.fill(0); + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + if (m_is_implicit_cell[cell_id]) { + const size_t pj_index = mapP(cell_id); + F[pj_index] = + m_Mj[cell_id] / dt * + (m_g_1_exp_S_Cv_inv_g[cell_id] * std::pow(-Uk[pj_index] + pi[cell_id], -m_inv_gamma[cell_id]) - + m_tau[cell_id]) - + volume_fluxes_sum[cell_id]; + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + const size_t uj_i_index = mapU(i_dimension, cell_id); + + F[uj_i_index] = + m_Mj[cell_id] / dt * (Uk[uj_i_index] - Un[uj_i_index]) + momentum_fluxes_sum[cell_id][i_dimension]; + } + } + }); + + return F; + } + + CRSMatrixDescriptor<double> + _getHessianJ(const double dt, const DiscreteScalarFunction& pi, const Vector<double>& Uk) + { + auto is_boundary_node = m_mesh.connectivity().isBoundaryNode(); + Array<int> non_zeros{(Dimension + 1) * m_number_of_implicit_cells}; + + const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); + const auto& node_to_cell_matrix = m_mesh.connectivity().nodeToCellMatrix(); + + non_zeros.fill(0); + for (CellId cell_id = 0; cell_id < m_number_of_implicit_cells; ++cell_id) { + if (m_is_implicit_cell[cell_id]) { + const auto& cell_node = cell_to_node_matrix[cell_id]; + std::vector<size_t> nb_neighboors; + nb_neighboors.reserve(30); + + for (size_t id_node = 0; id_node < cell_node.size(); ++id_node) { + if (not is_boundary_node[cell_node[id_node]]) { + NodeId node_id = cell_node[id_node]; + const auto& node_cell = node_to_cell_matrix[node_id]; + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + nb_neighboors.push_back(m_implicit_cell_index[id_cell_i]); + } + } + } + } + + std::sort(nb_neighboors.begin(), nb_neighboors.end()); + auto last = std::unique(nb_neighboors.begin(), nb_neighboors.end()); + nb_neighboors.resize(std::distance(nb_neighboors.begin(), last)); + + size_t line_index_p = mapP(cell_id); + non_zeros[line_index_p] = nb_neighboors.size(); + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + size_t line_index_u = mapU(i_dimension, cell_id); + non_zeros[line_index_u] = Dimension * nb_neighboors.size(); + } + + nb_neighboors.clear(); + } + } + + CRSMatrixDescriptor Hess_J{(Dimension + 1) * m_number_of_implicit_cells, + (Dimension + 1) * m_number_of_implicit_cells, non_zeros}; + + const auto& node_local_numbers_in_their_cells = m_mesh.connectivity().nodeLocalNumbersInTheirCells(); + + const double inv_dt = 1 / dt; + for (CellId cell_id = 0; cell_id < m_mesh.numberOfCells(); ++cell_id) { + if (m_is_implicit_cell[cell_id]) { + const int i_index_p = mapP(cell_id); + Hess_J(i_index_p, i_index_p) = + (inv_dt * m_Mj[cell_id]) * m_inv_gamma[cell_id] * m_tau_iter[cell_id] / (-Uk[i_index_p] + pi[cell_id]); + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + const int i_index_u = mapU(i_dimension, cell_id); + Hess_J(i_index_u, i_index_u) = inv_dt * m_Mj[cell_id]; + } + } + } + + for (NodeId node_id = 0; node_id < m_mesh.numberOfNodes(); ++node_id) { + const auto& node_cell = node_to_cell_matrix[node_id]; + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + + for (size_t j_cell = 0; j_cell < node_cell.size(); ++j_cell) { + const CellId cell_id_j = node_cell[j_cell]; + if (m_is_implicit_cell[cell_id_j]) { + const size_t node_nb_in_j_cell = node_local_number_in_its_cells[j_cell]; + + const auto Ajr = m_Ajr(cell_id_j, node_nb_in_j_cell); + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + const int i_index_u = mapU(i_dimension, cell_id_j); + for (size_t j_dimension = 0; j_dimension < Dimension; ++j_dimension) { + const int j_index_u = mapU(j_dimension, cell_id_j); + Hess_J(i_index_u, j_index_u) += Ajr(i_dimension, j_dimension); + } + } + } + } + + if (not is_boundary_node[node_id]) { + const auto& inv_Ar = m_inv_Ar[node_id]; + + for (size_t j_cell = 0; j_cell < node_cell.size(); ++j_cell) { + const CellId cell_id_j = node_cell[j_cell]; + if (m_is_implicit_cell[cell_id_j]) { + const size_t node_nb_in_j_cell = node_local_number_in_its_cells[j_cell]; + const int j_index_p = mapP(cell_id_j); + + const auto invArDjr = inv_Ar * m_Djr(cell_id_j, node_nb_in_j_cell); + + for (size_t i_cell = 0; i_cell < node_cell.size(); ++i_cell) { + const CellId cell_id_i = node_cell[i_cell]; + if (m_is_implicit_cell[cell_id_i]) { + const size_t node_nb_in_cell_i = node_local_number_in_its_cells[i_cell]; + const int i_index_p = mapP(cell_id_i); + Hess_J(i_index_p, j_index_p) += dot(m_Djr(cell_id_i, node_nb_in_cell_i), invArDjr); + } + } + } + } + + for (size_t j_cell = 0; j_cell < node_cell.size(); ++j_cell) { + const CellId cell_id_j = node_cell[j_cell]; + if (m_is_implicit_cell[cell_id_j]) { + const size_t node_nb_in_j_cell = node_local_number_in_its_cells[j_cell]; + + const auto Ajr_invAr = m_Ajr(cell_id_j, node_nb_in_j_cell) * inv_Ar; + + for (size_t i_cell = 0; i_cell < node_cell.size(); ++i_cell) { + const CellId cell_id_i = node_cell[i_cell]; + if (m_is_implicit_cell[cell_id_i]) { + const size_t node_nb_in_i_cell = node_local_number_in_its_cells[i_cell]; + + const auto Ajr_invAr_Air = Ajr_invAr * m_Ajr(cell_id_i, node_nb_in_i_cell); + + for (size_t j_dimension = 0; j_dimension < Dimension; ++j_dimension) { + const int index_u_j = mapU(j_dimension, cell_id_j); + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + const int index_u_i = mapU(i_dimension, cell_id_i); + Hess_J(index_u_j, index_u_i) -= Ajr_invAr_Air(j_dimension, i_dimension); + } + } + } + } + } + } + } + } + + // presure bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<PressureBoundaryCondition, T>) { + // const auto& node_list = bc.faceList(); + + // const auto& node_local_numbers_in_their_cells = m_mesh.connectivity().nodeLocalNumbersInTheirCells(); + // const auto& node_to_cell_matrix = m_mesh.connectivity().nodeToCellMatrix(); + + // for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + // const NodeId& node_id = node_list[i_node]; + // // if (m_is_implicit_node[node_id]) { + // const auto& node_cell = node_to_cell_matrix[node_id]; + + // for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + // const CellId id_cell_i = node_cell[cell_i]; + // if (m_is_implicit_cell[id_cell_i]) { + // const auto& node_local_number_in_its_cells = + // node_local_numbers_in_their_cells.itemArray(node_id); + + // const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + // const int i_index_p = mapP(id_cell_i); + + // for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + // const CellId id_cell_j = node_cell[cell_j]; + // if (m_is_implicit_cell[id_cell_j]) { + // const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + // const int j_index_p = mapP(id_cell_j); + + // Hess_J(i_index_p, j_index_p) += + // dot(m_Djr(id_cell_i, node_nb_in_i), m_inv_Ar[node_id] * m_Djr(id_cell_j, node_nb_in_j)); + // } + // } + // } + // } + // for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + // const CellId id_cell_i = node_cell[cell_i]; + // if (m_is_implicit_cell[id_cell_i]) { + // const auto& node_local_number_in_its_cells = + // node_local_numbers_in_their_cells.itemArray(node_id); + + // const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + // for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + // const int i_index_u = mapU(i_dimension, id_cell_i); + + // Hess_J(i_index_u, i_index_u) += m_Ajr(id_cell_i, node_nb_in_i)(i_dimension, i_dimension); + + // for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + // const CellId id_cell_j = node_cell[cell_j]; + // if (m_is_implicit_cell[id_cell_j]) { + // const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + // for (size_t j_dimension = 0; j_dimension < Dimension; ++j_dimension) { + // const int j_index_u = mapU(j_dimension, id_cell_j); + + // Hess_J(i_index_u, j_index_u) += -(m_Ajr(id_cell_i, node_nb_in_i) * m_inv_Ar[node_id] * + // m_Ajr(id_cell_j, node_nb_in_j))(i_dimension, + // j_dimension); + // } + // } + // } + // } + // } + // } + // } + } + }, + boundary_condition); + } + + NodeValue<bool> has_boundary_condition{m_mesh.connectivity()}; + has_boundary_condition.fill(false); + + // velocity bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<VelocityBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; + has_boundary_condition[node_id] = true; + } + } + }, + boundary_condition); + } + + if constexpr (Dimension > 1) { + // axis bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<AxisBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + + const Rd& t = bc.direction(); + const Rdxd txt = tensorProduct(t, t); + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId& node_id = node_list[i_node]; + + if (not has_boundary_condition[node_id]) { + const Rdxd inverse_Ar_times_txt = m_inv_Ar[node_id] * txt; + + const auto& node_cell = node_to_cell_matrix[node_id]; + + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + const CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + const int i_index_u = mapU(i_dimension, id_cell_i); + + for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + const CellId id_cell_j = node_cell[cell_j]; + if (m_is_implicit_cell[id_cell_j]) { + const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + + for (size_t j_dimension = 0; j_dimension < Dimension; ++j_dimension) { + const int j_index_u = mapU(j_dimension, id_cell_j); + Hess_J(i_index_u, j_index_u) += + (-m_Ajr(id_cell_i, node_nb_in_i) * inverse_Ar_times_txt * + m_Ajr(id_cell_j, node_nb_in_j))(i_dimension, j_dimension); + } + } + } + } + } + } + + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + const CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + + const int i_index_p = mapP(id_cell_i); + + for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + const CellId id_cell_j = node_cell[cell_j]; + if (m_is_implicit_cell[id_cell_j]) { + const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + const int j_index_p = mapP(id_cell_j); + Hess_J(i_index_p, j_index_p) += + dot(m_Djr(id_cell_i, node_nb_in_i), inverse_Ar_times_txt * m_Djr(id_cell_j, node_nb_in_j)); + } + } + } + } + has_boundary_condition[node_id] = true; + } + } + } + }, + boundary_condition); + } + } + + // symmetry bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<SymmetryBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + + const Rd& n = bc.outgoingNormal(); + const Rdxd I = identity; + const Rdxd nxn = tensorProduct(n, n); + const Rdxd P = I - nxn; + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId& node_id = node_list[i_node]; + + if (not has_boundary_condition[node_id]) { + const Rdxd inverse_ArxP = m_inv_Ar[node_id] * P; + + const auto& node_cell = node_to_cell_matrix[node_id]; + + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + const CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + const int i_index_u = mapU(i_dimension, id_cell_i); + + for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + const CellId id_cell_j = node_cell[cell_j]; + if (m_is_implicit_cell[id_cell_j]) { + const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + + for (size_t j_dimension = 0; j_dimension < Dimension; ++j_dimension) { + const int j_index_u = mapU(j_dimension, id_cell_j); + Hess_J(i_index_u, j_index_u) += (-m_Ajr(id_cell_i, node_nb_in_i) * inverse_ArxP * + m_Ajr(id_cell_j, node_nb_in_j))(i_dimension, j_dimension); + } + } + } + } + } + } + + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + const CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + + const int i_index_p = mapP(id_cell_i); + + for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + const CellId id_cell_j = node_cell[cell_j]; + if (m_is_implicit_cell[id_cell_j]) { + const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + const int j_index_p = mapP(id_cell_j); + Hess_J(i_index_p, j_index_p) += + dot(m_Djr(id_cell_i, node_nb_in_i), inverse_ArxP * m_Djr(id_cell_j, node_nb_in_j)); + } + } + } + } + has_boundary_condition[node_id] = true; + } + } + } + }, + boundary_condition); + } + return Hess_J; + } + + CRSMatrix<double> + _getGradientF(const CRSMatrix<double, int>& A, + const Vector<double>& Uk, + const DiscreteScalarFunction& pi, + const double dt) + { + CRSMatrixDescriptor<double> Hess_J = this->_getHessianJ(dt, pi, Uk); + + CRSMatrix Hess_J_crs = Hess_J.getCRSMatrix(); + + CRSMatrix gradient_f = Hess_J_crs - A; + + return gradient_f; + } + + BoundaryConditionList + _getBCList(const std::shared_ptr<const MeshType>& mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) + { + BoundaryConditionList bc_list; + + for (const auto& bc_descriptor : bc_descriptor_list) { + bool is_valid_boundary_condition = true; + + switch (bc_descriptor->type()) { + case IBoundaryConditionDescriptor::Type::axis: { + if constexpr (Dimension == 1) { + throw NormalError("Axis boundary condition makes no sense in dimension 1"); + } else { + const AxisBoundaryConditionDescriptor& axis_bc_descriptor = + dynamic_cast<const AxisBoundaryConditionDescriptor&>(*bc_descriptor); + + bc_list.push_back( + AxisBoundaryCondition{getMeshLineNodeBoundary(*mesh, axis_bc_descriptor.boundaryDescriptor())}); + } + break; + } + case IBoundaryConditionDescriptor::Type::symmetry: { + const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor = + dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor); + + bc_list.push_back( + SymmetryBoundaryCondition{getMeshFlatNodeBoundary(*mesh, sym_bc_descriptor.boundaryDescriptor())}); + break; + } + case IBoundaryConditionDescriptor::Type::dirichlet: { + const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor = + dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor); + if (dirichlet_bc_descriptor.name() == "velocity") { + MeshNodeBoundary mesh_node_boundary = + getMeshNodeBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + + Array<const TinyVector<Dimension>> value_list = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::node>(dirichlet_bc_descriptor.rhsSymbolId(), + mesh->xr(), mesh_node_boundary.nodeList()); + bc_list.push_back(VelocityBoundaryCondition{mesh_node_boundary.nodeList(), value_list}); + } else if (dirichlet_bc_descriptor.name() == "pressure") { + const FunctionSymbolId pressure_id = dirichlet_bc_descriptor.rhsSymbolId(); + + if constexpr (Dimension == 1) { + MeshNodeBoundary mesh_node_boundary = getMeshNodeBoundary(*mesh, bc_descriptor->boundaryDescriptor()); + + Array<const double> node_values = + InterpolateItemValue<double(Rd)>::template interpolate<ItemType::node>(pressure_id, mesh->xr(), + mesh_node_boundary.nodeList()); + + bc_list.emplace_back(PressureBoundaryCondition{mesh_node_boundary.nodeList(), node_values}); + } else { + constexpr ItemType FaceType = [] { + if constexpr (Dimension > 1) { + return ItemType::face; + } else { + return ItemType::node; + } + }(); + + MeshFaceBoundary mesh_face_boundary = getMeshFaceBoundary(*mesh, bc_descriptor->boundaryDescriptor()); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + Array<const double> face_values = + InterpolateItemValue<double(Rd)>::template interpolate<FaceType>(pressure_id, mesh_data.xl(), + mesh_face_boundary.faceList()); + bc_list.emplace_back(PressureBoundaryCondition{mesh_face_boundary.faceList(), face_values}); + } + } 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 acoustic solver"; + throw NormalError(error_msg.str()); + } + } + + return bc_list; + } + + void + _computeGradJU_AU(const DiscreteVectorFunction& u, + const DiscreteScalarFunction& p, + const DiscreteScalarFunction& pi, + const double& dt) + { + // const auto node_to_cell_matrix = m_mesh.connectivity().nodeToCellMatrix(); + const auto cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); + + m_number_of_implicit_cells = 0; + m_implicit_cell_index = [&] { + CellValue<int> implicit_cell_index(m_mesh.connectivity()); + implicit_cell_index.fill(-1); + + for (CellId cell_id = 0; cell_id < m_mesh.numberOfCells(); ++cell_id) { + if (m_is_implicit_cell[cell_id]) { + if (implicit_cell_index[cell_id] != -1) { + throw NormalError("implicit cell has already an implicit number"); + } + implicit_cell_index[cell_id] = m_number_of_implicit_cells; + m_number_of_implicit_cells++; + } + } + return implicit_cell_index; + }(); + + m_is_implicit_node = [&] { + NodeValue<bool> is_implicit_node(m_mesh.connectivity()); + is_implicit_node.fill(false); + + for (CellId cell_id = 0; cell_id < m_mesh.numberOfCells(); ++cell_id) { + if (m_is_implicit_cell[cell_id]) { + const auto& cell_nodes = cell_to_node_matrix[cell_id]; + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + is_implicit_node[cell_nodes[i_node]] = true; + } + } + } + + return is_implicit_node; + }(); + + m_number_of_implicit_nodes = 0; + m_implicit_node_index = [&] { + NodeValue<int> implicit_node_index(m_mesh.connectivity()); + implicit_node_index.fill(-1); + + for (NodeId node_id = 0; node_id < m_mesh.numberOfNodes(); ++node_id) { + if (m_is_implicit_node[node_id]) { + if (implicit_node_index[node_id] != -1) { + throw NormalError("implicit node has already an implicit number"); + } + implicit_node_index[node_id] = m_number_of_implicit_nodes; + m_number_of_implicit_nodes++; + } + } + return implicit_node_index; + }(); + + std::cout << "building A: " << std::flush; + const CRSMatrix A = this->_getA().getCRSMatrix(); + std::cout << "done\n" << std::flush; + + Vector<double> Un = this->_getU(p.cellValues(), u.cellValues()); + Vector<double> Uk = this->_getU(m_predicted_p, m_predicted_u); + + int nb_iter = 0; + double norm_inf_sol; + + Array<const double> abs_Un = [&]() { + Array<double> compute_abs_Un{Un.size()}; + parallel_for( + Un.size(), PUGS_LAMBDA(size_t i) { compute_abs_Un[i] = std::abs(Un[i]); }); + return compute_abs_Un; + }(); + + double norm_inf_Un = max(abs_Un); + if (m_number_of_implicit_cells > 0) { + do { + nb_iter++; + + Vector<double> f = this->_getF(Un, Uk, pi, dt); + + std::cout << "building gradf: " << std::flush; + CRSMatrix<double> gradient_f = this->_getGradientF(A, Uk, pi, dt); + std::cout << "done\n" << std::flush; + + auto l2Norm = [](const Vector<double>& x) { + double sum2 = 0; + for (size_t i = 0; i < x.size(); ++i) { + sum2 += x[i] * x[i]; + } + return std::sqrt(sum2); + }; + + Vector<double> sol{Uk.size()}; + sol.fill(0); + + LinearSolver solver; + std::cout << "solving linear system: " << std::flush; + solver.solveLocalSystem(gradient_f, sol, f); + std::cout << "done\n" << std::flush; + + std::cout << rang::style::bold << "norm resid = " << l2Norm(f - gradient_f * sol) << rang::style::reset << '\n'; + double theta = 1; + + for (CellId cell_id = 0; cell_id < m_number_of_implicit_cells; ++cell_id) { + size_t k = mapP(cell_id); + if (-Uk[k] + theta * sol[k] < -pi[cell_id]) { + double new_theta = 0.5 * (Uk[k] - pi[cell_id]) / sol[k]; + std::cout << "theta: " << theta << " -> " << new_theta << '\n'; + theta = new_theta; + } + } + + Vector<double> U_next = Uk - theta * sol; + + Array<const double> abs_sol = [&]() { + Array<double> compute_abs_sol{sol.size()}; + parallel_for( + sol.size(), PUGS_LAMBDA(size_t i) { compute_abs_sol[i] = std::abs(sol[i]); }); + return compute_abs_sol; + }(); + + norm_inf_sol = max(abs_sol); + + std::cout << "nb_iter= " << nb_iter << " | ratio Newton = " << norm_inf_sol / norm_inf_Un << "\n"; + + // when it is a hard case we relax newton + size_t neg_pressure_count = 0; + double min_pressure = 0; + for (CellId cell_id = 0; cell_id < m_number_of_implicit_cells; ++cell_id) { + size_t k = mapP(cell_id); + if (-U_next[k] + pi[cell_id] < 0) { + std::cout << " neg p: cell_id=" << cell_id << '\n'; + ++neg_pressure_count; + min_pressure = std::min(min_pressure, -U_next[k] + pi[cell_id]); + U_next[k] = Uk[k]; + } + } + + Uk = U_next; + + // if ((count_newton == 0) and (count_Djr == 0) and (count_timestep == 0)) { + // auto newt_mesh = std::make_shared<MeshType>(m_mesh.shared_connectivity(), m_mesh.xr()); + // double pseudo_time = 1E-8 * count_newton + 1E-3 * count_Djr + count_timestep; + + // std::vector<std::shared_ptr<const INamedDiscreteData>> output_list; + // output_list.push_back( + // std::make_shared<NamedItemValueVariant>(std::make_shared<ItemValueVariant>(m_predicted_p), + // "predicted_p")); + + // vtk_writer.writeOnMeshForced(newt_mesh, output_list, pseudo_time); + // } + + m_predicted_u = [&] { + CellValue<Rd> predicted_u = copy(u.cellValues()); + for (CellId cell_id = 0; cell_id < m_mesh.numberOfCells(); ++cell_id) { + if (m_is_implicit_cell[cell_id]) { + Rd vector_u = zero; + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + vector_u[i_dimension] = Uk[mapU(i_dimension, cell_id)]; + } + predicted_u[cell_id] = vector_u; + } + } + return predicted_u; + }(); + + m_predicted_p = [&] { + CellValue<double> predicted_p = copy(p.cellValues()); + for (CellId cell_id = 0; cell_id < m_mesh.numberOfCells(); ++cell_id) { + if (m_is_implicit_cell[cell_id]) { + predicted_p[cell_id] = -Uk[mapP(cell_id)]; + } + } + return predicted_p; + }(); + + NodeValue<const Rd> ur = _getUr(); + + NodeValue<Rd> newton_xr{m_mesh.connectivity()}; + for (NodeId node_id = 0; node_id < m_mesh.numberOfNodes(); ++node_id) { + newton_xr[node_id] = m_mesh.xr()[node_id] + dt * ur[node_id]; + } + + auto newt_mesh = std::make_shared<MeshType>(m_mesh.shared_connectivity(), newton_xr); + // double pseudo_time = 1E-4 * count_newton + 1E-2 * count_Djr + count_timestep; + + // std::vector<std::shared_ptr<const INamedDiscreteData>> output_list; + // output_list.push_back( + // std::make_shared<NamedItemValueVariant>(std::make_shared<ItemValueVariant>(m_predicted_p), "predicted_p")); + + // vtk_writer.writeOnMeshForced(newt_mesh, output_list, pseudo_time); + + if (neg_pressure_count > 0) { + std::cout << rang::fgB::magenta << "p est negatif sur " << neg_pressure_count + << " mailles min=" << min_pressure << rang::fg::reset << '\n'; + } + } while ((norm_inf_sol > 1e-12 * norm_inf_Un) and (nb_iter < 1000)); + } + + for (CellId j = 0; j < m_mesh.numberOfCells(); ++j) { + // faudrait mettre p+pi + if (m_predicted_p[j] <= 0) { + std::cout << "pression negative pour la maille" << j << '\n'; + } + } + } + + NodeValue<const Rd> + _getUr() const + { + const auto& node_to_cell_matrix = m_mesh.connectivity().nodeToCellMatrix(); + const auto& node_local_numbers_in_their_cells = m_mesh.connectivity().nodeLocalNumbersInTheirCells(); + + NodeValue<Rd> b{m_mesh.connectivity()}; + + parallel_for( + m_mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { + const auto& node_to_cell = node_to_cell_matrix[r]; + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(r); + + Rd br = zero; + for (size_t j = 0; j < node_to_cell.size(); ++j) { + const CellId J = node_to_cell[j]; + const unsigned int R = node_local_number_in_its_cells[j]; + br += m_Ajr(J, R) * m_predicted_u[J] + m_predicted_p[J] * m_Djr(J, R); + } + + b[r] = br; + }); + + NodeValue<bool> has_boundary_condition{m_mesh.connectivity()}; + has_boundary_condition.fill(false); + + // velocity bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<VelocityBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + const auto& value_list = bc.valueList(); + + parallel_for( + node_list.size(), PUGS_LAMBDA(const size_t i_node) { + const NodeId node_id = node_list[i_node]; + const auto& value = value_list[i_node]; + b[node_id] = value; + + has_boundary_condition[node_id] = true; + }); + } + }, + boundary_condition); + } + + // pressure bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<PressureBoundaryCondition, T>) { + MeshData<MeshType>& mesh_data = MeshDataManager::instance().getMeshData(m_mesh); + if constexpr (Dimension == 1) { + const NodeValuePerCell<const Rd> Cjr = mesh_data.Cjr(); + + const auto& node_list = bc.faceList(); + const auto& value_list = bc.valueList(); + parallel_for( + node_list.size(), PUGS_LAMBDA(size_t i_node) { + const NodeId node_id = node_list[i_node]; + const auto& node_cell_list = node_to_cell_matrix[node_id]; + Assert(node_cell_list.size() == 1); + + const CellId node_cell_id = node_cell_list[0]; + const size_t node_local_number_in_cell = node_local_numbers_in_their_cells(node_id, 0); + + b[node_id] -= value_list[i_node] * Cjr(node_cell_id, node_local_number_in_cell); + }); + } else { + const NodeValuePerFace<const Rd> Nlr = mesh_data.Nlr(); + + const auto& face_to_cell_matrix = m_mesh.connectivity().faceToCellMatrix(); + const auto& face_to_node_matrix = m_mesh.connectivity().faceToNodeMatrix(); + const auto& face_local_numbers_in_their_cells = m_mesh.connectivity().faceLocalNumbersInTheirCells(); + const auto& face_cell_is_reversed = m_mesh.connectivity().cellFaceIsReversed(); + + 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]; + const auto& face_cell_list = face_to_cell_matrix[face_id]; + Assert(face_cell_list.size() == 1); + + const CellId face_cell_id = face_cell_list[0]; + const size_t face_local_number_in_cell = face_local_numbers_in_their_cells(face_id, 0); + + const double sign = face_cell_is_reversed(face_cell_id, face_local_number_in_cell) ? -1 : 1; + + const auto& face_nodes = face_to_node_matrix[face_id]; + + for (size_t i_node = 0; i_node < face_nodes.size(); ++i_node) { + NodeId node_id = face_nodes[i_node]; + b[node_id] -= sign * value_list[i_face] * Nlr(face_id, i_node); + } + } + } + } + }, + boundary_condition); + } + + if constexpr (Dimension > 1) { + // axis bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<AxisBoundaryCondition, T>) { + const Rd& t = bc.direction(); + const Rdxd txt = tensorProduct(t, t); + + const auto& node_list = bc.nodeList(); + parallel_for( + bc.numberOfNodes(), PUGS_LAMBDA(const size_t i_node) { + const NodeId node_id = node_list[i_node]; + if (not has_boundary_condition[node_id]) { + b[node_id] = txt * b[node_id]; + + has_boundary_condition[node_id] = true; + } + }); + } + }, + boundary_condition); + } + } + + // symmetry bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<SymmetryBoundaryCondition, T>) { + const Rd& n = bc.outgoingNormal(); + const Rdxd I = identity; + const Rdxd nxn = tensorProduct(n, n); + const Rdxd P = I - nxn; + const auto& node_list = bc.nodeList(); + parallel_for( + bc.numberOfNodes(), PUGS_LAMBDA(const size_t i_node) { + const NodeId node_id = node_list[i_node]; + if (not has_boundary_condition[node_id]) { + b[node_id] = P * b[node_id]; + + has_boundary_condition[node_id] = true; + } + }); + } + }, + boundary_condition); + } + + const NodeValue<Rd> computed_ur(m_mesh.connectivity()); + parallel_for( + m_mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { computed_ur[r] = m_inv_Ar[r] * b[r]; }); + + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + + if constexpr ((Dimension > 1) and (std::is_same_v<AxisBoundaryCondition, T>)) { + const Rd& t = bc.direction(); + const Rdxd txt = tensorProduct(t, t); + + const Array<const NodeId>& node_list = bc.nodeList(); + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; // on fixe le sommet r + computed_ur[node_id] = txt * computed_ur[node_id]; + } + } else if constexpr (std::is_same_v<SymmetryBoundaryCondition, T>) { + const Rd& n = bc.outgoingNormal(); + + const Rdxd I = identity; + const Rdxd nxn = tensorProduct(n, n); + const Rdxd P = I - nxn; + + const Array<const NodeId>& node_list = bc.nodeList(); + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; // on fixe le sommet r + computed_ur[node_id] = P * computed_ur[node_id]; + } + } else if constexpr (std::is_same_v<VelocityBoundaryCondition, T>) { + const Array<const NodeId>& node_list = bc.nodeList(); + const Array<const Rd>& 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]; + computed_ur[node_id] = value_list[i_node]; + } + } + }, + boundary_condition); + } + + return computed_ur; + } + + NodeValuePerCell<const Rd> + _getFjr(const NodeValue<const Rd>& ur) const + { + const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); + const NodeValuePerCell<Rd> computed_Fjr(m_mesh.connectivity()); + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + for (size_t r = 0; r < cell_nodes.size(); ++r) { + computed_Fjr(j, r) = m_Ajr(j, r) * (m_predicted_u[j] - ur[cell_nodes[r]]) + (m_predicted_p[j] * m_Djr(j, r)); + } + }); + + return computed_Fjr; + } + + ImplicitAcousticO2Solver(const SolverType solver_type, + const std::shared_ptr<const MeshType>& p_mesh, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const DiscreteVectorFunction&, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::vector<std::shared_ptr<const IZoneDescriptor>>& explicit_zone_list, + const double&) + : m_solver_type{solver_type}, + m_boundary_condition_list{this->_getBCList(p_mesh, bc_descriptor_list)}, + m_mesh{*p_mesh} + { + m_is_implicit_cell = [&] { + CellValue<bool> is_implicit_cell(m_mesh.connectivity()); + is_implicit_cell.fill(true); + + for (auto explicit_zone : explicit_zone_list) { + auto mesh_cell_zone = getMeshCellZone(m_mesh, *explicit_zone); + const auto& cell_list = mesh_cell_zone.cellList(); + for (size_t i_cell = 0; i_cell < cell_list.size(); ++i_cell) { + const CellId cell_id = cell_list[i_cell]; + is_implicit_cell[cell_id] = false; + } + } + + return is_implicit_cell; + }(); + } + + ImplicitAcousticO2Solver(const SolverType solver_type, + const std::shared_ptr<const MeshType>& p_mesh, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const DiscreteVectorFunction&, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const DiscreteScalarFunction& chi_explicit, + const double&) + : m_solver_type{solver_type}, + m_boundary_condition_list{this->_getBCList(p_mesh, bc_descriptor_list)}, + m_mesh{*p_mesh} + { + m_is_implicit_cell = [&] { + CellValue<bool> is_implicit_cell(m_mesh.connectivity()); + + parallel_for( + m_mesh.numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { is_implicit_cell[cell_id] = (chi_explicit[cell_id] == 0); }); + + return is_implicit_cell; + }(); + } + + public: + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply(const double& dt, + const std::shared_ptr<const MeshType>& mesh, + const DiscreteScalarFunction& rho, + const DiscreteVectorFunction& u, + const DiscreteScalarFunction& E, + + const DiscreteScalarFunction& c, + const DiscreteScalarFunction& p, + const DiscreteScalarFunction& pi, + const DiscreteScalarFunction& gamma, + const DiscreteScalarFunction& Cv, + const DiscreteScalarFunction& entropy) + { + static Timer implicit_t; + implicit_t.start(); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + const NodeValuePerCell<const Rd> Cjr_n = mesh_data.Cjr(); + + m_Djr = copy(Cjr_n); + + CellValue<double> new_rho = copy(rho.cellValues()); + CellValue<Rd> new_u = copy(u.cellValues()); + CellValue<double> new_E = copy(E.cellValues()); + std::shared_ptr<const MeshType> new_mesh; + + m_Mj = [&]() { + const CellValue<const double>& Vj = mesh_data.Vj(); + CellValue<double> computed_Mj(m_mesh.connectivity()); + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { computed_Mj[j] = rho[j] * Vj[j]; }); + return computed_Mj; + }(); + + m_u = u.cellValues(); + + m_predicted_u = m_u; + m_predicted_p = p.cellValues(); + + m_tau = [&]() { + CellValue<double> computed_tau(m_mesh.connectivity()); + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { computed_tau[j] = 1 / rho[j]; }); + return computed_tau; + }(); + + m_Ajr = this->_computeAjr(rho, c); + + NodeValue<const Rdxd> Ar = this->_computeAr(m_Ajr); + + m_inv_Ar = this->_computeInvAr(Ar); + + m_inv_gamma = [&]() { + CellValue<double> computed_inv_gamma(m_mesh.connectivity()); + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { computed_inv_gamma[j] = 1 / gamma[j]; }); + return computed_inv_gamma; + }(); + + m_g_1_exp_S_Cv_inv_g = [&]() { + CellValue<double> computed_g_1_exp_S_Cv_inv_g(m_mesh.connectivity()); + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + if (m_is_implicit_cell[j]) { + computed_g_1_exp_S_Cv_inv_g[j] = std::pow((gamma[j] - 1) * exp(entropy[j] / Cv[j]), m_inv_gamma[j]); + } + }); + return computed_g_1_exp_S_Cv_inv_g; + }(); + + double max_tau_error; + int number_iter = 0; + do { + number_iter++; + + NodeValue<const Rd> ur_n = this->_getUr(); + NodeValuePerCell<const Rd> Fjr_n = this->_getFjr(ur_n); + + this->_computeGradJU_AU(u, p, pi, dt); + + NodeValue<const Rd> ur = this->_getUr(); + NodeValuePerCell<const Rd> Fjr = this->_getFjr(ur); + // time n+1 + + const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); + + NodeValue<Rd> new_xr = copy(mesh->xr()); + parallel_for( + mesh->numberOfNodes(), PUGS_LAMBDA(NodeId r) { new_xr[r] += 0.5 * dt * (ur[r] + ur_n[r]); }); + + new_mesh = std::make_shared<MeshType>(mesh->shared_connectivity(), new_xr); + + CellValue<const double> Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + new_rho = copy(rho.cellValues()); + new_u = copy(u.cellValues()); + new_E = copy(E.cellValues()); + + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + Rd momentum_fluxes = zero; + double energy_fluxes = 0; + for (size_t R = 0; R < cell_nodes.size(); ++R) { + const NodeId r = cell_nodes[R]; + momentum_fluxes += Fjr(j, R) + Fjr_n(j, R); + energy_fluxes += dot(Fjr(j, R), ur[r]) + dot(Fjr_n(j, R), ur_n[r]); + } + const double dt_over_Mj = dt / m_Mj[j]; + new_u[j] -= 0.5 * dt_over_Mj * momentum_fluxes; + new_E[j] -= 0.5 * dt_over_Mj * energy_fluxes; + }); + + // update Djr + const NodeValuePerCell<const Rd> new_Cjr = MeshDataManager::instance().getMeshData(*new_mesh).Cjr(); + + CellValue<double> new_Vj{m_mesh.connectivity()}; + + bool is_positive = true; + do { + is_positive = true; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { + new_Vj[j] = 0; + auto cell_nodes = cell_to_node_matrix[j]; + + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + const NodeId node_id = cell_nodes[i_node]; + new_Vj[j] += dot(new_Cjr[j][i_node], new_xr[node_id]); + } + new_Vj[j] *= 1. / Dimension; + }); + + double m = min(new_Vj); + if (m < 0) { + throw NormalError("Negative volume"); + parallel_for( + mesh->numberOfNodes(), + PUGS_LAMBDA(const NodeId node_id) { new_xr[node_id] = 0.5 * (new_xr[node_id] + mesh->xr()[node_id]); }); + is_positive = false; + } + } while (not is_positive); + + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { new_rho[j] *= Vj[j] / new_Vj[j]; }); + + CellValue<double> new_tau(m_mesh.connectivity()); + + for (CellId j = 0; j < mesh->numberOfCells(); ++j) { + double new_tau_fluxes = 0; + const auto& cell_nodes = cell_to_node_matrix[j]; + for (size_t R = 0; R < cell_nodes.size(); ++R) { + const NodeId r = cell_nodes[R]; + new_tau_fluxes += dot(m_Djr(j, R), ur[r] + ur_n[r]); + } + new_tau[j] = m_tau[j] + 0.5 * (dt / m_Mj[j]) * new_tau_fluxes; + // std::cout << "new_tau(" << j << ")=" << new_tau[j] << '\n'; + } + + // double sum_tau_rho = 0; + max_tau_error = 0; + + for (CellId j = 0; j < mesh->numberOfCells(); ++j) { + // const auto& cell_nodes = cell_to_node_matrix[j]; + // V_j^n+1/tau_j^n+1 - M_j + // sum_tau_rho += std::abs(new_tau[j] - 1. / new_rho[j]); + if (m_is_implicit_cell[j]) { + max_tau_error = std::max(max_tau_error, std::abs(1 - new_tau[j] * new_rho[j])); + } + } + // std::cout << "sum_tau_rho =" << sum_tau_rho << '\n'; + // std::cout << "max_tau_error=" << max_tau_error << '\n'; + + if constexpr (Dimension == 2) { + NodeValuePerCell<Rd> Djr{m_mesh.connectivity()}; + + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const auto& cell_nodes = cell_to_node_matrix[cell_id]; + if (m_is_implicit_cell[cell_id]) { + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + Djr(cell_id, i_node) = 0.5 * (Cjr_n(cell_id, i_node) + new_Cjr(cell_id, i_node)); + } + } else { + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + Djr(cell_id, i_node) = Cjr_n(cell_id, i_node); + } + } + }); + + m_Djr = Djr; + } else if constexpr (Dimension == 3) { + NodeValuePerFace<Rd> Nlr_delta(m_mesh.connectivity()); + const auto& face_to_node_matrix = m_mesh.connectivity().faceToNodeMatrix(); + + const auto& xr_n = m_mesh.xr(); + const auto& xr_np1 = new_xr; + + parallel_for( + m_mesh.numberOfFaces(), PUGS_LAMBDA(FaceId l) { + const auto& face_nodes = face_to_node_matrix[l]; + const size_t nb_nodes = face_nodes.size(); + std::vector<Rd> dxr_n(nb_nodes); + std::vector<Rd> dxr_np1(nb_nodes); + for (size_t r = 0; r < nb_nodes; ++r) { + dxr_n[r] = xr_n[face_nodes[(r + 1) % nb_nodes]] - xr_n[face_nodes[(r + nb_nodes - 1) % nb_nodes]]; + dxr_np1[r] = xr_np1[face_nodes[(r + 1) % nb_nodes]] - xr_np1[face_nodes[(r + nb_nodes - 1) % nb_nodes]]; + } + const double inv_12_nb_nodes = 1. / (12. * nb_nodes); + for (size_t r = 0; r < nb_nodes; ++r) { + Rd Nr = zero; + for (size_t s = 0; s < nb_nodes; ++s) { + Nr -= crossProduct((1. / 6) * (2 * (dxr_np1[r] - dxr_n[r]) - (dxr_np1[s] - dxr_n[s])), + xr_np1[face_nodes[s]] - xr_n[face_nodes[s]]); + } + Nr *= inv_12_nb_nodes; + Nlr_delta(l, r) = Nr; + } + }); + + const auto& cell_to_face_matrix = m_mesh.connectivity().cellToFaceMatrix(); + const auto& cell_face_is_reversed = m_mesh.connectivity().cellFaceIsReversed(); + + NodeValuePerCell<Rd> Djr{m_mesh.connectivity()}; + Djr.fill(zero); + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + const auto& cell_faces = cell_to_face_matrix[j]; + const auto& face_is_reversed = cell_face_is_reversed.itemArray(j); + + for (size_t L = 0; L < cell_faces.size(); ++L) { + const FaceId& l = cell_faces[L]; + const auto& face_nodes = face_to_node_matrix[l]; + + auto local_node_number_in_cell = [&](NodeId node_number) { + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + if (node_number == cell_nodes[i_node]) { + return i_node; + } + } + return std::numeric_limits<size_t>::max(); + }; + + if (face_is_reversed[L]) { + for (size_t rl = 0; rl < face_nodes.size(); ++rl) { + const size_t R = local_node_number_in_cell(face_nodes[rl]); + Djr(j, R) -= Nlr_delta(l, rl); + } + } else { + for (size_t rl = 0; rl < face_nodes.size(); ++rl) { + const size_t R = local_node_number_in_cell(face_nodes[rl]); + Djr(j, R) += Nlr_delta(l, rl); + } + } + } + }); + + parallel_for( + Djr.numberOfValues(), PUGS_LAMBDA(const size_t i) { Djr[i] += 0.5 * (Cjr_n[i] + new_Cjr[i]); }); + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + if (not m_is_implicit_cell[cell_id]) { + const auto& cell_nodes = cell_to_node_matrix[cell_id]; + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + Djr(cell_id, i_node) = Cjr_n(cell_id, i_node); + } + } + }); + + m_Djr = Djr; + + double max_err = 0; + + for (CellId cell_id = 0; cell_id < m_mesh.numberOfCells(); ++cell_id) { + if (m_is_implicit_cell[cell_id]) { + double delta_V = new_Vj[cell_id] - Vj[cell_id]; + const auto& node_list = cell_to_node_matrix[cell_id]; + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; + delta_V -= dt * dot(ur[node_id], Djr[cell_id][i_node]); + } + max_err = std::max(max_err, std::abs(delta_V)); + } + } + + std::cout << rang::fgB::yellow << "Max volume err = " << max_err << rang::fg::reset << '\n'; + } + + if constexpr (Dimension > 1) { + std::cout << rang::fgB::magenta << "number_iter_Djr=" << number_iter << " max_tau_error=" << max_tau_error + << rang::fg::reset << '\n'; + } + + // std::cout << "new rho=" << new_rho << '\n'; + } while ((Dimension > 1) and (max_tau_error > 1e-4) and (number_iter < 100)); + // std::cout << "prochaine boucle" << '\n'; + implicit_t.pause(); + // std::cout << "getA=" << get_A_t << "(" << count_getA << ")" + // << " getF=" << getF_t << "(" << count_getF << ")" + // << " getGradF=" << getGradF_t << "(" << count_getGradF << ")" + // << " HJ_A=" << HJ_A_t << '\n'; + // std::cout << "solver=" << solver_t << " total= " << implicit_t << '\n'; + + return {std::make_shared<MeshVariant>(new_mesh), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction{new_mesh, new_rho}), + std::make_shared<DiscreteFunctionVariant>(DiscreteVectorFunction{new_mesh, new_u}), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction{new_mesh, new_E})}; + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply(const double& dt, + 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>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy) + { + std::shared_ptr mesh_v = getCommonMesh({rho, u, E}); + if (not mesh_v) { + throw NormalError("discrete functions are not defined on the same mesh"); + } + + if (not checkDiscretizationType({rho, u, E}, DiscreteFunctionType::P0)) { + throw NormalError("acoustic solver expects P0 functions"); + } + + return this->apply(dt, mesh_v->get<MeshType>(), rho->get<DiscreteScalarFunction>(), + u->get<DiscreteVectorFunction>(), E->get<DiscreteScalarFunction>(), + c->get<DiscreteScalarFunction>(), p->get<DiscreteScalarFunction>(), + pi->get<DiscreteScalarFunction>(), gamma->get<DiscreteScalarFunction>(), + Cv->get<DiscreteScalarFunction>(), entropy->get<DiscreteScalarFunction>()); + } + + ImplicitAcousticO2Solver(const SolverType solver_type, + const std::shared_ptr<const MeshVariant>& mesh_v, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::vector<std::shared_ptr<const IZoneDescriptor>>& explicit_zone_list, + const double& dt) + : ImplicitAcousticO2Solver(solver_type, + mesh_v->get<MeshType>(), + rho->get<DiscreteScalarFunction>(), + c->get<DiscreteScalarFunction>(), + u->get<DiscreteFunctionP0<const Rd>>(), + p->get<DiscreteScalarFunction>(), + pi->get<DiscreteScalarFunction>(), + gamma->get<DiscreteScalarFunction>(), + Cv->get<DiscreteScalarFunction>(), + entropy->get<DiscreteScalarFunction>(), + bc_descriptor_list, + explicit_zone_list, + dt) + {} + + ImplicitAcousticO2Solver(const SolverType solver_type, + const std::shared_ptr<const MeshVariant>& mesh_v, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::shared_ptr<const DiscreteFunctionVariant>& chi_explicit, + const double& dt) + : ImplicitAcousticO2Solver(solver_type, + mesh_v->get<MeshType>(), + rho->get<DiscreteScalarFunction>(), + c->get<DiscreteScalarFunction>(), + u->get<DiscreteFunctionP0<const Rd>>(), + p->get<DiscreteScalarFunction>(), + pi->get<DiscreteScalarFunction>(), + gamma->get<DiscreteScalarFunction>(), + Cv->get<DiscreteScalarFunction>(), + entropy->get<DiscreteScalarFunction>(), + bc_descriptor_list, + chi_explicit->get<DiscreteScalarFunction>(), + dt) + {} + + ImplicitAcousticO2Solver() = default; + ImplicitAcousticO2Solver(ImplicitAcousticO2Solver&&) = default; + ~ImplicitAcousticO2Solver() = default; +}; + +template <MeshConcept MeshType> +class ImplicitAcousticO2SolverHandlerRhombus::ImplicitAcousticO2Solver<MeshType>::PressureBoundaryCondition +{ + 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; + } + + PressureBoundaryCondition(const Array<const FaceId>& face_list, const Array<const double>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + {} + + ~PressureBoundaryCondition() = default; +}; + +template <> +class ImplicitAcousticO2SolverHandlerRhombus::ImplicitAcousticO2Solver<Mesh<1>>::PressureBoundaryCondition +{ + private: + const Array<const double> m_value_list; + const Array<const NodeId> m_face_list; + + public: + const Array<const NodeId>& + faceList() const + { + return m_face_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + PressureBoundaryCondition(const Array<const NodeId>& face_list, const Array<const double>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + {} + + ~PressureBoundaryCondition() = default; +}; + +template <MeshConcept MeshType> +class ImplicitAcousticO2SolverHandlerRhombus::ImplicitAcousticO2Solver<MeshType>::VelocityBoundaryCondition +{ + private: + const Array<const TinyVector<Dimension>> m_value_list; + const Array<const NodeId> m_node_list; + + public: + const Array<const NodeId>& + nodeList() const + { + return m_node_list; + } + + const Array<const TinyVector<Dimension>>& + valueList() const + { + return m_value_list; + } + + VelocityBoundaryCondition(const Array<const NodeId>& node_list, const Array<const TinyVector<Dimension>>& value_list) + : m_value_list{value_list}, m_node_list{node_list} + {} + + ~VelocityBoundaryCondition() = default; +}; + +template <MeshConcept MeshType> +class ImplicitAcousticO2SolverHandlerRhombus::ImplicitAcousticO2Solver<MeshType>::SymmetryBoundaryCondition +{ + public: + using Rd = TinyVector<Dimension, double>; + + private: + const MeshFlatNodeBoundary<MeshType> m_mesh_flat_node_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(); + } + + SymmetryBoundaryCondition(const MeshFlatNodeBoundary<MeshType>& mesh_flat_node_boundary) + : m_mesh_flat_node_boundary(mesh_flat_node_boundary) + { + ; + } + + ~SymmetryBoundaryCondition() = default; +}; + +template <MeshConcept MeshType> +class ImplicitAcousticO2SolverHandlerRhombus::ImplicitAcousticO2Solver<MeshType>::AxisBoundaryCondition +{ + public: + using Rd = TinyVector<Dimension, double>; + + private: + const MeshLineNodeBoundary<MeshType> m_mesh_line_node_boundary; + + public: + const Rd& + direction() const + { + return m_mesh_line_node_boundary.direction(); + } + + size_t + numberOfNodes() const + { + return m_mesh_line_node_boundary.nodeList().size(); + } + + const Array<const NodeId>& + nodeList() const + { + return m_mesh_line_node_boundary.nodeList(); + } + + AxisBoundaryCondition(const MeshLineNodeBoundary<MeshType>& mesh_line_node_boundary) + : m_mesh_line_node_boundary(mesh_line_node_boundary) + { + ; + } + + ~AxisBoundaryCondition() = default; +}; + +template <> +class ImplicitAcousticO2SolverHandlerRhombus::ImplicitAcousticO2Solver<Mesh<1>>::AxisBoundaryCondition +{ + public: + AxisBoundaryCondition() = default; + ~AxisBoundaryCondition() = default; +}; + +ImplicitAcousticO2SolverHandlerRhombus::ImplicitAcousticO2SolverHandlerRhombus( + const SolverType solver_type, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::vector<std::shared_ptr<const IZoneDescriptor>>& explicit_zone_list, + const double& dt) +{ + std::shared_ptr mesh_v = getCommonMesh({rho, c, u, p, pi, gamma, Cv}); + if (not mesh_v) { + throw NormalError("discrete functions are not defined on the same mesh"); + } + + if (not checkDiscretizationType({rho, c, u, p}, DiscreteFunctionType::P0)) { + throw NormalError("acoustic solver expects P0 functions"); + } + + std::visit( + [&](auto&& mesh) { + using MeshType = mesh_type_t<decltype(mesh)>; + if constexpr (is_polygonal_mesh_v<MeshType>) { + m_implicit_acoustic_solver = + std::make_unique<ImplicitAcousticO2Solver<MeshType>>(solver_type, mesh_v, rho, c, u, p, pi, gamma, Cv, + entropy, bc_descriptor_list, explicit_zone_list, dt); + } else { + throw NormalError("unexpected mesh type"); + } + }, + mesh_v->variant()); +} + +ImplicitAcousticO2SolverHandlerRhombus::ImplicitAcousticO2SolverHandlerRhombus( + const SolverType solver_type, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::shared_ptr<const DiscreteFunctionVariant>& chi_explicit, + const double& dt) +{ + std::shared_ptr mesh_v = getCommonMesh({rho, c, u, p, pi, gamma, Cv, chi_explicit}); + if (not mesh_v) { + throw NormalError("discrete functions are not defined on the same mesh"); + } + + if (not checkDiscretizationType({rho, c, u, p}, DiscreteFunctionType::P0)) { + throw NormalError("acoustic solver expects P0 functions"); + } + + std::visit( + [&](auto&& mesh) { + using MeshType = mesh_type_t<decltype(mesh)>; + if constexpr (is_polygonal_mesh_v<MeshType>) { + m_implicit_acoustic_solver = + std::make_unique<ImplicitAcousticO2Solver<MeshType>>(solver_type, mesh_v, rho, c, u, p, pi, gamma, Cv, + entropy, bc_descriptor_list, chi_explicit, dt); + } else { + throw NormalError("unexpected mesh type"); + } + }, + mesh_v->variant()); +} diff --git a/src/scheme/ImplicitAcousticO2SolverRhombus.hpp b/src/scheme/ImplicitAcousticO2SolverRhombus.hpp new file mode 100644 index 0000000000000000000000000000000000000000..528db323459ec2cc6943e6ba24d102dbaa2194b4 --- /dev/null +++ b/src/scheme/ImplicitAcousticO2SolverRhombus.hpp @@ -0,0 +1,93 @@ +#ifndef IMPLICIT_ACOUSTIC_O2_SOLVER_RHOMBUS_HPP +#define IMPLICIT_ACOUSTIC_O2_SOLVER_RHOMBUS_HPP + +#include <mesh/MeshTraits.hpp> + +#include <memory> +#include <tuple> +#include <vector> + +class DiscreteFunctionVariant; +class MeshVariant; +class ItemValueVariant; +class SubItemValuePerItemVariant; +class IBoundaryConditionDescriptor; +class IZoneDescriptor; + +class ImplicitAcousticO2SolverHandlerRhombus +{ + public: + enum class SolverType + { + Glace1State, + Glace2States, + Eucclhyd + }; + + private: + struct IImplicitAcousticO2Solver + { + virtual std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>> + apply(const double& dt, + 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>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy) = 0; + + IImplicitAcousticO2Solver() = default; + IImplicitAcousticO2Solver(IImplicitAcousticO2Solver&&) = default; + IImplicitAcousticO2Solver& operator=(IImplicitAcousticO2Solver&&) = default; + + virtual ~IImplicitAcousticO2Solver() = default; + }; + + template <MeshConcept MeshType> + class ImplicitAcousticO2Solver; + + std::unique_ptr<IImplicitAcousticO2Solver> m_implicit_acoustic_solver; + + public: + IImplicitAcousticO2Solver& + solver() + { + return *m_implicit_acoustic_solver; + } + + ImplicitAcousticO2SolverHandlerRhombus( + SolverType solver_type, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::vector<std::shared_ptr<const IZoneDescriptor>>& explicit_zone_list, + const double& dt); + + ImplicitAcousticO2SolverHandlerRhombus( + const SolverType solver_type, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::shared_ptr<const DiscreteFunctionVariant>& chi_explicit, + const double& dt); +}; + +#endif // IMPLICIT_ACOUSTIC_O2_SOLVER_HPP diff --git a/src/scheme/ImplicitAcousticO2SolverRhombusModified.cpp b/src/scheme/ImplicitAcousticO2SolverRhombusModified.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a3df8d3f22680d0da7420b3af513a7e3d8b8c7df --- /dev/null +++ b/src/scheme/ImplicitAcousticO2SolverRhombusModified.cpp @@ -0,0 +1,2284 @@ +#include <algebra/CRSMatrix.hpp> +#include <algebra/CRSMatrixDescriptor.hpp> +#include <algebra/LinearSolver.hpp> +#include <language/utils/InterpolateItemValue.hpp> +#include <mesh/IZoneDescriptor.hpp> +#include <mesh/MeshCellZone.hpp> +#include <mesh/MeshFaceBoundary.hpp> +#include <mesh/MeshFlatNodeBoundary.hpp> +#include <mesh/MeshLineNodeBoundary.hpp> +#include <mesh/MeshNodeBoundary.hpp> +#include <scheme/AxisBoundaryConditionDescriptor.hpp> +#include <scheme/DirichletBoundaryConditionDescriptor.hpp> +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/DiscreteFunctionUtils.hpp> +#include <scheme/IBoundaryConditionDescriptor.hpp> +#include <scheme/IDiscreteFunctionDescriptor.hpp> +#include <scheme/ImplicitAcousticO2SolverRhombusModified.hpp> +#include <scheme/SymmetryBoundaryConditionDescriptor.hpp> + +#include <mesh/ItemValueVariant.hpp> +#include <output/NamedItemValueVariant.hpp> +#include <output/VTKWriter.hpp> +#include <utils/Timer.hpp> + +#include <variant> +#include <vector> + +// VTKWriter vtk_writer("imp/debug", 0); + +template <MeshConcept MeshType> +class ImplicitAcousticO2SolverHandlerRhombusModified::ImplicitAcousticO2Solver final + : public ImplicitAcousticO2SolverHandlerRhombusModified::IImplicitAcousticO2Solver +{ + private: + constexpr static size_t Dimension = MeshType::Dimension; + using Rdxd = TinyMatrix<Dimension>; + using Rd = TinyVector<Dimension>; + + using MeshDataType = MeshData<MeshType>; + + using DiscreteScalarFunction = DiscreteFunctionP0<const double>; + using DiscreteVectorFunction = DiscreteFunctionP0<const Rd>; + + class AxisBoundaryCondition; + class PressureBoundaryCondition; + class SymmetryBoundaryCondition; + class VelocityBoundaryCondition; + + using BoundaryCondition = std:: + variant<AxisBoundaryCondition, PressureBoundaryCondition, SymmetryBoundaryCondition, VelocityBoundaryCondition>; + + using BoundaryConditionList = std::vector<BoundaryCondition>; + + const SolverType m_solver_type; + BoundaryConditionList m_boundary_condition_list; + const MeshType& m_mesh; + + CellValue<const double> m_inv_gamma; + CellValue<const double> m_g_1_exp_S_Cv_inv_g; + CellValue<const double> m_tau_iter; + + CellValue<const Rd> m_u; + CellValue<const double> m_tau; + CellValue<const double> m_Mj; + + NodeValuePerCell<const Rdxd> m_Ajr; + NodeValue<const Rdxd> m_inv_Ar; + + NodeValuePerCell<const Rd> m_Djr; + + CellValue<const Rd> m_predicted_u; + CellValue<const double> m_predicted_p; + + CellValue<const bool> m_is_implicit_cell; + CellValue<const int> m_implicit_cell_index; + size_t m_number_of_implicit_cells; + + NodeValue<const bool> m_is_implicit_node; + NodeValue<const int> m_implicit_node_index; + size_t m_number_of_implicit_nodes; + + NodeValue<const double> + _getRhoCr(const DiscreteScalarFunction& rho, const DiscreteScalarFunction& c) const + { + Assert(rho.meshVariant()->id() == c.meshVariant()->id()); + NodeValue<double> rhocr{m_mesh.connectivity()}; + + const auto& node_to_cell_matrix = m_mesh.connectivity().nodeToCellMatrix(); + + parallel_for( + m_mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { + const auto& node_cells = node_to_cell_matrix[r]; + const size_t nb_cells = node_cells.size(); + double rhoc = 0; + + for (size_t J = 0; J < nb_cells; ++J) { + CellId j = node_cells[J]; + rhoc += rho[j] * c[j]; + } + rhocr[r] = rhoc * (1. / nb_cells); + }); + + return rhocr; + } + + NodeValuePerCell<const Rdxd> + _computeAjr(const DiscreteScalarFunction& rho, const DiscreteScalarFunction& c) const + { + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(m_mesh); + + const NodeValuePerCell<const Rd> Cjr_n = mesh_data.Cjr(); + const NodeValuePerCell<const Rd> njr_n = mesh_data.njr(); + + NodeValuePerCell<Rdxd> Ajr{m_mesh.connectivity()}; + const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); + + switch (m_solver_type) { + case SolverType::Glace1State: { + NodeValue<const double> rhocr = _getRhoCr(rho, c); + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const size_t& nb_nodes = Ajr.numberOfSubValues(j); + for (size_t r = 0; r < nb_nodes; ++r) { + const NodeId node_id = cell_to_node_matrix[j][r]; + Ajr(j, r) = tensorProduct(rhocr[node_id] * Cjr_n(j, r), njr_n(j, r)); + } + }); + break; + } + case SolverType::Glace2States: { + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const size_t& nb_nodes = Ajr.numberOfSubValues(j); + for (size_t r = 0; r < nb_nodes; ++r) { + Ajr(j, r) = tensorProduct(rho[j] * c[j] * Cjr_n(j, r), njr_n(j, r)); + } + }); + break; + } + case SolverType::Eucclhyd: { + if constexpr (Dimension > 1) { + const NodeValuePerFace<const Rd> Nlr = mesh_data.Nlr(); + const NodeValuePerFace<const Rd> nlr = mesh_data.nlr(); + const auto& face_to_node_matrix = m_mesh.connectivity().faceToNodeMatrix(); + const auto& cell_to_face_matrix = m_mesh.connectivity().cellToFaceMatrix(); + parallel_for( + Ajr.numberOfValues(), PUGS_LAMBDA(size_t jr) { Ajr[jr] = zero; }); + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + const auto& cell_faces = cell_to_face_matrix[j]; + + const double& rho_c = rho[j] * c[j]; + + for (size_t L = 0; L < cell_faces.size(); ++L) { + const FaceId& l = cell_faces[L]; + const auto& face_nodes = face_to_node_matrix[l]; + + auto local_node_number_in_cell = [&](NodeId node_number) { + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + if (node_number == cell_nodes[i_node]) { + return i_node; + } + } + return std::numeric_limits<size_t>::max(); + }; + + for (size_t rl = 0; rl < face_nodes.size(); ++rl) { + const size_t R = local_node_number_in_cell(face_nodes[rl]); + Ajr(j, R) += tensorProduct(rho_c * Nlr(l, rl), nlr(l, rl)); + } + } + }); + + break; + } else { + throw NotImplementedError("Eucclhyd switch is not implemented yet in 1d"); + } + } + } + + return Ajr; + } + + NodeValue<const Rdxd> + _computeAr(const NodeValuePerCell<const Rdxd>& Ajr) const + { + const auto& node_to_cell_matrix = m_mesh.connectivity().nodeToCellMatrix(); + const auto& node_local_numbers_in_their_cells = m_mesh.connectivity().nodeLocalNumbersInTheirCells(); + + NodeValue<Rdxd> Ar{m_mesh.connectivity()}; + + parallel_for( + m_mesh.numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { + Rdxd sum = zero; + const auto& node_to_cell = node_to_cell_matrix[node_id]; + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + + for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) { + const unsigned int i_node = node_local_number_in_its_cells[i_cell]; + const CellId cell_id = node_to_cell[i_cell]; + sum += Ajr(cell_id, i_node); + } + Ar[node_id] = sum; + }); + + NodeValue<bool> has_boundary_condition{m_mesh.connectivity()}; + has_boundary_condition.fill(false); + + // velocity bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<VelocityBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + + parallel_for( + node_list.size(), PUGS_LAMBDA(const size_t i_node) { + const NodeId node_id = node_list[i_node]; + + if (not has_boundary_condition[node_id]) { + Ar[node_id] = identity; + + has_boundary_condition[node_id] = true; + } + }); + } + }, + boundary_condition); + } + + if constexpr (Dimension > 1) { + // axis bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<AxisBoundaryCondition, T>) { + const Rd& t = bc.direction(); + + const Rdxd I = identity; + const Rdxd txt = tensorProduct(t, t); + + const Array<const NodeId>& node_list = bc.nodeList(); + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId& node_id = node_list[i_node]; + if (not has_boundary_condition[node_id]) { + Ar[node_id] = txt * Ar[node_id] * txt + (I - txt); + + has_boundary_condition[node_id] = true; + } + } + } + }, + boundary_condition); + } + } + + // symmetry bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<SymmetryBoundaryCondition, T>) { + const Rd& n = bc.outgoingNormal(); + + const Rdxd I = identity; + const Rdxd nxn = tensorProduct(n, n); + const Rdxd P = I - nxn; + + const Array<const NodeId>& node_list = bc.nodeList(); + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId& node_id = node_list[i_node]; + if (not has_boundary_condition[node_id]) { + Ar[node_id] = P * Ar[node_id] * P + nxn; + + has_boundary_condition[node_id] = true; + } + } + } + }, + boundary_condition); + } + + return Ar; + } + + NodeValue<const Rdxd> + _computeInvAr(const NodeValue<const Rdxd>& Ar) const + { + NodeValue<Rdxd> inv_Ar{m_mesh.connectivity()}; + + for (NodeId node_id = 0; node_id < m_mesh.numberOfNodes(); ++node_id) { + inv_Ar[node_id] = inverse(Ar[node_id]); + } + + return inv_Ar; + } + + int + mapP(CellId j) const + { + // return (1 + Dimension) * m_implicit_cell_index[j]; + return m_implicit_cell_index[j]; + } + + int + mapU(size_t k, CellId j) const + { + // return (1 + Dimension) * m_implicit_cell_index[j] + k + 1; + return m_number_of_implicit_cells + k + m_implicit_cell_index[j] * Dimension; + } + + CRSMatrixDescriptor<double> + _getA() const + { + Array<int> non_zeros{(Dimension + 1) * m_number_of_implicit_cells}; + + const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); + const auto& node_to_cell_matrix = m_mesh.connectivity().nodeToCellMatrix(); + + auto is_boundary_node = m_mesh.connectivity().isBoundaryNode(); + + non_zeros.fill(0); + for (CellId cell_j_id = 0; cell_j_id < m_mesh.numberOfCells(); ++cell_j_id) { + if (m_is_implicit_cell[cell_j_id]) { + const auto& cell_j_nodes = cell_to_node_matrix[cell_j_id]; + std::vector<size_t> nb_neighbors; + nb_neighbors.reserve(30); + + for (size_t id_node = 0; id_node < cell_j_nodes.size(); ++id_node) { + if (not is_boundary_node[cell_j_nodes[id_node]]) { + NodeId node_id = cell_j_nodes[id_node]; + const auto& node_cell = node_to_cell_matrix[node_id]; + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + nb_neighbors.push_back(m_implicit_cell_index[id_cell_i]); + } + } + } + } + std::sort(nb_neighbors.begin(), nb_neighbors.end()); + auto last = std::unique(nb_neighbors.begin(), nb_neighbors.end()); + nb_neighbors.resize(std::distance(nb_neighbors.begin(), last)); + + size_t line_index_p = mapP(cell_j_id); + non_zeros[line_index_p] = Dimension * nb_neighbors.size() + 1; + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + size_t line_index_u = mapU(i_dimension, cell_j_id); + non_zeros[line_index_u] = nb_neighbors.size() + 1; + } + + nb_neighbors.clear(); + } + } + + CRSMatrixDescriptor A{(Dimension + 1) * m_number_of_implicit_cells, non_zeros}; + const auto& node_local_numbers_in_their_cells = m_mesh.connectivity().nodeLocalNumbersInTheirCells(); + + for (NodeId node_id = 0; node_id < m_mesh.numberOfNodes(); ++node_id) { + if (not is_boundary_node[node_id]) { + const auto& node_cell = node_to_cell_matrix[node_id]; + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + const CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + const CellId id_cell_j = node_cell[cell_j]; + if (m_is_implicit_cell[id_cell_j]) { + const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + const int j_index_p = mapP(id_cell_j); + + const Rd Bji = m_Ajr(id_cell_i, node_nb_in_i) * m_inv_Ar[node_id] * m_Djr(id_cell_j, node_nb_in_j); + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + const int i_index_u = mapU(i_dimension, id_cell_i); + + A(j_index_p, i_index_u) += Bji[i_dimension]; + A(i_index_u, j_index_p) -= Bji[i_dimension]; + } + } + } + } + } + } + } + + // pressure bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<PressureBoundaryCondition, T>) { + // const auto& face_list = bc.faceList(); + + // const auto& face_local_numbers_in_their_cells = m_mesh.connectivity().faceLocalNumbersInTheirCells(); + + // const auto& face_to_cell_matrix = m_mesh.connectivity().faceToCellMatrix(); + + // for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + // for (size_t i_face = 0; i_face < face_list.size(); ++i_face) { + // const FaceId& face_id = face_list[i_face]; + // // if (m_is_implicit_node[node_id]) { + // const auto& face_cell = face_to_cell_matrix[face_id]; + // for (size_t cell_i = 0; cell_i < face_cell.size(); ++cell_i) { + // const CellId id_cell_i = face_cell[cell_i]; + // if (m_is_implicit_cell[id_cell_i]) { + // // const auto& face_local_number_in_its_cells = + // // face_local_numbers_in_their_cells.itemArray(face_id); const size_t node_nb_in_i = + // // face_local_number_in_its_cells[cell_i]; + // int index_p = mapP(id_cell_i); + // int index_u = mapU(i_dimension, id_cell_i); + + // // const Rd coef = m_Ajr(id_cell_i, face_local_number_in_its_cells[node_nb_in_i]) * + // // m_inv_Ar[face_id] * + // // m_Djr(id_cell_i, face_local_number_in_its_cells[node_nb_in_i]); + + // const Rd coef = zero; + // A(index_p, index_u) += coef[i_dimension]; + // A(index_u, index_p) -= coef[i_dimension]; + // //} + // } + // } + // } + // } + } + }, + boundary_condition); + } + + NodeValue<bool> has_boundary_condition{m_mesh.connectivity()}; + has_boundary_condition.fill(false); + + // velocity bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<VelocityBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + + parallel_for( + node_list.size(), PUGS_LAMBDA(const size_t i_node) { + const NodeId node_id = node_list[i_node]; + if (not has_boundary_condition[node_id]) { + has_boundary_condition[node_id] = true; + } + }); + } + }, + boundary_condition); + } + + if constexpr (Dimension > 1) { + // axis bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<AxisBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + + const Rd& t = bc.direction(); + const Rdxd txt = tensorProduct(t, t); + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId& node_id = node_list[i_node]; + if (not has_boundary_condition[node_id]) { + const Rdxd inverse_Ar_times_txt = m_inv_Ar[node_id] * txt; + const auto& node_cell = node_to_cell_matrix[node_id]; + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + const CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + const int i_index_u = mapU(i_dimension, id_cell_i); + for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + const CellId id_cell_j = node_cell[cell_j]; + if (m_is_implicit_cell[id_cell_j]) { + const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + const int j_index_p = mapP(id_cell_j); + + const Rd coef = + m_Ajr(id_cell_i, node_nb_in_i) * inverse_Ar_times_txt * m_Djr(id_cell_j, node_nb_in_j); + A(j_index_p, i_index_u) += coef[i_dimension]; + A(i_index_u, j_index_p) -= coef[i_dimension]; + } + } + } + } + } + has_boundary_condition[node_id] = true; + } + } + } + }, + boundary_condition); + } + } + + // symmetry bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<SymmetryBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + + const Rd& n = bc.outgoingNormal(); + const Rdxd I = identity; + const Rdxd nxn = tensorProduct(n, n); + const Rdxd P = I - nxn; + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId& node_id = node_list[i_node]; + if (not has_boundary_condition[node_id]) { + const Rdxd inverse_ArxP = m_inv_Ar[node_id] * P; + const auto& node_cell = node_to_cell_matrix[node_id]; + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + const CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + const int i_index_u = mapU(i_dimension, id_cell_i); + for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + const CellId id_cell_j = node_cell[cell_j]; + if (m_is_implicit_cell[id_cell_j]) { + const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + const int j_index_p = mapP(id_cell_j); + + const Rd coef = + m_Ajr(id_cell_i, node_nb_in_i) * inverse_ArxP * m_Djr(id_cell_j, node_nb_in_j); + A(j_index_p, i_index_u) += coef[i_dimension]; + A(i_index_u, j_index_p) -= coef[i_dimension]; + } + } + } + } + } + has_boundary_condition[node_id] = true; + } + } + } + }, + boundary_condition); + } + + return A; + } + + Vector<double> + _getU(const CellValue<const double>& p, const CellValue<const Rd>& u) + { + Vector<double> Un{(Dimension + 1) * m_number_of_implicit_cells}; + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + if (m_is_implicit_cell[j]) { + size_t i = mapP(j); + Un[i] = -p[j]; + } + }); + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + if (m_is_implicit_cell[j]) { + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + size_t i = mapU(i_dimension, j); + Un[i] = u[j][i_dimension]; + } + } + }); + + return Un; + } + + Vector<double> + _getF(const Vector<double>& Un, const Vector<double>& Uk, const DiscreteScalarFunction& pi, const double dt) + { + const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); + + NodeValue<const Rd> ur = this->_getUr(); + NodeValuePerCell<const Rd> Fjr = this->_getFjr(ur); + + m_tau_iter = [&]() { + CellValue<double> computed_tau_iter(m_mesh.connectivity()); + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + if (m_is_implicit_cell[j]) { + size_t k = mapP(j); + computed_tau_iter[j] = m_g_1_exp_S_Cv_inv_g[j] * std::pow(-Uk[k] + pi[j], -m_inv_gamma[j]); + } + }); + return computed_tau_iter; + }(); + + CellValue<double> volume_fluxes_sum{m_mesh.connectivity()}; + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + double sum = 0; + const auto& cell_nodes = cell_to_node_matrix[cell_id]; + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + const NodeId node_id = cell_nodes[i_node]; + sum += dot(m_Djr(cell_id, i_node), ur[node_id]); + } + volume_fluxes_sum[cell_id] = sum; + }); + + CellValue<Rd> momentum_fluxes_sum{m_mesh.connectivity()}; + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + Rd sum = zero; + const auto& cell_nodes = cell_to_node_matrix[cell_id]; + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + sum += Fjr(cell_id, i_node); + } + momentum_fluxes_sum[cell_id] = sum; + }); + + Vector<double> F{Un.size()}; + F.fill(0); + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + if (m_is_implicit_cell[cell_id]) { + const size_t pj_index = mapP(cell_id); + F[pj_index] = + m_Mj[cell_id] / dt * + (m_g_1_exp_S_Cv_inv_g[cell_id] * std::pow(-Uk[pj_index] + pi[cell_id], -m_inv_gamma[cell_id]) - + m_tau[cell_id]) - + volume_fluxes_sum[cell_id]; + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + const size_t uj_i_index = mapU(i_dimension, cell_id); + + F[uj_i_index] = + m_Mj[cell_id] / dt * (Uk[uj_i_index] - Un[uj_i_index]) + momentum_fluxes_sum[cell_id][i_dimension]; + } + } + }); + + return F; + } + + CRSMatrixDescriptor<double> + _getHessianJ(const double dt, const DiscreteScalarFunction& pi, const Vector<double>& Uk) + { + auto is_boundary_node = m_mesh.connectivity().isBoundaryNode(); + Array<int> non_zeros{(Dimension + 1) * m_number_of_implicit_cells}; + + const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); + const auto& node_to_cell_matrix = m_mesh.connectivity().nodeToCellMatrix(); + + non_zeros.fill(0); + for (CellId cell_id = 0; cell_id < m_number_of_implicit_cells; ++cell_id) { + if (m_is_implicit_cell[cell_id]) { + const auto& cell_node = cell_to_node_matrix[cell_id]; + std::vector<size_t> nb_neighboors; + nb_neighboors.reserve(30); + + for (size_t id_node = 0; id_node < cell_node.size(); ++id_node) { + if (not is_boundary_node[cell_node[id_node]]) { + NodeId node_id = cell_node[id_node]; + const auto& node_cell = node_to_cell_matrix[node_id]; + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + nb_neighboors.push_back(m_implicit_cell_index[id_cell_i]); + } + } + } + } + + std::sort(nb_neighboors.begin(), nb_neighboors.end()); + auto last = std::unique(nb_neighboors.begin(), nb_neighboors.end()); + nb_neighboors.resize(std::distance(nb_neighboors.begin(), last)); + + size_t line_index_p = mapP(cell_id); + non_zeros[line_index_p] = nb_neighboors.size(); + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + size_t line_index_u = mapU(i_dimension, cell_id); + non_zeros[line_index_u] = Dimension * nb_neighboors.size(); + } + + nb_neighboors.clear(); + } + } + + CRSMatrixDescriptor Hess_J{(Dimension + 1) * m_number_of_implicit_cells, + (Dimension + 1) * m_number_of_implicit_cells, non_zeros}; + + const auto& node_local_numbers_in_their_cells = m_mesh.connectivity().nodeLocalNumbersInTheirCells(); + + const double inv_dt = 1 / dt; + for (CellId cell_id = 0; cell_id < m_mesh.numberOfCells(); ++cell_id) { + if (m_is_implicit_cell[cell_id]) { + const int i_index_p = mapP(cell_id); + Hess_J(i_index_p, i_index_p) = + (inv_dt * m_Mj[cell_id]) * m_inv_gamma[cell_id] * m_tau_iter[cell_id] / (-Uk[i_index_p] + pi[cell_id]); + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + const int i_index_u = mapU(i_dimension, cell_id); + Hess_J(i_index_u, i_index_u) = inv_dt * m_Mj[cell_id]; + } + } + } + + for (NodeId node_id = 0; node_id < m_mesh.numberOfNodes(); ++node_id) { + const auto& node_cell = node_to_cell_matrix[node_id]; + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + + for (size_t j_cell = 0; j_cell < node_cell.size(); ++j_cell) { + const CellId cell_id_j = node_cell[j_cell]; + if (m_is_implicit_cell[cell_id_j]) { + const size_t node_nb_in_j_cell = node_local_number_in_its_cells[j_cell]; + + const auto Ajr = m_Ajr(cell_id_j, node_nb_in_j_cell); + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + const int i_index_u = mapU(i_dimension, cell_id_j); + for (size_t j_dimension = 0; j_dimension < Dimension; ++j_dimension) { + const int j_index_u = mapU(j_dimension, cell_id_j); + Hess_J(i_index_u, j_index_u) += Ajr(i_dimension, j_dimension); + } + } + } + } + + if (not is_boundary_node[node_id]) { + const auto& inv_Ar = m_inv_Ar[node_id]; + + for (size_t j_cell = 0; j_cell < node_cell.size(); ++j_cell) { + const CellId cell_id_j = node_cell[j_cell]; + if (m_is_implicit_cell[cell_id_j]) { + const size_t node_nb_in_j_cell = node_local_number_in_its_cells[j_cell]; + const int j_index_p = mapP(cell_id_j); + + const auto invArDjr = inv_Ar * m_Djr(cell_id_j, node_nb_in_j_cell); + + for (size_t i_cell = 0; i_cell < node_cell.size(); ++i_cell) { + const CellId cell_id_i = node_cell[i_cell]; + if (m_is_implicit_cell[cell_id_i]) { + const size_t node_nb_in_cell_i = node_local_number_in_its_cells[i_cell]; + const int i_index_p = mapP(cell_id_i); + Hess_J(i_index_p, j_index_p) += dot(m_Djr(cell_id_i, node_nb_in_cell_i), invArDjr); + } + } + } + } + + for (size_t j_cell = 0; j_cell < node_cell.size(); ++j_cell) { + const CellId cell_id_j = node_cell[j_cell]; + if (m_is_implicit_cell[cell_id_j]) { + const size_t node_nb_in_j_cell = node_local_number_in_its_cells[j_cell]; + + const auto Ajr_invAr = m_Ajr(cell_id_j, node_nb_in_j_cell) * inv_Ar; + + for (size_t i_cell = 0; i_cell < node_cell.size(); ++i_cell) { + const CellId cell_id_i = node_cell[i_cell]; + if (m_is_implicit_cell[cell_id_i]) { + const size_t node_nb_in_i_cell = node_local_number_in_its_cells[i_cell]; + + const auto Ajr_invAr_Air = Ajr_invAr * m_Ajr(cell_id_i, node_nb_in_i_cell); + + for (size_t j_dimension = 0; j_dimension < Dimension; ++j_dimension) { + const int index_u_j = mapU(j_dimension, cell_id_j); + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + const int index_u_i = mapU(i_dimension, cell_id_i); + Hess_J(index_u_j, index_u_i) -= Ajr_invAr_Air(j_dimension, i_dimension); + } + } + } + } + } + } + } + } + + // presure bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<PressureBoundaryCondition, T>) { + // const auto& node_list = bc.faceList(); + + // const auto& node_local_numbers_in_their_cells = m_mesh.connectivity().nodeLocalNumbersInTheirCells(); + // const auto& node_to_cell_matrix = m_mesh.connectivity().nodeToCellMatrix(); + + // for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + // const NodeId& node_id = node_list[i_node]; + // // if (m_is_implicit_node[node_id]) { + // const auto& node_cell = node_to_cell_matrix[node_id]; + + // for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + // const CellId id_cell_i = node_cell[cell_i]; + // if (m_is_implicit_cell[id_cell_i]) { + // const auto& node_local_number_in_its_cells = + // node_local_numbers_in_their_cells.itemArray(node_id); + + // const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + // const int i_index_p = mapP(id_cell_i); + + // for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + // const CellId id_cell_j = node_cell[cell_j]; + // if (m_is_implicit_cell[id_cell_j]) { + // const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + // const int j_index_p = mapP(id_cell_j); + + // Hess_J(i_index_p, j_index_p) += + // dot(m_Djr(id_cell_i, node_nb_in_i), m_inv_Ar[node_id] * m_Djr(id_cell_j, node_nb_in_j)); + // } + // } + // } + // } + // for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + // const CellId id_cell_i = node_cell[cell_i]; + // if (m_is_implicit_cell[id_cell_i]) { + // const auto& node_local_number_in_its_cells = + // node_local_numbers_in_their_cells.itemArray(node_id); + + // const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + // for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + // const int i_index_u = mapU(i_dimension, id_cell_i); + + // Hess_J(i_index_u, i_index_u) += m_Ajr(id_cell_i, node_nb_in_i)(i_dimension, i_dimension); + + // for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + // const CellId id_cell_j = node_cell[cell_j]; + // if (m_is_implicit_cell[id_cell_j]) { + // const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + // for (size_t j_dimension = 0; j_dimension < Dimension; ++j_dimension) { + // const int j_index_u = mapU(j_dimension, id_cell_j); + + // Hess_J(i_index_u, j_index_u) += -(m_Ajr(id_cell_i, node_nb_in_i) * m_inv_Ar[node_id] * + // m_Ajr(id_cell_j, node_nb_in_j))(i_dimension, + // j_dimension); + // } + // } + // } + // } + // } + // } + // } + } + }, + boundary_condition); + } + + NodeValue<bool> has_boundary_condition{m_mesh.connectivity()}; + has_boundary_condition.fill(false); + + // velocity bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<VelocityBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; + has_boundary_condition[node_id] = true; + } + } + }, + boundary_condition); + } + + if constexpr (Dimension > 1) { + // axis bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<AxisBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + + const Rd& t = bc.direction(); + const Rdxd txt = tensorProduct(t, t); + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId& node_id = node_list[i_node]; + + if (not has_boundary_condition[node_id]) { + const Rdxd inverse_Ar_times_txt = m_inv_Ar[node_id] * txt; + + const auto& node_cell = node_to_cell_matrix[node_id]; + + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + const CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + const int i_index_u = mapU(i_dimension, id_cell_i); + + for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + const CellId id_cell_j = node_cell[cell_j]; + if (m_is_implicit_cell[id_cell_j]) { + const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + + for (size_t j_dimension = 0; j_dimension < Dimension; ++j_dimension) { + const int j_index_u = mapU(j_dimension, id_cell_j); + Hess_J(i_index_u, j_index_u) += + (-m_Ajr(id_cell_i, node_nb_in_i) * inverse_Ar_times_txt * + m_Ajr(id_cell_j, node_nb_in_j))(i_dimension, j_dimension); + } + } + } + } + } + } + + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + const CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + + const int i_index_p = mapP(id_cell_i); + + for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + const CellId id_cell_j = node_cell[cell_j]; + if (m_is_implicit_cell[id_cell_j]) { + const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + const int j_index_p = mapP(id_cell_j); + Hess_J(i_index_p, j_index_p) += + dot(m_Djr(id_cell_i, node_nb_in_i), inverse_Ar_times_txt * m_Djr(id_cell_j, node_nb_in_j)); + } + } + } + } + has_boundary_condition[node_id] = true; + } + } + } + }, + boundary_condition); + } + } + + // symmetry bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<SymmetryBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + + const Rd& n = bc.outgoingNormal(); + const Rdxd I = identity; + const Rdxd nxn = tensorProduct(n, n); + const Rdxd P = I - nxn; + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId& node_id = node_list[i_node]; + + if (not has_boundary_condition[node_id]) { + const Rdxd inverse_ArxP = m_inv_Ar[node_id] * P; + + const auto& node_cell = node_to_cell_matrix[node_id]; + + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + const CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + const int i_index_u = mapU(i_dimension, id_cell_i); + + for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + const CellId id_cell_j = node_cell[cell_j]; + if (m_is_implicit_cell[id_cell_j]) { + const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + + for (size_t j_dimension = 0; j_dimension < Dimension; ++j_dimension) { + const int j_index_u = mapU(j_dimension, id_cell_j); + Hess_J(i_index_u, j_index_u) += (-m_Ajr(id_cell_i, node_nb_in_i) * inverse_ArxP * + m_Ajr(id_cell_j, node_nb_in_j))(i_dimension, j_dimension); + } + } + } + } + } + } + + for (size_t cell_i = 0; cell_i < node_cell.size(); ++cell_i) { + const CellId id_cell_i = node_cell[cell_i]; + if (m_is_implicit_cell[id_cell_i]) { + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id); + const size_t node_nb_in_i = node_local_number_in_its_cells[cell_i]; + + const int i_index_p = mapP(id_cell_i); + + for (size_t cell_j = 0; cell_j < node_cell.size(); ++cell_j) { + const CellId id_cell_j = node_cell[cell_j]; + if (m_is_implicit_cell[id_cell_j]) { + const size_t node_nb_in_j = node_local_number_in_its_cells[cell_j]; + const int j_index_p = mapP(id_cell_j); + Hess_J(i_index_p, j_index_p) += + dot(m_Djr(id_cell_i, node_nb_in_i), inverse_ArxP * m_Djr(id_cell_j, node_nb_in_j)); + } + } + } + } + has_boundary_condition[node_id] = true; + } + } + } + }, + boundary_condition); + } + return Hess_J; + } + + CRSMatrix<double> + _getGradientF(const CRSMatrix<double, int>& A, + const Vector<double>& Uk, + const DiscreteScalarFunction& pi, + const double dt) + { + CRSMatrixDescriptor<double> Hess_J = this->_getHessianJ(dt, pi, Uk); + + CRSMatrix Hess_J_crs = Hess_J.getCRSMatrix(); + + CRSMatrix gradient_f = Hess_J_crs - A; + + return gradient_f; + } + + BoundaryConditionList + _getBCList(const std::shared_ptr<const MeshType>& mesh, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) + { + BoundaryConditionList bc_list; + + for (const auto& bc_descriptor : bc_descriptor_list) { + bool is_valid_boundary_condition = true; + + switch (bc_descriptor->type()) { + case IBoundaryConditionDescriptor::Type::axis: { + if constexpr (Dimension == 1) { + throw NormalError("Axis boundary condition makes no sense in dimension 1"); + } else { + const AxisBoundaryConditionDescriptor& axis_bc_descriptor = + dynamic_cast<const AxisBoundaryConditionDescriptor&>(*bc_descriptor); + + bc_list.push_back( + AxisBoundaryCondition{getMeshLineNodeBoundary(*mesh, axis_bc_descriptor.boundaryDescriptor())}); + } + break; + } + case IBoundaryConditionDescriptor::Type::symmetry: { + const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor = + dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor); + + bc_list.push_back( + SymmetryBoundaryCondition{getMeshFlatNodeBoundary(*mesh, sym_bc_descriptor.boundaryDescriptor())}); + break; + } + case IBoundaryConditionDescriptor::Type::dirichlet: { + const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor = + dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor); + if (dirichlet_bc_descriptor.name() == "velocity") { + MeshNodeBoundary mesh_node_boundary = + getMeshNodeBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor()); + + Array<const TinyVector<Dimension>> value_list = InterpolateItemValue<TinyVector<Dimension>( + TinyVector<Dimension>)>::template interpolate<ItemType::node>(dirichlet_bc_descriptor.rhsSymbolId(), + mesh->xr(), mesh_node_boundary.nodeList()); + bc_list.push_back(VelocityBoundaryCondition{mesh_node_boundary.nodeList(), value_list}); + } else if (dirichlet_bc_descriptor.name() == "pressure") { + const FunctionSymbolId pressure_id = dirichlet_bc_descriptor.rhsSymbolId(); + + if constexpr (Dimension == 1) { + MeshNodeBoundary mesh_node_boundary = getMeshNodeBoundary(*mesh, bc_descriptor->boundaryDescriptor()); + + Array<const double> node_values = + InterpolateItemValue<double(Rd)>::template interpolate<ItemType::node>(pressure_id, mesh->xr(), + mesh_node_boundary.nodeList()); + + bc_list.emplace_back(PressureBoundaryCondition{mesh_node_boundary.nodeList(), node_values}); + } else { + constexpr ItemType FaceType = [] { + if constexpr (Dimension > 1) { + return ItemType::face; + } else { + return ItemType::node; + } + }(); + + MeshFaceBoundary mesh_face_boundary = getMeshFaceBoundary(*mesh, bc_descriptor->boundaryDescriptor()); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + Array<const double> face_values = + InterpolateItemValue<double(Rd)>::template interpolate<FaceType>(pressure_id, mesh_data.xl(), + mesh_face_boundary.faceList()); + bc_list.emplace_back(PressureBoundaryCondition{mesh_face_boundary.faceList(), face_values}); + } + } 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 acoustic solver"; + throw NormalError(error_msg.str()); + } + } + + return bc_list; + } + + void + _computeGradJU_AU(const DiscreteVectorFunction& u, + const DiscreteScalarFunction& p, + const DiscreteScalarFunction& pi, + const double& dt) + { + // const auto node_to_cell_matrix = m_mesh.connectivity().nodeToCellMatrix(); + const auto cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); + + m_number_of_implicit_cells = 0; + m_implicit_cell_index = [&] { + CellValue<int> implicit_cell_index(m_mesh.connectivity()); + implicit_cell_index.fill(-1); + + for (CellId cell_id = 0; cell_id < m_mesh.numberOfCells(); ++cell_id) { + if (m_is_implicit_cell[cell_id]) { + if (implicit_cell_index[cell_id] != -1) { + throw NormalError("implicit cell has already an implicit number"); + } + implicit_cell_index[cell_id] = m_number_of_implicit_cells; + m_number_of_implicit_cells++; + } + } + return implicit_cell_index; + }(); + + m_is_implicit_node = [&] { + NodeValue<bool> is_implicit_node(m_mesh.connectivity()); + is_implicit_node.fill(false); + + for (CellId cell_id = 0; cell_id < m_mesh.numberOfCells(); ++cell_id) { + if (m_is_implicit_cell[cell_id]) { + const auto& cell_nodes = cell_to_node_matrix[cell_id]; + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + is_implicit_node[cell_nodes[i_node]] = true; + } + } + } + + return is_implicit_node; + }(); + + m_number_of_implicit_nodes = 0; + m_implicit_node_index = [&] { + NodeValue<int> implicit_node_index(m_mesh.connectivity()); + implicit_node_index.fill(-1); + + for (NodeId node_id = 0; node_id < m_mesh.numberOfNodes(); ++node_id) { + if (m_is_implicit_node[node_id]) { + if (implicit_node_index[node_id] != -1) { + throw NormalError("implicit node has already an implicit number"); + } + implicit_node_index[node_id] = m_number_of_implicit_nodes; + m_number_of_implicit_nodes++; + } + } + return implicit_node_index; + }(); + + std::cout << "building A: " << std::flush; + const CRSMatrix A = this->_getA().getCRSMatrix(); + std::cout << "done\n" << std::flush; + + Vector<double> Un = this->_getU(p.cellValues(), u.cellValues()); + Vector<double> Uk = this->_getU(m_predicted_p, m_predicted_u); + + int nb_iter = 0; + double norm_inf_sol; + + Array<const double> abs_Un = [&]() { + Array<double> compute_abs_Un{Un.size()}; + parallel_for( + Un.size(), PUGS_LAMBDA(size_t i) { compute_abs_Un[i] = std::abs(Un[i]); }); + return compute_abs_Un; + }(); + + double norm_inf_Un = max(abs_Un); + if (m_number_of_implicit_cells > 0) { + do { + nb_iter++; + + Vector<double> f = this->_getF(Un, Uk, pi, dt); + + std::cout << "building gradf: " << std::flush; + CRSMatrix<double> gradient_f = this->_getGradientF(A, Uk, pi, dt); + std::cout << "done\n" << std::flush; + + auto l2Norm = [](const Vector<double>& x) { + double sum2 = 0; + for (size_t i = 0; i < x.size(); ++i) { + sum2 += x[i] * x[i]; + } + return std::sqrt(sum2); + }; + + Vector<double> sol{Uk.size()}; + sol.fill(0); + + LinearSolver solver; + std::cout << "solving linear system: " << std::flush; + solver.solveLocalSystem(gradient_f, sol, f); + std::cout << "done\n" << std::flush; + + std::cout << rang::style::bold << "norm resid = " << l2Norm(f - gradient_f * sol) << rang::style::reset << '\n'; + double theta = 1; + + for (CellId cell_id = 0; cell_id < m_number_of_implicit_cells; ++cell_id) { + size_t k = mapP(cell_id); + if (-Uk[k] + theta * sol[k] < -pi[cell_id]) { + double new_theta = 0.5 * (Uk[k] - pi[cell_id]) / sol[k]; + std::cout << "theta: " << theta << " -> " << new_theta << '\n'; + theta = new_theta; + } + } + + Vector<double> U_next = Uk - theta * sol; + + Array<const double> abs_sol = [&]() { + Array<double> compute_abs_sol{sol.size()}; + parallel_for( + sol.size(), PUGS_LAMBDA(size_t i) { compute_abs_sol[i] = std::abs(sol[i]); }); + return compute_abs_sol; + }(); + + norm_inf_sol = max(abs_sol); + + std::cout << "nb_iter= " << nb_iter << " | ratio Newton = " << norm_inf_sol / norm_inf_Un << "\n"; + + // when it is a hard case we relax newton + size_t neg_pressure_count = 0; + double min_pressure = 0; + for (CellId cell_id = 0; cell_id < m_number_of_implicit_cells; ++cell_id) { + size_t k = mapP(cell_id); + if (-U_next[k] + pi[cell_id] < 0) { + std::cout << " neg p: cell_id=" << cell_id << '\n'; + ++neg_pressure_count; + min_pressure = std::min(min_pressure, -U_next[k] + pi[cell_id]); + U_next[k] = Uk[k]; + } + } + + Uk = U_next; + + // if ((count_newton == 0) and (count_Djr == 0) and (count_timestep == 0)) { + // auto newt_mesh = std::make_shared<MeshType>(m_mesh.shared_connectivity(), m_mesh.xr()); + // double pseudo_time = 1E-8 * count_newton + 1E-3 * count_Djr + count_timestep; + + // std::vector<std::shared_ptr<const INamedDiscreteData>> output_list; + // output_list.push_back( + // std::make_shared<NamedItemValueVariant>(std::make_shared<ItemValueVariant>(m_predicted_p), + // "predicted_p")); + + // vtk_writer.writeOnMeshForced(newt_mesh, output_list, pseudo_time); + // } + + m_predicted_u = [&] { + CellValue<Rd> predicted_u = copy(u.cellValues()); + for (CellId cell_id = 0; cell_id < m_mesh.numberOfCells(); ++cell_id) { + if (m_is_implicit_cell[cell_id]) { + Rd vector_u = zero; + for (size_t i_dimension = 0; i_dimension < Dimension; ++i_dimension) { + vector_u[i_dimension] = Uk[mapU(i_dimension, cell_id)]; + } + predicted_u[cell_id] = vector_u; + } + } + return predicted_u; + }(); + + m_predicted_p = [&] { + CellValue<double> predicted_p = copy(p.cellValues()); + for (CellId cell_id = 0; cell_id < m_mesh.numberOfCells(); ++cell_id) { + if (m_is_implicit_cell[cell_id]) { + predicted_p[cell_id] = -Uk[mapP(cell_id)]; + } + } + return predicted_p; + }(); + + NodeValue<const Rd> ur = _getUr(); + + NodeValue<Rd> newton_xr{m_mesh.connectivity()}; + for (NodeId node_id = 0; node_id < m_mesh.numberOfNodes(); ++node_id) { + newton_xr[node_id] = m_mesh.xr()[node_id] + dt * ur[node_id]; + } + + auto newt_mesh = std::make_shared<MeshType>(m_mesh.shared_connectivity(), newton_xr); + // double pseudo_time = 1E-4 * count_newton + 1E-2 * count_Djr + count_timestep; + + // std::vector<std::shared_ptr<const INamedDiscreteData>> output_list; + // output_list.push_back( + // std::make_shared<NamedItemValueVariant>(std::make_shared<ItemValueVariant>(m_predicted_p), "predicted_p")); + + // vtk_writer.writeOnMeshForced(newt_mesh, output_list, pseudo_time); + + if (neg_pressure_count > 0) { + std::cout << rang::fgB::magenta << "p est negatif sur " << neg_pressure_count + << " mailles min=" << min_pressure << rang::fg::reset << '\n'; + } + } while ((norm_inf_sol > 1e-12 * norm_inf_Un) and (nb_iter < 1000)); + } + + for (CellId j = 0; j < m_mesh.numberOfCells(); ++j) { + // faudrait mettre p+pi + if (m_predicted_p[j] <= 0) { + std::cout << "pression negative pour la maille" << j << '\n'; + } + } + } + + NodeValue<const Rd> + _getUr() const + { + const auto& node_to_cell_matrix = m_mesh.connectivity().nodeToCellMatrix(); + const auto& node_local_numbers_in_their_cells = m_mesh.connectivity().nodeLocalNumbersInTheirCells(); + + NodeValue<Rd> b{m_mesh.connectivity()}; + + parallel_for( + m_mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { + const auto& node_to_cell = node_to_cell_matrix[r]; + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(r); + + Rd br = zero; + for (size_t j = 0; j < node_to_cell.size(); ++j) { + const CellId J = node_to_cell[j]; + const unsigned int R = node_local_number_in_its_cells[j]; + br += m_Ajr(J, R) * m_predicted_u[J] + m_predicted_p[J] * m_Djr(J, R); + } + + b[r] = br; + }); + + NodeValue<bool> has_boundary_condition{m_mesh.connectivity()}; + has_boundary_condition.fill(false); + + // velocity bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<VelocityBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + const auto& value_list = bc.valueList(); + + parallel_for( + node_list.size(), PUGS_LAMBDA(const size_t i_node) { + const NodeId node_id = node_list[i_node]; + const auto& value = value_list[i_node]; + b[node_id] = value; + + has_boundary_condition[node_id] = true; + }); + } + }, + boundary_condition); + } + + // pressure bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<PressureBoundaryCondition, T>) { + MeshData<MeshType>& mesh_data = MeshDataManager::instance().getMeshData(m_mesh); + if constexpr (Dimension == 1) { + const NodeValuePerCell<const Rd> Cjr = mesh_data.Cjr(); + + const auto& node_list = bc.faceList(); + const auto& value_list = bc.valueList(); + parallel_for( + node_list.size(), PUGS_LAMBDA(size_t i_node) { + const NodeId node_id = node_list[i_node]; + const auto& node_cell_list = node_to_cell_matrix[node_id]; + Assert(node_cell_list.size() == 1); + + const CellId node_cell_id = node_cell_list[0]; + const size_t node_local_number_in_cell = node_local_numbers_in_their_cells(node_id, 0); + + b[node_id] -= value_list[i_node] * Cjr(node_cell_id, node_local_number_in_cell); + }); + } else { + const NodeValuePerFace<const Rd> Nlr = mesh_data.Nlr(); + + const auto& face_to_cell_matrix = m_mesh.connectivity().faceToCellMatrix(); + const auto& face_to_node_matrix = m_mesh.connectivity().faceToNodeMatrix(); + const auto& face_local_numbers_in_their_cells = m_mesh.connectivity().faceLocalNumbersInTheirCells(); + const auto& face_cell_is_reversed = m_mesh.connectivity().cellFaceIsReversed(); + + 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]; + const auto& face_cell_list = face_to_cell_matrix[face_id]; + Assert(face_cell_list.size() == 1); + + const CellId face_cell_id = face_cell_list[0]; + const size_t face_local_number_in_cell = face_local_numbers_in_their_cells(face_id, 0); + + const double sign = face_cell_is_reversed(face_cell_id, face_local_number_in_cell) ? -1 : 1; + + const auto& face_nodes = face_to_node_matrix[face_id]; + + for (size_t i_node = 0; i_node < face_nodes.size(); ++i_node) { + NodeId node_id = face_nodes[i_node]; + b[node_id] -= sign * value_list[i_face] * Nlr(face_id, i_node); + } + } + } + } + }, + boundary_condition); + } + + if constexpr (Dimension > 1) { + // axis bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<AxisBoundaryCondition, T>) { + const Rd& t = bc.direction(); + const Rdxd txt = tensorProduct(t, t); + + const auto& node_list = bc.nodeList(); + parallel_for( + bc.numberOfNodes(), PUGS_LAMBDA(const size_t i_node) { + const NodeId node_id = node_list[i_node]; + if (not has_boundary_condition[node_id]) { + b[node_id] = txt * b[node_id]; + + has_boundary_condition[node_id] = true; + } + }); + } + }, + boundary_condition); + } + } + + // symmetry bc + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<SymmetryBoundaryCondition, T>) { + const Rd& n = bc.outgoingNormal(); + const Rdxd I = identity; + const Rdxd nxn = tensorProduct(n, n); + const Rdxd P = I - nxn; + const auto& node_list = bc.nodeList(); + parallel_for( + bc.numberOfNodes(), PUGS_LAMBDA(const size_t i_node) { + const NodeId node_id = node_list[i_node]; + if (not has_boundary_condition[node_id]) { + b[node_id] = P * b[node_id]; + + has_boundary_condition[node_id] = true; + } + }); + } + }, + boundary_condition); + } + + const NodeValue<Rd> computed_ur(m_mesh.connectivity()); + parallel_for( + m_mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { computed_ur[r] = m_inv_Ar[r] * b[r]; }); + + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + + if constexpr ((Dimension > 1) and (std::is_same_v<AxisBoundaryCondition, T>)) { + const Rd& t = bc.direction(); + const Rdxd txt = tensorProduct(t, t); + + const Array<const NodeId>& node_list = bc.nodeList(); + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; // on fixe le sommet r + computed_ur[node_id] = txt * computed_ur[node_id]; + } + } else if constexpr (std::is_same_v<SymmetryBoundaryCondition, T>) { + const Rd& n = bc.outgoingNormal(); + + const Rdxd I = identity; + const Rdxd nxn = tensorProduct(n, n); + const Rdxd P = I - nxn; + + const Array<const NodeId>& node_list = bc.nodeList(); + + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; // on fixe le sommet r + computed_ur[node_id] = P * computed_ur[node_id]; + } + } else if constexpr (std::is_same_v<VelocityBoundaryCondition, T>) { + const Array<const NodeId>& node_list = bc.nodeList(); + const Array<const Rd>& 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]; + computed_ur[node_id] = value_list[i_node]; + } + } + }, + boundary_condition); + } + + return computed_ur; + } + + NodeValuePerCell<const Rd> + _getFjr(const NodeValue<const Rd>& ur) const + { + const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); + const NodeValuePerCell<Rd> computed_Fjr(m_mesh.connectivity()); + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + for (size_t r = 0; r < cell_nodes.size(); ++r) { + computed_Fjr(j, r) = m_Ajr(j, r) * (m_predicted_u[j] - ur[cell_nodes[r]]) + (m_predicted_p[j] * m_Djr(j, r)); + } + }); + + return computed_Fjr; + } + + ImplicitAcousticO2Solver(const SolverType solver_type, + const std::shared_ptr<const MeshType>& p_mesh, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const DiscreteVectorFunction&, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::vector<std::shared_ptr<const IZoneDescriptor>>& explicit_zone_list, + const double&) + : m_solver_type{solver_type}, + m_boundary_condition_list{this->_getBCList(p_mesh, bc_descriptor_list)}, + m_mesh{*p_mesh} + { + m_is_implicit_cell = [&] { + CellValue<bool> is_implicit_cell(m_mesh.connectivity()); + is_implicit_cell.fill(true); + + for (auto explicit_zone : explicit_zone_list) { + auto mesh_cell_zone = getMeshCellZone(m_mesh, *explicit_zone); + const auto& cell_list = mesh_cell_zone.cellList(); + for (size_t i_cell = 0; i_cell < cell_list.size(); ++i_cell) { + const CellId cell_id = cell_list[i_cell]; + is_implicit_cell[cell_id] = false; + } + } + + return is_implicit_cell; + }(); + } + + ImplicitAcousticO2Solver(const SolverType solver_type, + const std::shared_ptr<const MeshType>& p_mesh, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const DiscreteVectorFunction&, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const DiscreteScalarFunction&, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const DiscreteScalarFunction& chi_explicit, + const double&) + : m_solver_type{solver_type}, + m_boundary_condition_list{this->_getBCList(p_mesh, bc_descriptor_list)}, + m_mesh{*p_mesh} + { + m_is_implicit_cell = [&] { + CellValue<bool> is_implicit_cell(m_mesh.connectivity()); + + parallel_for( + m_mesh.numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { is_implicit_cell[cell_id] = (chi_explicit[cell_id] == 0); }); + + return is_implicit_cell; + }(); + } + + public: + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + double> + apply(const double& dt, + const std::shared_ptr<const MeshType>& mesh, + const DiscreteScalarFunction& rho, + const DiscreteVectorFunction& u, + const DiscreteScalarFunction& E, + + const DiscreteScalarFunction& c, + const DiscreteScalarFunction& p, + const DiscreteScalarFunction& pi, + const DiscreteScalarFunction& gamma, + const DiscreteScalarFunction& Cv, + const DiscreteScalarFunction& entropy) + { + static Timer implicit_t; + implicit_t.start(); + + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + const NodeValuePerCell<const Rd> Cjr_n = mesh_data.Cjr(); + + m_Djr = copy(Cjr_n); + + CellValue<double> new_rho = copy(rho.cellValues()); + CellValue<Rd> new_u = copy(u.cellValues()); + CellValue<double> new_E = copy(E.cellValues()); + std::shared_ptr<const MeshType> new_mesh; + + m_Mj = [&]() { + const CellValue<const double>& Vj = mesh_data.Vj(); + CellValue<double> computed_Mj(m_mesh.connectivity()); + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { computed_Mj[j] = rho[j] * Vj[j]; }); + return computed_Mj; + }(); + + m_u = u.cellValues(); + + m_predicted_u = m_u; + m_predicted_p = p.cellValues(); + + m_tau = [&]() { + CellValue<double> computed_tau(m_mesh.connectivity()); + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { computed_tau[j] = 1 / rho[j]; }); + return computed_tau; + }(); + + m_Ajr = this->_computeAjr(rho, c); + + NodeValue<const Rdxd> Ar = this->_computeAr(m_Ajr); + + m_inv_Ar = this->_computeInvAr(Ar); + + m_inv_gamma = [&]() { + CellValue<double> computed_inv_gamma(m_mesh.connectivity()); + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { computed_inv_gamma[j] = 1 / gamma[j]; }); + return computed_inv_gamma; + }(); + + m_g_1_exp_S_Cv_inv_g = [&]() { + CellValue<double> computed_g_1_exp_S_Cv_inv_g(m_mesh.connectivity()); + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + if (m_is_implicit_cell[j]) { + computed_g_1_exp_S_Cv_inv_g[j] = std::pow((gamma[j] - 1) * exp(entropy[j] / Cv[j]), m_inv_gamma[j]); + } + }); + return computed_g_1_exp_S_Cv_inv_g; + }(); + bool CFL = false; + double dt_f = dt; + do { + bool test = true; + double max_tau_error; + int number_iter = 0; + do { + number_iter++; + + NodeValue<const Rd> ur_n = this->_getUr(); + NodeValuePerCell<const Rd> Fjr_n = this->_getFjr(ur_n); + + this->_computeGradJU_AU(u, p, pi, dt); + + NodeValue<const Rd> ur = this->_getUr(); + NodeValuePerCell<const Rd> Fjr = this->_getFjr(ur); + // time n+1 + + const auto& cell_to_node_matrix = m_mesh.connectivity().cellToNodeMatrix(); + + NodeValue<Rd> new_xr = copy(mesh->xr()); + parallel_for( + mesh->numberOfNodes(), PUGS_LAMBDA(NodeId r) { new_xr[r] += 0.5 * dt * (ur[r] + ur_n[r]); }); + + new_mesh = std::make_shared<MeshType>(mesh->shared_connectivity(), new_xr); + + CellValue<const double> Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + new_rho = copy(rho.cellValues()); + new_u = copy(u.cellValues()); + new_E = copy(E.cellValues()); + + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + Rd momentum_fluxes = zero; + double energy_fluxes = 0; + for (size_t R = 0; R < cell_nodes.size(); ++R) { + const NodeId r = cell_nodes[R]; + momentum_fluxes += Fjr(j, R) + Fjr_n(j, R); + energy_fluxes += dot(Fjr(j, R), ur[r]) + dot(Fjr_n(j, R), ur_n[r]); + } + const double dt_over_Mj = dt / m_Mj[j]; + new_u[j] -= 0.5 * dt_over_Mj * momentum_fluxes; + new_E[j] -= 0.5 * dt_over_Mj * energy_fluxes; + }); + + // update Djr + const NodeValuePerCell<const Rd> new_Cjr = MeshDataManager::instance().getMeshData(*new_mesh).Cjr(); + + CellValue<double> new_Vj{m_mesh.connectivity()}; + + bool is_positive = true; + do { + is_positive = true; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { + new_Vj[j] = 0; + auto cell_nodes = cell_to_node_matrix[j]; + + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + const NodeId node_id = cell_nodes[i_node]; + new_Vj[j] += dot(new_Cjr[j][i_node], new_xr[node_id]); + } + new_Vj[j] *= 1. / Dimension; + }); + + double m = min(new_Vj); + if (m < 0) { + // std::cout << "negative volume" << '/n'; + // throw NormalError("Negative volume"); + } + } while (not is_positive); + + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { new_rho[j] *= Vj[j] / new_Vj[j]; }); + + CellValue<double> new_tau(m_mesh.connectivity()); + + for (CellId j = 0; j < mesh->numberOfCells(); ++j) { + double new_tau_fluxes = 0; + const auto& cell_nodes = cell_to_node_matrix[j]; + for (size_t R = 0; R < cell_nodes.size(); ++R) { + const NodeId r = cell_nodes[R]; + new_tau_fluxes += dot(m_Djr(j, R), ur[r]) + dot(m_Djr(j, R), ur_n[r]); + } + new_tau[j] = m_tau[j] + 0.5 * (dt / m_Mj[j]) * new_tau_fluxes; + // std::cout << "new_tau(" << j << ")=" << new_tau[j] << '\n'; + } + + // double sum_tau_rho = 0; + max_tau_error = 0; + + for (CellId j = 0; j < mesh->numberOfCells(); ++j) { + // const auto& cell_nodes = cell_to_node_matrix[j]; + // V_j^n+1/tau_j^n+1 - M_j + // sum_tau_rho += std::abs(new_tau[j] - 1. / new_rho[j]); + if (m_is_implicit_cell[j]) { + max_tau_error = std::max(max_tau_error, std::abs(1 - new_tau[j] * new_rho[j])); + } + } + // std::cout << "sum_tau_rho =" << sum_tau_rho << '\n'; + // std::cout << "max_tau_error=" << max_tau_error << '\n'; + + if constexpr (Dimension == 2) { + NodeValuePerCell<Rd> Djr{m_mesh.connectivity()}; + + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const auto& cell_nodes = cell_to_node_matrix[cell_id]; + if (m_is_implicit_cell[cell_id]) { + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + Djr(cell_id, i_node) = 0.5 * (Cjr_n(cell_id, i_node) + new_Cjr(cell_id, i_node)); + } + } else { + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + Djr(cell_id, i_node) = Cjr_n(cell_id, i_node); + } + } + }); + + m_Djr = Djr; + } else if constexpr (Dimension == 3) { + NodeValuePerFace<Rd> Nlr_delta(m_mesh.connectivity()); + const auto& face_to_node_matrix = m_mesh.connectivity().faceToNodeMatrix(); + + const auto& xr_n = m_mesh.xr(); + const auto& xr_np1 = new_xr; + + parallel_for( + m_mesh.numberOfFaces(), PUGS_LAMBDA(FaceId l) { + const auto& face_nodes = face_to_node_matrix[l]; + const size_t nb_nodes = face_nodes.size(); + std::vector<Rd> dxr_n(nb_nodes); + std::vector<Rd> dxr_np1(nb_nodes); + for (size_t r = 0; r < nb_nodes; ++r) { + dxr_n[r] = xr_n[face_nodes[(r + 1) % nb_nodes]] - xr_n[face_nodes[(r + nb_nodes - 1) % nb_nodes]]; + dxr_np1[r] = xr_np1[face_nodes[(r + 1) % nb_nodes]] - xr_np1[face_nodes[(r + nb_nodes - 1) % nb_nodes]]; + } + const double inv_12_nb_nodes = 1. / (12. * nb_nodes); + for (size_t r = 0; r < nb_nodes; ++r) { + Rd Nr = zero; + for (size_t s = 0; s < nb_nodes; ++s) { + Nr -= crossProduct((1. / 6) * (2 * (dxr_np1[r] - dxr_n[r]) - (dxr_np1[s] - dxr_n[s])), + xr_np1[face_nodes[s]] - xr_n[face_nodes[s]]); + } + Nr *= inv_12_nb_nodes; + Nlr_delta(l, r) = Nr; + } + }); + + const auto& cell_to_face_matrix = m_mesh.connectivity().cellToFaceMatrix(); + const auto& cell_face_is_reversed = m_mesh.connectivity().cellFaceIsReversed(); + + NodeValuePerCell<Rd> Djr{m_mesh.connectivity()}; + Djr.fill(zero); + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + + const auto& cell_faces = cell_to_face_matrix[j]; + const auto& face_is_reversed = cell_face_is_reversed.itemArray(j); + + for (size_t L = 0; L < cell_faces.size(); ++L) { + const FaceId& l = cell_faces[L]; + const auto& face_nodes = face_to_node_matrix[l]; + + auto local_node_number_in_cell = [&](NodeId node_number) { + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + if (node_number == cell_nodes[i_node]) { + return i_node; + } + } + return std::numeric_limits<size_t>::max(); + }; + + if (face_is_reversed[L]) { + for (size_t rl = 0; rl < face_nodes.size(); ++rl) { + const size_t R = local_node_number_in_cell(face_nodes[rl]); + Djr(j, R) -= Nlr_delta(l, rl); + } + } else { + for (size_t rl = 0; rl < face_nodes.size(); ++rl) { + const size_t R = local_node_number_in_cell(face_nodes[rl]); + Djr(j, R) += Nlr_delta(l, rl); + } + } + } + }); + + parallel_for( + Djr.numberOfValues(), PUGS_LAMBDA(const size_t i) { Djr[i] += 0.5 * (Cjr_n[i] + new_Cjr[i]); }); + + parallel_for( + m_mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + if (not m_is_implicit_cell[cell_id]) { + const auto& cell_nodes = cell_to_node_matrix[cell_id]; + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + Djr(cell_id, i_node) = Cjr_n(cell_id, i_node); + } + } + }); + + m_Djr = Djr; + + double max_err = 0; + + for (CellId cell_id = 0; cell_id < m_mesh.numberOfCells(); ++cell_id) { + if (m_is_implicit_cell[cell_id]) { + double delta_V = new_Vj[cell_id] - Vj[cell_id]; + const auto& node_list = cell_to_node_matrix[cell_id]; + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; + delta_V -= dt * dot(ur[node_id], Djr[cell_id][i_node]); + } + max_err = std::max(max_err, std::abs(delta_V)); + } + } + + std::cout << rang::fgB::yellow << "Max volume err = " << max_err << rang::fg::reset << '\n'; + } + + if constexpr (Dimension > 1) { + std::cout << rang::fgB::magenta << "number_iter_Djr=" << number_iter << " max_tau_error=" << max_tau_error + << rang::fg::reset << '\n'; + } + + // std::cout << "new rho=" << new_rho << '\n'; + } while ((Dimension > 1) and (max_tau_error > 1e-4) and (number_iter < 100)); + if (test) { + CFL = true; + } else { + dt_f = 0.5 * dt_f; + } + } while (CFL == false); + // std::cout << "prochaine boucle" << '\n'; + implicit_t.pause(); + // std::cout << "getA=" << get_A_t << "(" << count_getA << ")" + // << " getF=" << getF_t << "(" << count_getF << ")" + // << " getGradF=" << getGradF_t << "(" << count_getGradF << ")" + // << " HJ_A=" << HJ_A_t << '\n'; + // std::cout << "solver=" << solver_t << " total= " << implicit_t << '\n'; + + return {std::make_shared<MeshVariant>(new_mesh), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction{new_mesh, new_rho}), + std::make_shared<DiscreteFunctionVariant>(DiscreteVectorFunction{new_mesh, new_u}), + std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction{new_mesh, new_E}), dt_f}; + } + + std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + double> + apply(const double& dt, + 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>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy) + { + std::shared_ptr mesh_v = getCommonMesh({rho, u, E}); + if (not mesh_v) { + throw NormalError("discrete functions are not defined on the same mesh"); + } + + if (not checkDiscretizationType({rho, u, E}, DiscreteFunctionType::P0)) { + throw NormalError("acoustic solver expects P0 functions"); + } + + return this->apply(dt, mesh_v->get<MeshType>(), rho->get<DiscreteScalarFunction>(), + u->get<DiscreteVectorFunction>(), E->get<DiscreteScalarFunction>(), + c->get<DiscreteScalarFunction>(), p->get<DiscreteScalarFunction>(), + pi->get<DiscreteScalarFunction>(), gamma->get<DiscreteScalarFunction>(), + Cv->get<DiscreteScalarFunction>(), entropy->get<DiscreteScalarFunction>()); + } + + ImplicitAcousticO2Solver(const SolverType solver_type, + const std::shared_ptr<const MeshVariant>& mesh_v, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::vector<std::shared_ptr<const IZoneDescriptor>>& explicit_zone_list, + const double& dt) + : ImplicitAcousticO2Solver(solver_type, + mesh_v->get<MeshType>(), + rho->get<DiscreteScalarFunction>(), + c->get<DiscreteScalarFunction>(), + u->get<DiscreteFunctionP0<const Rd>>(), + p->get<DiscreteScalarFunction>(), + pi->get<DiscreteScalarFunction>(), + gamma->get<DiscreteScalarFunction>(), + Cv->get<DiscreteScalarFunction>(), + entropy->get<DiscreteScalarFunction>(), + bc_descriptor_list, + explicit_zone_list, + dt) + {} + + ImplicitAcousticO2Solver(const SolverType solver_type, + const std::shared_ptr<const MeshVariant>& mesh_v, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::shared_ptr<const DiscreteFunctionVariant>& chi_explicit, + const double& dt) + : ImplicitAcousticO2Solver(solver_type, + mesh_v->get<MeshType>(), + rho->get<DiscreteScalarFunction>(), + c->get<DiscreteScalarFunction>(), + u->get<DiscreteFunctionP0<const Rd>>(), + p->get<DiscreteScalarFunction>(), + pi->get<DiscreteScalarFunction>(), + gamma->get<DiscreteScalarFunction>(), + Cv->get<DiscreteScalarFunction>(), + entropy->get<DiscreteScalarFunction>(), + bc_descriptor_list, + chi_explicit->get<DiscreteScalarFunction>(), + dt) + {} + + ImplicitAcousticO2Solver() = default; + ImplicitAcousticO2Solver(ImplicitAcousticO2Solver&&) = default; + ~ImplicitAcousticO2Solver() = default; +}; + +template <MeshConcept MeshType> +class ImplicitAcousticO2SolverHandlerRhombusModified::ImplicitAcousticO2Solver<MeshType>::PressureBoundaryCondition +{ + 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; + } + + PressureBoundaryCondition(const Array<const FaceId>& face_list, const Array<const double>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + {} + + ~PressureBoundaryCondition() = default; +}; + +template <> +class ImplicitAcousticO2SolverHandlerRhombusModified::ImplicitAcousticO2Solver<Mesh<1>>::PressureBoundaryCondition +{ + private: + const Array<const double> m_value_list; + const Array<const NodeId> m_face_list; + + public: + const Array<const NodeId>& + faceList() const + { + return m_face_list; + } + + const Array<const double>& + valueList() const + { + return m_value_list; + } + + PressureBoundaryCondition(const Array<const NodeId>& face_list, const Array<const double>& value_list) + : m_value_list{value_list}, m_face_list{face_list} + {} + + ~PressureBoundaryCondition() = default; +}; + +template <MeshConcept MeshType> +class ImplicitAcousticO2SolverHandlerRhombusModified::ImplicitAcousticO2Solver<MeshType>::VelocityBoundaryCondition +{ + private: + const Array<const TinyVector<Dimension>> m_value_list; + const Array<const NodeId> m_node_list; + + public: + const Array<const NodeId>& + nodeList() const + { + return m_node_list; + } + + const Array<const TinyVector<Dimension>>& + valueList() const + { + return m_value_list; + } + + VelocityBoundaryCondition(const Array<const NodeId>& node_list, const Array<const TinyVector<Dimension>>& value_list) + : m_value_list{value_list}, m_node_list{node_list} + {} + + ~VelocityBoundaryCondition() = default; +}; + +template <MeshConcept MeshType> +class ImplicitAcousticO2SolverHandlerRhombusModified::ImplicitAcousticO2Solver<MeshType>::SymmetryBoundaryCondition +{ + public: + using Rd = TinyVector<Dimension, double>; + + private: + const MeshFlatNodeBoundary<MeshType> m_mesh_flat_node_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(); + } + + SymmetryBoundaryCondition(const MeshFlatNodeBoundary<MeshType>& mesh_flat_node_boundary) + : m_mesh_flat_node_boundary(mesh_flat_node_boundary) + { + ; + } + + ~SymmetryBoundaryCondition() = default; +}; + +template <MeshConcept MeshType> +class ImplicitAcousticO2SolverHandlerRhombusModified::ImplicitAcousticO2Solver<MeshType>::AxisBoundaryCondition +{ + public: + using Rd = TinyVector<Dimension, double>; + + private: + const MeshLineNodeBoundary<MeshType> m_mesh_line_node_boundary; + + public: + const Rd& + direction() const + { + return m_mesh_line_node_boundary.direction(); + } + + size_t + numberOfNodes() const + { + return m_mesh_line_node_boundary.nodeList().size(); + } + + const Array<const NodeId>& + nodeList() const + { + return m_mesh_line_node_boundary.nodeList(); + } + + AxisBoundaryCondition(const MeshLineNodeBoundary<MeshType>& mesh_line_node_boundary) + : m_mesh_line_node_boundary(mesh_line_node_boundary) + { + ; + } + + ~AxisBoundaryCondition() = default; +}; + +template <> +class ImplicitAcousticO2SolverHandlerRhombusModified::ImplicitAcousticO2Solver<Mesh<1>>::AxisBoundaryCondition +{ + public: + AxisBoundaryCondition() = default; + ~AxisBoundaryCondition() = default; +}; + +ImplicitAcousticO2SolverHandlerRhombusModified::ImplicitAcousticO2SolverHandlerRhombusModified( + const SolverType solver_type, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::vector<std::shared_ptr<const IZoneDescriptor>>& explicit_zone_list, + const double& dt) +{ + std::shared_ptr mesh_v = getCommonMesh({rho, c, u, p, pi, gamma, Cv}); + if (not mesh_v) { + throw NormalError("discrete functions are not defined on the same mesh"); + } + + if (not checkDiscretizationType({rho, c, u, p}, DiscreteFunctionType::P0)) { + throw NormalError("acoustic solver expects P0 functions"); + } + + std::visit( + [&](auto&& mesh) { + using MeshType = mesh_type_t<decltype(mesh)>; + if constexpr (is_polygonal_mesh_v<MeshType>) { + m_implicit_acoustic_solver = + std::make_unique<ImplicitAcousticO2Solver<MeshType>>(solver_type, mesh_v, rho, c, u, p, pi, gamma, Cv, + entropy, bc_descriptor_list, explicit_zone_list, dt); + } else { + throw NormalError("unexpected mesh type"); + } + }, + mesh_v->variant()); +} + +ImplicitAcousticO2SolverHandlerRhombusModified::ImplicitAcousticO2SolverHandlerRhombusModified( + const SolverType solver_type, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::shared_ptr<const DiscreteFunctionVariant>& chi_explicit, + const double& dt) +{ + std::shared_ptr mesh_v = getCommonMesh({rho, c, u, p, pi, gamma, Cv, chi_explicit}); + if (not mesh_v) { + throw NormalError("discrete functions are not defined on the same mesh"); + } + + if (not checkDiscretizationType({rho, c, u, p}, DiscreteFunctionType::P0)) { + throw NormalError("acoustic solver expects P0 functions"); + } + + std::visit( + [&](auto&& mesh) { + using MeshType = mesh_type_t<decltype(mesh)>; + if constexpr (is_polygonal_mesh_v<MeshType>) { + m_implicit_acoustic_solver = + std::make_unique<ImplicitAcousticO2Solver<MeshType>>(solver_type, mesh_v, rho, c, u, p, pi, gamma, Cv, + entropy, bc_descriptor_list, chi_explicit, dt); + } else { + throw NormalError("unexpected mesh type"); + } + }, + mesh_v->variant()); +} diff --git a/src/scheme/ImplicitAcousticO2SolverRhombusModified.hpp b/src/scheme/ImplicitAcousticO2SolverRhombusModified.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2edf4f8b78546fef972a64b8f795d6cade4c28a1 --- /dev/null +++ b/src/scheme/ImplicitAcousticO2SolverRhombusModified.hpp @@ -0,0 +1,94 @@ +#ifndef IMPLICIT_ACOUSTIC_O2_SOLVER_MODIFIED_HPP +#define IMPLICIT_ACOUSTIC_O2_SOLVER_MODIFIED_HPP + +#include <mesh/MeshTraits.hpp> + +#include <memory> +#include <tuple> +#include <vector> + +class DiscreteFunctionVariant; +class MeshVariant; +class ItemValueVariant; +class SubItemValuePerItemVariant; +class IBoundaryConditionDescriptor; +class IZoneDescriptor; + +class ImplicitAcousticO2SolverHandlerRhombusModified +{ + public: + enum class SolverType + { + Glace1State, + Glace2States, + Eucclhyd + }; + + private: + struct IImplicitAcousticO2Solver + { + virtual std::tuple<std::shared_ptr<const MeshVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + std::shared_ptr<const DiscreteFunctionVariant>, + double> + apply(const double& dt, + 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>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy) = 0; + + IImplicitAcousticO2Solver() = default; + IImplicitAcousticO2Solver(IImplicitAcousticO2Solver&&) = default; + IImplicitAcousticO2Solver& operator=(IImplicitAcousticO2Solver&&) = default; + + virtual ~IImplicitAcousticO2Solver() = default; + }; + + template <MeshConcept MeshType> + class ImplicitAcousticO2Solver; + + std::unique_ptr<IImplicitAcousticO2Solver> m_implicit_acoustic_solver; + + public: + IImplicitAcousticO2Solver& + solver() + { + return *m_implicit_acoustic_solver; + } + + ImplicitAcousticO2SolverHandlerRhombusModified( + SolverType solver_type, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::vector<std::shared_ptr<const IZoneDescriptor>>& explicit_zone_list, + const double& dt); + + ImplicitAcousticO2SolverHandlerRhombusModified( + const SolverType solver_type, + const std::shared_ptr<const DiscreteFunctionVariant>& rho, + const std::shared_ptr<const DiscreteFunctionVariant>& c, + const std::shared_ptr<const DiscreteFunctionVariant>& u, + const std::shared_ptr<const DiscreteFunctionVariant>& p, + const std::shared_ptr<const DiscreteFunctionVariant>& pi, + const std::shared_ptr<const DiscreteFunctionVariant>& gamma, + const std::shared_ptr<const DiscreteFunctionVariant>& Cv, + const std::shared_ptr<const DiscreteFunctionVariant>& entropy, + const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list, + const std::shared_ptr<const DiscreteFunctionVariant>& chi_explicit, + const double& dt); +}; + +#endif // IMPLICIT_ACOUSTIC_O2_SOLVER_HPP