// Copyright (c) 2010-2023, 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 "lor_ads.hpp" #include "../../general/forall.hpp" #include "../../fem/pbilinearform.hpp" namespace mfem { #ifdef MFEM_USE_MPI BatchedLOR_ADS::BatchedLOR_ADS(ParFiniteElementSpace &pfes_ho_, const Vector &X_vert) : face_fes(pfes_ho_), order(face_fes.GetMaxElementOrder()), edge_fec(order, dim), edge_fes(face_fes.GetParMesh(), &edge_fec), ams(edge_fes, X_vert) { MFEM_VERIFY(face_fes.GetParMesh()->Dimension() == dim, "Bad dimension.") FormCurlMatrix(); } void BatchedLOR_ADS::Form3DFaceToEdge(Array &face2edge) { const int o = order; const int op1 = o + 1; const int nface = dim*o*o*op1; face2edge.SetSize(4*nface); auto f2e = Reshape(face2edge.HostWrite(), 4, nface); for (int c=0; c face2edge; Form3DFaceToEdge(face2edge); ElementDofOrdering ordering = ElementDofOrdering::LEXICOGRAPHIC; const auto *R_f = dynamic_cast( face_fes.GetElementRestriction(ordering)); const auto *R_e = dynamic_cast( edge_fes.GetElementRestriction(ordering)); MFEM_VERIFY(R_f != NULL && R_e != NULL, ""); const int nel_ho = edge_fes.GetNE(); const int nedge_per_el = dim*order*(order+1)*(order+1); const int nface_per_el = dim*order*order*(order+1); const auto offsets_f = R_f->Offsets().Read(); const auto indices_f = R_f->Indices().Read(); const auto gather_e = Reshape(R_e->GatherMap().Read(), nedge_per_el, nel_ho); const auto f2e = Reshape(face2edge.Read(), 4, nface_per_el); // Fill J and data C_local.GetMemoryJ().New(nnz, Device::GetDeviceMemoryType()); C_local.GetMemoryData().New(nnz, Device::GetDeviceMemoryType()); auto J = C_local.WriteJ(); auto V = C_local.WriteData(); // Loop over Raviart-Thomas L-DOFs mfem::forall(nface_dof, [=] MFEM_HOST_DEVICE (int i) { const int sj = indices_f[offsets_f[i]]; // signed const int j = (sj >= 0) ? sj : -1 - sj; const int sgn_f = (sj >= 0) ? 1 : -1; const int j_loc = j%nface_per_el; const int j_el = j/nface_per_el; for (int k=0; k<4; ++k) { const int je_loc = f2e(k, j_loc); const int sje = gather_e(je_loc, j_el); // signed const int je = (sje >= 0) ? sje : -1 - sje; const int sgn_e = (sje >= 0) ? 1 : -1; const int sgn = (k == 1 || k == 2) ? -1 : 1; J[i*4 + k] = je; V[i*4 + k] = sgn*sgn_f*sgn_e; } }); // Create a block diagonal parallel matrix OperatorHandle C_diag(Operator::Hypre_ParCSR); C_diag.MakeRectangularBlockDiag(edge_fes.GetComm(), face_fes.GlobalVSize(), edge_fes.GlobalVSize(), face_fes.GetDofOffsets(), edge_fes.GetDofOffsets(), &C_local); // Assemble the parallel gradient matrix, must be deleted by the caller if (IsIdentityProlongation(edge_fes.GetProlongationMatrix())) { C = C_diag.As(); C_diag.SetOperatorOwner(false); HypreStealOwnership(*C, C_local); } else { OperatorHandle Rt(Transpose(*face_fes.GetRestrictionMatrix())); OperatorHandle Rt_diag(Operator::Hypre_ParCSR); Rt_diag.MakeRectangularBlockDiag(face_fes.GetComm(), face_fes.GlobalVSize(), face_fes.GlobalTrueVSize(), face_fes.GetDofOffsets(), face_fes.GetTrueDofOffsets(), Rt.As()); C = RAP(Rt_diag.As(), C_diag.As(), edge_fes.Dof_TrueDof_Matrix()); } C->CopyRowStarts(); C->CopyColStarts(); } HypreParMatrix *BatchedLOR_ADS::StealCurlMatrix() { return StealPointer(C); } BatchedLOR_ADS::~BatchedLOR_ADS() { delete C; } LORSolver::LORSolver( ParBilinearForm &a_ho, const Array &ess_tdof_list, int ref_type) { MFEM_VERIFY(a_ho.FESpace()->GetMesh()->Dimension() == 3, "The ADS solver is only valid in 3D."); if (BatchedLORAssembly::FormIsSupported(a_ho)) { ParFiniteElementSpace &pfes = *a_ho.ParFESpace(); BatchedLORAssembly batched_lor(pfes); BatchedLOR_ADS lor_ads(pfes, batched_lor.GetLORVertexCoordinates()); BatchedLOR_AMS &lor_ams = lor_ads.GetAMS(); batched_lor.Assemble(a_ho, ess_tdof_list, A); xyz = lor_ams.StealCoordinateVector(); solver = new HypreADS(*A.As(), lor_ads.StealCurlMatrix(), lor_ams.StealGradientMatrix(), lor_ams.StealXCoordinate(), lor_ams.StealYCoordinate(), lor_ams.StealZCoordinate()); } else { ParLORDiscretization lor(a_ho, ess_tdof_list, ref_type); // Assume ownership of the system matrix so that `lor` can be safely // deleted A.Reset(lor.GetAssembledSystem().Ptr()); lor.GetAssembledSystem().SetOperatorOwner(false); solver = new HypreADS(lor.GetAssembledMatrix(), &lor.GetParFESpace()); } width = solver->Width(); height = solver->Height(); } void LORSolver::SetOperator(const Operator &op) { solver->SetOperator(op); } void LORSolver::Mult(const Vector &x, Vector &y) const { solver->Mult(x, y); } HypreADS &LORSolver::GetSolver() { return *solver; } const HypreADS &LORSolver::GetSolver() const { return *solver; } LORSolver::~LORSolver() { delete solver; delete xyz; } #endif } // namespace mfem