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.
111 lines
4.1 KiB
111 lines
4.1 KiB
2 years ago
|
#ifndef MEDUSA_BITS_DOMAINS_HALFLINKSREFINE_HPP_
|
||
|
#define MEDUSA_BITS_DOMAINS_HALFLINKSREFINE_HPP_
|
||
|
|
||
|
/**
|
||
|
* @file
|
||
|
* Implementation of the half-link refinement algorithm.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#include "HalfLinksRefine_fwd.hpp"
|
||
|
#include <medusa/bits/utils/assert.hpp>
|
||
|
#include <medusa/bits/spatial_search/KDTreeMutable.hpp>
|
||
|
#include <medusa/bits/types/Range.hpp>
|
||
|
#include <numeric>
|
||
|
|
||
|
namespace mm {
|
||
|
|
||
|
template <typename vec_t>
|
||
|
Range<int> HalfLinksRefine::operator()(DomainDiscretization<vec_t>& domain) const {
|
||
|
KDTreeMutable<vec_t> domain_tree(domain.positions());
|
||
|
return operator()(domain, domain_tree);
|
||
|
}
|
||
|
|
||
|
template <typename vec_t>
|
||
|
Range<int> HalfLinksRefine::operator()(
|
||
|
DomainDiscretization<vec_t>& domain, KDTreeMutable<vec_t>& tree) const {
|
||
|
Range<int> region = region_;
|
||
|
if (region.empty()) { region = domain.all(); }
|
||
|
|
||
|
// sort: boundary nodes first
|
||
|
std::sort(region.begin(), region.end(),
|
||
|
[&](int i, int j) { return domain.type(i) < domain.type(j); });
|
||
|
|
||
|
return refine_impl(domain, region, fraction_, tree);
|
||
|
}
|
||
|
|
||
|
template <class vec_t>
|
||
|
Range<int> HalfLinksRefine::refine_impl(
|
||
|
DomainDiscretization<vec_t>& domain, const Range<int>& region,
|
||
|
double fraction, KDTreeMutable<vec_t>& domain_tree) {
|
||
|
int region_size = region.size();
|
||
|
int N = domain.size();
|
||
|
using scalar_t = typename vec_t::scalar_t;
|
||
|
|
||
|
assert_msg(region_size > 0, "The region to refine is empty.");
|
||
|
|
||
|
// Iterate through points in region and generate new points
|
||
|
int num_new_points = 0;
|
||
|
for (int i = 0; i < region_size; i++) {
|
||
|
int c = region[i]; // the global domain index
|
||
|
const vec_t pos = domain.pos(c);
|
||
|
const Range<int> supp = domain.support(c);
|
||
|
assert_msg(supp.size() >= 2, "At least 2 nodes must be in support of every node, %d "
|
||
|
"found in support of node %d.", supp.size(), c);
|
||
|
scalar_t min_dist = fraction * domain.dr(c);
|
||
|
|
||
|
int n = supp.size();
|
||
|
// Half links to my support.
|
||
|
for (int j = 1; j < n; ++j) {
|
||
|
int s = supp[j];
|
||
|
vec_t candidate = 0.5 * (domain.pos(c) + domain.pos(s));
|
||
|
|
||
|
// Decide the type of new node and project to boundary if necessary.
|
||
|
if (domain.type(c) == 0 || domain.type(s) == 0) continue;
|
||
|
int candidate_type = std::max(domain.type(c), domain.type(s));
|
||
|
vec_t normal_vector;
|
||
|
if (candidate_type < 0) {
|
||
|
normal_vector = domain.normal(c) + domain.normal(s);
|
||
|
if (normal_vector.squaredNorm() < 1e-15) {
|
||
|
// normal_vector of given points point in opposite directions.
|
||
|
continue;
|
||
|
}
|
||
|
normal_vector.normalize();
|
||
|
auto result = domain.shape().projectPointToBoundary(candidate, normal_vector);
|
||
|
if (result.first) {
|
||
|
candidate = result.second;
|
||
|
} else {
|
||
|
std::cerr << format("Adding point %s with type %d to the boundary along "
|
||
|
"normal %s was not successful.",
|
||
|
candidate, candidate_type, normal_vector)
|
||
|
<< std::endl;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If nodes are out of the domain, they should be thrown away.
|
||
|
if (!domain.shape().contains(candidate)) continue;
|
||
|
// Distance to closest node.
|
||
|
double dist2 = domain_tree.query(candidate).second[0];
|
||
|
if (dist2 >= min_dist * min_dist) { // add new point
|
||
|
++num_new_points;
|
||
|
domain_tree.insert(candidate);
|
||
|
if (candidate_type < 0) {
|
||
|
domain.addBoundaryNode(candidate, candidate_type, normal_vector);
|
||
|
} else {
|
||
|
domain.addInternalNode(candidate, candidate_type);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// return back indices of new nodes
|
||
|
Range<int> new_ids(num_new_points);
|
||
|
std::iota(new_ids.begin(), new_ids.end(), N);
|
||
|
return new_ids;
|
||
|
}
|
||
|
|
||
|
} // namespace mm
|
||
|
|
||
|
#endif // MEDUSA_BITS_DOMAINS_HALFLINKSREFINE_HPP_
|