You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

125 lines
4.9 KiB

#ifndef MEDUSA_BITS_DOMAINS_FINDBALANCEDSUPPORT_HPP_
#define MEDUSA_BITS_DOMAINS_FINDBALANCEDSUPPORT_HPP_
/**
* @file
* Implementation of balanced supports.
*/
#include <medusa/bits/spatial_search/KDTree.hpp>
#include "FindBalancedSupport_fwd.hpp"
#include "Eigen/SVD"
/**
* @file
* Implementation of class for finding support nodes.
*/
namespace mm {
/// @cond
template <class vec_t>
Eigen::Matrix<typename vec_t::scalar_t, vec_t::dim, vec_t::dim - 1>
FindBalancedSupport::getFrame(const vec_t& normal) {
return normal.transpose().jacobiSvd(Eigen::ComputeFullV).matrixV().rightCols(vec_t::dim-1);
}
/// @endcond
template <typename domain_t>
void mm::FindBalancedSupport::operator()(domain_t& domain) const {
auto for_which = for_which_;
if (for_which.empty()) for_which = domain.all();
auto search_among = search_among_;
if (search_among.empty()) {
search_among = Range<int>::seq(domain.size()); // include potential ghost nodes
}
assert_msg(!domain.positions().empty(), "Cannot find support in an empty domain.");
assert_msg(min_support > 0, "Support size must be greater than 0, got %d.",
min_support);
assert_msg(min_support <= search_among.size(), "Support size (%d) cannot exceed number of "
"points that we are searching among (%d).", min_support, search_among.size());
assert_msg(!for_which.empty(), "Set of nodes for which to find the support is empty.");
assert_msg(!search_among.empty(), "Set of nodes to search among is empty.");
for (int x : for_which) {
assert_msg(0 <= x && x < domain.size(), "Index %d out of range [%d, %d) in for_which.",
x, 0, domain.size());
}
for (int x : search_among) {
assert_msg(0 <= x && x < domain.size(), "Index %d out of range [%d, %d) in "
"search_among.", x, 0, domain.size());
}
KDTree<typename domain_t::vector_t> tree(domain.positions()[search_among]);
for (int i : for_which) {
domain.support(i) = balancedSupport(
domain, tree, i, min_support, max_support, search_among);
}
}
template <class vec_t>
bool FindBalancedSupport::mark_quadrant(
const vec_t& dx, typename vec_t::scalar_t tol, std::vector<bool>& octants_covered,
const Eigen::Matrix<typename vec_t::scalar_t, vec_t::dim, -1>& basis) {
// for each basis vector, determine if it is in the same or opposite direction
// record that value in `octants`
int octant = 0;
Eigen::Matrix<typename vec_t::scalar_t, Eigen::Dynamic, 1> s = basis.transpose()*dx;
for (int b = 0; b < basis.cols(); ++b) {
if (std::abs(s[b]) < tol) {
octant = -1;
break;
}
if (s[b] > 0) octant += (1 << b);
}
// mark computed octant as covered
if (octant != -1 && !octants_covered[octant]) {
octants_covered[octant] = true;
return true;
}
return false;
}
template <typename domain_t, typename kdtree_t>
Range<int> FindBalancedSupport::balancedSupport(domain_t& domain, const kdtree_t& tree, int i,
int min_support_size, int max_support_size,
const Range<int>& search_among) {
const int dim = domain_t::dim;
typedef typename domain_t::vector_t vec_t;
typedef typename domain_t::scalar_t scalar_t;
Eigen::Matrix<scalar_t, dim, Eigen::Dynamic> basis =
Eigen::Matrix<scalar_t, dim, dim>::Identity();
if (domain.type(i) < 0) basis = getFrame(domain.normal(i));
int support_size = min_support_size;
const vec_t& center = domain.pos(i);
double tol = 1e-6;
int target = 1 << basis.cols();
int boundary_neighbours_required = (domain.type(i) < 0) ? 2*(dim-1)+1 : 0;
std::vector<bool> octants_covered(target, false);
int pos_idx = 0;
Range<int> indices;
while (pos_idx < max_support_size) {
indices = tree.query(center, support_size).first;
for (int& j : indices) { j = search_among[j]; }
for (; pos_idx < indices.size(); ++pos_idx) {
vec_t dx = domain.pos(indices[pos_idx]) - center;
if (domain.type(indices[pos_idx]) < 0 && domain.type(i) < 0) {
boundary_neighbours_required--;
target -= mark_quadrant(dx, tol, octants_covered, basis);
} else if (domain.type(i) > 0) {
target -= mark_quadrant(dx, tol, octants_covered, basis);
}
if (target <= 0 && boundary_neighbours_required <= 0 &&
pos_idx >= min_support_size-1) {
return Range<int>(indices.begin(), indices.begin()+pos_idx+1);
}
}
support_size *= 2;
if (support_size > max_support_size) { support_size = max_support_size; }
}
return indices;
}
} // namespace mm
#endif // MEDUSA_BITS_DOMAINS_FINDBALANCEDSUPPORT_HPP_