libMesh
meshfree_interpolation.C
Go to the documentation of this file.
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 void MeshfreeInterpolation::print_info (std::ostream & os) const
39 {
40  os << "MeshfreeInterpolation"
41  << "\n n_source_points()=" << _src_pts.size()
42  << "\n n_field_variables()=" << this->n_field_variables()
43  << "\n";
44 
45  if (this->n_field_variables())
46  {
47  os << " variables = ";
48  for (auto v : make_range(this->n_field_variables()))
49  os << _names[v] << " ";
50  os << std::endl;
51  }
52 
53 }
54 
55 
56 
57 std::ostream & operator << (std::ostream & os, const MeshfreeInterpolation & mfi)
58 {
59  mfi.print_info(os);
60  return os;
61 }
62 
63 
64 
66 {
67  _names.clear();
68  _src_pts.clear();
69  _src_vals.clear();
70 }
71 
72 
73 
74 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  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  if (!_names.empty())
84  {
85  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  for (auto v : index_range(_names))
89  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  _names = field_names;
96 
97  // append the data
98  _src_pts.insert (_src_pts.end(),
99  pts.begin(),
100  pts.end());
101 
102  _src_vals.insert (_src_vals.end(),
103  vals.begin(),
104  vals.end());
105 
106  libmesh_assert_equal_to (_src_vals.size(),
107  _src_pts.size()*this->n_field_variables());
108 }
109 
110 
111 
113 {
115  {
116  case SYNC_SOURCES:
117  this->gather_remote_data();
118  break;
119 
120  case INVALID_STRATEGY:
121  libmesh_error_msg("Invalid _parallelization_strategy = " << _parallelization_strategy);
122 
123  default:
124  // no preparation required for other strategies
125  break;
126  }
127 }
128 
129 
130 
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  parallel_object_only();
142 
143  LOG_SCOPE ("gather_remote_data()", "MeshfreeInterpolation");
144 
145  this->comm().allgather(_src_pts);
146  this->comm().allgather(_src_vals);
147 
148 #endif // LIBMESH_HAVE_MPI
149 }
150 
151 
152 
153 //--------------------------------------------------------------------------------
154 // InverseDistanceInterpolation methods
155 template <unsigned int KDDim>
157 {
158 #ifdef LIBMESH_HAVE_NANOFLANN
159 
160  LOG_SCOPE ("construct_kd_tree()", "InverseDistanceInterpolation<>");
161 
162  // Initialize underlying KD tree
163  if (_kd_tree.get() == nullptr)
164  _kd_tree = std::make_unique<kd_tree_t>
165  (KDDim,
166  _point_list_adaptor,
167  nanoflann::KDTreeSingleIndexAdaptorParams(10 /* max leaf */));
168 
169  libmesh_assert (_kd_tree.get() != nullptr);
170 
171  _kd_tree->buildIndex();
172 #endif
173 }
174 
175 
176 
177 template <unsigned int KDDim>
179 {
180 #ifdef LIBMESH_HAVE_NANOFLANN
181  // Delete the KD Tree and start fresh
182  if (_kd_tree.get())
183  _kd_tree.reset (nullptr);
184 #endif
185 
186  // Call base class clear method
188 }
189 
190 
191 
192 template <unsigned int KDDim>
193 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  if (_kd_tree.get() == nullptr)
203 #endif
204 
205  LOG_SCOPE ("interpolate_field_data()", "InverseDistanceInterpolation<>");
206 
207  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  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  for (auto v : index_range(_names))
215  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  tgt_vals.resize (tgt_pts.size()*this->n_field_variables());
219 
220 #ifdef LIBMESH_HAVE_NANOFLANN
221  {
222  std::vector<Number>::iterator out_it = tgt_vals.begin();
223 
224  const size_t num_results = std::min((size_t) _n_interp_pts, _src_pts.size());
225 
226  std::vector<size_t> ret_index(num_results);
227  std::vector<Real> ret_dist_sqr(num_results);
228 
229  for (const auto & tgt : tgt_pts)
230  {
231  const Real query_pt[] = { tgt(0), tgt(1), tgt(2) };
232 
233  _kd_tree->knnSearch(query_pt, num_results, ret_index.data(), ret_dist_sqr.data());
234 
235  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 }
255 
256 template <unsigned int KDDim>
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  if (!src_dist_sqr.empty())
266  {
267  Real min_dist = src_dist_sqr.front();
268 
269  for (auto i : src_dist_sqr)
270  {
271  libmesh_error_msg_if(i < min_dist, i << " was less than min_dist = " << min_dist);
272 
273  min_dist = i;
274  }
275  }
276 #endif
277 
278 
279  libmesh_assert_equal_to (src_dist_sqr.size(), src_indices.size());
280 
281 
282  // Compute the interpolation weights & interpolated value
283  const unsigned int n_fv = this->n_field_variables();
284  _vals.resize(n_fv);
285 
286  std::fill (_vals.begin(), _vals.end(), Number(0.));
287  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  if (_background_eff_dist > 0.0)
292  {
293  const Real background_wt = _background_eff_dist * _background_eff_dist > std::numeric_limits<Real>::epsilon()
294  ? 1.0 / std::pow(_background_eff_dist * _background_eff_dist, _half_power)
295  : 0.0;
296  tot_weight += background_wt;
297  std::fill (_vals.begin(), _vals.end(), Number(_background_value * background_wt));
298  }
299 
300  std::vector<Real>::const_iterator src_dist_sqr_it=src_dist_sqr.begin();
301  std::vector<size_t>::const_iterator src_idx_it=src_indices.begin();
302 
303  // Loop over source points
304  while ((src_dist_sqr_it != src_dist_sqr.end()) &&
305  (src_idx_it != src_indices.end()))
306  {
307  libmesh_assert_greater_equal (*src_dist_sqr_it, 0.);
308 
309  const Real
310  dist_sq = std::max(*src_dist_sqr_it, std::numeric_limits<Real>::epsilon()),
311  weight = 1./std::pow(dist_sq, _half_power);
312 
313  tot_weight += weight;
314 
315  const std::size_t src_idx = *src_idx_it;
316 
317  // loop over field variables
318  for (unsigned int v=0; v<n_fv; v++)
319  {
320  libmesh_assert_less (src_idx*n_fv+v, _src_vals.size());
321  _vals[v] += _src_vals[src_idx*n_fv+v]*weight;
322  }
323 
324  ++src_dist_sqr_it;
325  ++src_idx_it;
326  }
327 
328  // don't forget normalizing term & set the output buffer!
329  for (unsigned int v=0; v<n_fv; v++, ++out_it)
330  {
331  _vals[v] /= tot_weight;
332 
333  *out_it = _vals[v];
334  }
335 }
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
unsigned int n_field_variables() const
The number of field variables.
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
Inverse distance interpolation.
std::ostream & operator<<(std::ostream &os, const OrderWrapper &order)
Overload stream operators.
Definition: fe_type.h:182
const Parallel::Communicator & comm() const
The libMesh namespace provides an interface to certain functionality in the library.
virtual void interpolate(const Point &pt, const std::vector< size_t > &src_indices, const std::vector< Real > &src_dist_sqr, std::vector< Number >::iterator &out_it) const
Performs inverse distance interpolation at the input point from the specified points.
T pow(const T &x)
Definition: utility.h:328
virtual void clear()
Clears all internal data structures and restores to a pristine state.
ParallelizationStrategy _parallelization_strategy
virtual void add_field_data(const std::vector< std::string > &field_names, const std::vector< Point > &pts, const std::vector< Number > &vals)
Sets source data at specified points.
dof_id_type weight(const MeshBase &mesh, const processor_id_type pid)
Definition: mesh_tools.C:437
void print_info(std::ostream &os=libMesh::out) const
Prints information about this object, by default to libMesh::out.
Base class to support various mesh-free interpolation methods.
libmesh_assert(ctx)
virtual void construct_kd_tree()
Build & initialize the KD tree, if needed.
virtual void clear() override
Clears all internal data structures and restores to a pristine state.
virtual void gather_remote_data()
Gathers source points and values that have been added on other processors.
virtual void prepare_for_use()
Prepares data structures for use.
virtual void interpolate_field_data(const std::vector< std::string > &field_names, const std::vector< Point > &tgt_pts, std::vector< Number > &tgt_vals) const override
Interpolate source data at target points.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::vector< std::string > _names
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:140
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:117