LCOV - code coverage report
Current view: top level - src/solution_transfer - meshfree_interpolation.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 82 113 72.6 %
Date: 2025-08-19 19:27:09 Functions: 9 25 36.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
       3             : 
       4             : // This library is free software; you can redistribute it and/or
       5             : // modify it under the terms of the GNU Lesser General Public
       6             : // License as published by the Free Software Foundation; either
       7             : // version 2.1 of the License, or (at your option) any later version.
       8             : 
       9             : // This library is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             : // Lesser General Public License for more details.
      13             : 
      14             : // You should have received a copy of the GNU Lesser General Public
      15             : // License along with this library; if not, write to the Free Software
      16             : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      17             : 
      18             : 
      19             : 
      20             : // Local includes
      21             : #include "libmesh/meshfree_interpolation.h"
      22             : 
      23             : #include "libmesh/int_range.h"
      24             : #include "libmesh/libmesh_logging.h"
      25             : #include "libmesh/parallel.h"
      26             : #include "libmesh/parallel_algebra.h"
      27             : #include "libmesh/point.h"
      28             : 
      29             : // C++ includes
      30             : #include <iomanip>
      31             : #include <memory>
      32             : 
      33             : namespace libMesh
      34             : {
      35             : 
      36             : //--------------------------------------------------------------------------------
      37             : // MeshfreeInterpolation methods
      38          71 : void MeshfreeInterpolation::print_info (std::ostream & os) const
      39             : {
      40             :   os << "MeshfreeInterpolation"
      41          71 :      << "\n n_source_points()=" << _src_pts.size()
      42          69 :      << "\n n_field_variables()=" << this->n_field_variables()
      43          71 :      <<  "\n";
      44             : 
      45          71 :   if (this->n_field_variables())
      46             :     {
      47          71 :       os << "  variables = ";
      48         213 :       for (auto v : make_range(this->n_field_variables()))
      49         142 :         os << _names[v] << " ";
      50           2 :       os << std::endl;
      51             :     }
      52             : 
      53          71 : }
      54             : 
      55             : 
      56             : 
      57          71 : std::ostream & operator << (std::ostream & os, const MeshfreeInterpolation & mfi)
      58             : {
      59          71 :   mfi.print_info(os);
      60          71 :   return os;
      61             : }
      62             : 
      63             : 
      64             : 
      65           0 : void MeshfreeInterpolation::clear ()
      66             : {
      67           0 :   _names.clear();
      68           0 :   _src_pts.clear();
      69           0 :   _src_vals.clear();
      70           0 : }
      71             : 
      72             : 
      73             : 
      74           0 : void MeshfreeInterpolation::add_field_data (const std::vector<std::string> & field_names,
      75             :                                             const std::vector<Point> & pts,
      76             :                                             const std::vector<Number> & vals)
      77             : {
      78             :   libmesh_experimental();
      79           0 :   libmesh_assert_equal_to (field_names.size()*pts.size(), vals.size());
      80             : 
      81             :   // If we already have field variables, we assume we are appending.
      82             :   // that means the names and ordering better be identical!
      83           0 :   if (!_names.empty())
      84             :     {
      85           0 :       libmesh_error_msg_if(_names.size() != field_names.size(),
      86             :                            "ERROR:  when adding field data to an existing list the \nvariable list must be the same!");
      87             : 
      88           0 :       for (auto v : index_range(_names))
      89           0 :         libmesh_error_msg_if(_names[v] != field_names[v],
      90             :                              "ERROR:  when adding field data to an existing list the \nvariable list must be the same!");
      91             :     }
      92             : 
      93             :   // otherwise copy the names
      94             :   else
      95           0 :     _names = field_names;
      96             : 
      97             :   // append the data
      98           0 :   _src_pts.insert (_src_pts.end(),
      99             :                    pts.begin(),
     100           0 :                    pts.end());
     101             : 
     102           0 :   _src_vals.insert (_src_vals.end(),
     103             :                     vals.begin(),
     104           0 :                     vals.end());
     105             : 
     106           0 :   libmesh_assert_equal_to (_src_vals.size(),
     107             :                            _src_pts.size()*this->n_field_variables());
     108           0 : }
     109             : 
     110             : 
     111             : 
     112         284 : void MeshfreeInterpolation::prepare_for_use ()
     113             : {
     114         284 :   switch (_parallelization_strategy)
     115             :     {
     116         284 :     case SYNC_SOURCES:
     117         284 :       this->gather_remote_data();
     118         284 :       break;
     119             : 
     120           0 :     case INVALID_STRATEGY:
     121           0 :       libmesh_error_msg("Invalid _parallelization_strategy = " << _parallelization_strategy);
     122             : 
     123           0 :     default:
     124             :       // no preparation required for other strategies
     125           0 :       break;
     126             :     }
     127         284 : }
     128             : 
     129             : 
     130             : 
     131         284 : void MeshfreeInterpolation::gather_remote_data ()
     132             : {
     133             : #ifndef LIBMESH_HAVE_MPI
     134             : 
     135             :   // no MPI -- no-op
     136             :   return;
     137             : 
     138             : #else
     139             : 
     140             :   // This function must be run on all processors at once
     141           8 :   parallel_object_only();
     142             : 
     143          16 :   LOG_SCOPE ("gather_remote_data()", "MeshfreeInterpolation");
     144             : 
     145         284 :   this->comm().allgather(_src_pts);
     146         284 :   this->comm().allgather(_src_vals);
     147             : 
     148             : #endif // LIBMESH_HAVE_MPI
     149         284 : }
     150             : 
     151             : 
     152             : 
     153             : //--------------------------------------------------------------------------------
     154             : // InverseDistanceInterpolation methods
     155             : template <unsigned int KDDim>
     156         284 : void InverseDistanceInterpolation<KDDim>::construct_kd_tree ()
     157             : {
     158             : #ifdef LIBMESH_HAVE_NANOFLANN
     159             : 
     160          16 :   LOG_SCOPE ("construct_kd_tree()", "InverseDistanceInterpolation<>");
     161             : 
     162             :   // Initialize underlying KD tree
     163         284 :   if (_kd_tree.get() == nullptr)
     164         292 :     _kd_tree = std::make_unique<kd_tree_t>
     165             :       (KDDim,
     166         284 :        _point_list_adaptor,
     167         268 :        nanoflann::KDTreeSingleIndexAdaptorParams(10 /* max leaf */));
     168             : 
     169           8 :   libmesh_assert (_kd_tree.get() != nullptr);
     170             : 
     171         284 :   _kd_tree->buildIndex();
     172             : #endif
     173         284 : }
     174             : 
     175             : 
     176             : 
     177             : template <unsigned int KDDim>
     178           0 : void InverseDistanceInterpolation<KDDim>::clear()
     179             : {
     180             : #ifdef LIBMESH_HAVE_NANOFLANN
     181             :   // Delete the KD Tree and start fresh
     182           0 :   if (_kd_tree.get())
     183           0 :     _kd_tree.reset (nullptr);
     184             : #endif
     185             : 
     186             :   // Call  base class clear method
     187           0 :   MeshfreeInterpolation::clear();
     188           0 : }
     189             : 
     190             : 
     191             : 
     192             : template <unsigned int KDDim>
     193       36922 : void InverseDistanceInterpolation<KDDim>::interpolate_field_data (const std::vector<std::string> & field_names,
     194             :                                                                   const std::vector<Point> & tgt_pts,
     195             :                                                                   std::vector<Number> & tgt_vals) const
     196             : {
     197             :   libmesh_experimental();
     198             : 
     199             :   // forcibly initialize, if needed
     200             : #ifdef LIBMESH_HAVE_NANOFLANN
     201       36922 :   if (_kd_tree.get() == nullptr)
     202         142 :     const_cast<InverseDistanceInterpolation<KDDim> *>(this)->construct_kd_tree();
     203             : #endif
     204             : 
     205        5900 :   LOG_SCOPE ("interpolate_field_data()", "InverseDistanceInterpolation<>");
     206             : 
     207        2950 :   libmesh_assert_equal_to (field_names.size(), this->n_field_variables());
     208             : 
     209             :   // If we already have field variables, we assume we are appending.
     210             :   // that means the names and ordering better be identical!
     211       42822 :   libmesh_error_msg_if(_names.size() != field_names.size(),
     212             :                        "ERROR:  when adding field data to an existing list the \nvariable list must be the same!");
     213             : 
     214       73915 :   for (auto v : index_range(_names))
     215       36993 :     libmesh_error_msg_if(_names[v] != field_names[v],
     216             :                          "ERROR:  when adding field data to an existing list the \nvariable list must be the same!");
     217             : 
     218       39872 :   tgt_vals.resize (tgt_pts.size()*this->n_field_variables());
     219             : 
     220             : #ifdef LIBMESH_HAVE_NANOFLANN
     221             :   {
     222       36922 :     std::vector<Number>::iterator out_it = tgt_vals.begin();
     223             : 
     224       39872 :     const size_t num_results = std::min((size_t) _n_interp_pts, _src_pts.size());
     225             : 
     226       39872 :     std::vector<size_t> ret_index(num_results);
     227       39872 :     std::vector<Real>   ret_dist_sqr(num_results);
     228             : 
     229       80873 :     for (const auto & tgt : tgt_pts)
     230             :       {
     231       43951 :         const Real query_pt[] = { tgt(0), tgt(1), tgt(2) };
     232             : 
     233       43951 :         _kd_tree->knnSearch(query_pt, num_results, ret_index.data(), ret_dist_sqr.data());
     234             : 
     235       43951 :         this->interpolate (tgt, ret_index, ret_dist_sqr, out_it);
     236             : 
     237             :         // libMesh::out << "knnSearch(): num_results=" << num_results << "\n";
     238             :         // for (size_t i=0;i<num_results;i++)
     239             :         //   libMesh::out << "idx[" << i << "]="
     240             :         //       << std::setw(6) << ret_index[i]
     241             :         //       << "\t dist["<< i << "]=" << ret_dist_sqr[i]
     242             :         //       << "\t val[" << std::setw(6) << ret_index[i] << "]=" << _src_vals[ret_index[i]]
     243             :         //       << std::endl;
     244             :         // libMesh::out << "\n";
     245             : 
     246             :         // libMesh::out << "ival=" << _vals[0] << '\n';
     247             :       }
     248             :   }
     249             : #else
     250             : 
     251             :   libmesh_error_msg("ERROR: This functionality requires the library to be configured with nanoflann support!");
     252             : 
     253             : #endif
     254       36922 : }
     255             : 
     256             : template <unsigned int KDDim>
     257       43951 : void InverseDistanceInterpolation<KDDim>::interpolate (const Point               & /* pt */,
     258             :                                                        const std::vector<size_t> & src_indices,
     259             :                                                        const std::vector<Real>   & src_dist_sqr,
     260             :                                                        std::vector<Number>::iterator & out_it) const
     261             : {
     262             :   // We explicitly assume that the input source points are sorted from closest to
     263             :   // farthest.  assert that assumption in DEBUG mode.
     264             : #ifdef DEBUG
     265        3148 :   if (!src_dist_sqr.empty())
     266             :     {
     267        3148 :       Real min_dist = src_dist_sqr.front();
     268             : 
     269       16540 :       for (auto i : src_dist_sqr)
     270             :         {
     271       13392 :           libmesh_error_msg_if(i < min_dist, i << " was less than min_dist = " << min_dist);
     272             : 
     273       13392 :           min_dist = i;
     274             :         }
     275             :     }
     276             : #endif
     277             : 
     278             : 
     279        3148 :   libmesh_assert_equal_to (src_dist_sqr.size(), src_indices.size());
     280             : 
     281             : 
     282             :   // Compute the interpolation weights & interpolated value
     283        3148 :   const unsigned int n_fv = this->n_field_variables();
     284       43951 :   _vals.resize(n_fv);
     285             : 
     286        3148 :   std::fill (_vals.begin(), _vals.end(), Number(0.));
     287        3148 :   Real tot_weight(0.);
     288             :   // The background value is optional
     289             :   // If background value option is enabled, add it to the total weight
     290             :   // If not, a "zero" weight is added
     291       43951 :   if (_background_eff_dist > 0.0)
     292             :   {
     293           0 :     const Real background_wt = _background_eff_dist * _background_eff_dist > std::numeric_limits<Real>::epsilon()
     294           0 :                            ? 1.0 / std::pow(_background_eff_dist * _background_eff_dist, _half_power)
     295             :                            : 0.0;
     296           0 :     tot_weight += background_wt;
     297           0 :     std::fill (_vals.begin(), _vals.end(), Number(_background_value * background_wt));
     298             :   }
     299             : 
     300        6296 :   std::vector<Real>::const_iterator src_dist_sqr_it=src_dist_sqr.begin();
     301        6296 :   std::vector<size_t>::const_iterator src_idx_it=src_indices.begin();
     302             : 
     303             :   // Loop over source points
     304      261547 :   while ((src_dist_sqr_it != src_dist_sqr.end()) &&
     305       43324 :          (src_idx_it      != src_indices.end()))
     306             :     {
     307       13392 :       libmesh_assert_greater_equal (*src_dist_sqr_it, 0.);
     308             : 
     309             :       const Real
     310      204204 :         dist_sq = std::max(*src_dist_sqr_it, std::numeric_limits<Real>::epsilon()),
     311      204204 :         weight = 1./std::pow(dist_sq, _half_power);
     312             : 
     313      204204 :       tot_weight += weight;
     314             : 
     315      204204 :       const std::size_t src_idx = *src_idx_it;
     316             : 
     317             :       // loop over field variables
     318      465208 :       for (unsigned int v=0; v<n_fv; v++)
     319             :         {
     320       14992 :           libmesh_assert_less (src_idx*n_fv+v, _src_vals.size());
     321      290988 :           _vals[v] += _src_vals[src_idx*n_fv+v]*weight;
     322             :         }
     323             : 
     324       13392 :       ++src_dist_sqr_it;
     325       13392 :       ++src_idx_it;
     326             :     }
     327             : 
     328             :   // don't forget normalizing term & set the output buffer!
     329       95002 :   for (unsigned int v=0; v<n_fv; v++, ++out_it)
     330             :     {
     331       51051 :       _vals[v] /= tot_weight;
     332             : 
     333       51051 :       *out_it = _vals[v];
     334             :     }
     335       43951 : }
     336             : 
     337             : 
     338             : 
     339             : // ------------------------------------------------------------
     340             : // Explicit Instantiations
     341             : template class LIBMESH_EXPORT InverseDistanceInterpolation<1>;
     342             : template class LIBMESH_EXPORT InverseDistanceInterpolation<2>;
     343             : template class LIBMESH_EXPORT InverseDistanceInterpolation<3>;
     344             : 
     345             : } // namespace libMesh

Generated by: LCOV version 1.14