PRAgMaTIc  master
Coarsen.h
Go to the documentation of this file.
1 /* Copyright (C) 2010 Imperial College London and others.
2  *
3  * Please see the AUTHORS file in the main source directory for a
4  * full list of copyright holders.
5  *
6  * Gerard Gorman
7  * Applied Modelling and Computation Group
8  * Department of Earth Science and Engineering
9  * Imperial College London
10  *
11  * g.gorman@imperial.ac.uk
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  * notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above
19  * copyright notice, this list of conditions and the following
20  * disclaimer in the documentation and/or other materials provided
21  * with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
28  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
30  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
32  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
34  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #ifndef COARSEN_H
39 #define COARSEN_H
40 
41 #include <algorithm>
42 #include <cstring>
43 #include <limits>
44 #include <set>
45 #include <vector>
46 
47 #ifdef HAVE_BOOST_UNORDERED_MAP_HPP
48 #include <boost/unordered_map.hpp>
49 #endif
50 
51 #include "DeferredOperations.h"
52 #include "ElementProperty.h"
53 #include "Mesh.h"
54 
59 template<typename real_t, int dim> class Coarsen{
60  public:
63  _mesh = &mesh;
64 
65  property = NULL;
66  size_t NElements = _mesh->get_number_elements();
67  for(size_t i=0;i<NElements;i++){
68  const int *n=_mesh->get_element(i);
69  if(n[0]<0)
70  continue;
71 
72  if(dim==2)
73  property = new ElementProperty<real_t>(_mesh->get_coords(n[0]),
74  _mesh->get_coords(n[1]),
75  _mesh->get_coords(n[2]));
76  else
77  property = new ElementProperty<real_t>(_mesh->get_coords(n[0]),
78  _mesh->get_coords(n[1]),
79  _mesh->get_coords(n[2]),
80  _mesh->get_coords(n[3]));
81 
82  break;
83  }
84 
85  nnodes_reserve = 0;
86  nthreads = pragmatic_nthreads();
87 
88  dynamic_vertex = NULL;
89 
90  for(int i=0; i<3; ++i){
91  worklist[i] = NULL;
92  }
93 
94  node_colour = NULL;
95 
96  for(int i=0; i<3; ++i)
97  ind_set_size[i].resize(max_colour, 0);
98  ind_sets.resize(nthreads, std::vector< std::vector<index_t> >(max_colour));
99  range_indexer.resize(nthreads, std::vector< std::pair<size_t,size_t> >(max_colour, std::pair<size_t,size_t>(0,0)));
100 
101  def_ops = new DeferredOperations<real_t>(_mesh, nthreads, defOp_scaling_factor);
102  }
103 
106  if(property!=NULL)
107  delete property;
108 
109  if(dynamic_vertex!=NULL)
110  delete[] dynamic_vertex;
111 
112  if(node_colour!=NULL)
113  delete[] node_colour;
114 
115  for(int i=0; i<3; ++i){
116  if(worklist[i] != NULL)
117  delete[] worklist[i];
118  }
119 
120  delete def_ops;
121  }
122 
126  void coarsen(real_t L_low, real_t L_max, bool enable_sliver_deletion=false){
127  size_t NNodes = _mesh->get_number_nodes();
128 
129  _L_low = L_low;
130  _L_max = L_max;
131  delete_slivers = enable_sliver_deletion;
132 
133  if(nnodes_reserve<NNodes){
134  nnodes_reserve = NNodes;
135 
136  if(dynamic_vertex!=NULL)
137  delete [] dynamic_vertex;
138 
139  dynamic_vertex = new index_t[NNodes];
140 
141  GlobalActiveSet.resize(NNodes);
142 
143  for(int i=0; i<3; ++i){
144  if(worklist[i] != NULL)
145  delete[] worklist[i];
146  worklist[i] = new index_t[NNodes];
147  }
148 
149  if(node_colour!=NULL)
150  delete[] node_colour;
151 
152  node_colour = new int[NNodes];
153  }
154 
155 #pragma omp parallel
156  {
157  const int tid = pragmatic_thread_id();
158 
159  // Thread-private array of forbidden colours
160  std::vector<index_t> forbiddenColours(max_colour, std::numeric_limits<index_t>::max());
161 
162  /* dynamic_vertex[i] >= 0 :: target to collapse node i
163  * dynamic_vertex[i] = -1 :: node inactive (deleted/locked)
164  * dynamic_vertex[i] = -2 :: recalculate collapse - this is how propagation is implemented
165  */
166 
167 #pragma omp single nowait
168  memset(node_colour, 0, NNodes * sizeof(int));
169 
170 #pragma omp single nowait
171  {
172  for(int i=0; i<max_colour; ++i)
173  ind_set_size[0][i] = 0;
174  GlobalActiveSet_size[0] = 0;
175  }
176 
177  // Mark all vertices for evaluation.
178 #pragma omp for schedule(guided)
179  for(size_t i=0; i<NNodes; ++i){
180  dynamic_vertex[i] = coarsen_identify_kernel(i, L_low, L_max);
181  }
182 
183  // Variable for accessing GlobalActiveSet_size[rnd] and ind_set_size[rnd]
184  int rnd = 2;
185 
186  bool first_time = true;
187  do{
188  // Switch to the next round
189  rnd = (rnd+1)%3;
190 
191  // Prepare worklists for conflict resolution.
192  // Reset GlobalActiveSet_size and ind_set_size for next (not this!) round.
193 #pragma omp single nowait
194  {
195  for(int i=0; i<3; ++i)
196  worklist_size[i] = 0;
197 
198  int next_rnd = (rnd+1)%3;
199  for(int i=0; i<max_colour; ++i)
200  ind_set_size[next_rnd][i] = 0;
201  GlobalActiveSet_size[next_rnd] = 0;
202  }
203 
204  if(!first_time){
205 #pragma omp for schedule(guided)
206  for(size_t i=0; i<NNodes; ++i){
207  if(dynamic_vertex[i] == -2){
208  dynamic_vertex[i] = coarsen_identify_kernel(i, L_low, L_max);
209  }
210  node_colour[i] = 0;
211  }
212  }else
213  first_time = false;
214 
215 #pragma omp barrier
216  // Colour the active sub-mesh
217  std::vector<index_t> local_coloured;
218 #pragma omp for schedule(guided)
219  for(size_t i=0; i<NNodes; ++i){
220  if(dynamic_vertex[i]>=0){
221  /*
222  * Create subNNList for vertex i and also execute the first parallel
223  * loop of RokosGorman colouring. This way, two time-consuming barriers,
224  * the one at the end of the aforementioned loop and the one a few lines
225  * below this comment, are merged into one.
226  */
227  for(typename std::vector<index_t>::const_iterator jt=_mesh->NNList[i].begin(); jt!=_mesh->NNList[i].end(); ++jt){
228  if(dynamic_vertex[*jt]>=0){
229  forbiddenColours[node_colour[*jt]] = (index_t) i;
230  }
231 
232  for(size_t j=0; j<forbiddenColours.size(); ++j){
233  if(forbiddenColours[j] != (index_t) i){
234  node_colour[i] = (int) j;
235  break;
236  }
237  }
238  }
239 
240  local_coloured.push_back(i);
241  }
242  }
243 
244  if(local_coloured.size()>0){
245  size_t pos;
246  pos = pragmatic_omp_atomic_capture(&GlobalActiveSet_size[rnd], local_coloured.size());
247  memcpy(&GlobalActiveSet[pos], &local_coloured[0], local_coloured.size() * sizeof(index_t));
248  }
249 
250 #pragma omp barrier
251  if(GlobalActiveSet_size[rnd]>0){
252  for(int set_no=0; set_no<max_colour; ++set_no){
253  ind_sets[tid][set_no].clear();
254  range_indexer[tid][set_no].first = std::numeric_limits<size_t>::infinity();
255  range_indexer[tid][set_no].second = std::numeric_limits<size_t>::infinity();
256  }
257 
258  // Continue colouring and coarsening
259  std::vector<index_t> conflicts;
260 
261 #pragma omp for schedule(guided)
262  for(size_t i=0; i<GlobalActiveSet_size[rnd]; ++i){
263  bool defective = false;
264  index_t n = GlobalActiveSet[i];
265  for(typename std::vector<index_t>::const_iterator jt=_mesh->NNList[n].begin(); jt!=_mesh->NNList[n].end(); ++jt){
266  if(dynamic_vertex[*jt]>=0){
267  if(node_colour[n] == node_colour[*jt]){
268  // No need to mark both vertices as defectively coloured.
269  // Just mark the one with the lesser ID.
270  if(n < *jt){
271  defective = true;
272  break;
273  }
274  }
275  }
276  }
277 
278  if(defective){
279  conflicts.push_back(n);
280 
281  for(typename std::vector<index_t>::const_iterator jt=_mesh->NNList[n].begin(); jt!=_mesh->NNList[n].end(); ++jt){
282  if(dynamic_vertex[*jt]>=0){
283  int c = node_colour[*jt];
284  forbiddenColours[c] = n;
285  }
286  }
287 
288  for(size_t j=0; j<forbiddenColours.size(); j++){
289  if(forbiddenColours[j] != n){
290  node_colour[n] = (int) j;
291  break;
292  }
293  }
294  }else{
295  ind_sets[tid][node_colour[n]].push_back(n);
296  }
297  }
298 
299  size_t pos;
300  pos = pragmatic_omp_atomic_capture(&worklist_size[0], conflicts.size());
301 
302  memcpy(&worklist[0][pos], &conflicts[0], conflicts.size() * sizeof(index_t));
303 
304  conflicts.clear();
305 #pragma omp barrier
306 
307  int wl = 0;
308 
309  while(worklist_size[wl]){
310 #pragma omp for schedule(guided)
311  for(size_t item=0; item<worklist_size[wl]; ++item){
312  index_t n = worklist[wl][item];
313  bool defective = false;
314  for(typename std::vector<index_t>::const_iterator jt=_mesh->NNList[n].begin(); jt!=_mesh->NNList[n].end(); ++jt){
315  if(dynamic_vertex[*jt]>=0){
316  if(node_colour[n] == node_colour[*jt]){
317  // No need to mark both vertices as defectively coloured.
318  // Just mark the one with the lesser ID.
319  if(n < *jt){
320  defective = true;
321  break;
322  }
323  }
324  }
325  }
326 
327  if(defective){
328  conflicts.push_back(n);
329 
330  for(typename std::vector<index_t>::const_iterator jt=_mesh->NNList[n].begin(); jt!=_mesh->NNList[n].end(); ++jt){
331  if(dynamic_vertex[*jt]>=0){
332  int c = node_colour[*jt];
333  forbiddenColours[c] = n;
334  }
335  }
336 
337  for(size_t j=0; j<forbiddenColours.size(); j++){
338  if(forbiddenColours[j] != n){
339  node_colour[n] = j;
340  break;
341  }
342  }
343  }else{
344  ind_sets[tid][node_colour[n]].push_back(n);
345  }
346  }
347 
348  // Switch worklist
349  wl = (wl+1)%3;
350 
351  size_t pos = pragmatic_omp_atomic_capture(&worklist_size[wl], conflicts.size());
352 
353  memcpy(&worklist[wl][pos], &conflicts[0], conflicts.size() * sizeof(index_t));
354 
355  conflicts.clear();
356 
357  // Clear the next worklist
358 #pragma omp single
359  {
360  worklist_size[(wl+1)%3] = 0;
361  }
362  }
363 
364  for(int set_no=0; set_no<max_colour; ++set_no){
365  if(ind_sets[tid][set_no].size()>0){
366  range_indexer[tid][set_no].first = pragmatic_omp_atomic_capture(&ind_set_size[rnd][set_no], ind_sets[tid][set_no].size());
367  range_indexer[tid][set_no].second = range_indexer[tid][set_no].first + ind_sets[tid][set_no].size();
368  }
369  }
370 
371 #pragma omp barrier
372  /* Start processing independent sets. After processing each set, colouring
373  * might be invalid. More precisely, it's the target vertices whose colours
374  * might clash with their neighbours' colours. To avoid hazards, we just
375  * un-colour these vertices if their colour clashes with the colours of
376  * their new neighbours. Being a neighbour of a removed vertex, the target
377  * vertex will be marked for re-evaluation during coarsening of rm_vertex,
378  * i.e. dynamic_vertex[target_target] == -2, so it will get a chance to be
379  * processed at the next iteration of the do...while loop, when a new
380  * colouring of the active sub-mesh will have been established. This is an
381  * optimised approach compared to only processing the maximal independent
382  * set and then discarding the other colours and looping all over again -
383  * at least we make use of the existing colouring as much as possible.
384  */
385 
386  for(int set_no=0; set_no<max_colour; ++set_no){
387  if(ind_set_size[rnd][set_no] == 0)
388  continue;
389 
390  // Sort range indexer
391  std::vector<range_element> range;
392  for(int t=0; t<nthreads; ++t){
393  if(range_indexer[t][set_no].first != range_indexer[t][set_no].second)
394  range.push_back(range_element(range_indexer[t][set_no], t));
395  }
396  std::sort(range.begin(), range.end(), pragmatic_range_element_comparator);
397 
398 #pragma omp for schedule(guided)
399  for(size_t idx=0; idx<ind_set_size[rnd][set_no]; ++idx){
400  // Find which vertex corresponds to idx.
401  index_t rm_vertex = -1;
402  std::vector<range_element>::iterator ele = std::lower_bound(range.begin(), range.end(),
403  range_element(std::pair<size_t,size_t> (idx,idx), 0), pragmatic_range_element_finder);
404  assert(ele != range.end());
405  assert(idx >= range_indexer[ele->second][set_no].first && idx < range_indexer[ele->second][set_no].second);
406  rm_vertex = ind_sets[ele->second][set_no][idx - range_indexer[ele->second][set_no].first];
407  assert(rm_vertex>=0);
408 
409  // If the node has been un-coloured, skip it.
410  if(node_colour[rm_vertex] != set_no)
411  continue;
412 
413  /* If this rm_vertex is marked for re-evaluation, it means that the
414  * local neighbourhood has changed since coarsen_identify_kernel was
415  * called for this vertex. Call coarsen_identify_kernel again.
416  * Obviously, this check is redundant for set_no=0.
417  */
418 
419  if(dynamic_vertex[rm_vertex] == -2)
420  dynamic_vertex[rm_vertex] = coarsen_identify_kernel(rm_vertex, L_low, L_max);
421 
422  if(dynamic_vertex[rm_vertex] < 0)
423  continue;
424 
425  index_t target_vertex = dynamic_vertex[rm_vertex];
426 
427  // Mark neighbours for re-evaluation.
428  for(typename std::vector<index_t>::const_iterator jt=_mesh->NNList[rm_vertex].begin();jt!=_mesh->NNList[rm_vertex].end();++jt)
429  def_ops->propagate_coarsening(*jt, tid);
430 
431  // Un-colour target_vertex if its colour clashes with any of its new neighbours.
432  if(node_colour[target_vertex] >= 0){
433  for(typename std::vector<index_t>::const_iterator jt=_mesh->NNList[rm_vertex].begin();jt!=_mesh->NNList[rm_vertex].end();++jt){
434  if(*jt != target_vertex){
435  if(node_colour[*jt] == node_colour[target_vertex]){
436  def_ops->reset_colour(target_vertex, tid);
437  break;
438  }
439  }
440  }
441  }
442 
443  // Mark rm_vertex as inactive.
444  dynamic_vertex[rm_vertex] = -1;
445 
446  // Coarsen the edge.
447  coarsen_kernel(rm_vertex, target_vertex, tid);
448  }
449 
450 #pragma omp for schedule(guided)
451  for(size_t vtid=0; vtid<defOp_scaling_factor*nthreads; ++vtid){
452  for(int i=0; i<nthreads; ++i){
453  def_ops->commit_remNN(i, vtid);
454  def_ops->commit_addNN(i, vtid);
455  def_ops->commit_remNE(i, vtid);
456  def_ops->commit_addNE(i, vtid);
457  def_ops->commit_repEN(i, vtid);
458  def_ops->commit_coarsening_propagation(dynamic_vertex, i, vtid);
459  def_ops->commit_colour_reset(node_colour, i, vtid);
460  }
461  }
462  }
463  }
464  }while(GlobalActiveSet_size[rnd]>0);
465  }
466  }
467 
468  private:
469 
474  int coarsen_identify_kernel(index_t rm_vertex, real_t L_low, real_t L_max) const{
475  // Cannot delete if already deleted.
476  if(_mesh->NNList[rm_vertex].empty())
477  return -1;
478 
479  // If this is not owned then return -1.
480  if(!_mesh->is_owned_node(rm_vertex))
481  return -1;
482 
483  // For now, lock the halo
484  if(_mesh->is_halo_node(rm_vertex))
485  return -1;
486 
487  //
488  bool delete_with_extreme_prejudice = false;
489  double q_linf;
490  if(delete_slivers && dim==3){
491  std::set<index_t>::iterator ee=_mesh->NEList[rm_vertex].begin();
492  const int *n=_mesh->get_element(*ee);
493 
494  q_linf = property->lipnikov(_mesh->get_coords(n[0]),
495  _mesh->get_coords(n[1]),
496  _mesh->get_coords(n[2]),
497  _mesh->get_coords(n[3]),
498  _mesh->get_metric(n[0]),
499  _mesh->get_metric(n[1]),
500  _mesh->get_metric(n[2]),
501  _mesh->get_metric(n[3]));
502  ++ee;
503  for(;ee!=_mesh->NEList[rm_vertex].end();++ee){
504  n=_mesh->get_element(*ee);
505 
506  q_linf = std::min(q_linf, property->lipnikov(_mesh->get_coords(n[0]),
507  _mesh->get_coords(n[1]),
508  _mesh->get_coords(n[2]),
509  _mesh->get_coords(n[3]),
510  _mesh->get_metric(n[0]),
511  _mesh->get_metric(n[1]),
512  _mesh->get_metric(n[2]),
513  _mesh->get_metric(n[3])));
514 
515  }
516  if(q_linf<1.0e-6){
517  delete_with_extreme_prejudice = true;
518  }
519  }
520 
521  /* Sort the edges according to length. We want to collapse the
522  shortest. If it is not possible to collapse the edge then move
523  onto the next shortest.*/
524  std::multimap<real_t, index_t> short_edges;
525  for(typename std::vector<index_t>::const_iterator nn=_mesh->NNList[rm_vertex].begin();nn!=_mesh->NNList[rm_vertex].end();++nn){
526  double length = _mesh->calc_edge_length(rm_vertex, *nn);
527  if(length<L_low || delete_with_extreme_prejudice)
528  short_edges.insert(std::pair<real_t, index_t>(length, *nn));
529  }
530 
531  bool reject_collapse = false;
532  index_t target_vertex=-1;
533  while(short_edges.size()){
534  // Get the next shortest edge.
535  target_vertex = short_edges.begin()->second;
536  short_edges.erase(short_edges.begin());
537 
538  // Assume the best.
539  reject_collapse=false;
540 
541  /* Check the properties of new elements. If the
542  new properties are not acceptable then continue. */
543 
544  // Check area/volume of new elements.
545  long double total_old_av=0;
546  long double total_new_av=0;
547  bool better=true;
548  for(typename std::set<index_t>::iterator ee=_mesh->NEList[rm_vertex].begin();ee!=_mesh->NEList[rm_vertex].end();++ee){
549  const int *old_n=_mesh->get_element(*ee);
550 
551  double old_av;
552  if(dim==2)
553  old_av = property->area(_mesh->get_coords(old_n[0]),
554  _mesh->get_coords(old_n[1]),
555  _mesh->get_coords(old_n[2]));
556  else
557  old_av = property->volume(_mesh->get_coords(old_n[0]),
558  _mesh->get_coords(old_n[1]),
559  _mesh->get_coords(old_n[2]),
560  _mesh->get_coords(old_n[3]));
561 
562  total_old_av+=old_av;
563 
564  // Skip if this element would be deleted under the operation.
565  if(_mesh->NEList[target_vertex].find(*ee)!=_mesh->NEList[target_vertex].end())
566  continue;
567 
568  // Create a copy of the proposed element
569  std::vector<int> n(nloc);
570  for(size_t i=0;i<nloc;i++){
571  int nid = old_n[i];
572  if(nid==rm_vertex)
573  n[i] = target_vertex;
574  else
575  n[i] = nid;
576  }
577 
578  // Check the area/volume of this new element.
579  double new_av;
580  if(dim==2)
581  new_av = property->area(_mesh->get_coords(n[0]),
582  _mesh->get_coords(n[1]),
583  _mesh->get_coords(n[2]));
584  else{
585  new_av = property->volume(_mesh->get_coords(n[0]),
586  _mesh->get_coords(n[1]),
587  _mesh->get_coords(n[2]),
588  _mesh->get_coords(n[3]));
589  double new_q = property->lipnikov(_mesh->get_coords(n[0]),
590  _mesh->get_coords(n[1]),
591  _mesh->get_coords(n[2]),
592  _mesh->get_coords(n[3]),
593  _mesh->get_metric(n[0]),
594  _mesh->get_metric(n[1]),
595  _mesh->get_metric(n[2]),
596  _mesh->get_metric(n[3]));
597  if(new_q<q_linf)
598  better=false;
599  }
600  total_new_av+=new_av;
601 
602  // Reject inverted elements.
603  if(new_av<DBL_EPSILON){
604  reject_collapse=true;
605  break;
606  }
607  }
608 
609  // Check we are not removing surface features.
610  if(!reject_collapse && fabs(total_new_av-total_old_av)>DBL_EPSILON){
611  reject_collapse=true;
612  }
613 
614  /*
615  // Check if any of the new edges are longer than L_max.
616  if(!reject_collapse && !delete_with_extreme_prejudice){
617  for(typename std::vector<index_t>::const_iterator nn=_mesh->NNList[rm_vertex].begin();nn!=_mesh->NNList[rm_vertex].end();++nn){
618  if(target_vertex==*nn)
619  continue;
620 
621  if(_mesh->calc_edge_length(target_vertex, *nn)>L_max){
622  reject_collapse=true;
623  break;
624  }
625  }
626  }
627  */
628  if(!better)
629  reject_collapse=true;
630 
631  // If this edge is ok to collapse then jump out.
632  if(!reject_collapse)
633  break;
634  }
635 
636  // If we've checked all edges and none are collapsible then return.
637  if(reject_collapse)
638  return -2;
639 
640  if(delete_with_extreme_prejudice)
641  std::cerr<<"-";
642 
643  return target_vertex;
644  }
645 
649  void coarsen_kernel(index_t rm_vertex, index_t target_vertex, int tid){
650  std::set<index_t> deleted_elements;
651  std::set_intersection(_mesh->NEList[rm_vertex].begin(), _mesh->NEList[rm_vertex].end(),
652  _mesh->NEList[target_vertex].begin(), _mesh->NEList[target_vertex].end(),
653  std::inserter(deleted_elements, deleted_elements.begin()));
654 
655  // This is the set of vertices which are common neighbours between rm_vertex and target_vertex.
656  std::set<index_t> common_patch;
657 
658  // Remove deleted elements from node-element adjacency list and from element-node list.
659  for(typename std::set<index_t>::const_iterator de=deleted_elements.begin(); de!=deleted_elements.end();++de){
660  index_t eid = *de;
661 
662  // Remove element from NEList[rm_vertex].
663  _mesh->NEList[rm_vertex].erase(eid);
664 
665  // Remove element from NEList of the other two vertices.
666  size_t lrm_vertex;
667  std::vector<index_t> other_vertex;
668  for(size_t i=0; i<nloc; ++i){
669  index_t vid = _mesh->_ENList[eid*nloc+i];
670  if(vid==rm_vertex){
671  lrm_vertex = i;
672  }else{
673  def_ops->remNE(vid, eid, tid);
674 
675  // If this vertex is neither rm_vertex nor target_vertex, it is one of the common neighbours.
676  if(vid != target_vertex){
677  other_vertex.push_back(vid);
678  common_patch.insert(vid);
679  }
680  }
681  }
682 
683  // Handle vertex collapsing onto boundary.
684  if(_mesh->boundary[eid*nloc+lrm_vertex]>0){
685  // Find element whose internal edge will be pulled into an external edge.
686  std::set<index_t> otherNE;
687  if(dim==2){
688  assert(other_vertex.size()==1);
689  otherNE = _mesh->NEList[other_vertex[0]];
690  }else{
691  assert(other_vertex.size()==2);
692  std::set_intersection(_mesh->NEList[other_vertex[0]].begin(), _mesh->NEList[other_vertex[0]].end(),
693  _mesh->NEList[other_vertex[1]].begin(), _mesh->NEList[other_vertex[1]].end(),
694  std::inserter(otherNE, otherNE.begin()));
695  }
696  std::set<index_t> new_boundary_eid;
697  std::set_intersection(_mesh->NEList[rm_vertex].begin(), _mesh->NEList[rm_vertex].end(),
698  otherNE.begin(), otherNE.end(), std::inserter(new_boundary_eid, new_boundary_eid.begin()));
699 
700  if(!new_boundary_eid.empty()){
701  // eid has been removed from NEList[rm_vertex],
702  // so new_boundary_eid contains only the other element.
703  assert(new_boundary_eid.size()==1);
704  index_t target_eid = *new_boundary_eid.begin();
705  for(int i=0;i<nloc;i++){
706  int nid=_mesh->_ENList[target_eid*nloc+i];
707  if(dim==2){
708  if(nid!=rm_vertex && nid!=other_vertex[0]){
709  _mesh->boundary[target_eid*nloc+i] = _mesh->boundary[eid*nloc+lrm_vertex];
710  break;
711  }
712  }else{
713  if(nid!=rm_vertex && nid!=other_vertex[0] && nid!=other_vertex[1]){
714  _mesh->boundary[target_eid*nloc+i] = _mesh->boundary[eid*nloc+lrm_vertex];
715  break;
716  }
717  }
718  }
719  }
720  }
721 
722  // Remove element from mesh.
723  _mesh->_ENList[eid*nloc] = -1;
724  }
725 
726  assert((dim==2 && common_patch.size() == deleted_elements.size()) || (dim==3));
727 
728  // For all adjacent elements, replace rm_vertex with target_vertex in ENList.
729  for(typename std::set<index_t>::iterator ee=_mesh->NEList[rm_vertex].begin();ee!=_mesh->NEList[rm_vertex].end();++ee){
730  for(size_t i=0;i<nloc;i++){
731  if(_mesh->_ENList[nloc*(*ee)+i]==rm_vertex){
732  def_ops->repEN(nloc*(*ee)+i, target_vertex, tid);
733  break;
734  }
735  }
736 
737  // Add element to target_vertex's NEList.
738  def_ops->addNE(target_vertex, *ee, tid);
739  }
740 
741  // Update surrounding NNList.
742  common_patch.insert(target_vertex);
743  for(typename std::vector<index_t>::const_iterator nn=_mesh->NNList[rm_vertex].begin();nn!=_mesh->NNList[rm_vertex].end();++nn){
744  def_ops->remNN(*nn, rm_vertex, tid);
745 
746  // Find all entries pointing back to rm_vertex and update them to target_vertex.
747  if(common_patch.count(*nn)==0){
748  def_ops->addNN(*nn, target_vertex, tid);
749  def_ops->addNN(target_vertex, *nn, tid);
750  }
751  }
752 
753  _mesh->erase_vertex(rm_vertex);
754  }
755 
756  Mesh<real_t> *_mesh;
757  ElementProperty<real_t> *property;
758 
759  size_t nnodes_reserve;
760  index_t *dynamic_vertex;
761 
762  // Colouring
763  int *node_colour;
764  static const int max_colour = (dim==2?16:64);
765  std::vector<index_t> GlobalActiveSet;
766  size_t GlobalActiveSet_size[3];
767  std::vector<size_t> ind_set_size[3];
768  std::vector< std::vector< std::vector<index_t> > > ind_sets;
769  std::vector< std::vector< std::pair<size_t,size_t> > > range_indexer;
770 
771  // Used for iterative colouring
772  index_t* worklist[3];
773  size_t worklist_size[3];
774 
776  static const int defOp_scaling_factor = 4;
777 
778  real_t _L_low, _L_max;
779  bool delete_slivers;
780 
781  const static size_t ndims=dim;
782  const static size_t nloc=dim+1;
783  const static size_t msize=(dim==2?3:6);
784 
785  int nthreads;
786 };
787 
788 #endif
Performs 2D mesh coarsening.
Definition: Coarsen.h:59
int index_t
Calculates a number of element properties.
Manages mesh data.
Definition: Mesh.h:70
bool pragmatic_range_element_comparator(range_element p1, range_element p2)
void coarsen(real_t L_low, real_t L_max, bool enable_sliver_deletion=false)
Definition: Coarsen.h:126
int pragmatic_nthreads()
~Coarsen()
Default destructor.
Definition: Coarsen.h:105
bool pragmatic_range_element_finder(range_element p1, range_element p2)
Coarsen(Mesh< real_t > &mesh)
Default constructor.
Definition: Coarsen.h:62
int pragmatic_thread_id()
size_t pragmatic_omp_atomic_capture(size_t *shared, size_t inc)
std::pair< std::pair< size_t, size_t >, int > range_element