// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. #include "face_nbr_geom.hpp" #include "../general/forall.hpp" namespace mfem { FaceNeighborGeometricFactors::FaceNeighborGeometricFactors( const GeometricFactors &geom_) : num_neighbor_elems(0), geom(geom_) { #ifdef MFEM_USE_MPI if (const ParMesh *par_mesh = dynamic_cast(geom.mesh)) { const int flags = geom.computed_factors; const int dim = par_mesh->Dimension(); const int sdim = par_mesh->SpaceDimension(); const_cast(par_mesh)->ExchangeFaceNbrData(); num_neighbor_elems = par_mesh->GetNFaceNeighborElements(); if (flags & GeometricFactors::COORDINATES) { ExchangeFaceNbrQVectors(geom.X, X, sdim); } if (flags & GeometricFactors::JACOBIANS) { ExchangeFaceNbrQVectors(geom.J, J, dim*sdim); } if (flags & GeometricFactors::DETERMINANTS) { ExchangeFaceNbrQVectors(geom.detJ, detJ, 1); } // Free memory of work arrays. send_data.Destroy(); send_offsets.DeleteAll(); recv_offsets.DeleteAll(); } #endif } void FaceNeighborGeometricFactors::ExchangeFaceNbrQVectors( const Vector &x_local, Vector &x_shared, const int vdim) { #ifdef MFEM_USE_MPI const int nq = geom.IntRule->Size(); const int ndof_per_el = vdim * nq; const ParMesh *mesh = static_cast(geom.mesh); const int n_face_nbr = mesh->GetNFaceNeighbors(); if (n_face_nbr == 0) { return; } const int ne_send = mesh->send_face_nbr_elements.Size_of_connections(); x_shared.SetSize(ndof_per_el * num_neighbor_elems); send_offsets.SetSize(n_face_nbr + 1); send_data.SetSize(ndof_per_el * ne_send); auto h_send_data = Reshape(send_data.HostWrite(), ndof_per_el, ne_send); const auto h_x_local = Reshape(x_local.HostRead(), ndof_per_el, mesh->GetNE()); int idx = 0; Array row; for (int i = 0; i < n_face_nbr; ++i) { send_offsets[i] = ndof_per_el*idx; mesh->send_face_nbr_elements.GetRow(i, row); for (const int el : row) { for (int j = 0; j < ndof_per_el; ++j) { h_send_data(j, idx) = h_x_local(j, el); } ++idx; } } send_offsets[n_face_nbr] = ndof_per_el*idx; MFEM_ASSERT(send_offsets[n_face_nbr] == send_data.Size(), ""); recv_offsets.SetSize(n_face_nbr + 1); for (int i = 0; i < n_face_nbr + 1; ++i) { recv_offsets[i] = ndof_per_el * mesh->face_nbr_elements_offset[i]; } MFEM_ASSERT(recv_offsets[n_face_nbr] == x_shared.Size(), ""); MPI_Comm comm = mesh->GetComm(); std::vector send_reqs(n_face_nbr); std::vector recv_reqs(n_face_nbr); std::vector statuses(n_face_nbr); bool mpi_gpu_aware = Device::GetGPUAwareMPI(); const auto send_data_ptr = mpi_gpu_aware ? send_data.Read() : send_data.HostRead(); auto x_shared_ptr = mpi_gpu_aware ? x_shared.Write() : x_shared.HostWrite(); for (int i = 0; i < n_face_nbr; ++i) { const int nbr_rank = mesh->GetFaceNbrRank(i); const int tag = 0; MPI_Isend(send_data_ptr + send_offsets[i], send_offsets[i+1] - send_offsets[i], MPITypeMap::mpi_type, nbr_rank, tag, comm, &send_reqs[i]); MPI_Irecv(x_shared_ptr + recv_offsets[i], recv_offsets[i+1] - recv_offsets[i], MPITypeMap::mpi_type, nbr_rank, tag, comm, &recv_reqs[i]); } MPI_Waitall(n_face_nbr, send_reqs.data(), statuses.data()); MPI_Waitall(n_face_nbr, recv_reqs.data(), statuses.data()); #endif } } // namespace mfem