LCOV - code coverage report
Current view: top level - fem - interpolator.cpp (source / functions) Coverage Total Hit
Test: Palace Coverage Report Lines: 4.5 % 110 5
Test Date: 2025-10-23 22:45:05 Functions: 20.0 % 5 1
Legend: Lines: hit not hit

            Line data    Source code
       1              : // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
       2              : // SPDX-License-Identifier: Apache-2.0
       3              : 
       4              : #include "interpolator.hpp"
       5              : 
       6              : #include <algorithm>
       7              : #include "fem/fespace.hpp"
       8              : #include "fem/gridfunction.hpp"
       9              : #include "utils/communication.hpp"
      10              : #include "utils/iodata.hpp"
      11              : 
      12              : namespace palace
      13              : {
      14              : 
      15              : namespace
      16              : {
      17              : 
      18              : constexpr auto GSLIB_BB_TOL = 0.01;  // MFEM defaults, slightly reduced bounding box
      19              : constexpr auto GSLIB_NEWTON_TOL = 1.0e-12;
      20              : 
      21              : }  // namespace
      22           15 : InterpolationOperator::InterpolationOperator(const IoData &iodata,
      23           15 :                                              FiniteElementSpace &nd_space)
      24              : #if defined(MFEM_USE_GSLIB)
      25           15 :   : op(nd_space.GetParMesh().GetComm()), v_dim_fes(nd_space.Get().GetVectorDim())
      26              : {
      27              :   auto &mesh = nd_space.GetParMesh();
      28              :   // Set up probes interpolation. All processes search for all points.
      29           15 :   if (iodata.domains.postpro.probe.empty())
      30              :   {
      31           15 :     return;
      32              :   }
      33              :   const int dim = mesh.SpaceDimension();
      34            0 :   MFEM_VERIFY(
      35              :       mesh.Dimension() == dim,
      36              :       "Probe postprocessing functionality requires mesh dimension == space dimension!");
      37            0 :   const int npts = static_cast<int>(iodata.domains.postpro.probe.size());
      38            0 :   mfem::Vector xyz(npts * dim);
      39            0 :   op_idx.resize(npts);
      40              :   int i = 0;
      41            0 :   for (const auto &[idx, data] : iodata.domains.postpro.probe)
      42              :   {
      43            0 :     for (int d = 0; d < dim; d++)
      44              :     {
      45              :       // Use default ordering byNODES.
      46            0 :       xyz(d * npts + i) = data.center[d];
      47              :     }
      48            0 :     op_idx[i++] = idx;
      49              :   }
      50            0 :   op.Setup(mesh, GSLIB_BB_TOL, GSLIB_NEWTON_TOL, npts);
      51            0 :   op.FindPoints(xyz, mfem::Ordering::byNODES);
      52              :   op.SetDefaultInterpolationValue(0.0);
      53              :   i = 0;
      54            0 :   for (const auto &[idx, data] : iodata.domains.postpro.probe)
      55              :   {
      56            0 :     if (op.GetCode()[i++] == 2)
      57              :     {
      58            0 :       Mpi::Warning(
      59              :           "Probe {:d} at ({:.3e}) m could not be found!\n Using default value 0.0!\n", idx,
      60            0 :           fmt::join(iodata.units.Dimensionalize<Units::ValueType::LENGTH>(data.center),
      61              :                     ", "));
      62              :     }
      63              :   }
      64            0 : }
      65              : #else
      66              : {
      67              :   MFEM_CONTRACT_VAR(GSLIB_BB_TOL);
      68              :   MFEM_CONTRACT_VAR(GSLIB_NEWTON_TOL);
      69              :   MFEM_VERIFY(iodata.domains.postpro.probe.empty(),
      70              :               "InterpolationOperator class requires MFEM_USE_GSLIB!");
      71              : }
      72              : #endif
      73              : 
      74            0 : std::vector<double> InterpolationOperator::ProbeField(const mfem::ParGridFunction &U)
      75              : {
      76              : #if defined(MFEM_USE_GSLIB)
      77              :   // Interpolated vector values are returned from GSLIB interpolator with the same ordering
      78              :   // as the source grid function, which we transform to byVDIM for output.
      79              :   const int npts = op.GetCode().Size();
      80            0 :   const int vdim = U.VectorDim();
      81            0 :   std::vector<double> vals(npts * vdim);
      82            0 :   if (U.FESpace()->GetOrdering() == mfem::Ordering::byVDIM)
      83              :   {
      84              :     mfem::Vector v(vals.data(), npts * vdim);
      85            0 :     op.Interpolate(U, v);
      86              :   }
      87              :   else
      88              :   {
      89              :     mfem::Vector v(npts * vdim);
      90            0 :     op.Interpolate(U, v);
      91            0 :     for (int d = 0; d < vdim; d++)
      92              :     {
      93            0 :       for (int i = 0; i < npts; i++)
      94              :       {
      95            0 :         vals[i * vdim + d] = v(d * npts + i);
      96              :       }
      97              :     }
      98              :   }
      99            0 :   return vals;
     100              : #else
     101              :   MFEM_ABORT("InterpolationOperator class requires MFEM_USE_GSLIB!");
     102              :   return {};
     103              : #endif
     104              : }
     105              : 
     106            0 : std::vector<std::complex<double>> InterpolationOperator::ProbeField(const GridFunction &U)
     107              : {
     108            0 :   std::vector<double> vr = ProbeField(U.Real());
     109            0 :   if (U.HasImag())
     110              :   {
     111            0 :     std::vector<double> vi = ProbeField(U.Imag());
     112            0 :     std::vector<std::complex<double>> vals(vr.size());
     113              :     std::transform(vr.begin(), vr.end(), vi.begin(), vals.begin(),
     114              :                    [](double xr, double xi) { return std::complex<double>(xr, xi); });
     115              :     return vals;
     116              :   }
     117              :   else
     118              :   {
     119            0 :     return {vr.begin(), vr.end()};
     120              :   }
     121              : }
     122              : 
     123              : namespace fem
     124              : {
     125              : 
     126            0 : void InterpolateFunction(const mfem::GridFunction &U, mfem::GridFunction &V)
     127              : {
     128              : #if defined(MFEM_USE_GSLIB)
     129              :   // Generate list of points where the grid function will be evaluated. If the grid function
     130              :   // to interpolate is an H1 space of the same order as the mesh nodes, we can use the
     131              :   // mesh node points directly. Otherwise, for a different basis order or type, we generate
     132              :   // the interpolation points in the physical space manually.
     133              :   auto &dest_mesh = *V.FESpace()->GetMesh();
     134            0 :   MFEM_VERIFY(dest_mesh.GetNodes(), "Destination mesh has no nodal FE space!");
     135              :   const int dim = dest_mesh.SpaceDimension();
     136              :   mfem::Vector xyz;
     137              :   mfem::Ordering::Type ordering;
     138              :   const auto *dest_fec_h1 =
     139            0 :       dynamic_cast<const mfem::H1_FECollection *>(V.FESpace()->FEColl());
     140            0 :   const auto *dest_nodes_h1 = dynamic_cast<const mfem::H1_FECollection *>(
     141              :       dest_mesh.GetNodes()->FESpace()->FEColl());
     142            0 :   int dest_fespace_order = V.FESpace()->GetMaxElementOrder();
     143            0 :   int dest_nodes_order = dest_mesh.GetNodes()->FESpace()->GetMaxElementOrder();
     144            0 :   dest_mesh.GetNodes()->HostRead();
     145            0 :   if (dest_fec_h1 && dest_nodes_h1 && dest_fespace_order == dest_nodes_order)
     146              :   {
     147              :     xyz.MakeRef(*dest_mesh.GetNodes(), 0, dest_mesh.GetNodes()->Size());
     148            0 :     ordering = dest_mesh.GetNodes()->FESpace()->GetOrdering();
     149              :   }
     150              :   else
     151              :   {
     152              :     int npts = 0, offset = 0;
     153            0 :     for (int i = 0; i < dest_mesh.GetNE(); i++)
     154              :     {
     155            0 :       npts += V.FESpace()->GetFE(i)->GetNodes().GetNPoints();
     156              :     }
     157            0 :     xyz.SetSize(npts * dim);
     158            0 :     mfem::DenseMatrix pointmat;
     159            0 :     for (int i = 0; i < dest_mesh.GetNE(); i++)
     160              :     {
     161            0 :       const mfem::FiniteElement &fe = *V.FESpace()->GetFE(i);
     162            0 :       mfem::ElementTransformation &T = *dest_mesh.GetElementTransformation(i);
     163            0 :       T.Transform(fe.GetNodes(), pointmat);
     164            0 :       for (int j = 0; j < pointmat.Width(); j++)
     165              :       {
     166            0 :         for (int d = 0; d < dim; d++)
     167              :         {
     168              :           // Use default ordering byNODES.
     169            0 :           xyz(d * npts + offset + j) = pointmat(d, j);
     170              :         }
     171              :       }
     172            0 :       offset += pointmat.Width();
     173              :     }
     174              :     ordering = mfem::Ordering::byNODES;
     175            0 :   }
     176            0 :   const int npts = xyz.Size() / dim;
     177              : 
     178              :   // Set up the interpolator.
     179              :   auto &src_mesh = *U.FESpace()->GetMesh();
     180            0 :   MFEM_VERIFY(src_mesh.GetNodes(), "Source mesh has no nodal FE space!");
     181            0 :   auto *src_pmesh = dynamic_cast<mfem::ParMesh *>(&src_mesh);
     182            0 :   MPI_Comm comm = (src_pmesh) ? src_pmesh->GetComm() : MPI_COMM_SELF;
     183            0 :   mfem::FindPointsGSLIB op(comm);
     184            0 :   op.Setup(src_mesh, GSLIB_BB_TOL, GSLIB_NEWTON_TOL, npts);
     185              : 
     186              :   // Perform the interpolation and fill the target GridFunction (see MFEM's field-interp
     187              :   // miniapp).
     188            0 :   const int vdim = U.VectorDim();
     189            0 :   mfem::Vector vals(npts * vdim);
     190              :   op.SetDefaultInterpolationValue(0.0);
     191              :   op.SetL2AvgType(mfem::FindPointsGSLIB::NONE);
     192            0 :   op.Interpolate(xyz, U, vals, ordering);
     193              :   const auto *dest_fec_l2 =
     194            0 :       dynamic_cast<const mfem::L2_FECollection *>(V.FESpace()->FEColl());
     195            0 :   if (dest_fec_h1 || dest_fec_l2)
     196              :   {
     197            0 :     if (dest_fec_h1 && dest_fespace_order != dest_nodes_order)
     198              :     {
     199              :       // H1 with order != mesh order needs to handle duplicated interpolation points.
     200              :       mfem::Vector elem_vals;
     201              :       mfem::Array<int> vdofs;
     202              :       int offset = 0;
     203            0 :       for (int i = 0; i < dest_mesh.GetNE(); i++)
     204              :       {
     205            0 :         const mfem::FiniteElement &fe = *V.FESpace()->GetFE(i);
     206              :         const int elem_npts = fe.GetNodes().GetNPoints();
     207            0 :         elem_vals.SetSize(elem_npts * vdim);
     208            0 :         for (int d = 0; d < vdim; d++)
     209              :         {
     210            0 :           for (int j = 0; j < elem_npts; j++)
     211              :           {
     212              :             // Arrange element values byNODES to align with GetElementVDofs.
     213              :             int idx = (U.FESpace()->GetOrdering() == mfem::Ordering::byNODES)
     214            0 :                           ? d * npts + offset + j
     215            0 :                           : (offset + j) * vdim + d;
     216            0 :             elem_vals(d * elem_npts + j) = vals(idx);
     217              :           }
     218              :         }
     219            0 :         const auto *dof_trans = V.FESpace()->GetElementVDofs(i, vdofs);
     220            0 :         if (dof_trans)
     221              :         {
     222              :           dof_trans->TransformPrimal(elem_vals);
     223              :         }
     224            0 :         V.SetSubVector(vdofs, elem_vals);
     225            0 :         offset += elem_npts;
     226              :       }
     227              :     }
     228              :     else
     229              :     {
     230              :       // Otherwise, H1 and L2 copy interpolated values to vdofs.
     231              :       MFEM_ASSERT(V.Size() == vals.Size(),
     232              :                   "Unexpected size mismatch for interpolated values and grid function!");
     233            0 :       V = vals;
     234              :     }
     235              :   }
     236              :   else
     237              :   {
     238              :     // H(div) or H(curl) use ProjectFromNodes.
     239              :     mfem::Vector elem_vals, v;
     240              :     mfem::Array<int> vdofs;
     241              :     int offset = 0;
     242            0 :     for (int i = 0; i < dest_mesh.GetNE(); i++)
     243              :     {
     244            0 :       const mfem::FiniteElement &fe = *V.FESpace()->GetFE(i);
     245            0 :       mfem::ElementTransformation &T = *dest_mesh.GetElementTransformation(i);
     246              :       const int elem_npts = fe.GetNodes().GetNPoints();
     247            0 :       elem_vals.SetSize(elem_npts * vdim);
     248            0 :       for (int d = 0; d < vdim; d++)
     249              :       {
     250            0 :         for (int j = 0; j < elem_npts; j++)
     251              :         {
     252              :           // Arrange element values byVDIM for ProjectFromNodes.
     253              :           int idx = (U.FESpace()->GetOrdering() == mfem::Ordering::byNODES)
     254            0 :                         ? d * npts + offset + j
     255            0 :                         : (offset + j) * vdim + d;
     256            0 :           elem_vals(j * vdim + d) = vals(idx);
     257              :         }
     258              :       }
     259            0 :       const auto *dof_trans = V.FESpace()->GetElementVDofs(i, vdofs);
     260            0 :       v.SetSize(vdofs.Size());
     261            0 :       fe.ProjectFromNodes(elem_vals, T, v);
     262            0 :       if (dof_trans)
     263              :       {
     264              :         dof_trans->TransformPrimal(v);
     265              :       }
     266            0 :       V.SetSubVector(vdofs, v);
     267            0 :       offset += elem_npts;
     268              :     }
     269              :   }
     270              : #else
     271              :   MFEM_ABORT("InterpolateFunction requires MFEM_USE_GSLIB!");
     272              : #endif
     273            0 : }
     274              : 
     275            0 : void InterpolateFunction(const mfem::Vector &xyz, const mfem::GridFunction &U,
     276              :                          mfem::Vector &vals, mfem::Ordering::Type ordering)
     277              : {
     278              : #if defined(MFEM_USE_GSLIB)
     279              :   // Set up the interpolator.
     280              :   auto &src_mesh = *U.FESpace()->GetMesh();
     281            0 :   MFEM_VERIFY(src_mesh.GetNodes(), "Source mesh has no nodal FE space!");
     282              :   const int dim = src_mesh.SpaceDimension();
     283            0 :   const int npts = xyz.Size() / dim;
     284            0 :   auto *src_pmesh = dynamic_cast<mfem::ParMesh *>(&src_mesh);
     285            0 :   MPI_Comm comm = (src_pmesh) ? src_pmesh->GetComm() : MPI_COMM_SELF;
     286            0 :   mfem::FindPointsGSLIB op(comm);
     287            0 :   op.Setup(src_mesh, GSLIB_BB_TOL, GSLIB_NEWTON_TOL, npts);
     288              : 
     289              :   // Perform the interpolation, with the ordering of the returned values matching the
     290              :   // ordering of the source grid function.
     291            0 :   const int vdim = U.VectorDim();
     292            0 :   MFEM_VERIFY(vals.Size() == npts * vdim, "Incorrect size for interpolated values vector!");
     293              :   op.SetDefaultInterpolationValue(0.0);
     294              :   op.SetL2AvgType(mfem::FindPointsGSLIB::NONE);
     295            0 :   op.Interpolate(xyz, U, vals, ordering);
     296              : #else
     297              :   MFEM_ABORT("InterpolateFunction requires MFEM_USE_GSLIB!");
     298              : #endif
     299            0 : }
     300              : 
     301              : }  // namespace fem
     302              : 
     303              : }  // namespace palace
        

Generated by: LCOV version 2.0-1