band.cc
Go to the documentation of this file.
// Banded level set routines
//
// Author: Pierre Saramito
//
#include "rheolef/band.h"
#include "rheolef/field_expr.h"
#include "rheolef/rheostream.h"
namespace rheolef {
// extern:
template<class T, class M>
geo_basic<T,M>
level_set_internal (
const field_basic<T,M>&,
const level_set_option&,
std::vector<size_t>&,
disarray<size_t,M>&);
// =========================================================================
// part 0: utility
// =========================================================================
static Float band_epsilon = 100*std::numeric_limits<Float>::epsilon();
template <class T>
static
bool
band_is_zero (const T& x) {
return fabs(x) <= band_epsilon;
}
// =========================================================================
// part I: subroutine: build vertex cc
// =========================================================================
template<class T, class M>
static
geo_element::size_type
build_vertex_connex_component (
const geo_basic<T,M>& band,
const std::vector<geo_element::size_type>& zero_iv_list,
const std::vector<geo_element::size_type>& isolated_ie_list)
{
band.neighbour_guard(); // will use neighbours
typedef geo_element::size_type size_type;
communicator comm = band.sizes().node_ownership.comm();
size_type map_dim = band.map_dimension();
size_type not_marked = std::numeric_limits<size_type>::max();
//
// 1) isolated elements are marked 0
// and icc-th connex component will be marked 1+icc (icc=0,1..)
//
distributor element_ownership = band.sizes().ownership_by_dimension[map_dim];
disarray<size_type,M> element_mark (element_ownership, not_marked);
for (size_type iie = 0, ine = isolated_ie_list.size(); iie < ine; iie++) {
element_mark [isolated_ie_list [iie]] = 0;
}
//
// 2) propagate a front that stop at isolated of zero marked vertices
//
// element_mark [ie] = 0; when isolated
// element_mark [ie] = 1+icc; when on the i-th connex component
//
size_type first_dis_ie = element_ownership.first_index();
size_type icc = 0;
bool cc_need_dis_fusion = false;
std::vector<std::vector<size_type> > cc_ie_list_table;
size_type side_dim = map_dim-1;
index_set ext_ie_set;
for (size_type ie0 = 0, ne = band.size(map_dim); ie0 < ne; ie0++) {
if (element_mark [ie0] != not_marked) continue;
element_mark [ie0] = 1+icc;
std::list<size_type> front;
front.push_back (ie0);
cc_ie_list_table.push_back (std::vector<size_type>());
std::vector<size_type>& cc_ie_list = cc_ie_list_table [icc];
cc_ie_list.push_back (ie0);
while (front.size() != 0) {
size_type ie = *(front.begin());
front.pop_front();
const geo_element& K = band [ie];
for (size_type loc_isid = 0, loc_nsid = K.n_subgeo(side_dim); loc_isid < loc_nsid; loc_isid++) {
size_type dis_je = band.neighbour (ie, loc_isid);
if (dis_je == std::numeric_limits<size_type>::max()) continue; // no neighbour
if (! element_ownership.is_owned (dis_je)) {
// K' belongs to another partition: cc is split on several parts
ext_ie_set += dis_je;
cc_need_dis_fusion = true;
continue;
}
// K' belongs to my partition
size_type je = dis_je - first_dis_ie;
if (element_mark [je] != not_marked) continue; // K' already studied
element_mark[je] = 1+icc;
front.push_back(je);
cc_ie_list.push_back(je);
}
}
icc++;
}
size_type ncc = cc_ie_list_table.size();
distributor cc_ownership (distributor::decide, comm, ncc);
size_type dis_ncc = cc_ownership.dis_size();
size_type first_dis_icc = cc_ownership.first_index();
//
// 4) here, cc fusion across partitions are neded...
// => fusion of vertex cc that are split by partition boundaries
//
// non-zero marks are changed from 1+icc to 1+dis_icc numbering
for (size_type ie = 0, ne = element_ownership.size(); ie < ne; ie++) {
check_macro (element_mark [ie] != not_marked, "unexpected ie="<<ie<<" not marked");
if (element_mark [ie] != 0) { element_mark [ie] += first_dis_icc; }
}
element_mark.set_dis_indexes (ext_ie_set);
//
// 5) compute the adjacency matrix A of cc across boundaries:
//
// send all in a pseudo-sequential matrix on process io_proc
size_type io_proc = odiststream::io_proc();
size_type my_proc = comm.rank();
distributor io_cc_ownership (dis_ncc, comm, (my_proc == io_proc ? dis_ncc : 0));
asr<Float,M> a_asr (io_cc_ownership, io_cc_ownership);
for (size_type ie = 0, ne = element_ownership.size(); ie < ne; ie++) {
if (element_mark [ie] == 0) continue;
const geo_element& K1 = band [ie];
size_type K1_dis_icc = element_mark [ie] - 1;
for (size_type loc_isid = 0, loc_nsid = K1.n_subgeo(side_dim); loc_isid < loc_nsid; loc_isid++) {
size_type dis_je = band.neighbour (ie, loc_isid);
if (dis_je == std::numeric_limits<size_type>::max()) continue; // no neighbour
size_type K2_mark = element_mark.dis_at (dis_je);
if (K2_mark == 0) continue;
size_type K2_dis_icc = K2_mark - 1;
a_asr.dis_entry (K1_dis_icc, K2_dis_icc) += 1;
a_asr.dis_entry (K2_dis_icc, K1_dis_icc) += 1;
}
}
a_asr.dis_entry_assembly();
// convert to csr:
csr<Float,M> a_csr (a_asr);
size_type nnz_a = a_csr.dis_nnz();
//
// 6) compute the closure adjacency matrix: C = lim_{n->infty} A^n
// A is a boolean matrix: the sequence A^n converge for a finite n
//
// convert A_csr to A as dense Eigen::Matrix on io_proc
//
// note: using "bool" instead of "int" in Eigen works with g++ but not with clang++
Eigen::Matrix<int,Eigen::Dynamic,Eigen::Dynamic> a = Eigen::Matrix<int,Eigen::Dynamic,Eigen::Dynamic>::Zero (a_csr.nrow(), a_csr.ncol());
typename csr<T,M>::const_iterator dia_ia = a_csr.begin();
typename csr<T,M>::const_iterator ext_ia = a_csr.ext_begin();
size_type first_i = a_csr.row_ownership().first_index();
for (size_type i = 0, n = a_csr.nrow(), q = 0; i < n; i++) {
size_type dis_i = first_i + i;
a (dis_i,dis_i) = 1; // force auto-connection, for locally empty cc (fix a bug)
for (typename csr<T,M>::const_data_iterator p = dia_ia[i]; p < dia_ia[i+1]; p++) {
size_type dis_j = first_i + (*p).first;
a (dis_i,dis_j) = 1;
}
}
Eigen::Matrix<int,Eigen::Dynamic,Eigen::Dynamic> c = a;
size_type nnz_c = nnz_a;
for (size_type k = 0, n = a_csr.nrow(); k < n; k++) {
Eigen::Matrix<int,Eigen::Dynamic,Eigen::Dynamic> c_tmp = c*a;
c = c_tmp;
size_type new_nnz_c = 0;
for (size_type i = 0; i < n; i++)
for (size_type j = 0; j < n; j++)
if (c(i,j) != 0) new_nnz_c++;
if (new_nnz_c == nnz_c) break; // converged
}
//
// 7) each row of C indicates the cc that are split by a partition bdry
// => re-number icc on io_proc into merged_icc
//
size_type unset = std::numeric_limits<size_type>::max();
disarray<size_type,M> icc2merged_dis_icc (cc_ownership, unset);
size_type merged_dis_ncc = 0;
if (my_proc == io_proc) {
std::vector<size_type> tmp_icc2merged_dis_icc (cc_ownership.dis_size(), unset);
for (size_type dis_icc = 0; dis_icc < dis_ncc; dis_icc++) {
if (tmp_icc2merged_dis_icc [dis_icc] != unset) continue;
for (size_type dis_jcc = 0; dis_jcc < dis_ncc; dis_jcc++) {
if (! c (dis_icc,dis_jcc)) continue;
// here dis_icc and dis_jcc are two parts of the same cc
// these two parts have been cutted by a partition boundary: now merge their indexes
check_macro (tmp_icc2merged_dis_icc [dis_jcc] == unset, "closure adjacency of connex component failed");
tmp_icc2merged_dis_icc [dis_jcc] = merged_dis_ncc;
}
merged_dis_ncc++;
}
// distribute the tmp_icc2merged_dis_icc disarray:
for (size_type dis_icc = 0; dis_icc < dis_ncc; dis_icc++) {
icc2merged_dis_icc.dis_entry (dis_icc) = tmp_icc2merged_dis_icc [dis_icc];
}
}
icc2merged_dis_icc.dis_entry_assembly();
#ifdef _RHEOLEF_HAVE_MPI
if (is_distributed<M>::value) {
mpi::broadcast (comm, merged_dis_ncc, io_proc);
}
#endif // _RHEOLEF_HAVE_MPI
//
// 8) on each proc, merge parts of cc components
//
std::map<size_type,std::vector<size_type> > merged_cc_ie_list; // merged_cc[merged_icc] -> list of elt indexes
for (size_type icc = 0; icc < ncc; icc++) {
size_type merged_dis_icc = icc2merged_dis_icc [icc];
std::vector<size_type>& merged_cc = merged_cc_ie_list [merged_dis_icc];
index_set tmp_cc; // used to have a sorted index list
for (std::vector<size_type>::const_iterator
iter = cc_ie_list_table[icc].begin(),
last = cc_ie_list_table[icc].end(); iter != last; ++iter) {
size_type ie = *iter;
tmp_cc.insert (ie);
}
for (index_set::const_iterator
iter = tmp_cc.begin(),
last = tmp_cc.end(); iter != last; ++iter) {
merged_cc.push_back(*iter);
}
}
//
// 9) merged icc numbering depends upon nproc: renum by sorting
// cc based on the smallest ios element index (nproc independant)
//
std::map<size_type,size_type> min_ios_dis_ie2dis_icc;
for (size_type merged_dis_icc = 0; merged_dis_icc < merged_dis_ncc; merged_dis_icc++) {
std::map<size_type,std::vector<size_type> >::const_iterator iter
= merged_cc_ie_list.find (merged_dis_icc);
size_type min_ios_dis_ie = std::numeric_limits<size_type>::max();
if (iter != merged_cc_ie_list.end()) {
const std::vector<size_type>& ie_list = (*iter).second;
for (size_type iie = 0, ine = ie_list.size(); iie < ine; iie++) {
size_type ie = ie_list [iie];
size_type ios_dis_ie = band.ige2ios_dis_ige (map_dim, ie);
min_ios_dis_ie = std::min (min_ios_dis_ie, ios_dis_ie);
}
}
#ifdef _RHEOLEF_HAVE_MPI
if (is_distributed<M>::value) {
min_ios_dis_ie = mpi::all_reduce (comm, min_ios_dis_ie, mpi::minimum<size_type>());
}
#endif // _RHEOLEF_HAVE_MPI
min_ios_dis_ie2dis_icc [min_ios_dis_ie] = merged_dis_icc;
}
check_macro (min_ios_dis_ie2dis_icc.size() == merged_dis_ncc, "something wrong appends");
std::vector<size_type> ios_merged_dis_icc2merged_dis_icc (merged_dis_ncc, unset);
{
size_type ios_merged_dis_icc = 0;
for (std::map<size_type,size_type>::const_iterator
iter = min_ios_dis_ie2dis_icc.begin(),
last = min_ios_dis_ie2dis_icc.end(); iter != last; ++iter, ++ios_merged_dis_icc) {
size_type merged_dis_icc = (*iter).second;
ios_merged_dis_icc2merged_dis_icc [ios_merged_dis_icc] = merged_dis_icc;
}
}
// 10) build distributed domains from merged parts of each procs
//
std::vector<size_type> merged_cc_ie_empty_list;
for (size_type ios_merged_dis_icc = 0; ios_merged_dis_icc < merged_dis_ncc; ios_merged_dis_icc++) {
size_type merged_dis_icc = ios_merged_dis_icc2merged_dis_icc [ios_merged_dis_icc];
check_macro (merged_dis_icc < merged_dis_ncc, "merged_dis_icc="<<merged_dis_icc<<" out of range {0:"<<merged_dis_ncc<<"[");
std::string merged_cc_name = "cc" + itos (ios_merged_dis_icc);
const std::vector<size_type>* ptr_merged_cc_ie_list;
std::map<size_type,std::vector<size_type> >::const_iterator iter
= merged_cc_ie_list.find (merged_dis_icc);
if (iter != merged_cc_ie_list.end()) {
ptr_merged_cc_ie_list = & ((*iter).second);
} else {
ptr_merged_cc_ie_list = & merged_cc_ie_empty_list;
}
domain_indirect_basic<M> merged_cc_dom (band, merged_cc_name, map_dim, comm, *ptr_merged_cc_ie_list);
band.insert_domain_indirect (merged_cc_dom);
}
return merged_dis_ncc;
}
// =========================================================================
// part II: the main band constructor
// =========================================================================
template<class T, class M>
band_basic<T,M>::band_basic (
const field_basic<T,M>& phi_h,
const level_set_option& opt)
: _gamma(),
_band(),
_sid_ie2bnd_ie(),
_ncc(0)
{
using namespace details;
band_epsilon = opt.epsilon; // set global variable
//
// 1) build the level set
//
std::vector<size_type> bnd_dom_ie_list;
_gamma = level_set_internal (phi_h, opt, bnd_dom_ie_list, _sid_ie2bnd_ie);
//
// 2) build the band
//
// 2.1) build the band domain in lambda
const geo_basic<T,M>& lambda = phi_h.get_geo();
check_macro(lambda.order() == 1, "Only order=1 band supported");
size_type map_dim = lambda.map_dimension();
communicator comm = lambda.geo_element_ownership(map_dim).comm();
domain_indirect_basic<M> band_indirect (lambda, "band", map_dim, comm, bnd_dom_ie_list);
geo_basic<T,M> band_domain = geo_basic<T,M> (band_indirect, lambda);
// 2.2) reduce phi_h to the band domain & get its geo_domain band
field_basic<T,M> phi_h_band (phi_h [band_domain]);
_band = phi_h_band.get_geo();
//
// 3) create the "zero" containing vertices xi such that phi(xi) = 0
// TODO: Pk and k>=2: zero is a domain of idofs: Bh.block(isolated); Bh.unblock (zeros)
//
check_macro(phi_h_band.get_approx() == "P1", "Only P1 level set function supported");
std::vector<size_type> zero_iv_list;
for (size_type iv = 0, nv = _band.n_vertex(); iv < nv; iv++) {
size_type idof = iv; // assume phi_h is P1
if (band_is_zero (phi_h_band.dof(idof))) zero_iv_list.push_back (iv);
}
domain_indirect_basic<M> zero_dom (_band, "zero", 0, comm, zero_iv_list);
_band.insert_domain_indirect (zero_dom);
//
// 4) create the "isolated" domain containing vertices xi such that:
// (i) phi(xi) != 0
// (ii) for all element K that contains xi,
// we have phi(xj) = 0 for all vertex xj of K and xj != xi
//
// 4.1) mark isolated vertices
phi_h_band.dis_dof_update();
distributor element_ownership = _band.sizes().ownership_by_dimension[map_dim];
size_type not_marked = std::numeric_limits<size_type>::max();
disarray<size_type,M> is_isolated (element_ownership, 1); // 1=true, for bool
std::vector<size_type> isolated_ie_list;
size_type ie = 0;
for (typename geo_basic<T,M>::const_iterator
iter = _band.begin(map_dim),
last = _band.end(map_dim); iter != last; ++iter, ++ie) {
const geo_element& K = *iter;
size_type count_non_zero = 0;
// TODO Pk: K is isolated when all non-zero are located inside one face exactly
// but on this face some can be non-zero (at least 2 ?)
// here: only for P1: only one dof is non-zero
size_type loc_inod_isolated = std::numeric_limits<size_type>::max();
for (size_type loc_inod = 0, loc_nnod = K.size(); loc_inod < loc_nnod; loc_inod++) {
size_type dis_inod = K [loc_inod];
if (! band_is_zero (phi_h_band.dis_dof (dis_inod))) {
count_non_zero++;
loc_inod_isolated = loc_inod;
}
}
if (count_non_zero == 1) {
is_isolated[ie] = 1;
isolated_ie_list.push_back (ie);
}
}
domain_indirect_basic<M> isolated_dom (_band, "isolated", map_dim, comm, isolated_ie_list);
_band.insert_domain_indirect (isolated_dom);
//
// 5) build cc
//
_ncc = build_vertex_connex_component (_band, zero_iv_list, isolated_ie_list);
}
// ----------------------------------------------------------------------------
// instanciation in library
// ----------------------------------------------------------------------------
#define _RHEOLEF_instanciation(T,M) \
template class band_basic<T,M>;
_RHEOLEF_instanciation(Float,sequential)
#ifdef _RHEOLEF_HAVE_MPI
_RHEOLEF_instanciation(Float,distributed)
#endif // _RHEOLEF_HAVE_MPI
} // namespace
rheolef::geo_basic< T, M >
rheolef::field_basic::get_approx
std::string get_approx() const
Definition: field.h:303
check_macro
check_macro(expr1.have_homogeneous_space(Xh1), "dual(expr1,expr2); expr1 should have homogeneous space. HINT: use dual(interpolate(Xh, expr1),expr2)")
rheolef::band_basic::band_basic
band_basic()
Definition: band.h:133
rheolef::distributor
see the distributor page for the full documentation
Definition: distributor.h:62
band
see the band page for the full documentation
rheolef::geo_element::size
size_type size() const
Definition: geo_element.h:168
rheolef::geo_element
see the geo_element page for the full documentation
Definition: geo_element.h:102
mkgeo_ball.c
c
Definition: mkgeo_ball.sh:153
rheolef::distributor::decide
static const size_type decide
Definition: distributor.h:76
rheolef::size_type
size_t size_type
Definition: basis_get.cc:76
rheolef::field_basic::dis_dof
const T & dis_dof(size_type dis_idof) const
Definition: field.cc:376
rheolef::level_set_option::epsilon
Float epsilon
Definition: level_set.h:68
rheolef::level_set_option
Definition: level_set.h:66
p
Definition: sphere.icc:25
rheolef::field_basic::get_geo
const geo_type & get_geo() const
Definition: field.h:301
rheolef::odiststream::io_proc
static size_type io_proc()
Definition: diststream.cc:78
rheolef::field_basic::dis_dof_update
void dis_dof_update() const
Definition: field.cc:410
rheolef::geo_element::size_type
reference_element::size_type size_type
Definition: geo_element.h:125
a
Definition: diffusion_isotropic.h:25
rheolef::band_basic< float_type, M >::size_type
geo_basic< float_type, M >::size_type size_type
Definition: band.h:98
_RHEOLEF_instanciation
#define _RHEOLEF_instanciation(T, M)
Definition: band.cc:386
rheolef::field_basic
Definition: field_expr_utilities.h:38
rheolef::field_basic::dof
T & dof(size_type idof)
Definition: field.h:658
rheolef
This file is part of Rheolef.
Definition: compiler_eigen.h:37
rheolef::space_numbering::dis_inod
void dis_inod(const basis_basic< T > &b, const geo_size &gs, const geo_element &K, typename std::vector< size_type >::iterator dis_inod_tab)
Definition: space_numbering.cc:177
Float
see the Float page for the full documentation
rheolef::disarray< size_type, M >
mkgeo_ball.a
a
Definition: mkgeo_ball.sh:151
rheolef::band_epsilon
static Float band_epsilon
Definition: band.cc:43
mkgeo_ball.n
n
Definition: mkgeo_ball.sh:150
size_type
field::size_type size_type
Definition: branch.cc:425
mkgeo_ball.map_dim
map_dim
Definition: mkgeo_ball.sh:337
epsilon
Float epsilon
Definition: transmission_error.cc:25
rheolef::itos
std::string itos(std::string::size_type i)
itos: see the rheostream page for the full documentation
rheolef::domain_indirect_basic
the finite element boundary domain
Definition: domain_indirect.h:335
rheolef::is_distributed::value
static const bool value
Definition: distributed.h:37
T
Expr1::float_type T
Definition: field_expr.h:218
lambda
Definition: yield_slip_circle.h:34
rheolef::level_set_internal
geo_basic< T, M > level_set_internal(const field_basic< T, M > &, const level_set_option &, std::vector< size_t > &, disarray< size_t, M > &)
Definition: level_set.cc:415