33
44#include " spaceoperator.hpp"
55
6- #include < limits>
76#include < set>
87#include < type_traits>
98#include " fem/bilinearform.hpp"
1211#include " fem/mesh.hpp"
1312#include " fem/multigrid.hpp"
1413#include " linalg/hypre.hpp"
15- #include " linalg/iterative.hpp"
16- #include " linalg/jacobi.hpp"
17- #include " linalg/ksp.hpp"
1814#include " linalg/rap.hpp"
1915#include " utils/communication.hpp"
2016#include " utils/geodata.hpp"
@@ -549,93 +545,6 @@ auto BuildLevelParOperator<ComplexOperator>(std::unique_ptr<Operator> &&br,
549545 return std::make_unique<ComplexParOperator>(std::move (br), std::move (bi), fespace);
550546}
551547
552- // Project a boundary coefficient into a Vector via a boundary mass matrix solve.
553- //
554- // This should be done by ParGridFunction::ProjectBdrCoefficientTangent, including parallel
555- // reduction. See also https://github.com/mfem/mfem/pull/606. However there seems to be a
556- // bug in MFEM that breaks this for Nédélec elements, perhaps due to orientation signs.
557- // TODO(future): Investigate and fix this bug.
558- //
559- // Here project via CG solve of boundary mass matrix system M_bdr * e = f.
560- void ProjectBdrCoefficientViaMassSolve (SumVectorCoefficient &fb, const LumpedPortData &data,
561- const MaterialOperator &mat_op,
562- FiniteElementSpace &nd_fespace, MPI_Comm comm,
563- Vector &result)
564- {
565- // Assemble the boundary linear form f = ∫ φ_i · coeff dS (parallel-correct via P^T).
566- mfem::LinearForm rhs (&nd_fespace.Get ());
567- rhs.AddBoundaryIntegrator (new VectorFEBoundaryLFIntegrator (fb));
568- rhs.UseFastAssembly (false );
569- rhs.UseDevice (false );
570- rhs.Assemble ();
571- rhs.UseDevice (true );
572- nd_fespace.GetProlongationMatrix ()->MultTranspose (rhs, result);
573-
574- // Assemble boundary mass matrix M_bdr = ∫ φ_i · φ_j dS on port surface.
575- MaterialPropertyCoefficient fb_mass (mat_op.MaxCeedBdrAttribute ());
576- for (const auto &elem : data.elems )
577- {
578- fb_mass.AddMaterialProperty (mat_op.GetCeedBdrAttributes (elem->GetAttrList ()), 1.0 );
579- }
580- BilinearForm m_bdr (nd_fespace);
581- if (!fb_mass.empty ())
582- {
583- m_bdr.AddBoundaryIntegrator <VectorFEMassIntegrator>(fb_mass);
584- }
585- auto M_bdr = std::make_unique<ParOperator>(m_bdr.Assemble (false ), nd_fespace);
586-
587- // The boundary mass matrix is zero for all DOFs not on the port surface, making it
588- // singular on the full space. Set non-port DOFs as essential with DIAG_ONE so that M_bdr
589- // acts as identity there, giving a full-rank SPD system. non_port_tdof_list must outlive
590- // M_bdr because SetEssentialTrueDofs uses pointer not copy.
591- mfem::Array<int > attr_list;
592- for (const auto &elem : data.elems )
593- {
594- attr_list.Append (elem->GetAttrList ());
595- }
596- const auto &mesh = nd_fespace.GetParMesh ();
597- int bdr_attr_max = mesh.bdr_attributes .Size () ? mesh.bdr_attributes .Max () : 0 ;
598- mfem::Array<int > attr_marker;
599- mesh::AttrToMarker (bdr_attr_max, attr_list, attr_marker);
600-
601- mfem::Array<int > port_tdof_list;
602- nd_fespace.Get ().GetEssentialTrueDofs (attr_marker, port_tdof_list);
603-
604- mfem::Array<int > non_port_tdof_list;
605- {
606- std::vector<bool > is_port (nd_fespace.GetTrueVSize (), false );
607- for (int i = 0 ; i < port_tdof_list.Size (); i++)
608- {
609- is_port[port_tdof_list[i]] = true ;
610- }
611- for (int i = 0 ; i < nd_fespace.GetTrueVSize (); i++)
612- {
613- if (!is_port[i])
614- {
615- non_port_tdof_list.Append (i);
616- }
617- }
618- }
619- M_bdr->SetEssentialTrueDofs (non_port_tdof_list, Operator::DIAG_ONE);
620-
621- // CG solve M_bdr * e = f entirely in T-vector space.
622- // TODO: Make solver parameters configurable from IoData, or inherit other settings.
623- auto pcg = std::make_unique<CgSolver<Operator>>(comm, 0 );
624- pcg->SetInitialGuess (false );
625- pcg->SetRelTol (1.0e-14 );
626- pcg->SetAbsTol (std::numeric_limits<double >::epsilon ());
627- pcg->SetMaxIter (200 );
628- auto jac = std::make_unique<JacobiSmoother<Operator>>(comm);
629- auto ksp = std::make_unique<BaseKspSolver<Operator>>(std::move (pcg), std::move (jac));
630- ksp->SetOperators (*M_bdr, *M_bdr);
631-
632- Vector sol (nd_fespace.GetTrueVSize ());
633- sol.UseDevice (true );
634- sol = 0.0 ;
635- ksp->Mult (result, sol);
636- result = sol;
637- }
638-
639548} // namespace
640549
641550void SpaceOperator::AssemblePreconditioner (
@@ -953,33 +862,29 @@ void SpaceOperator::GetLumpedPortExcitationVectorPrimaryEt(int port_idx,
953862 const auto &data = GetLumpedPortOp ().GetPort (port_idx);
954863
955864 SumVectorCoefficient fb (GetMesh ().SpaceDimension ());
865+ mfem::Array<int > attr_list;
956866 for (const auto &elem : data.elems )
957867 {
958868 const double Rs = 1.0 * data.GetToSquare (*elem);
959869 const double Einc = std::sqrt (
960870 Rs / (elem->GetGeometryWidth () * elem->GetGeometryLength () * data.elems .size ()));
961871 fb.AddCoefficient (elem->GetModeCoefficient (Einc));
872+ attr_list.Append (elem->GetAttrList ());
962873 }
963874
964875 Et_primary.SetSize (GetNDSpace ().GetTrueVSize ());
965876 Et_primary.UseDevice (true );
966877 Et_primary = 0.0 ;
967878
968- // Broken code that should work using ParGridFunction::ProjectBdrCoefficientTangent.
969- // See ProjectBdrCoefficientViaMassSolve comment above.
970-
971- // mfem::Array<int> attr_marker;
972- //
973- // const auto &mesh = GetNDSpace().GetParMesh();
974- // int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0;
975- // mesh::AttrToMarker(bdr_attr_max, attr_list, attr_marker);
976- //
977- // GridFunction rhs(GetNDSpace());
978- // rhs = 0.0;
979- // rhs.Real().ProjectBdrCoefficientTangent(fb, attr_marker);
879+ const auto &mesh = GetNDSpace ().GetParMesh ();
880+ int bdr_attr_max = mesh.bdr_attributes .Size () ? mesh.bdr_attributes .Max () : 0 ;
881+ mfem::Array<int > attr_marker;
882+ mesh::AttrToMarker (bdr_attr_max, attr_list, attr_marker);
980883
981- ProjectBdrCoefficientViaMassSolve (fb, data, mat_op, GetNDSpace (), GetComm (),
982- Et_primary.Real ());
884+ GridFunction rhs (GetNDSpace ());
885+ rhs = 0.0 ;
886+ rhs.Real ().ProjectBdrCoefficientTangent (fb, attr_marker);
887+ GetNDSpace ().GetRestrictionMatrix ()->Mult (rhs.Real (), Et_primary.Real ());
983888
984889 if (zero_metal)
985890 {
@@ -994,21 +899,29 @@ void SpaceOperator::GetLumpedPortExcitationVectorPrimaryHtcn(int port_idx,
994899 const auto &data = lumped_port_op.GetPort (port_idx);
995900
996901 SumVectorCoefficient fb (GetMesh ().SpaceDimension ());
902+ mfem::Array<int > attr_list;
997903 for (const auto &elem : data.elems )
998904 {
999905 const double Rs = 1.0 * data.GetToSquare (*elem);
1000906 const double Hinc = 1.0 / std::sqrt (Rs * elem->GetGeometryWidth () *
1001907 elem->GetGeometryLength () * data.elems .size ());
1002908 fb.AddCoefficient (elem->GetModeCoefficient (Hinc));
909+ attr_list.Append (elem->GetAttrList ());
1003910 }
1004911
1005912 Htcn_primary.SetSize (GetNDSpace ().GetTrueVSize ());
1006913 Htcn_primary.UseDevice (true );
1007914 Htcn_primary = 0.0 ;
1008915
1009- // See ParGridFunction::ProjectBdrCoefficientTangent issue above.
1010- ProjectBdrCoefficientViaMassSolve (fb, data, mat_op, GetNDSpace (), GetComm (),
1011- Htcn_primary.Real ());
916+ const auto &mesh = GetNDSpace ().GetParMesh ();
917+ int bdr_attr_max = mesh.bdr_attributes .Size () ? mesh.bdr_attributes .Max () : 0 ;
918+ mfem::Array<int > attr_marker;
919+ mesh::AttrToMarker (bdr_attr_max, attr_list, attr_marker);
920+
921+ GridFunction rhs (GetNDSpace ());
922+ rhs = 0.0 ;
923+ rhs.Real ().ProjectBdrCoefficientTangent (fb, attr_marker);
924+ GetNDSpace ().GetRestrictionMatrix ()->Mult (rhs.Real (), Htcn_primary.Real ());
1012925
1013926 if (zero_metal)
1014927 {
0 commit comments