// 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. // Implementation of class BilinearForm #include "fem.hpp" #include "../general/device.hpp" #include namespace mfem { void BilinearForm::AllocMat() { if (static_cond) { return; } if (precompute_sparsity == 0 || fes->GetVDim() > 1) { mat = new SparseMatrix(height); return; } const Table &elem_dof = fes->GetElementToDofTable(); Table dof_dof; if (fbfi.Size() > 0) { // the sparsity pattern is defined from the map: face->element->dof Table face_dof, dof_face; { Table *face_elem = fes->GetMesh()->GetFaceToElementTable(); mfem::Mult(*face_elem, elem_dof, face_dof); delete face_elem; } Transpose(face_dof, dof_face, height); mfem::Mult(dof_face, face_dof, dof_dof); } else { // the sparsity pattern is defined from the map: element->dof Table dof_elem; Transpose(elem_dof, dof_elem, height); mfem::Mult(dof_elem, elem_dof, dof_dof); } dof_dof.SortRows(); int *I = dof_dof.GetI(); int *J = dof_dof.GetJ(); double *data = Memory(I[height]); mat = new SparseMatrix(I, J, data, height, height, true, true, true); *mat = 0.0; dof_dof.LoseData(); } BilinearForm::BilinearForm(FiniteElementSpace * f) : Matrix (f->GetVSize()) { fes = f; sequence = f->GetSequence(); mat = mat_e = NULL; extern_bfs = 0; element_matrices = NULL; static_cond = NULL; hybridization = NULL; precompute_sparsity = 0; diag_policy = DIAG_KEEP; assembly = AssemblyLevel::FULL; batch = 1; ext = NULL; } BilinearForm::BilinearForm (FiniteElementSpace * f, BilinearForm * bf, int ps) : Matrix (f->GetVSize()) { fes = f; sequence = f->GetSequence(); mat_e = NULL; extern_bfs = 1; element_matrices = NULL; static_cond = NULL; hybridization = NULL; precompute_sparsity = ps; diag_policy = DIAG_KEEP; assembly = AssemblyLevel::FULL; batch = 1; ext = NULL; // Copy the pointers to the integrators dbfi = bf->dbfi; bbfi = bf->bbfi; bbfi_marker = bf->bbfi_marker; fbfi = bf->fbfi; bfbfi = bf->bfbfi; bfbfi_marker = bf->bfbfi_marker; AllocMat(); } void BilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level) { if (ext) { MFEM_ABORT("the assembly level has already been set!"); } assembly = assembly_level; switch (assembly) { case AssemblyLevel::FULL: // ext = new FABilinearFormExtension(this); // Use the original BilinearForm implementation for now break; case AssemblyLevel::ELEMENT: ext = new EABilinearFormExtension(this); break; case AssemblyLevel::PARTIAL: ext = new PABilinearFormExtension(this); break; case AssemblyLevel::NONE: mfem_error("Matrix-free action not supported yet... stay tuned!"); // ext = new MFBilinearFormExtension(this); break; default: mfem_error("Unknown assembly level"); } } void BilinearForm::EnableStaticCondensation() { delete static_cond; if (assembly != AssemblyLevel::FULL) { static_cond = NULL; MFEM_WARNING("Static condensation not supported for this assembly level"); return; } static_cond = new StaticCondensation(fes); if (static_cond->ReducesTrueVSize()) { bool symmetric = false; // TODO bool block_diagonal = false; // TODO static_cond->Init(symmetric, block_diagonal); } else { delete static_cond; static_cond = NULL; } } void BilinearForm::EnableHybridization(FiniteElementSpace *constr_space, BilinearFormIntegrator *constr_integ, const Array &ess_tdof_list) { delete hybridization; if (assembly != AssemblyLevel::FULL) { delete constr_integ; hybridization = NULL; MFEM_WARNING("Hybridization not supported for this assembly level"); return; } hybridization = new Hybridization(fes, constr_space); hybridization->SetConstraintIntegrator(constr_integ); hybridization->Init(ess_tdof_list); } void BilinearForm::UseSparsity(int *I, int *J, bool isSorted) { if (static_cond) { return; } if (mat) { if (mat->Finalized() && mat->GetI() == I && mat->GetJ() == J) { return; // mat is already using the given sparsity } delete mat; } height = width = fes->GetVSize(); mat = new SparseMatrix(I, J, NULL, height, width, false, true, isSorted); } void BilinearForm::UseSparsity(SparseMatrix &A) { MFEM_ASSERT(A.Height() == fes->GetVSize() && A.Width() == fes->GetVSize(), "invalid matrix A dimensions: " << A.Height() << " x " << A.Width()); MFEM_ASSERT(A.Finalized(), "matrix A must be Finalized"); UseSparsity(A.GetI(), A.GetJ(), A.ColumnsAreSorted()); } double& BilinearForm::Elem (int i, int j) { return mat -> Elem(i,j); } const double& BilinearForm::Elem (int i, int j) const { return mat -> Elem(i,j); } MatrixInverse * BilinearForm::Inverse() const { return mat -> Inverse(); } void BilinearForm::Finalize (int skip_zeros) { if (assembly == AssemblyLevel::FULL) { if (!static_cond) { mat->Finalize(skip_zeros); } if (mat_e) { mat_e->Finalize(skip_zeros); } if (static_cond) { static_cond->Finalize(); } if (hybridization) { hybridization->Finalize(); } } } void BilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi) { dbfi.Append(bfi); } void BilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi) { bbfi.Append (bfi); bbfi_marker.Append(NULL); // NULL marker means apply everywhere } void BilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi, Array &bdr_marker) { bbfi.Append (bfi); bbfi_marker.Append(&bdr_marker); } void BilinearForm::AddInteriorFaceIntegrator (BilinearFormIntegrator * bfi) { fbfi.Append (bfi); } void BilinearForm::AddBdrFaceIntegrator(BilinearFormIntegrator *bfi) { bfbfi.Append(bfi); bfbfi_marker.Append(NULL); // NULL marker means apply everywhere } void BilinearForm::AddBdrFaceIntegrator(BilinearFormIntegrator *bfi, Array &bdr_marker) { bfbfi.Append(bfi); bfbfi_marker.Append(&bdr_marker); } void BilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat) { if (element_matrices) { elmat.SetSize(element_matrices->SizeI(), element_matrices->SizeJ()); elmat = element_matrices->GetData(i); return; } if (dbfi.Size()) { const FiniteElement &fe = *fes->GetFE(i); ElementTransformation *eltrans = fes->GetElementTransformation(i); dbfi[0]->AssembleElementMatrix(fe, *eltrans, elmat); for (int k = 1; k < dbfi.Size(); k++) { dbfi[k]->AssembleElementMatrix(fe, *eltrans, elemmat); elmat += elemmat; } } else { fes->GetElementVDofs(i, vdofs); elmat.SetSize(vdofs.Size()); elmat = 0.0; } } void BilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat) { if (bbfi.Size()) { const FiniteElement &be = *fes->GetBE(i); ElementTransformation *eltrans = fes->GetBdrElementTransformation(i); bbfi[0]->AssembleElementMatrix(be, *eltrans, elmat); for (int k = 1; k < bbfi.Size(); k++) { bbfi[k]->AssembleElementMatrix(be, *eltrans, elemmat); elmat += elemmat; } } else { fes->GetBdrElementVDofs(i, vdofs); elmat.SetSize(vdofs.Size()); elmat = 0.0; } } void BilinearForm::AssembleElementMatrix( int i, const DenseMatrix &elmat, int skip_zeros) { AssembleElementMatrix(i, elmat, vdofs, skip_zeros); } void BilinearForm::AssembleElementMatrix( int i, const DenseMatrix &elmat, Array &vdofs, int skip_zeros) { fes->GetElementVDofs(i, vdofs); if (static_cond) { static_cond->AssembleMatrix(i, elmat); } else { if (mat == NULL) { AllocMat(); } mat->AddSubMatrix(vdofs, vdofs, elmat, skip_zeros); if (hybridization) { hybridization->AssembleMatrix(i, elmat); } } } void BilinearForm::AssembleBdrElementMatrix( int i, const DenseMatrix &elmat, int skip_zeros) { AssembleBdrElementMatrix(i, elmat, vdofs, skip_zeros); } void BilinearForm::AssembleBdrElementMatrix( int i, const DenseMatrix &elmat, Array &vdofs, int skip_zeros) { fes->GetBdrElementVDofs(i, vdofs); if (static_cond) { static_cond->AssembleBdrMatrix(i, elmat); } else { if (mat == NULL) { AllocMat(); } mat->AddSubMatrix(vdofs, vdofs, elmat, skip_zeros); if (hybridization) { hybridization->AssembleBdrMatrix(i, elmat); } } } void BilinearForm::Assemble(int skip_zeros) { if (ext) { ext->Assemble(); return; } ElementTransformation *eltrans; Mesh *mesh = fes -> GetMesh(); DenseMatrix elmat, *elmat_p; if (mat == NULL) { AllocMat(); } #ifdef MFEM_USE_LEGACY_OPENMP int free_element_matrices = 0; if (!element_matrices) { ComputeElementMatrices(); free_element_matrices = 1; } #endif if (dbfi.Size()) { for (int i = 0; i < fes -> GetNE(); i++) { fes->GetElementVDofs(i, vdofs); if (element_matrices) { elmat_p = &(*element_matrices)(i); } else { const FiniteElement &fe = *fes->GetFE(i); eltrans = fes->GetElementTransformation(i); dbfi[0]->AssembleElementMatrix(fe, *eltrans, elmat); for (int k = 1; k < dbfi.Size(); k++) { dbfi[k]->AssembleElementMatrix(fe, *eltrans, elemmat); elmat += elemmat; } elmat_p = &elmat; } if (static_cond) { static_cond->AssembleMatrix(i, *elmat_p); } else { mat->AddSubMatrix(vdofs, vdofs, *elmat_p, skip_zeros); if (hybridization) { hybridization->AssembleMatrix(i, *elmat_p); } } } } if (bbfi.Size()) { // Which boundary attributes need to be processed? Array bdr_attr_marker(mesh->bdr_attributes.Size() ? mesh->bdr_attributes.Max() : 0); bdr_attr_marker = 0; for (int k = 0; k < bbfi.Size(); k++) { if (bbfi_marker[k] == NULL) { bdr_attr_marker = 1; break; } Array &bdr_marker = *bbfi_marker[k]; MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(), "invalid boundary marker for boundary integrator #" << k << ", counting from zero"); for (int i = 0; i < bdr_attr_marker.Size(); i++) { bdr_attr_marker[i] |= bdr_marker[i]; } } for (int i = 0; i < fes -> GetNBE(); i++) { const int bdr_attr = mesh->GetBdrAttribute(i); if (bdr_attr_marker[bdr_attr-1] == 0) { continue; } const FiniteElement &be = *fes->GetBE(i); fes -> GetBdrElementVDofs (i, vdofs); eltrans = fes -> GetBdrElementTransformation (i); int k = 0; for (; k < bbfi.Size(); k++) { if (bbfi_marker[k] && (*bbfi_marker[k])[bdr_attr-1] == 0) { continue; } bbfi[k]->AssembleElementMatrix(be, *eltrans, elmat); k++; break; } for (; k < bbfi.Size(); k++) { if (bbfi_marker[k] && (*bbfi_marker[k])[bdr_attr-1] == 0) { continue; } bbfi[k]->AssembleElementMatrix(be, *eltrans, elemmat); elmat += elemmat; } if (!static_cond) { mat->AddSubMatrix(vdofs, vdofs, elmat, skip_zeros); if (hybridization) { hybridization->AssembleBdrMatrix(i, elmat); } } else { static_cond->AssembleBdrMatrix(i, elmat); } } } if (fbfi.Size()) { FaceElementTransformations *tr; Array vdofs2; int nfaces = mesh->GetNumFaces(); for (int i = 0; i < nfaces; i++) { tr = mesh -> GetInteriorFaceTransformations (i); if (tr != NULL) { fes -> GetElementVDofs (tr -> Elem1No, vdofs); fes -> GetElementVDofs (tr -> Elem2No, vdofs2); vdofs.Append (vdofs2); for (int k = 0; k < fbfi.Size(); k++) { fbfi[k] -> AssembleFaceMatrix (*fes -> GetFE (tr -> Elem1No), *fes -> GetFE (tr -> Elem2No), *tr, elemmat); mat -> AddSubMatrix (vdofs, vdofs, elemmat, skip_zeros); } } } } if (bfbfi.Size()) { FaceElementTransformations *tr; const FiniteElement *fe1, *fe2; // Which boundary attributes need to be processed? Array bdr_attr_marker(mesh->bdr_attributes.Size() ? mesh->bdr_attributes.Max() : 0); bdr_attr_marker = 0; for (int k = 0; k < bfbfi.Size(); k++) { if (bfbfi_marker[k] == NULL) { bdr_attr_marker = 1; break; } Array &bdr_marker = *bfbfi_marker[k]; MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(), "invalid boundary marker for boundary face integrator #" << k << ", counting from zero"); for (int i = 0; i < bdr_attr_marker.Size(); i++) { bdr_attr_marker[i] |= bdr_marker[i]; } } for (int i = 0; i < fes -> GetNBE(); i++) { const int bdr_attr = mesh->GetBdrAttribute(i); if (bdr_attr_marker[bdr_attr-1] == 0) { continue; } tr = mesh -> GetBdrFaceTransformations (i); if (tr != NULL) { fes -> GetElementVDofs (tr -> Elem1No, vdofs); fe1 = fes -> GetFE (tr -> Elem1No); // The fe2 object is really a dummy and not used on the boundaries, // but we can't dereference a NULL pointer, and we don't want to // actually make a fake element. fe2 = fe1; for (int k = 0; k < bfbfi.Size(); k++) { if (bfbfi_marker[k] && (*bfbfi_marker[k])[bdr_attr-1] == 0) { continue; } bfbfi[k] -> AssembleFaceMatrix (*fe1, *fe2, *tr, elemmat); mat -> AddSubMatrix (vdofs, vdofs, elemmat, skip_zeros); } } } } #ifdef MFEM_USE_LEGACY_OPENMP if (free_element_matrices) { FreeElementMatrices(); } #endif } void BilinearForm::ConformingAssemble() { // Do not remove zero entries to preserve the symmetric structure of the // matrix which in turn will give rise to symmetric structure in the new // matrix. This ensures that subsequent calls to EliminateRowCol will work // correctly. Finalize(0); MFEM_ASSERT(mat, "the BilinearForm is not assembled"); const SparseMatrix *P = fes->GetConformingProlongation(); if (!P) { return; } // conforming mesh SparseMatrix *R = Transpose(*P); SparseMatrix *RA = mfem::Mult(*R, *mat); delete mat; if (mat_e) { SparseMatrix *RAe = mfem::Mult(*R, *mat_e); delete mat_e; mat_e = RAe; } delete R; mat = mfem::Mult(*RA, *P); delete RA; if (mat_e) { SparseMatrix *RAeP = mfem::Mult(*mat_e, *P); delete mat_e; mat_e = RAeP; } height = mat->Height(); width = mat->Width(); } void BilinearForm::AssembleDiagonal(Vector &diag) const { if (ext) { MFEM_ASSERT(diag.Size() == fes->GetTrueVSize(), "Vector for holding diagonal has wrong size!"); const Operator *P = fes->GetProlongationMatrix(); if (!IsIdentityProlongation(P)) { Vector local_diag(P->Height()); ext->AssembleDiagonal(local_diag); P->MultTranspose(local_diag, diag); } else { ext->AssembleDiagonal(diag); } } else { MFEM_ABORT("Not implemented. Maybe assemble your bilinear form into a " "matrix and use SparseMatrix::GetDiag?"); } } void BilinearForm::FormLinearSystem(const Array &ess_tdof_list, Vector &x, Vector &b, OperatorHandle &A, Vector &X, Vector &B, int copy_interior) { if (ext) { ext->FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior); return; } const SparseMatrix *P = fes->GetConformingProlongation(); FormSystemMatrix(ess_tdof_list, A); // Transform the system and perform the elimination in B, based on the // essential BC values from x. Restrict the BC part of x in X, and set the // non-BC part to zero. Since there is no good initial guess for the Lagrange // multipliers, set X = 0.0 for hybridization. if (static_cond) { // Schur complement reduction to the exposed dofs static_cond->ReduceSystem(x, b, X, B, copy_interior); } else if (!P) // conforming space { if (hybridization) { // Reduction to the Lagrange multipliers system EliminateVDofsInRHS(ess_tdof_list, x, b); hybridization->ReduceRHS(b, B); X.SetSize(B.Size()); X = 0.0; } else { // A, X and B point to the same data as mat, x and b EliminateVDofsInRHS(ess_tdof_list, x, b); X.NewMemoryAndSize(x.GetMemory(), x.Size(), false); B.NewMemoryAndSize(b.GetMemory(), b.Size(), false); if (!copy_interior) { X.SetSubVectorComplement(ess_tdof_list, 0.0); } } } else // non-conforming space { if (hybridization) { // Reduction to the Lagrange multipliers system const SparseMatrix *R = fes->GetConformingRestriction(); Vector conf_b(P->Width()), conf_x(P->Width()); P->MultTranspose(b, conf_b); R->Mult(x, conf_x); EliminateVDofsInRHS(ess_tdof_list, conf_x, conf_b); R->MultTranspose(conf_b, b); // store eliminated rhs in b hybridization->ReduceRHS(conf_b, B); X.SetSize(B.Size()); X = 0.0; } else { // Variational restriction with P const SparseMatrix *R = fes->GetConformingRestriction(); B.SetSize(P->Width()); P->MultTranspose(b, B); X.SetSize(R->Height()); R->Mult(x, X); EliminateVDofsInRHS(ess_tdof_list, X, B); if (!copy_interior) { X.SetSubVectorComplement(ess_tdof_list, 0.0); } } } } void BilinearForm::FormSystemMatrix(const Array &ess_tdof_list, OperatorHandle &A) { if (ext) { ext->FormSystemMatrix(ess_tdof_list, A); return; } // Finish the matrix assembly and perform BC elimination, storing the // eliminated part of the matrix. if (static_cond) { if (!static_cond->HasEliminatedBC()) { static_cond->SetEssentialTrueDofs(ess_tdof_list); static_cond->Finalize(); // finalize Schur complement (to true dofs) static_cond->EliminateReducedTrueDofs(diag_policy); static_cond->Finalize(); // finalize eliminated part } A.Reset(&static_cond->GetMatrix(), false); } else { if (!mat_e) { const SparseMatrix *P = fes->GetConformingProlongation(); if (P) { ConformingAssemble(); } EliminateVDofs(ess_tdof_list, diag_policy); const int remove_zeros = 0; Finalize(remove_zeros); } if (hybridization) { A.Reset(&hybridization->GetMatrix(), false); } else { A.Reset(mat, false); } } } void BilinearForm::RecoverFEMSolution(const Vector &X, const Vector &b, Vector &x) { if (ext) { ext->RecoverFEMSolution(X, b, x); return; } const SparseMatrix *P = fes->GetConformingProlongation(); if (!P) // conforming space { if (static_cond) { // Private dofs back solve static_cond->ComputeSolution(b, X, x); } else if (hybridization) { // Primal unknowns recovery hybridization->ComputeSolution(b, X, x); } else { // X and x point to the same data // If the validity flags of X's Memory were changed (e.g. if it was // moved to device memory) then we need to tell x about that. x.SyncMemory(X); } } else // non-conforming space { if (static_cond) { // Private dofs back solve static_cond->ComputeSolution(b, X, x); } else if (hybridization) { // Primal unknowns recovery Vector conf_b(P->Width()), conf_x(P->Width()); P->MultTranspose(b, conf_b); const SparseMatrix *R = fes->GetConformingRestriction(); R->Mult(x, conf_x); // get essential b.c. from x hybridization->ComputeSolution(conf_b, X, conf_x); x.SetSize(P->Height()); P->Mult(conf_x, x); } else { // Apply conforming prolongation x.SetSize(P->Height()); P->Mult(X, x); } } } void BilinearForm::ComputeElementMatrices() { if (element_matrices || dbfi.Size() == 0 || fes->GetNE() == 0) { return; } int num_elements = fes->GetNE(); int num_dofs_per_el = fes->GetFE(0)->GetDof() * fes->GetVDim(); element_matrices = new DenseTensor(num_dofs_per_el, num_dofs_per_el, num_elements); DenseMatrix tmp; IsoparametricTransformation eltrans; #ifdef MFEM_USE_LEGACY_OPENMP #pragma omp parallel for private(tmp,eltrans) #endif for (int i = 0; i < num_elements; i++) { DenseMatrix elmat(element_matrices->GetData(i), num_dofs_per_el, num_dofs_per_el); const FiniteElement &fe = *fes->GetFE(i); #ifdef MFEM_DEBUG if (num_dofs_per_el != fe.GetDof()*fes->GetVDim()) mfem_error("BilinearForm::ComputeElementMatrices:" " all elements must have same number of dofs"); #endif fes->GetElementTransformation(i, &eltrans); dbfi[0]->AssembleElementMatrix(fe, eltrans, elmat); for (int k = 1; k < dbfi.Size(); k++) { // note: some integrators may not be thread-safe dbfi[k]->AssembleElementMatrix(fe, eltrans, tmp); elmat += tmp; } elmat.ClearExternalData(); } } void BilinearForm::EliminateEssentialBC(const Array &bdr_attr_is_ess, const Vector &sol, Vector &rhs, DiagonalPolicy dpolicy) { Array ess_dofs, conf_ess_dofs; fes->GetEssentialVDofs(bdr_attr_is_ess, ess_dofs); if (fes->GetVSize() == height) { EliminateEssentialBCFromDofs(ess_dofs, sol, rhs, dpolicy); } else { fes->GetRestrictionMatrix()->BooleanMult(ess_dofs, conf_ess_dofs); EliminateEssentialBCFromDofs(conf_ess_dofs, sol, rhs, dpolicy); } } void BilinearForm::EliminateEssentialBC(const Array &bdr_attr_is_ess, DiagonalPolicy dpolicy) { Array ess_dofs, conf_ess_dofs; fes->GetEssentialVDofs(bdr_attr_is_ess, ess_dofs); if (fes->GetVSize() == height) { EliminateEssentialBCFromDofs(ess_dofs, dpolicy); } else { fes->GetRestrictionMatrix()->BooleanMult(ess_dofs, conf_ess_dofs); EliminateEssentialBCFromDofs(conf_ess_dofs, dpolicy); } } void BilinearForm::EliminateEssentialBCDiag (const Array &bdr_attr_is_ess, double value) { Array ess_dofs, conf_ess_dofs; fes->GetEssentialVDofs(bdr_attr_is_ess, ess_dofs); if (fes->GetVSize() == height) { EliminateEssentialBCFromDofsDiag(ess_dofs, value); } else { fes->GetRestrictionMatrix()->BooleanMult(ess_dofs, conf_ess_dofs); EliminateEssentialBCFromDofsDiag(conf_ess_dofs, value); } } void BilinearForm::EliminateVDofs(const Array &vdofs, const Vector &sol, Vector &rhs, DiagonalPolicy dpolicy) { for (int i = 0; i < vdofs.Size(); i++) { int vdof = vdofs[i]; if ( vdof >= 0 ) { mat -> EliminateRowCol (vdof, sol(vdof), rhs, dpolicy); } else { mat -> EliminateRowCol (-1-vdof, sol(-1-vdof), rhs, dpolicy); } } } void BilinearForm::EliminateVDofs(const Array &vdofs, DiagonalPolicy dpolicy) { if (mat_e == NULL) { mat_e = new SparseMatrix(height); } for (int i = 0; i < vdofs.Size(); i++) { int vdof = vdofs[i]; if ( vdof >= 0 ) { mat -> EliminateRowCol (vdof, *mat_e, dpolicy); } else { mat -> EliminateRowCol (-1-vdof, *mat_e, dpolicy); } } } void BilinearForm::EliminateEssentialBCFromDofs( const Array &ess_dofs, const Vector &sol, Vector &rhs, DiagonalPolicy dpolicy) { MFEM_ASSERT(ess_dofs.Size() == height, "incorrect dof Array size"); MFEM_ASSERT(sol.Size() == height, "incorrect sol Vector size"); MFEM_ASSERT(rhs.Size() == height, "incorrect rhs Vector size"); for (int i = 0; i < ess_dofs.Size(); i++) if (ess_dofs[i] < 0) { mat -> EliminateRowCol (i, sol(i), rhs, dpolicy); } } void BilinearForm::EliminateEssentialBCFromDofs (const Array &ess_dofs, DiagonalPolicy dpolicy) { MFEM_ASSERT(ess_dofs.Size() == height, "incorrect dof Array size"); for (int i = 0; i < ess_dofs.Size(); i++) if (ess_dofs[i] < 0) { mat -> EliminateRowCol (i, dpolicy); } } void BilinearForm::EliminateEssentialBCFromDofsDiag (const Array &ess_dofs, double value) { MFEM_ASSERT(ess_dofs.Size() == height, "incorrect dof Array size"); for (int i = 0; i < ess_dofs.Size(); i++) if (ess_dofs[i] < 0) { mat -> EliminateRowColDiag (i, value); } } void BilinearForm::EliminateVDofsInRHS( const Array &vdofs, const Vector &x, Vector &b) { mat_e->AddMult(x, b, -1.); mat->PartMult(vdofs, x, b); } void BilinearForm::Mult(const Vector &x, Vector &y) const { if (ext) { ext->Mult(x, y); } else { mat->Mult(x, y); } } void BilinearForm::Update(FiniteElementSpace *nfes) { bool full_update; if (nfes && nfes != fes) { full_update = true; fes = nfes; } else { // Check for different size (e.g. assembled form on non-conforming space) // or different sequence number. full_update = (fes->GetVSize() != Height() || sequence < fes->GetSequence()); } delete mat_e; mat_e = NULL; FreeElementMatrices(); delete static_cond; static_cond = NULL; if (full_update) { delete mat; mat = NULL; delete hybridization; hybridization = NULL; sequence = fes->GetSequence(); } else { if (mat) { *mat = 0.0; } if (hybridization) { hybridization->Reset(); } } height = width = fes->GetVSize(); if (ext) { ext->Update(); } } void BilinearForm::SetDiagonalPolicy(DiagonalPolicy policy) { diag_policy = policy; } BilinearForm::~BilinearForm() { delete mat_e; delete mat; delete element_matrices; delete static_cond; delete hybridization; if (!extern_bfs) { int k; for (k=0; k < dbfi.Size(); k++) { delete dbfi[k]; } for (k=0; k < bbfi.Size(); k++) { delete bbfi[k]; } for (k=0; k < fbfi.Size(); k++) { delete fbfi[k]; } for (k=0; k < bfbfi.Size(); k++) { delete bfbfi[k]; } } delete ext; } MixedBilinearForm::MixedBilinearForm (FiniteElementSpace *tr_fes, FiniteElementSpace *te_fes) : Matrix(te_fes->GetVSize(), tr_fes->GetVSize()) { trial_fes = tr_fes; test_fes = te_fes; mat = NULL; mat_e = NULL; extern_bfs = 0; assembly = AssemblyLevel::FULL; ext = NULL; } MixedBilinearForm::MixedBilinearForm (FiniteElementSpace *tr_fes, FiniteElementSpace *te_fes, MixedBilinearForm * mbf) : Matrix(te_fes->GetVSize(), tr_fes->GetVSize()) { trial_fes = tr_fes; test_fes = te_fes; mat = NULL; mat_e = NULL; extern_bfs = 1; ext = NULL; // Copy the pointers to the integrators dbfi = mbf->dbfi; bbfi = mbf->bbfi; tfbfi = mbf->tfbfi; btfbfi = mbf->btfbfi; bbfi_marker = mbf->bbfi_marker; btfbfi_marker = mbf->btfbfi_marker; assembly = AssemblyLevel::FULL; ext = NULL; } void MixedBilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level) { if (ext) { MFEM_ABORT("the assembly level has already been set!"); } assembly = assembly_level; switch (assembly) { case AssemblyLevel::FULL: // ext = new FAMixedBilinearFormExtension(this); // Use the original BilinearForm implementation for now break; case AssemblyLevel::ELEMENT: mfem_error("Element assembly not supported yet... stay tuned!"); // ext = new EAMixedBilinearFormExtension(this); break; case AssemblyLevel::PARTIAL: ext = new PAMixedBilinearFormExtension(this); break; case AssemblyLevel::NONE: mfem_error("Matrix-free action not supported yet... stay tuned!"); // ext = new MFMixedBilinearFormExtension(this); break; default: mfem_error("Unknown assembly level"); } } double & MixedBilinearForm::Elem (int i, int j) { return (*mat)(i, j); } const double & MixedBilinearForm::Elem (int i, int j) const { return (*mat)(i, j); } void MixedBilinearForm::Mult(const Vector & x, Vector & y) const { y = 0.0; AddMult(x, y); } void MixedBilinearForm::AddMult(const Vector & x, Vector & y, const double a) const { if (ext) { ext->AddMult(x, y, a); } else { mat->AddMult(x, y, a); } } void MixedBilinearForm::MultTranspose(const Vector & x, Vector & y) const { y = 0.0; AddMultTranspose(x, y); } void MixedBilinearForm::AddMultTranspose(const Vector & x, Vector & y, const double a) const { if (ext) { ext->AddMultTranspose(x, y, a); } else { mat->AddMultTranspose(x, y, a); } } MatrixInverse * MixedBilinearForm::Inverse() const { if (assembly != AssemblyLevel::FULL) { MFEM_WARNING("MixedBilinearForm::Inverse not possible with this assembly level!"); return NULL; } else { return mat -> Inverse (); } } void MixedBilinearForm::Finalize (int skip_zeros) { if (assembly == AssemblyLevel::FULL) { mat -> Finalize (skip_zeros); } } void MixedBilinearForm::GetBlocks(Array2D &blocks) const { MFEM_VERIFY(trial_fes->GetOrdering() == Ordering::byNODES && test_fes->GetOrdering() == Ordering::byNODES, "MixedBilinearForm::GetBlocks: both trial and test spaces " "must use Ordering::byNODES!"); blocks.SetSize(test_fes->GetVDim(), trial_fes->GetVDim()); mat->GetBlocks(blocks); } void MixedBilinearForm::AddDomainIntegrator (BilinearFormIntegrator * bfi) { dbfi.Append (bfi); } void MixedBilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi) { bbfi.Append (bfi); bbfi_marker.Append(NULL); // NULL marker means apply everywhere } void MixedBilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi, Array &bdr_marker) { bbfi.Append (bfi); bbfi_marker.Append(&bdr_marker); } void MixedBilinearForm::AddTraceFaceIntegrator (BilinearFormIntegrator * bfi) { tfbfi.Append (bfi); } void MixedBilinearForm::AddBdrTraceFaceIntegrator(BilinearFormIntegrator *bfi) { btfbfi.Append(bfi); btfbfi_marker.Append(NULL); // NULL marker means apply everywhere } void MixedBilinearForm::AddBdrTraceFaceIntegrator(BilinearFormIntegrator *bfi, Array &bdr_marker) { btfbfi.Append(bfi); btfbfi_marker.Append(&bdr_marker); } void MixedBilinearForm::Assemble (int skip_zeros) { if (ext) { ext->Assemble(); return; } Array tr_vdofs, te_vdofs; ElementTransformation *eltrans; DenseMatrix elemmat; Mesh *mesh = test_fes -> GetMesh(); if (mat == NULL) { mat = new SparseMatrix(height, width); } if (dbfi.Size()) { for (int i = 0; i < test_fes -> GetNE(); i++) { trial_fes -> GetElementVDofs (i, tr_vdofs); test_fes -> GetElementVDofs (i, te_vdofs); eltrans = test_fes -> GetElementTransformation (i); for (int k = 0; k < dbfi.Size(); k++) { dbfi[k] -> AssembleElementMatrix2 (*trial_fes -> GetFE(i), *test_fes -> GetFE(i), *eltrans, elemmat); mat -> AddSubMatrix (te_vdofs, tr_vdofs, elemmat, skip_zeros); } } } if (bbfi.Size()) { // Which boundary attributes need to be processed? Array bdr_attr_marker(mesh->bdr_attributes.Size() ? mesh->bdr_attributes.Max() : 0); bdr_attr_marker = 0; for (int k = 0; k < bbfi.Size(); k++) { if (bbfi_marker[k] == NULL) { bdr_attr_marker = 1; break; } Array &bdr_marker = *bbfi_marker[k]; MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(), "invalid boundary marker for boundary integrator #" << k << ", counting from zero"); for (int i = 0; i < bdr_attr_marker.Size(); i++) { bdr_attr_marker[i] |= bdr_marker[i]; } } for (int i = 0; i < test_fes -> GetNBE(); i++) { const int bdr_attr = mesh->GetBdrAttribute(i); if (bdr_attr_marker[bdr_attr-1] == 0) { continue; } trial_fes -> GetBdrElementVDofs (i, tr_vdofs); test_fes -> GetBdrElementVDofs (i, te_vdofs); eltrans = test_fes -> GetBdrElementTransformation (i); for (int k = 0; k < bbfi.Size(); k++) { if (bbfi_marker[k] && (*bbfi_marker[k])[bdr_attr-1] == 0) { continue; } bbfi[k] -> AssembleElementMatrix2 (*trial_fes -> GetBE(i), *test_fes -> GetBE(i), *eltrans, elemmat); mat -> AddSubMatrix (te_vdofs, tr_vdofs, elemmat, skip_zeros); } } } if (tfbfi.Size()) { FaceElementTransformations *ftr; Array te_vdofs2; const FiniteElement *trial_face_fe, *test_fe1, *test_fe2; int nfaces = mesh->GetNumFaces(); for (int i = 0; i < nfaces; i++) { ftr = mesh->GetFaceElementTransformations(i); trial_fes->GetFaceVDofs(i, tr_vdofs); test_fes->GetElementVDofs(ftr->Elem1No, te_vdofs); trial_face_fe = trial_fes->GetFaceElement(i); test_fe1 = test_fes->GetFE(ftr->Elem1No); if (ftr->Elem2No >= 0) { test_fes->GetElementVDofs(ftr->Elem2No, te_vdofs2); te_vdofs.Append(te_vdofs2); test_fe2 = test_fes->GetFE(ftr->Elem2No); } else { // The test_fe2 object is really a dummy and not used on the // boundaries, but we can't dereference a NULL pointer, and we don't // want to actually make a fake element. test_fe2 = test_fe1; } for (int k = 0; k < tfbfi.Size(); k++) { tfbfi[k]->AssembleFaceMatrix(*trial_face_fe, *test_fe1, *test_fe2, *ftr, elemmat); mat->AddSubMatrix(te_vdofs, tr_vdofs, elemmat, skip_zeros); } } } if (btfbfi.Size()) { FaceElementTransformations *ftr; Array te_vdofs2; const FiniteElement *trial_face_fe, *test_fe1, *test_fe2; // Which boundary attributes need to be processed? Array bdr_attr_marker(mesh->bdr_attributes.Size() ? mesh->bdr_attributes.Max() : 0); bdr_attr_marker = 0; for (int k = 0; k < btfbfi.Size(); k++) { if (btfbfi_marker[k] == NULL) { bdr_attr_marker = 1; break; } Array &bdr_marker = *btfbfi_marker[k]; MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(), "invalid boundary marker for boundary trace face integrator #" << k << ", counting from zero"); for (int i = 0; i < bdr_attr_marker.Size(); i++) { bdr_attr_marker[i] |= bdr_marker[i]; } } for (int i = 0; i < trial_fes -> GetNBE(); i++) { const int bdr_attr = mesh->GetBdrAttribute(i); if (bdr_attr_marker[bdr_attr-1] == 0) { continue; } ftr = mesh->GetBdrFaceTransformations(i); if (ftr) { trial_fes->GetFaceVDofs(i, tr_vdofs); test_fes->GetElementVDofs(ftr->Elem1No, te_vdofs); trial_face_fe = trial_fes->GetFaceElement(i); test_fe1 = test_fes->GetFE(ftr->Elem1No); // The test_fe2 object is really a dummy and not used on the // boundaries, but we can't dereference a NULL pointer, and we don't // want to actually make a fake element. test_fe2 = test_fe1; for (int k = 0; k < btfbfi.Size(); k++) { if (btfbfi_marker[k] && (*btfbfi_marker[k])[bdr_attr-1] == 0) { continue; } btfbfi[k]->AssembleFaceMatrix(*trial_face_fe, *test_fe1, *test_fe2, *ftr, elemmat); mat->AddSubMatrix(te_vdofs, tr_vdofs, elemmat, skip_zeros); } } } } } void MixedBilinearForm::AssembleDiagonal_ADAt(const Vector &D, Vector &diag) const { if (ext) { MFEM_ASSERT(diag.Size() == test_fes->GetTrueVSize(), "Vector for holding diagonal has wrong size!"); MFEM_ASSERT(D.Size() == trial_fes->GetTrueVSize(), "Vector for holding diagonal has wrong size!"); const Operator *P_trial = trial_fes->GetProlongationMatrix(); const Operator *P_test = test_fes->GetProlongationMatrix(); if (!IsIdentityProlongation(P_trial)) { Vector local_D(P_trial->Height()); P_trial->Mult(D, local_D); if (!IsIdentityProlongation(P_test)) { Vector local_diag(P_test->Height()); ext->AssembleDiagonal_ADAt(local_D, local_diag); P_test->MultTranspose(local_diag, diag); } else { ext->AssembleDiagonal_ADAt(local_D, diag); } } else { if (!IsIdentityProlongation(P_test)) { Vector local_diag(P_test->Height()); ext->AssembleDiagonal_ADAt(D, local_diag); P_test->MultTranspose(local_diag, diag); } else { ext->AssembleDiagonal_ADAt(D, diag); } } } else { MFEM_ABORT("Not implemented. Maybe assemble your bilinear form into a " "matrix and use SparseMatrix functions?"); } } void MixedBilinearForm::ConformingAssemble() { if (assembly != AssemblyLevel::FULL) { MFEM_WARNING("Conforming assemble not supported for this assembly level!"); return; } Finalize(); const SparseMatrix *P2 = test_fes->GetConformingProlongation(); if (P2) { SparseMatrix *R = Transpose(*P2); SparseMatrix *RA = mfem::Mult(*R, *mat); delete R; delete mat; mat = RA; } const SparseMatrix *P1 = trial_fes->GetConformingProlongation(); if (P1) { SparseMatrix *RAP = mfem::Mult(*mat, *P1); delete mat; mat = RAP; } height = mat->Height(); width = mat->Width(); } void MixedBilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat) { if (dbfi.Size()) { const FiniteElement &trial_fe = *trial_fes->GetFE(i); const FiniteElement &test_fe = *test_fes->GetFE(i); ElementTransformation *eltrans = test_fes->GetElementTransformation(i); dbfi[0]->AssembleElementMatrix2(trial_fe, test_fe, *eltrans, elmat); for (int k = 1; k < dbfi.Size(); k++) { dbfi[k]->AssembleElementMatrix2(trial_fe, test_fe, *eltrans, elemmat); elmat += elemmat; } } else { trial_fes->GetElementVDofs(i, trial_vdofs); test_fes->GetElementVDofs(i, test_vdofs); elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size()); elmat = 0.0; } } void MixedBilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat) { if (bbfi.Size()) { const FiniteElement &trial_be = *trial_fes->GetBE(i); const FiniteElement &test_be = *test_fes->GetBE(i); ElementTransformation *eltrans = test_fes->GetBdrElementTransformation(i); bbfi[0]->AssembleElementMatrix2(trial_be, test_be, *eltrans, elmat); for (int k = 1; k < bbfi.Size(); k++) { bbfi[k]->AssembleElementMatrix2(trial_be, test_be, *eltrans, elemmat); elmat += elemmat; } } else { trial_fes->GetBdrElementVDofs(i, trial_vdofs); test_fes->GetBdrElementVDofs(i, test_vdofs); elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size()); elmat = 0.0; } } void MixedBilinearForm::AssembleElementMatrix( int i, const DenseMatrix &elmat, int skip_zeros) { AssembleElementMatrix(i, elmat, trial_vdofs, test_vdofs, skip_zeros); } void MixedBilinearForm::AssembleElementMatrix( int i, const DenseMatrix &elmat, Array &trial_vdofs, Array &test_vdofs, int skip_zeros) { trial_fes->GetElementVDofs(i, trial_vdofs); test_fes->GetElementVDofs(i, test_vdofs); if (mat == NULL) { mat = new SparseMatrix(height, width); } mat->AddSubMatrix(test_vdofs, trial_vdofs, elmat, skip_zeros); } void MixedBilinearForm::AssembleBdrElementMatrix( int i, const DenseMatrix &elmat, int skip_zeros) { AssembleBdrElementMatrix(i, elmat, trial_vdofs, test_vdofs, skip_zeros); } void MixedBilinearForm::AssembleBdrElementMatrix( int i, const DenseMatrix &elmat, Array &trial_vdofs, Array &test_vdofs, int skip_zeros) { trial_fes->GetBdrElementVDofs(i, trial_vdofs); test_fes->GetBdrElementVDofs(i, test_vdofs); if (mat == NULL) { mat = new SparseMatrix(height, width); } mat->AddSubMatrix(test_vdofs, trial_vdofs, elmat, skip_zeros); } void MixedBilinearForm::EliminateTrialDofs ( const Array &bdr_attr_is_ess, const Vector &sol, Vector &rhs ) { int i, j, k; Array tr_vdofs, cols_marker (trial_fes -> GetVSize()); cols_marker = 0; for (i = 0; i < trial_fes -> GetNBE(); i++) if (bdr_attr_is_ess[trial_fes -> GetBdrAttribute (i)-1]) { trial_fes -> GetBdrElementVDofs (i, tr_vdofs); for (j = 0; j < tr_vdofs.Size(); j++) { if ( (k = tr_vdofs[j]) < 0 ) { k = -1-k; } cols_marker[k] = 1; } } mat -> EliminateCols (cols_marker, &sol, &rhs); } void MixedBilinearForm::EliminateEssentialBCFromTrialDofs ( const Array &marked_vdofs, const Vector &sol, Vector &rhs) { mat -> EliminateCols (marked_vdofs, &sol, &rhs); } void MixedBilinearForm::EliminateTestDofs (const Array &bdr_attr_is_ess) { int i, j, k; Array te_vdofs; for (i = 0; i < test_fes -> GetNBE(); i++) if (bdr_attr_is_ess[test_fes -> GetBdrAttribute (i)-1]) { test_fes -> GetBdrElementVDofs (i, te_vdofs); for (j = 0; j < te_vdofs.Size(); j++) { if ( (k = te_vdofs[j]) < 0 ) { k = -1-k; } mat -> EliminateRow (k); } } } void MixedBilinearForm::FormRectangularSystemMatrix(const Array &trial_tdof_list, const Array &test_tdof_list, OperatorHandle &A) { if (ext) { ext->FormRectangularSystemOperator(trial_tdof_list, test_tdof_list, A); return; } const SparseMatrix *test_P = test_fes->GetConformingProlongation(); const SparseMatrix *trial_P = trial_fes->GetConformingProlongation(); mat->Finalize(); if (test_P) // TODO: Must actually check for trial_P too { SparseMatrix *m = RAP(*test_P, *mat, *trial_P); delete mat; mat = m; } Array ess_trial_tdof_marker, ess_test_tdof_marker; FiniteElementSpace::ListToMarker(trial_tdof_list, trial_fes->GetTrueVSize(), ess_trial_tdof_marker); FiniteElementSpace::ListToMarker(test_tdof_list, test_fes->GetTrueVSize(), ess_test_tdof_marker); mat_e = new SparseMatrix(mat->Height(), mat->Width()); mat->EliminateCols(ess_trial_tdof_marker, *mat_e); for (int i=0; iEliminateRow(test_tdof_list[i]); } mat_e->Finalize(); A.Reset(mat, false); } void MixedBilinearForm::FormRectangularLinearSystem(const Array &trial_tdof_list, const Array &test_tdof_list, Vector &x, Vector &b, OperatorHandle &A, Vector &X, Vector &B) { if (ext) { ext->FormRectangularLinearSystem(trial_tdof_list, test_tdof_list, x, b, A, X, B); return; } const Operator *Pi = this->GetProlongation(); const Operator *Po = this->GetOutputProlongation(); const Operator *Ri = this->GetRestriction(); InitTVectors(Po, Ri, Pi, x, b, X, B); if (!mat_e) { FormRectangularSystemMatrix(trial_tdof_list, test_tdof_list, A); // Set A = mat_e } // Eliminate essential BCs with B -= Ab xb mat_e->AddMult(X, B, -1.0); B.SetSubVector(test_tdof_list, 0.0); } void MixedBilinearForm::Update() { delete mat; mat = NULL; delete mat_e; mat_e = NULL; height = test_fes->GetVSize(); width = trial_fes->GetVSize(); if (ext) { ext->Update(); } } MixedBilinearForm::~MixedBilinearForm() { if (mat) { delete mat; } if (mat_e) { delete mat_e; } if (!extern_bfs) { int i; for (i = 0; i < dbfi.Size(); i++) { delete dbfi[i]; } for (i = 0; i < bbfi.Size(); i++) { delete bbfi[i]; } for (i = 0; i < tfbfi.Size(); i++) { delete tfbfi[i]; } for (i = 0; i < btfbfi.Size(); i++) { delete btfbfi[i]; } } delete ext; } void DiscreteLinearOperator::Assemble(int skip_zeros) { Array dom_vdofs, ran_vdofs; ElementTransformation *T; const FiniteElement *dom_fe, *ran_fe; DenseMatrix totelmat, elmat; if (mat == NULL) { mat = new SparseMatrix(height, width); } if (dbfi.Size() > 0) { for (int i = 0; i < test_fes->GetNE(); i++) { trial_fes->GetElementVDofs(i, dom_vdofs); test_fes->GetElementVDofs(i, ran_vdofs); T = test_fes->GetElementTransformation(i); dom_fe = trial_fes->GetFE(i); ran_fe = test_fes->GetFE(i); dbfi[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, totelmat); for (int j = 1; j < dbfi.Size(); j++) { dbfi[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, elmat); totelmat += elmat; } mat->SetSubMatrix(ran_vdofs, dom_vdofs, totelmat, skip_zeros); } } if (tfbfi.Size()) { const int nfaces = test_fes->GetMesh()->GetNumFaces(); for (int i = 0; i < nfaces; i++) { trial_fes->GetFaceVDofs(i, dom_vdofs); test_fes->GetFaceVDofs(i, ran_vdofs); T = test_fes->GetMesh()->GetFaceTransformation(i); dom_fe = trial_fes->GetFaceElement(i); ran_fe = test_fes->GetFaceElement(i); tfbfi[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, totelmat); for (int j = 1; j < tfbfi.Size(); j++) { tfbfi[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, elmat); totelmat += elmat; } mat->SetSubMatrix(ran_vdofs, dom_vdofs, totelmat, skip_zeros); } } } }