// Copyright (c) 2010-2020, 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. // Implementations of classes FABilinearFormExtension, EABilinearFormExtension, // PABilinearFormExtension and MFBilinearFormExtension. #include "../general/forall.hpp" #include "bilinearform.hpp" #include "libceed/ceed.hpp" namespace mfem { BilinearFormExtension::BilinearFormExtension(BilinearForm *form) : Operator(form->Size()), a(form) { // empty } const Operator *BilinearFormExtension::GetProlongation() const { return a->GetProlongation(); } const Operator *BilinearFormExtension::GetRestriction() const { return a->GetRestriction(); } // Data and methods for partially-assembled bilinear forms PABilinearFormExtension::PABilinearFormExtension(BilinearForm *form) : BilinearFormExtension(form), trialFes(a->FESpace()), testFes(a->FESpace()) { elem_restrict = NULL; int_face_restrict_lex = NULL; bdr_face_restrict_lex = NULL; } void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m) { ElementDofOrdering ordering = UsesTensorBasis(*a->FESpace())? ElementDofOrdering::LEXICOGRAPHIC: ElementDofOrdering::NATIVE; elem_restrict = trialFes->GetElementRestriction(ordering); if (elem_restrict) { localX.SetSize(elem_restrict->Height(), Device::GetDeviceMemoryType()); localY.SetSize(elem_restrict->Height(), Device::GetDeviceMemoryType()); localY.UseDevice(true); // ensure 'localY = 0.0' is done on device } // Construct face restriction operators only if the bilinear form has // interior or boundary face integrators if (int_face_restrict_lex == NULL && a->GetFBFI()->Size() > 0) { int_face_restrict_lex = trialFes->GetFaceRestriction( ElementDofOrdering::LEXICOGRAPHIC, FaceType::Interior); faceIntX.SetSize(int_face_restrict_lex->Height(), Device::GetMemoryType()); faceIntY.SetSize(int_face_restrict_lex->Height(), Device::GetMemoryType()); faceIntY.UseDevice(true); // ensure 'faceIntY = 0.0' is done on device } if (bdr_face_restrict_lex == NULL && a->GetBFBFI()->Size() > 0) { bdr_face_restrict_lex = trialFes->GetFaceRestriction( ElementDofOrdering::LEXICOGRAPHIC, FaceType::Boundary, m); faceBdrX.SetSize(bdr_face_restrict_lex->Height(), Device::GetMemoryType()); faceBdrY.SetSize(bdr_face_restrict_lex->Height(), Device::GetMemoryType()); faceBdrY.UseDevice(true); // ensure 'faceBoundY = 0.0' is done on device } } void PABilinearFormExtension::Assemble() { SetupRestrictionOperators(L2FaceValues::DoubleValued); Array &integrators = *a->GetDBFI(); const int integratorCount = integrators.Size(); for (int i = 0; i < integratorCount; ++i) { integrators[i]->AssemblePA(*a->FESpace()); } Array &intFaceIntegrators = *a->GetFBFI(); const int intFaceIntegratorCount = intFaceIntegrators.Size(); for (int i = 0; i < intFaceIntegratorCount; ++i) { intFaceIntegrators[i]->AssemblePAInteriorFaces(*a->FESpace()); } Array &bdrFaceIntegrators = *a->GetBFBFI(); const int boundFaceIntegratorCount = bdrFaceIntegrators.Size(); for (int i = 0; i < boundFaceIntegratorCount; ++i) { bdrFaceIntegrators[i]->AssemblePABoundaryFaces(*a->FESpace()); } } void PABilinearFormExtension::AssembleDiagonal(Vector &y) const { Array &integrators = *a->GetDBFI(); const int iSz = integrators.Size(); if (elem_restrict) { localY = 0.0; for (int i = 0; i < iSz; ++i) { integrators[i]->AssembleDiagonalPA(localY); } const ElementRestriction* H1elem_restrict = dynamic_cast(elem_restrict); if (H1elem_restrict) { H1elem_restrict->MultTransposeUnsigned(localY, y); } else { elem_restrict->MultTranspose(localY, y); } } else { y.UseDevice(true); // typically this is a large vector, so store on device y = 0.0; for (int i = 0; i < iSz; ++i) { integrators[i]->AssembleDiagonalPA(y); } } } void PABilinearFormExtension::Update() { FiniteElementSpace *fes = a->FESpace(); height = width = fes->GetVSize(); trialFes = fes; testFes = fes; elem_restrict = nullptr; int_face_restrict_lex = nullptr; bdr_face_restrict_lex = nullptr; } void PABilinearFormExtension::FormSystemMatrix(const Array &ess_tdof_list, OperatorHandle &A) { Operator *oper; Operator::FormSystemOperator(ess_tdof_list, oper); A.Reset(oper); // A will own oper } void PABilinearFormExtension::FormLinearSystem(const Array &ess_tdof_list, Vector &x, Vector &b, OperatorHandle &A, Vector &X, Vector &B, int copy_interior) { Operator *oper; Operator::FormLinearSystem(ess_tdof_list, x, b, oper, X, B, copy_interior); A.Reset(oper); // A will own oper } void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const { Array &integrators = *a->GetDBFI(); const int iSz = integrators.Size(); if (DeviceCanUseCeed() || !elem_restrict) { y.UseDevice(true); // typically this is a large vector, so store on device y = 0.0; for (int i = 0; i < iSz; ++i) { integrators[i]->AddMultPA(x, y); } } else { elem_restrict->Mult(x, localX); localY = 0.0; for (int i = 0; i < iSz; ++i) { integrators[i]->AddMultPA(localX, localY); } elem_restrict->MultTranspose(localY, y); } Array &intFaceIntegrators = *a->GetFBFI(); const int iFISz = intFaceIntegrators.Size(); if (int_face_restrict_lex && iFISz>0) { int_face_restrict_lex->Mult(x, faceIntX); if (faceIntX.Size()>0) { faceIntY = 0.0; for (int i = 0; i < iFISz; ++i) { intFaceIntegrators[i]->AddMultPA(faceIntX, faceIntY); } int_face_restrict_lex->MultTranspose(faceIntY, y); } } Array &bdrFaceIntegrators = *a->GetBFBFI(); const int bFISz = bdrFaceIntegrators.Size(); if (bdr_face_restrict_lex && bFISz>0) { bdr_face_restrict_lex->Mult(x, faceBdrX); if (faceBdrX.Size()>0) { faceBdrY = 0.0; for (int i = 0; i < bFISz; ++i) { bdrFaceIntegrators[i]->AddMultPA(faceBdrX, faceBdrY); } bdr_face_restrict_lex->MultTranspose(faceBdrY, y); } } } void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const { Array &integrators = *a->GetDBFI(); const int iSz = integrators.Size(); if (elem_restrict) { elem_restrict->Mult(x, localX); localY = 0.0; for (int i = 0; i < iSz; ++i) { integrators[i]->AddMultTransposePA(localX, localY); } elem_restrict->MultTranspose(localY, y); } else { y.UseDevice(true); y = 0.0; for (int i = 0; i < iSz; ++i) { integrators[i]->AddMultTransposePA(x, y); } } Array &intFaceIntegrators = *a->GetFBFI(); const int iFISz = intFaceIntegrators.Size(); if (int_face_restrict_lex && iFISz>0) { int_face_restrict_lex->Mult(x, faceIntX); if (faceIntX.Size()>0) { faceIntY = 0.0; for (int i = 0; i < iFISz; ++i) { intFaceIntegrators[i]->AddMultTransposePA(faceIntX, faceIntY); } int_face_restrict_lex->MultTranspose(faceIntY, y); } } Array &bdrFaceIntegrators = *a->GetBFBFI(); const int bFISz = bdrFaceIntegrators.Size(); if (bdr_face_restrict_lex && bFISz>0) { bdr_face_restrict_lex->Mult(x, faceBdrX); if (faceBdrX.Size()>0) { faceBdrY = 0.0; for (int i = 0; i < bFISz; ++i) { bdrFaceIntegrators[i]->AddMultTransposePA(faceBdrX, faceBdrY); } bdr_face_restrict_lex->MultTranspose(faceBdrY, y); } } } // Data and methods for element-assembled bilinear forms EABilinearFormExtension::EABilinearFormExtension(BilinearForm *form) : PABilinearFormExtension(form) { } void EABilinearFormExtension::Assemble() { SetupRestrictionOperators(L2FaceValues::SingleValued); ne = trialFes->GetMesh()->GetNE(); elemDofs = trialFes->GetFE(0)->GetDof(); ea_data.SetSize(ne*elemDofs*elemDofs, Device::GetMemoryType()); ea_data.UseDevice(true); ea_data = 0.0; Array &integrators = *a->GetDBFI(); const int integratorCount = integrators.Size(); for (int i = 0; i < integratorCount; ++i) { integrators[i]->AssembleEA(*a->FESpace(), ea_data); } faceDofs = trialFes -> GetTraceElement(0, trialFes->GetMesh()->GetFaceBaseGeometry(0)) -> GetDof(); Array &intFaceIntegrators = *a->GetFBFI(); const int intFaceIntegratorCount = intFaceIntegrators.Size(); if (intFaceIntegratorCount>0) { nf_int = trialFes->GetNFbyType(FaceType::Interior); ea_data_int.SetSize(2*nf_int*faceDofs*faceDofs, Device::GetMemoryType()); ea_data_ext.SetSize(2*nf_int*faceDofs*faceDofs, Device::GetMemoryType()); ea_data_int = 0.0; ea_data_ext = 0.0; } for (int i = 0; i < intFaceIntegratorCount; ++i) { intFaceIntegrators[i]->AssembleEAInteriorFaces(*a->FESpace(), ea_data_int, ea_data_ext); } Array &bdrFaceIntegrators = *a->GetBFBFI(); const int boundFaceIntegratorCount = bdrFaceIntegrators.Size(); if (boundFaceIntegratorCount>0) { nf_bdr = trialFes->GetNFbyType(FaceType::Boundary); ea_data_bdr.SetSize(nf_bdr*faceDofs*faceDofs, Device::GetMemoryType()); ea_data_bdr = 0.0; } for (int i = 0; i < boundFaceIntegratorCount; ++i) { bdrFaceIntegrators[i]->AssembleEABoundaryFaces(*a->FESpace(),ea_data_bdr); } } void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const { // Apply the Element Restriction const bool useRestrict = !DeviceCanUseCeed() && elem_restrict; if (!useRestrict) { y.UseDevice(true); // typically this is a large vector, so store on device y = 0.0; } else { elem_restrict->Mult(x, localX); localY = 0.0; } // Apply the Element Matrices const int NDOFS = elemDofs; auto X = Reshape(useRestrict?localX.Read():x.Read(), NDOFS, ne); auto Y = Reshape(useRestrict?localY.ReadWrite():y.ReadWrite(), NDOFS, ne); auto A = Reshape(ea_data.Read(), NDOFS, NDOFS, ne); MFEM_FORALL(glob_j, ne*NDOFS, { const int e = glob_j/NDOFS; const int j = glob_j%NDOFS; double res = 0.0; for (int i = 0; i < NDOFS; i++) { res += A(i, j, e)*X(i, e); } Y(j, e) += res; }); // Apply the Element Restriction transposed if (useRestrict) { elem_restrict->MultTranspose(localY, y); } // Treatment of interior faces Array &intFaceIntegrators = *a->GetFBFI(); const int iFISz = intFaceIntegrators.Size(); if (int_face_restrict_lex && iFISz>0) { // Apply the Interior Face Restriction int_face_restrict_lex->Mult(x, faceIntX); if (faceIntX.Size()>0) { faceIntY = 0.0; // Apply the interior face matrices const int NDOFS = faceDofs; auto X = Reshape(faceIntX.Read(), NDOFS, 2, nf_int); auto Y = Reshape(faceIntY.ReadWrite(), NDOFS, 2, nf_int); auto A_int = Reshape(ea_data_int.Read(), NDOFS, NDOFS, 2, nf_int); MFEM_FORALL(glob_j, nf_int*NDOFS, { const int f = glob_j/NDOFS; const int j = glob_j%NDOFS; double res = 0.0; for (int i = 0; i < NDOFS; i++) { res += A_int(i, j, 0, f)*X(i, 0, f); } Y(j, 0, f) += res; res = 0.0; for (int i = 0; i < NDOFS; i++) { res += A_int(i, j, 1, f)*X(i, 1, f); } Y(j, 1, f) += res; }); auto A_ext = Reshape(ea_data_ext.Read(), NDOFS, NDOFS, 2, nf_int); MFEM_FORALL(glob_j, nf_int*NDOFS, { const int f = glob_j/NDOFS; const int j = glob_j%NDOFS; double res = 0.0; for (int i = 0; i < NDOFS; i++) { res += A_ext(i, j, 0, f)*X(i, 0, f); } Y(j, 1, f) += res; res = 0.0; for (int i = 0; i < NDOFS; i++) { res += A_ext(i, j, 1, f)*X(i, 1, f); } Y(j, 0, f) += res; }); // Apply the Interior Face Restriction transposed int_face_restrict_lex->MultTranspose(faceIntY, y); } } // Treatment of boundary faces Array &bdrFaceIntegrators = *a->GetBFBFI(); const int bFISz = bdrFaceIntegrators.Size(); if (bdr_face_restrict_lex && bFISz>0) { // Apply the Boundary Face Restriction bdr_face_restrict_lex->Mult(x, faceBdrX); if (faceBdrX.Size()>0) { faceBdrY = 0.0; // Apply the boundary face matrices const int NDOFS = faceDofs; auto X = Reshape(faceBdrX.Read(), NDOFS, nf_bdr); auto Y = Reshape(faceBdrY.ReadWrite(), NDOFS, nf_bdr); auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr); MFEM_FORALL(glob_j, nf_bdr*NDOFS, { const int f = glob_j/NDOFS; const int j = glob_j%NDOFS; double res = 0.0; for (int i = 0; i < NDOFS; i++) { res += A(i, j, f)*X(i, f); } Y(j, f) += res; }); // Apply the Boundary Face Restriction transposed bdr_face_restrict_lex->MultTranspose(faceBdrY, y); } } } void EABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const { // Apply the Element Restriction const bool useRestrict = DeviceCanUseCeed() || !elem_restrict; if (!useRestrict) { y.UseDevice(true); // typically this is a large vector, so store on device y = 0.0; } else { elem_restrict->Mult(x, localX); localY = 0.0; } // Apply the Element Matrices transposed const int NDOFS = elemDofs; auto X = Reshape(useRestrict?localX.Read():x.Read(), NDOFS, ne); auto Y = Reshape(useRestrict?localY.ReadWrite():y.ReadWrite(), NDOFS, ne); auto A = Reshape(ea_data.Read(), NDOFS, NDOFS, ne); MFEM_FORALL(glob_j, ne*NDOFS, { const int e = glob_j/NDOFS; const int j = glob_j%NDOFS; double res = 0.0; for (int i = 0; i < NDOFS; i++) { res += A(j, i, e)*X(i, e); } Y(j, e) += res; }); // Apply the Element Restriction transposed if (useRestrict) { elem_restrict->MultTranspose(localY, y); } // Treatment of interior faces Array &intFaceIntegrators = *a->GetFBFI(); const int iFISz = intFaceIntegrators.Size(); if (int_face_restrict_lex && iFISz>0) { // Apply the Interior Face Restriction int_face_restrict_lex->Mult(x, faceIntX); if (faceIntX.Size()>0) { faceIntY = 0.0; // Apply the interior face matrices transposed const int NDOFS = faceDofs; auto X = Reshape(faceIntX.Read(), NDOFS, 2, nf_int); auto Y = Reshape(faceIntY.ReadWrite(), NDOFS, 2, nf_int); auto A_int = Reshape(ea_data_int.Read(), NDOFS, NDOFS, 2, nf_int); MFEM_FORALL(glob_j, nf_int*NDOFS, { const int f = glob_j/NDOFS; const int j = glob_j%NDOFS; double res = 0.0; for (int i = 0; i < NDOFS; i++) { res += A_int(j, i, 0, f)*X(i, 0, f); } Y(j, 0, f) += res; res = 0.0; for (int i = 0; i < NDOFS; i++) { res += A_int(j, i, 1, f)*X(i, 1, f); } Y(j, 1, f) += res; }); auto A_ext = Reshape(ea_data_ext.Read(), NDOFS, NDOFS, 2, nf_int); MFEM_FORALL(glob_j, nf_int*NDOFS, { const int f = glob_j/NDOFS; const int j = glob_j%NDOFS; double res = 0.0; for (int i = 0; i < NDOFS; i++) { res += A_ext(j, i, 0, f)*X(i, 0, f); } Y(j, 1, f) += res; res = 0.0; for (int i = 0; i < NDOFS; i++) { res += A_ext(j, i, 1, f)*X(i, 1, f); } Y(j, 0, f) += res; }); // Apply the Interior Face Restriction transposed int_face_restrict_lex->MultTranspose(faceIntY, y); } } // Treatment of boundary faces Array &bdrFaceIntegrators = *a->GetBFBFI(); const int bFISz = bdrFaceIntegrators.Size(); if (bdr_face_restrict_lex && bFISz>0) { // Apply the Boundary Face Restriction bdr_face_restrict_lex->Mult(x, faceBdrX); if (faceBdrX.Size()>0) { faceBdrY = 0.0; // Apply the boundary face matrices transposed const int NDOFS = faceDofs; auto X = Reshape(faceBdrX.Read(), NDOFS, nf_bdr); auto Y = Reshape(faceBdrY.ReadWrite(), NDOFS, nf_bdr); auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr); MFEM_FORALL(glob_j, nf_bdr*NDOFS, { const int f = glob_j/NDOFS; const int j = glob_j%NDOFS; double res = 0.0; for (int i = 0; i < NDOFS; i++) { res += A(j, i, f)*X(i, f); } Y(j, f) += res; }); // Apply the Boundary Face Restriction transposed bdr_face_restrict_lex->MultTranspose(faceBdrY, y); } } } MixedBilinearFormExtension::MixedBilinearFormExtension(MixedBilinearForm *form) : Operator(form->Height(), form->Width()), a(form) { // empty } const Operator *MixedBilinearFormExtension::GetProlongation() const { return a->GetProlongation(); } const Operator *MixedBilinearFormExtension::GetRestriction() const { return a->GetRestriction(); } const Operator *MixedBilinearFormExtension::GetOutputProlongation() const { return a->GetOutputProlongation(); } const Operator *MixedBilinearFormExtension::GetOutputRestriction() const { return a->GetOutputRestriction(); } // Data and methods for partially-assembled bilinear forms PAMixedBilinearFormExtension::PAMixedBilinearFormExtension( MixedBilinearForm *form) : MixedBilinearFormExtension(form), trialFes(form->TrialFESpace()), testFes(form->TestFESpace()), elem_restrict_trial(NULL), elem_restrict_test(NULL) { Update(); } void PAMixedBilinearFormExtension::Assemble() { Array &integrators = *a->GetDBFI(); const int integratorCount = integrators.Size(); for (int i = 0; i < integratorCount; ++i) { integrators[i]->AssemblePA(*trialFes, *testFes); } } void PAMixedBilinearFormExtension::Update() { trialFes = a->TrialFESpace(); testFes = a->TestFESpace(); height = testFes->GetVSize(); width = trialFes->GetVSize(); elem_restrict_trial = trialFes->GetElementRestriction( ElementDofOrdering::LEXICOGRAPHIC); elem_restrict_test = testFes->GetElementRestriction( ElementDofOrdering::LEXICOGRAPHIC); if (elem_restrict_trial) { localTrial.UseDevice(true); localTrial.SetSize(elem_restrict_trial->Height(), Device::GetMemoryType()); } if (elem_restrict_test) { localTest.UseDevice(true); // ensure 'localY = 0.0' is done on device localTest.SetSize(elem_restrict_test->Height(), Device::GetMemoryType()); } } void PAMixedBilinearFormExtension::FormRectangularSystemOperator( const Array &trial_tdof_list, const Array &test_tdof_list, OperatorHandle &A) { Operator * oper; Operator::FormRectangularSystemOperator(trial_tdof_list, test_tdof_list, oper); A.Reset(oper); // A will own oper } void PAMixedBilinearFormExtension::FormRectangularLinearSystem( const Array &trial_tdof_list, const Array &test_tdof_list, Vector &x, Vector &b, OperatorHandle &A, Vector &X, Vector &B) { Operator *oper; Operator::FormRectangularLinearSystem(trial_tdof_list, test_tdof_list, x, b, oper, X, B); A.Reset(oper); // A will own oper } void PAMixedBilinearFormExtension::SetupMultInputs( const Operator *elem_restrict_x, const Vector &x, Vector &localX, const Operator *elem_restrict_y, Vector &y, Vector &localY, const double c) const { // * G operation: localX = c*local(x) if (elem_restrict_x) { elem_restrict_x->Mult(x, localX); if (c != 1.0) { localX *= c; } } else { if (c == 1.0) { localX.SyncAliasMemory(x); } else { localX.Set(c, x); } } if (elem_restrict_y) { localY = 0.0; } else { y.UseDevice(true); localY.SyncAliasMemory(y); } } void PAMixedBilinearFormExtension::Mult(const Vector &x, Vector &y) const { y = 0.0; AddMult(x, y); } void PAMixedBilinearFormExtension::AddMult(const Vector &x, Vector &y, const double c) const { Array &integrators = *a->GetDBFI(); const int iSz = integrators.Size(); // * G operation SetupMultInputs(elem_restrict_trial, x, localTrial, elem_restrict_test, y, localTest, c); // * B^TDB operation for (int i = 0; i < iSz; ++i) { integrators[i]->AddMultPA(localTrial, localTest); } // * G^T operation if (elem_restrict_test) { tempY.SetSize(y.Size()); elem_restrict_test->MultTranspose(localTest, tempY); y += tempY; } } void PAMixedBilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const { y = 0.0; AddMultTranspose(x, y); } void PAMixedBilinearFormExtension::AddMultTranspose(const Vector &x, Vector &y, const double c) const { Array &integrators = *a->GetDBFI(); const int iSz = integrators.Size(); // * G operation SetupMultInputs(elem_restrict_test, x, localTest, elem_restrict_trial, y, localTrial, c); // * B^TD^TB operation for (int i = 0; i < iSz; ++i) { integrators[i]->AddMultTransposePA(localTest, localTrial); } // * G^T operation if (elem_restrict_trial) { tempY.SetSize(y.Size()); elem_restrict_trial->MultTranspose(localTrial, tempY); y += tempY; } } void PAMixedBilinearFormExtension::AssembleDiagonal_ADAt(const Vector &D, Vector &diag) const { Array &integrators = *a->GetDBFI(); const int iSz = integrators.Size(); if (elem_restrict_trial) { const ElementRestriction* H1elem_restrict_trial = dynamic_cast(elem_restrict_trial); if (H1elem_restrict_trial) { H1elem_restrict_trial->MultUnsigned(D, localTrial); } else { elem_restrict_trial->Mult(D, localTrial); } } if (elem_restrict_test) { localTest = 0.0; for (int i = 0; i < iSz; ++i) { if (elem_restrict_trial) { integrators[i]->AssembleDiagonalPA_ADAt(localTrial, localTest); } else { integrators[i]->AssembleDiagonalPA_ADAt(D, localTest); } } const ElementRestriction* H1elem_restrict_test = dynamic_cast(elem_restrict_test); if (H1elem_restrict_test) { H1elem_restrict_test->MultTransposeUnsigned(localTest, diag); } else { elem_restrict_test->MultTranspose(localTest, diag); } } else { diag.UseDevice(true); // typically this is a large vector, so store on device diag = 0.0; for (int i = 0; i < iSz; ++i) { if (elem_restrict_trial) { integrators[i]->AssembleDiagonalPA_ADAt(localTrial, diag); } else { integrators[i]->AssembleDiagonalPA_ADAt(D, diag); } } } } } // namespace mfem