// Copyright (c) 2010-2022, 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 LinearForm #include "fem.hpp" namespace mfem { LinearForm::LinearForm(FiniteElementSpace *f, LinearForm *lf) : Vector(f->GetVSize()) { // Linear forms are stored on the device UseDevice(true); fes = f; ext = nullptr; extern_lfs = 1; // Copy the pointers to the integrators domain_integs = lf->domain_integs; domain_delta_integs = lf->domain_delta_integs; boundary_integs = lf->boundary_integs; boundary_face_integs = lf->boundary_face_integs; boundary_face_integs_marker = lf->boundary_face_integs_marker; } void LinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi) { DeltaLFIntegrator *maybe_delta = dynamic_cast(lfi); if (!maybe_delta || !maybe_delta->IsDelta()) { domain_integs.Append(lfi); } else { domain_delta_integs.Append(maybe_delta); } domain_integs_marker.Append(NULL); } void LinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi, Array &elem_marker) { DeltaLFIntegrator *maybe_delta = dynamic_cast(lfi); if (!maybe_delta || !maybe_delta->IsDelta()) { domain_integs.Append(lfi); } else { domain_delta_integs.Append(maybe_delta); } domain_integs_marker.Append(&elem_marker); } void LinearForm::AddBoundaryIntegrator (LinearFormIntegrator * lfi) { boundary_integs.Append (lfi); boundary_integs_marker.Append(NULL); // NULL -> all attributes are active } void LinearForm::AddBoundaryIntegrator (LinearFormIntegrator * lfi, Array &bdr_attr_marker) { boundary_integs.Append (lfi); boundary_integs_marker.Append(&bdr_attr_marker); } void LinearForm::AddBdrFaceIntegrator (LinearFormIntegrator * lfi) { boundary_face_integs.Append(lfi); // NULL -> all attributes are active boundary_face_integs_marker.Append(NULL); } void LinearForm::AddBdrFaceIntegrator(LinearFormIntegrator *lfi, Array &bdr_attr_marker) { boundary_face_integs.Append(lfi); boundary_face_integs_marker.Append(&bdr_attr_marker); } void LinearForm::AddInteriorFaceIntegrator(LinearFormIntegrator *lfi) { interior_face_integs.Append(lfi); } bool LinearForm::SupportsDevice() { // return false for NURBS meshs, so we don’t convert it to non-NURBS // through Assemble, AssembleDevice, GetGeometricFactors and EnsureNodes if (fes->GetMesh()->NURBSext != nullptr) { return false; } // scan domain integrator to verify that all can use device assembly if (domain_integs.Size() > 0) { for (int k = 0; k < domain_integs.Size(); k++) { if (!domain_integs[k]->SupportsDevice()) { return false; } } } // boundary, delta and face integrators are not supported yet if (GetBLFI()->Size() > 0 || GetFLFI()->Size() > 0 || GetDLFI_Delta()->Size() > 0 || GetIFLFI()->Size() > 0) { return false; } const Mesh &mesh = *fes->GetMesh(); // no support for elements with varying polynomial orders if (fes->IsVariableOrder()) { return false; } // no support for 1D and embedded meshes const int mesh_dim = mesh.Dimension(); if (mesh_dim == 1 || mesh_dim != mesh.SpaceDimension()) { return false; } // tensor-product finite element space only // with point values preserving scalar fields for (int e = 0; e < fes->GetNE(); ++e) { const FiniteElement *fe = fes->GetFE(e); if (fe->GetMapType() != FiniteElement::VALUE) { return false; } if (!dynamic_cast(fe)) { return false; } } return true; } void LinearForm::Assemble(bool use_device) { Array vdofs; ElementTransformation *eltrans; DofTransformation *doftrans; Vector elemvect; if (!ext && use_device && SupportsDevice()) { ext = new LinearFormExtension(this); } Vector::operator=(0.0); // The above operation is executed on device because of UseDevice(). // The first use of AddElementVector() below will move it back to host // because both 'vdofs' and 'elemvect' are on host. if (ext) { return ext->Assemble(); } if (domain_integs.Size()) { for (int k = 0; k < domain_integs.Size(); k++) { if (domain_integs_marker[k] != NULL) { MFEM_VERIFY(domain_integs_marker[k]->Size() == (fes->GetMesh()->attributes.Size() ? fes->GetMesh()->attributes.Max() : 0), "invalid element marker for domain linear form " "integrator #" << k << ", counting from zero"); } } for (int i = 0; i < fes -> GetNE(); i++) { int elem_attr = fes->GetMesh()->GetAttribute(i); for (int k = 0; k < domain_integs.Size(); k++) { if ( domain_integs_marker[k] == NULL || (*(domain_integs_marker[k]))[elem_attr-1] == 1 ) { doftrans = fes -> GetElementVDofs (i, vdofs); eltrans = fes -> GetElementTransformation (i); domain_integs[k]->AssembleRHSElementVect(*fes->GetFE(i), *eltrans, elemvect); if (doftrans) { doftrans->TransformDual(elemvect); } AddElementVector (vdofs, elemvect); } } } } AssembleDelta(); if (boundary_integs.Size()) { Mesh *mesh = fes->GetMesh(); // 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 < boundary_integs.Size(); k++) { if (boundary_integs_marker[k] == NULL) { bdr_attr_marker = 1; break; } Array &bdr_marker = *boundary_integs_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; } doftrans = fes -> GetBdrElementVDofs (i, vdofs); eltrans = fes -> GetBdrElementTransformation (i); for (int k=0; k < boundary_integs.Size(); k++) { if (boundary_integs_marker[k] && (*boundary_integs_marker[k])[bdr_attr-1] == 0) { continue; } boundary_integs[k]->AssembleRHSElementVect(*fes->GetBE(i), *eltrans, elemvect); if (doftrans) { doftrans->TransformDual(elemvect); } AddElementVector (vdofs, elemvect); } } } if (boundary_face_integs.Size()) { FaceElementTransformations *tr; Mesh *mesh = fes->GetMesh(); // 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 < boundary_face_integs.Size(); k++) { if (boundary_face_integs_marker[k] == NULL) { bdr_attr_marker = 1; break; } Array &bdr_marker = *boundary_face_integs_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 < mesh->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); for (int k = 0; k < boundary_face_integs.Size(); k++) { if (boundary_face_integs_marker[k] && (*boundary_face_integs_marker[k])[bdr_attr-1] == 0) { continue; } boundary_face_integs[k]-> AssembleRHSElementVect(*fes->GetFE(tr->Elem1No), *tr, elemvect); AddElementVector (vdofs, elemvect); } } } } if (interior_face_integs.Size()) { Mesh *mesh = fes->GetMesh(); for (int k = 0; k < interior_face_integs.Size(); k++) { for (int i = 0; i < mesh->GetNumFaces(); i++) { FaceElementTransformations *tr = NULL; tr = mesh->GetInteriorFaceTransformations (i); if (tr != NULL) { fes -> GetElementVDofs (tr -> Elem1No, vdofs); Array vdofs2; fes -> GetElementVDofs (tr -> Elem2No, vdofs2); vdofs.Append(vdofs2); interior_face_integs[k]-> AssembleRHSElementVect(*fes->GetFE(tr->Elem1No), *fes->GetFE(tr->Elem2No), *tr, elemvect); AddElementVector (vdofs, elemvect); } } } } } void LinearForm::Update() { SetSize(fes->GetVSize()); ResetDeltaLocations(); if (ext) { ext->Update(); } } void LinearForm::Update(FiniteElementSpace *f, Vector &v, int v_offset) { MFEM_ASSERT(v.Size() >= v_offset + f->GetVSize(), ""); fes = f; v.UseDevice(true); this->Vector::MakeRef(v, v_offset, fes->GetVSize()); ResetDeltaLocations(); if (ext) { ext->Update(); } } void LinearForm::MakeRef(FiniteElementSpace *f, Vector &v, int v_offset) { Update(f, v, v_offset); } void LinearForm::AssembleDelta() { if (domain_delta_integs.Size() == 0) { return; } if (!HaveDeltaLocations()) { int sdim = fes->GetMesh()->SpaceDimension(); Vector center; DenseMatrix centers(sdim, domain_delta_integs.Size()); for (int i = 0; i < centers.Width(); i++) { centers.GetColumnReference(i, center); domain_delta_integs[i]->GetDeltaCenter(center); MFEM_VERIFY(center.Size() == sdim, "Point dim " << center.Size() << " does not match space dim " << sdim); } fes->GetMesh()->FindPoints(centers, domain_delta_integs_elem_id, domain_delta_integs_ip); } Array vdofs; Vector elemvect; for (int i = 0; i < domain_delta_integs.Size(); i++) { int elem_id = domain_delta_integs_elem_id[i]; // The delta center may be outside of this sub-domain, or // (Par)Mesh::FindPoints() failed to find this point: if (elem_id < 0) { continue; } const IntegrationPoint &ip = domain_delta_integs_ip[i]; ElementTransformation &Trans = *fes->GetElementTransformation(elem_id); Trans.SetIntPoint(&ip); fes->GetElementVDofs(elem_id, vdofs); domain_delta_integs[i]->AssembleDeltaElementVect(*fes->GetFE(elem_id), Trans, elemvect); AddElementVector(vdofs, elemvect); } } LinearForm & LinearForm::operator=(double value) { Vector::operator=(value); return *this; } LinearForm & LinearForm::operator=(const Vector &v) { MFEM_ASSERT(fes && v.Size() == fes->GetVSize(), ""); Vector::operator=(v); return *this; } LinearForm::~LinearForm() { if (!extern_lfs) { int k; for (k=0; k < domain_delta_integs.Size(); k++) { delete domain_delta_integs[k]; } for (k=0; k < domain_integs.Size(); k++) { delete domain_integs[k]; } for (k=0; k < boundary_integs.Size(); k++) { delete boundary_integs[k]; } for (k=0; k < boundary_face_integs.Size(); k++) { delete boundary_face_integs[k]; } for (k=0; k < interior_face_integs.Size(); k++) { delete interior_face_integs[k]; } } delete ext; } }