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.
170 lines
7.8 KiB
170 lines
7.8 KiB
#ifndef MEDUSA_BITS_DOMAINS_BASICRELAX_HPP_
|
|
#define MEDUSA_BITS_DOMAINS_BASICRELAX_HPP_
|
|
|
|
#include "BasicRelax_fwd.hpp"
|
|
#include <medusa/bits/utils/assert.hpp>
|
|
#include <medusa/bits/types/Range.hpp>
|
|
#include <medusa/bits/spatial_search/KDTree.hpp>
|
|
#include <medusa/bits/spatial_search/KDTreeMutable.hpp>
|
|
|
|
/**
|
|
* @file
|
|
* Implementation of class for box shaped domains.
|
|
*/
|
|
|
|
namespace mm {
|
|
|
|
|
|
template <class domain_t, class radius_func_type>
|
|
void BasicRelax::operator()(domain_t& domain, const radius_func_type& r_func) const {
|
|
typedef typename domain_t::vector_t vec_t;
|
|
typedef typename domain_t::scalar_t scalar_t;
|
|
const int dim = domain_t::dim;
|
|
|
|
// if no nodes are supplied, all interior nodes are processed
|
|
Range<int> nodes = (nodes_.empty()) ? static_cast<Range<int>>(domain.interior()) : nodes_;
|
|
|
|
int N = domain.size();
|
|
assert_msg(N > num_neighbours, "Cannot relax a domain with less that num_neighbours nodes, "
|
|
"num_neighbours = %d, N = %d.", num_neighbours, N);
|
|
assert_msg(num_neighbours >= 1, "At least two neighbours are required, got %d.",
|
|
num_neighbours);
|
|
for (int i : nodes) {
|
|
assert_msg(0 <= i && i < N, "Node index %d out of range [0, %d) in relax.", i, N);
|
|
}
|
|
|
|
Range<int> bnd = domain.boundary();
|
|
assert_msg(!bnd.empty(), "Relax requires boundary nodes, but this domain has none!");
|
|
KDTreeMutable<vec_t> boundary_tree(domain.positions()[bnd]);
|
|
KDTree<vec_t> tree;
|
|
Range<int> indices;
|
|
|
|
Range<int> to_delete = {}; // nodes to be deleted from domain after relax
|
|
bool removed_nodes_warning = false;
|
|
|
|
for (int c = 0; c < num_iterations; ++c) {
|
|
if (c == 0 || c % rebuild_tree_after == 0) {
|
|
tree.reset(domain.positions());
|
|
}
|
|
// nodes to be removed from relax procedure
|
|
Range<int> to_remove = {};
|
|
// Annealing factor -- depends only on iteration step
|
|
double a_f = ((initial_heat - final_heat) * (num_iterations - c) / num_iterations
|
|
+ final_heat);
|
|
// Range of all displacements
|
|
Range<vec_t> dp(nodes.size(), vec_t(0.0));
|
|
int k;
|
|
// #if defined(_OPENMP)
|
|
// #pragma omp parallel for private(k) schedule(static)
|
|
// before enabling openmp vectors to_delete and to_remove
|
|
// have to be introduced for each thread and combined at the end of the loop
|
|
// otherwise expect se.g.\ fault
|
|
// #endif
|
|
for (k = 0; k < nodes.size(); ++k) {
|
|
int i = nodes[k];
|
|
assert_msg(domain.type(i) > 0, "Only interior nodes are to be relaxed");
|
|
vec_t pos = domain.pos(i);
|
|
indices = tree.query(pos, num_neighbours + 1).first;
|
|
double d0 = std::pow((domain.pos(indices[1]) - pos).norm(), 1.0 / dim);
|
|
// loop over support and compute potential contributions
|
|
scalar_t r_0 = 0;
|
|
for (int j = 1; j < indices.size(); ++j) {
|
|
// central node to support nodes vector
|
|
vec_t r = domain.pos(indices[j]) - pos;
|
|
assert_msg(r.norm() > 1e-15, "Nodes %d and %d have the same coordinates %s. "
|
|
"Try using lower initial heat.", i, indices[j], pos);
|
|
// potential gradient
|
|
// target density in terms of radius to the closest node
|
|
r_0 = r_func(domain.pos(indices[j]));
|
|
dp[k] += std::pow(std::abs(r_0) / r.norm(), potential_order) * r;
|
|
}
|
|
// no-distribution mode
|
|
if (r_0 < 0) dp[k] = 0.2 * dp[k].normalized() * a_f * d0;
|
|
dp[k] = -a_f * d0 * dp[k];
|
|
// Project escaped nodes on the boundary
|
|
if (!domain.shape().contains(pos + dp[k])) {
|
|
vec_t tmp, normal; // projection temp variables
|
|
bool project = true;
|
|
switch (projection_type) {
|
|
case DO_NOT_PROJECT: // mark escaped nodes for deletion
|
|
to_remove.push_back(k);
|
|
to_delete.push_back(nodes[k]);
|
|
project = false;
|
|
break;
|
|
case PROJECT_IN_DIRECTION: // project on boundary in direction of relax
|
|
tmp = 0.5 * (2 * pos + dp[k]);
|
|
normal = dp[k].normalized();
|
|
break;
|
|
case PROJECT_BETWEEN_CLOSEST: // project between closest nodes on boundary
|
|
Range<int> idx = boundary_tree.query(pos, 2).first;
|
|
int s = bnd[idx[0]], t = bnd[idx[1]];
|
|
tmp = 0.5 * (domain.pos(s) + domain.pos(t));
|
|
normal = (0.5 * (domain.normal(s) + domain.normal(t))).normalized();
|
|
break;
|
|
} // projection method selection end
|
|
if (project) {
|
|
// performs actual projection
|
|
bool success;
|
|
vec_t proj;
|
|
std::tie(success, proj) = domain.shape().projectPointToBoundary(tmp, normal);
|
|
if (success) {
|
|
// check if projected node is not too close to boundary node
|
|
Range<double> dist;
|
|
Range<int> ind;
|
|
std::tie(ind, dist) = boundary_tree.query(proj, 2);
|
|
if (std::sqrt(dist[0] / dist[1]) < boundary_projection_threshold) {
|
|
dp[k] = -dp[k];
|
|
continue;
|
|
}
|
|
double d_1 = std::sqrt(dist[0]);
|
|
double d_2 = std::sqrt(dist[1]);
|
|
double d_0 = std::sqrt(dist[0] + dist[1]);
|
|
|
|
normal = ((1 - d_1 / d_0) * domain.normal(bnd[ind[0]])
|
|
+ (1 - d_2 / d_0) * domain.normal(bnd[ind[1]])).normalized();
|
|
int boundary_type = domain.type(bnd[ind[0]]);
|
|
domain.changeToBoundary(i, proj, boundary_type, normal);
|
|
boundary_tree.insert(proj);
|
|
bnd.push_back(i);
|
|
// mark projected node to be removed from the list for relax
|
|
to_remove.push_back(k);
|
|
} else {
|
|
removed_nodes_warning = true;
|
|
// if projection failed, remove node
|
|
to_remove.push_back(k);
|
|
to_delete.push_back(nodes[k]);
|
|
}
|
|
} // project if end
|
|
} // escaped nodes if end
|
|
} // spatial loop end
|
|
nodes.remove(to_remove);
|
|
assert_msg(nodes.size() > num_neighbours + 1,
|
|
"No nodes in relax pool anymore, perhaps use lower heat");
|
|
// apply displacement
|
|
// #if defined(_OPENMP)
|
|
// #pragma omp parallel for private(k) schedule(static)
|
|
// #endif
|
|
for (k = 0; k < nodes.size(); ++k) {
|
|
domain.pos(nodes[k]) += dp[k];
|
|
}
|
|
// DEBUG OUT -- for plotting
|
|
// debug_out << "nodes(" << c + 1 << ",:,:)=" << domain.positions << ";\n";
|
|
}
|
|
// remove nodes at the end of relax to avoid mess with indices
|
|
domain.removeNodes(to_delete);
|
|
#if 0 // debug
|
|
debug_out << "nodes_final(:,:)=" << domain.positions << ";\n";
|
|
debug_out << "normal =" << domain.normals() << ";\n";
|
|
Range<int> boundary = domain.types < 0;
|
|
debug_out << "boundary =" << boundary << ";\n";
|
|
debug_out << "boundary_map =" << domain.boundary_map() << ";\n";
|
|
debug_out.close();
|
|
#endif
|
|
if (removed_nodes_warning) {
|
|
print_red("warning -- nodes removed during relax due to projection fail\n");
|
|
}
|
|
}
|
|
|
|
} // namespace mm
|
|
|
|
#endif // MEDUSA_BITS_DOMAINS_BASICRELAX_HPP_
|
|
|