MAST
Multidisciplinary-design Adaptation and Sensitivity Toolkit (MAST)
filter_base.cpp
Go to the documentation of this file.
1 /*
2  * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit
3  * Copyright (C) 2013-2020 Manav Bhatia and MAST authors
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 // C++ includes
21 #include <iomanip>
22 
23 // MAST includes
24 #include "level_set/filter_base.h"
25 
26 // libMesh includes
27 #include "libmesh/mesh_base.h"
28 #include "libmesh/node.h"
29 #include "libmesh/numeric_vector.h"
30 #ifdef LIBMESH_HAVE_NANOFLANN
31 #include "libmesh/nanoflann.hpp"
32 #endif
33 
34 
35 MAST::FilterBase::FilterBase(libMesh::System& sys,
36  const Real radius,
37  const std::set<unsigned int>& dv_dof_ids):
38 _level_set_system (sys),
39 _radius (radius),
40 _level_set_fe_size (0.),
41 _dv_dof_ids (dv_dof_ids) {
42 
43  libmesh_assert_greater(radius, 0.);
44 
45 #ifdef LIBMESH_HAVE_NANOFLANN
46  _init2(); // KD-tree search using NanoFlann
47 #else
48  _init(); // linear filter search
49 #endif
50 }
51 
52 
54 
55 }
56 
57 
58 void
59 MAST::FilterBase::compute_filtered_values(const libMesh::NumericVector<Real>& input,
60  libMesh::NumericVector<Real>& output,
61  bool close_vector) const {
62 
63  libmesh_assert_equal_to(input.size(), _filter_map.size());
64  libmesh_assert_equal_to(output.size(), _filter_map.size());
65 
66  output.zero();
67 
68  std::vector<Real> input_vals(input.size(), 0.);
69  input.localize(input_vals);
70 
71  std::map<unsigned int, std::vector<std::pair<unsigned int, Real>>>::const_iterator
72  map_it = _filter_map.begin(),
73  map_end = _filter_map.end();
74 
75  for ( ; map_it != map_end; map_it++) {
76 
77  std::vector<std::pair<unsigned int, Real>>::const_iterator
78  vec_it = map_it->second.begin(),
79  vec_end = map_it->second.end();
80 
81  for ( ; vec_it != vec_end; vec_it++) {
82  if (map_it->first >= input.first_local_index() &&
83  map_it->first < input.last_local_index()) {
84 
85  if (_dv_dof_ids.count(map_it->first))
86  output.add(map_it->first, input_vals[vec_it->first] * vec_it->second);
87  else
88  output.set(map_it->first, input_vals[map_it->first]);
89  }
90  }
91  }
92 
93  if (close_vector)
94  output.close();
95 }
96 
97 
98 
99 void
100 MAST::FilterBase::compute_filtered_values(std::map<unsigned int, Real>& nonzero_input,
101  libMesh::NumericVector<Real>& output,
102  bool close_vector) const {
103 
104  libmesh_assert_equal_to(output.size(), _filter_map.size());
105 
106  output.zero();
107 
108  std::map<unsigned int, std::vector<std::pair<unsigned int, Real>>>::const_iterator
109  map_it = _filter_map.begin(),
110  map_end = _filter_map.end();
111 
112  for ( ; map_it != map_end; map_it++) {
113 
114  std::vector<std::pair<unsigned int, Real>>::const_iterator
115  vec_it = map_it->second.begin(),
116  vec_end = map_it->second.end();
117 
118  for ( ; vec_it != vec_end; vec_it++) {
119  if (nonzero_input.count(vec_it->first)) {
120 
121  if (output.type() == libMesh::SERIAL ||
122  (map_it->first >= output.first_local_index() &&
123  map_it->first < output.last_local_index())) {
124 
125  if (_dv_dof_ids.count(map_it->first))
126  output.add(map_it->first, nonzero_input[vec_it->first] * vec_it->second);
127  else
128  output.set(map_it->first, nonzero_input[map_it->first]);
129  }
130  }
131  }
132  }
133 
134  if (close_vector)
135  output.close();
136 }
137 
138 
139 
140 
141 void
142 MAST::FilterBase::compute_filtered_values(const std::vector<Real>& input,
143  std::vector<Real>& output) const {
144 
145  libmesh_assert_equal_to(input.size(), _filter_map.size());
146  libmesh_assert_equal_to(output.size(), _filter_map.size());
147 
148  std::fill(output.begin(), output.end(), 0.);
149 
150  std::map<unsigned int, std::vector<std::pair<unsigned int, Real>>>::const_iterator
151  map_it = _filter_map.begin(),
152  map_end = _filter_map.end();
153 
154  for ( ; map_it != map_end; map_it++) {
155 
156  std::vector<std::pair<unsigned int, Real>>::const_iterator
157  vec_it = map_it->second.begin(),
158  vec_end = map_it->second.end();
159 
160  for ( ; vec_it != vec_end; vec_it++) {
161  if (_dv_dof_ids.count(map_it->first))
162  output[map_it->first] += input[vec_it->first] * vec_it->second;
163  else
164  output[map_it->first] += input[map_it->first];
165  }
166  }
167 }
168 
169 
170 bool
172 if_elem_in_domain_of_influence(const libMesh::Elem& elem,
173  const libMesh::Node& level_set_node) const {
174 
175  Real
176  d = 1.e12; // arbitrarily large value to initialize the search
177 
178  libMesh::Point
179  pt;
180 
181  // first get the smallest distance from the node to the element nodes
182  for (unsigned int i=0; i<elem.n_nodes(); i++) {
183  pt = elem.point(i);
184  pt -= level_set_node;
185 
186  if (pt.norm() < d)
187  d = pt.norm();
188  }
189 
190  // if this distance is outside the domain of influence, then this
191  // element is not influenced by the design variable
192  if (d > _radius + _level_set_fe_size)
193  return false;
194  else
195  return true;
196 }
197 
198 
199 #ifdef LIBMESH_HAVE_NANOFLANN
200 // Nanoflann uses "duck typing" to allow users to define their own adaptors...
201 template <unsigned int Dim>
202 class NanoflannMeshAdaptor
203 {
204 private:
205  // Constant reference to the Mesh we are adapting for use in Nanoflann
206  const libMesh::MeshBase & _mesh;
207 
208 public:
209  NanoflannMeshAdaptor (const libMesh::MeshBase & mesh) :
210  _mesh(mesh)
211  {}
212 
216  typedef Real coord_t;
217 
221  inline size_t
222  kdtree_get_point_count() const { return _mesh.n_nodes(); }
223 
228  inline coord_t
229  kdtree_distance(const coord_t * p1,
230  const size_t idx_p2,
231  size_t size) const {
232 
233  libmesh_assert_equal_to (size, Dim);
234 
235  // Construct a libmesh Point object from the input coord_t. This
236  // assumes LIBMESH_DIM==3.
237  libMesh::Point point1(p1[0],
238  size > 1 ? p1[1] : 0.,
239  size > 2 ? p1[2] : 0.);
240 
241  // Get the referred-to point from the Mesh
242  const libMesh::Point & point2 = _mesh.point(idx_p2);
243 
244  // Compute Euclidean distance
245  return (point1 - point2).norm_sq();
246  }
247 
253  inline coord_t
254  kdtree_get_pt(const size_t idx, int dim) const
255  {
256  libmesh_assert_less (dim, (int) Dim);
257  libmesh_assert_less (idx, _mesh.n_nodes());
258  libmesh_assert_less (dim, 3);
259 
260  return _mesh.point(idx)(dim);
261  }
262 
269  template <class BBOX>
270  bool kdtree_get_bbox(BBOX & /* bb */) const { return false; }
271 };
272 
273 
274 void
276 
277  //libmesh_assert(!_filter_map.size());
278 
279  libMesh::MeshBase& mesh = _level_set_system.get_mesh();
280 
281  // currently implemented for replicated mesh
282  libmesh_assert(mesh.is_replicated());
283 
284  // Loop over nodes to try and detect duplicates. We use nanoflann
285  // for this, inspired by
286  // https://gist.github.com/jwpeterson/7a36f9f794df67d51126#file-detect_slit-cc-L65
287  // which was inspired by nanoflann example in libMesh source:
288  // contrib/nanoflann/examples/pointcloud_adaptor_example.cpp
289 
290  // Declare a type templated on NanoflannMeshAdaptor
291  typedef nanoflann::L2_Simple_Adaptor<Real, NanoflannMeshAdaptor<3> > adatper_t;
292 
293  // Declare a KDTree type based on NanoflannMeshAdaptor
294  typedef nanoflann::KDTreeSingleIndexAdaptor<adatper_t, NanoflannMeshAdaptor<3>, 3> kd_tree_t;
295 
296  // Build adaptor and tree objects
297  NanoflannMeshAdaptor<3> mesh_adaptor(mesh);
298  kd_tree_t kd_tree(3, mesh_adaptor, nanoflann::KDTreeSingleIndexAdaptorParams(/*max leaf=*/10));
299 
300  // Construct the tree
301  kd_tree.buildIndex();
302 
303  Real
304  d_12 = 0.,
305  sum = 0.;
306 
307  unsigned int
308  dof_1,
309  dof_2;
310 
311  libMesh::MeshBase::const_node_iterator
312  node_it = mesh.nodes_begin(),
313  node_end = mesh.nodes_end();
314 
315  // For every node in the mesh, search the KDtree and find any
316  // nodes at _radius distance from the current
317  // node being searched... this will be added to the .
318  for (; node_it != node_end; node_it++) {
319 
320  const libMesh::Node* node = *node_it;
321 
322  dof_1 = node->dof_number(_level_set_system.number(), 0, 0);
323 
324  Real query_pt[3] = {(*node)(0), (*node)(1), (*node)(2)};
325 
326  std::vector<std::pair<size_t, Real>>
327  indices_dists;
328  nanoflann::RadiusResultSet<Real, size_t>
329  resultSet(_radius*_radius, indices_dists);
330 
331  kd_tree.findNeighbors(resultSet, query_pt, nanoflann::SearchParams());
332 
333  sum = 0.;
334 
335  for (unsigned r=0; r<indices_dists.size(); ++r) {
336 
337  d_12 = std::sqrt(indices_dists[r].second);
338 
339  // the distance of this node should be less than or equal to the
340  // specified search radius
341  libmesh_assert_less_equal(d_12, _radius);
342 
343  sum += _radius - d_12;
344  dof_2 = mesh.node_ptr(indices_dists[r].first)->dof_number(_level_set_system.number(), 0, 0);
345 
346  _filter_map[dof_1].push_back(std::pair<unsigned int, Real>(dof_2, _radius - d_12));
347  }
348 
349  libmesh_assert_greater(sum, 0.);
350 
351  // with the coefficients computed for dof_1, divide each coefficient
352  // with the sum
353  std::vector<std::pair<unsigned int, Real>>& vec = _filter_map[dof_1];
354  for (unsigned int i=0; i<vec.size(); i++) {
355 
356  vec[i].second /= sum;
357  libmesh_assert_less_equal(vec[i].second, 1.);
358  }
359  }
360 
361  // compute the largest element size
362  libMesh::MeshBase::const_element_iterator
363  e_it = mesh.elements_begin(),
364  e_end = mesh.elements_end();
365 
366  for ( ; e_it != e_end; e_it++) {
367  const libMesh::Elem* e = *e_it;
368  d_12 = e->hmax();
369 
370  if (_level_set_fe_size < d_12)
371  _level_set_fe_size = d_12;
372  }
373 }
374 #endif
375 
376 
377 void
379 
380  libmesh_assert(!_filter_map.size());
381 
382  libMesh::MeshBase& mesh = _level_set_system.get_mesh();
383 
384  // currently implemented for replicated mesh
385  libmesh_assert(mesh.is_replicated());
386 
387  // iterate over all nodes to compute the
388  libMesh::MeshBase::const_node_iterator
389  node_it_1 = mesh.nodes_begin(),
390  node_it_2 = mesh.nodes_begin(),
391  node_end = mesh.nodes_end();
392 
393  libMesh::Point
394  d;
395 
396  Real
397  d_12 = 0.,
398  sum = 0.;
399 
400  unsigned int
401  dof_1,
402  dof_2;
403 
404  for ( ; node_it_1 != node_end; node_it_1++) {
405 
406  dof_1 = (*node_it_1)->dof_number(_level_set_system.number(), 0, 0);
407 
408  node_it_2 = mesh.nodes_begin();
409  sum = 0.;
410 
411  for ( ; node_it_2 != node_end; node_it_2++) {
412 
413  // compute the distance between the two nodes
414  d = (**node_it_1) - (**node_it_2);
415  d_12 = d.norm();
416 
417  // if the nodes is within the filter radius, add it to the map
418  if (d_12 <= _radius) {
419 
420  sum += _radius - d_12;
421  dof_2 = (*node_it_2)->dof_number(_level_set_system.number(), 0, 0);
422 
423  _filter_map[dof_1].push_back(std::pair<unsigned int, Real>(dof_2, _radius - d_12));
424  }
425  }
426 
427  libmesh_assert_greater(sum, 0.);
428 
429  // with the coefficients computed for dof_1, divide each coefficient
430  // with the sum
431  std::vector<std::pair<unsigned int, Real>>& vec = _filter_map[dof_1];
432  for (unsigned int i=0; i<vec.size(); i++) {
433 
434  vec[i].second /= sum;
435  libmesh_assert_less_equal(vec[i].second, 1.);
436  }
437  }
438 
439  // compute the largest element size
440  libMesh::MeshBase::const_element_iterator
441  e_it = mesh.elements_begin(),
442  e_end = mesh.elements_end();
443 
444  for ( ; e_it != e_end; e_it++) {
445  const libMesh::Elem* e = *e_it;
446  d_12 = e->hmax();
447 
448  if (_level_set_fe_size < d_12)
449  _level_set_fe_size = d_12;
450  }
451 }
452 
453 
454 void
455 MAST::FilterBase::print(std::ostream& o) const {
456 
457  o << "Filter radius: " << _radius << std::endl;
458 
459  o
460  << std::setw(20) << "Filtered ID"
461  << std::setw(20) << "Dependent Vars" << std::endl;
462 
463  std::map<unsigned int, std::vector<std::pair<unsigned int, Real>>>::const_iterator
464  map_it = _filter_map.begin(),
465  map_end = _filter_map.end();
466 
467  for ( ; map_it != map_end; map_it++) {
468 
469  o
470  << std::setw(20) << map_it->first;
471 
472  std::vector<std::pair<unsigned int, Real>>::const_iterator
473  vec_it = map_it->second.begin(),
474  vec_end = map_it->second.end();
475 
476  for ( ; vec_it != vec_end; vec_it++) {
477 
478  if (_dv_dof_ids.count(map_it->first))
479  o
480  << " : " << std::setw(8) << vec_it->first
481  << " (" << std::setw(8) << vec_it->second << " )";
482  else
483  libMesh::out << " : " << map_it->first;
484  }
485  libMesh::out << std::endl;
486  }
487 }
488 
libMesh::System & _level_set_system
system on which the level set discrete function is defined
Definition: filter_base.h:101
std::map< unsigned int, std::vector< std::pair< unsigned int, Real > > > _filter_map
Algebraic relation between filtered level set values and the design variables .
Definition: filter_base.h:124
Real _level_set_fe_size
largest element size in the level set mesh
Definition: filter_base.h:112
libMesh::Real Real
void compute_filtered_values(const libMesh::NumericVector< Real > &input, libMesh::NumericVector< Real > &output, bool close_vector=true) const
computes the filtered output from the provided input.
Definition: filter_base.cpp:59
const std::set< unsigned int > & _dv_dof_ids
dof ids that are design variables.
Definition: filter_base.h:118
Real _radius
radius of the filter.
Definition: filter_base.h:106
void _init()
initializes the algebraic data structures
FilterBase(libMesh::System &sys, const Real radius, const std::set< unsigned int > &dv_dof_ids)
Definition: filter_base.cpp:35
virtual void print(std::ostream &o) const
prints the filter data.
virtual ~FilterBase()
Definition: filter_base.cpp:53
bool if_elem_in_domain_of_influence(const libMesh::Elem &elem, const libMesh::Node &level_set_node) const
function identifies if the given element is within the domain of influence of this specified level se...