libMesh
Loading...
Searching...
No Matches
meshfree_interpolation.C
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2026 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
33namespace libMesh
34{
35
36//--------------------------------------------------------------------------------
37// MeshfreeInterpolation methods
38void 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
57std::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
74void 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
155template <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
177template <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
192template <unsigned int KDDim>
193void 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)
202 const_cast<InverseDistanceInterpolation<KDDim> *>(this)->construct_kd_tree();
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
256template <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
341template class LIBMESH_EXPORT InverseDistanceInterpolation<1>;
342template class LIBMESH_EXPORT InverseDistanceInterpolation<2>;
343template class LIBMESH_EXPORT InverseDistanceInterpolation<3>;
344
345} // namespace libMesh
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
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.
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.
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.
Base class to support various mesh-free interpolation methods.
virtual void gather_remote_data()
Gathers source points and values that have been added on other processors.
std::vector< std::string > _names
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.
void print_info(std::ostream &os=libMesh::out) const
Prints information about this object, by default to libMesh::out.
unsigned int n_field_variables() const
The number of field variables.
virtual void prepare_for_use()
Prepares data structures for use.
ParallelizationStrategy _parallelization_strategy
virtual void clear()
Clears all internal data structures and restores to a pristine state.
const Parallel::Communicator & comm() const
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
The libMesh namespace provides an interface to certain functionality in the library.
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:153
libmesh_assert(ctx)
std::ostream & operator<<(std::ostream &os, const OrderWrapper &order)
Overload stream operators.
Definition fe_type.h:182
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
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:176