PRAgMaTIc  master
Refine.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 REFINE_H
39 #define REFINE_H
40 
41 #include <algorithm>
42 #include <set>
43 #include <vector>
44 
45 #include <string.h>
46 #include <inttypes.h>
47 
48 #include "DeferredOperations.h"
49 #include "Edge.h"
50 #include "ElementProperty.h"
51 #include "Mesh.h"
52 
56 template<typename real_t, int dim> class Refine{
57  public:
60  _mesh = &mesh;
61 
62  size_t NElements = _mesh->get_number_elements();
63 
64  // Set the orientation of elements.
65  property = NULL;
66  for(size_t i=0;i<NElements;i++){
67  const int *n=_mesh->get_element(i);
68  if(n[0]<0)
69  continue;
70 
71  if(dim==2)
72  property = new ElementProperty<real_t>(_mesh->get_coords(n[0]),
73  _mesh->get_coords(n[1]), _mesh->get_coords(n[2]));
74  else if(dim==3)
75  property = new ElementProperty<real_t>(_mesh->get_coords(n[0]),
76  _mesh->get_coords(n[1]), _mesh->get_coords(n[2]), _mesh->get_coords(n[3]));
77 
78  break;
79  }
80 
81  MPI_Comm comm = _mesh->get_mpi_comm();
82 
83  nprocs = pragmatic_nprocesses(comm);
84  rank = pragmatic_process_id(comm);
85 
86  nthreads = pragmatic_nthreads();
87 
88  newVertices.resize(nthreads);
89  newElements.resize(nthreads);
90  newBoundaries.resize(nthreads);
91  newCoords.resize(nthreads);
92  newMetric.resize(nthreads);
93 
94  // Pre-allocate the maximum size that might be required
95  allNewVertices.resize(_mesh->_ENList.size());
96 
97  threadIdx.resize(nthreads);
98  splitCnt.resize(nthreads);
99 
100  def_ops = new DeferredOperations<real_t>(_mesh, nthreads, defOp_scaling_factor);
101 
102  cidRecv_additional.resize(nprocs);
103  cidSend_additional.resize(nprocs);
104 
105  if(dim==2){
106  refineMode2D[0] = &Refine<real_t,dim>::refine2D_1;
107  refineMode2D[1] = &Refine<real_t,dim>::refine2D_2;
108  refineMode2D[2] = &Refine<real_t,dim>::refine2D_3;
109  }else{
110  refineMode3D[0] = &Refine<real_t,dim>::refine3D_1;
111  refineMode3D[1] = &Refine<real_t,dim>::refine3D_2;
112  refineMode3D[2] = &Refine<real_t,dim>::refine3D_3;
113  refineMode3D[3] = &Refine<real_t,dim>::refine3D_4;
114  refineMode3D[4] = &Refine<real_t,dim>::refine3D_5;
115  refineMode3D[5] = &Refine<real_t,dim>::refine3D_6;
116  }
117  }
118 
121  delete property;
122  delete def_ops;
123  }
124 
132  void refine(real_t L_max){
133  size_t origNElements = _mesh->get_number_elements();
134  size_t origNNodes = _mesh->get_number_nodes();
135  size_t edgeSplitCnt = 0;
136 
137 #pragma omp parallel
138  {
139 #pragma omp single nowait
140  {
141  new_vertices_per_element.resize(nedge*origNElements);
142  std::fill(new_vertices_per_element.begin(), new_vertices_per_element.end(), -1);
143  }
144 
145  int tid = pragmatic_thread_id();
146  splitCnt[tid] = 0;
147 
148  /*
149  * Average vertex degree in 2D is ~6, so there
150  * are approx. (6/2)*NNodes edges in the mesh.
151  * In 3D, average vertex degree is ~12.
152  */
153  size_t reserve_size = nedge*origNNodes/nthreads;
154  newVertices[tid].clear(); newVertices[tid].reserve(reserve_size);
155  newCoords[tid].clear(); newCoords[tid].reserve(ndims*reserve_size);
156  newMetric[tid].clear(); newMetric[tid].reserve(msize*reserve_size);
157 
158  /* Loop through all edges and select them for refinement if
159  its length is greater than L_max in transformed space. */
160 #pragma omp for schedule(guided) nowait
161  for(size_t i=0;i<origNNodes;++i){
162  for(size_t it=0;it<_mesh->NNList[i].size();++it){
163  index_t otherVertex = _mesh->NNList[i][it];
164  assert(otherVertex>=0);
165 
166  /* Conditional statement ensures that the edge length is only calculated once.
167  * By ordering the vertices according to their gnn, we ensure that all processes
168  * calculate the same edge length when they fall on the halo.
169  */
170  if(_mesh->lnn2gnn[i] < _mesh->lnn2gnn[otherVertex]){
171  double length = _mesh->calc_edge_length(i, otherVertex);
172  if(length>L_max){
173  ++splitCnt[tid];
174  refine_edge(i, otherVertex, tid);
175  }
176  }
177  }
178  }
179 
180  threadIdx[tid] = pragmatic_omp_atomic_capture(&_mesh->NNodes, splitCnt[tid]);
181  assert(newVertices[tid].size()==splitCnt[tid]);
182 
183 #pragma omp barrier
184 
185 #pragma omp single
186  {
187  size_t reserve = 1.1*_mesh->NNodes; // extra space is required for centroidals
188  if(_mesh->_coords.size()<reserve*ndims){
189  _mesh->_coords.resize(reserve*ndims);
190  _mesh->metric.resize(reserve*msize);
191  _mesh->NNList.resize(reserve);
192  _mesh->NEList.resize(reserve);
193  _mesh->node_owner.resize(reserve);
194  _mesh->lnn2gnn.resize(reserve);
195  }
196  edgeSplitCnt = _mesh->NNodes - origNNodes;
197  }
198 
199  // Append new coords and metric to the mesh.
200  memcpy(&_mesh->_coords[ndims*threadIdx[tid]], &newCoords[tid][0], ndims*splitCnt[tid]*sizeof(real_t));
201  memcpy(&_mesh->metric[msize*threadIdx[tid]], &newMetric[tid][0], msize*splitCnt[tid]*sizeof(double));
202 
203  // Fix IDs of new vertices
204  assert(newVertices[tid].size()==splitCnt[tid]);
205  for(size_t i=0;i<splitCnt[tid];i++){
206  newVertices[tid][i].id = threadIdx[tid]+i;
207  }
208 
209  // Accumulate all newVertices in a contiguous array
210  memcpy(&allNewVertices[threadIdx[tid]-origNNodes], &newVertices[tid][0], newVertices[tid].size()*sizeof(DirectedEdge<index_t>));
211 
212  // Mark each element with its new vertices,
213  // update NNList for all split edges.
214 #pragma omp barrier
215 #pragma omp for schedule(guided)
216  for(size_t i=0; i<edgeSplitCnt; ++i){
217  index_t vid = allNewVertices[i].id;
218  index_t firstid = allNewVertices[i].edge.first;
219  index_t secondid = allNewVertices[i].edge.second;
220 
221  // Find which elements share this edge and mark them with their new vertices.
222  std::set<index_t> intersection;
223  std::set_intersection(_mesh->NEList[firstid].begin(), _mesh->NEList[firstid].end(),
224  _mesh->NEList[secondid].begin(), _mesh->NEList[secondid].end(),
225  std::inserter(intersection, intersection.begin()));
226 
227  for(typename std::set<index_t>::const_iterator element=intersection.begin(); element!=intersection.end(); ++element){
228  index_t eid = *element;
229  size_t edgeOffset = edgeNumber(eid, firstid, secondid);
230  new_vertices_per_element[nedge*eid+edgeOffset] = vid;
231  }
232 
233  /*
234  * Update NNList for newly created vertices. This has to be done here, it cannot be
235  * done during element refinement, because a split edge is shared between two elements
236  * and we run the risk that these updates will happen twice, once for each element.
237  */
238  _mesh->NNList[vid].push_back(firstid);
239  _mesh->NNList[vid].push_back(secondid);
240 
241  def_ops->remNN(firstid, secondid, tid);
242  def_ops->addNN(firstid, vid, tid);
243  def_ops->remNN(secondid, firstid, tid);
244  def_ops->addNN(secondid, vid, tid);
245 
246  // This branch is always taken or always not taken for every vertex,
247  // so the branch predictor should have no problem handling it.
248  if(nprocs==1){
249  _mesh->node_owner[vid] = 0;
250  _mesh->lnn2gnn[vid] = vid;
251  }else{
252  /*
253  * Perhaps we should introduce a system of alternating min/max assignments,
254  * i.e. one time the node is assigned to the min rank, one time to the max
255  * rank and so on, so as to avoid having the min rank accumulate the majority
256  * of newly created vertices and disturbing load balance among MPI processes.
257  */
258  int owner0 = _mesh->node_owner[firstid];
259  int owner1 = _mesh->node_owner[secondid];
260  int owner = std::min(owner0, owner1);
261  _mesh->node_owner[vid] = owner;
262 
263  if(_mesh->node_owner[vid] == rank)
264  _mesh->lnn2gnn[vid] = _mesh->gnn_offset+vid;
265  }
266  }
267 
268  if(dim==3){
269  // If in 3D, we need to refine facets first.
270 #pragma omp for schedule(guided)
271  for(index_t eid=0; eid<origNElements; ++eid){
272  // Find the 4 facets comprising the element
273  const index_t *n = _mesh->get_element(eid);
274  if(n[0] < 0)
275  continue;
276 
277  const index_t facets[4][3] = {{n[0], n[1], n[2]},
278  {n[0], n[1], n[3]},
279  {n[0], n[2], n[3]},
280  {n[1], n[2], n[3]}};
281 
282  for(int j=0; j<4; ++j){
283  // Find which elements share this facet j
284  const index_t *facet = facets[j];
285  std::set<index_t> intersection01, EE;
286  std::set_intersection(_mesh->NEList[facet[0]].begin(), _mesh->NEList[facet[0]].end(),
287  _mesh->NEList[facet[1]].begin(), _mesh->NEList[facet[1]].end(),
288  std::inserter(intersection01, intersection01.begin()));
289  std::set_intersection(_mesh->NEList[facet[2]].begin(), _mesh->NEList[facet[2]].end(),
290  intersection01.begin(), intersection01.end(),
291  std::inserter(EE, EE.begin()));
292 
293  assert(EE.size() <= 2 );
294  assert(EE.count(eid) == 1);
295 
296  // Prevent facet from being refined twice:
297  // Only refine it if this is the element with the highest ID.
298  if(eid == *EE.rbegin())
299  for(size_t k=0; k<3; ++k)
300  if(new_vertices_per_element[nedge*eid+edgeNumber(eid, facet[k], facet[(k+1)%3])] != -1){
301  refine_facet(eid, facet, tid);
302  break;
303  }
304  }
305  }
306 
307 #pragma omp for schedule(guided)
308  for(int vtid=0; vtid<defOp_scaling_factor*nthreads; ++vtid){
309  for(int i=0; i<nthreads; ++i){
310  def_ops->commit_remNN(i, vtid);
311  def_ops->commit_addNN(i, vtid);
312  }
313  }
314  }
315 
316  // Start element refinement.
317  splitCnt[tid] = 0;
318  newElements[tid].clear(); newBoundaries[tid].clear();
319  newElements[tid].reserve(dim*dim*origNElements/nthreads);
320  newBoundaries[tid].reserve(dim*dim*origNElements/nthreads);
321 
322 #pragma omp for schedule(guided) nowait
323  for(size_t eid=0; eid<origNElements; ++eid){
324  //If the element has been deleted, continue.
325  const index_t *n = _mesh->get_element(eid);
326  if(n[0] < 0)
327  continue;
328 
329  for(size_t j=0; j<nedge; ++j)
330  if(new_vertices_per_element[nedge*eid+j] != -1){
331  refine_element(eid, tid);
332  break;
333  }
334  }
335 
336  threadIdx[tid] = pragmatic_omp_atomic_capture(&_mesh->NElements, splitCnt[tid]);
337 
338 #pragma omp barrier
339 #pragma omp single
340  {
341  if(_mesh->_ENList.size()<_mesh->NElements*nloc){
342  _mesh->_ENList.resize(_mesh->NElements*nloc);
343  _mesh->boundary.resize(_mesh->NElements*nloc);
344  }
345  }
346 
347  // Append new elements to the mesh and commit deferred operations
348  memcpy(&_mesh->_ENList[nloc*threadIdx[tid]], &newElements[tid][0], nloc*splitCnt[tid]*sizeof(index_t));
349  memcpy(&_mesh->boundary[nloc*threadIdx[tid]], &newBoundaries[tid][0], nloc*splitCnt[tid]*sizeof(int));
350 
351  // Commit deferred operations.
352 #pragma omp for schedule(guided)
353  for(int vtid=0; vtid<defOp_scaling_factor*nthreads; ++vtid){
354  for(int i=0; i<nthreads; ++i){
355  def_ops->commit_remNN(i, vtid);
356  def_ops->commit_addNN(i, vtid);
357  def_ops->commit_remNE(i, vtid);
358  def_ops->commit_addNE(i, vtid);
359  def_ops->commit_addNE_fix(threadIdx, i, vtid);
360  }
361  }
362 
363  // Update halo.
364 #ifdef HAVE_MPI
365  if(nprocs>1){
366 #pragma omp single
367  {
368  std::vector< std::set< DirectedEdge<index_t> > > recv_additional(nprocs), send_additional(nprocs);
369 
370  for(size_t i=0; i<edgeSplitCnt; ++i){
371  DirectedEdge<index_t> *vert = &allNewVertices[i];
372 
373  if(_mesh->node_owner[vert->id] != rank){
374  // Vertex is owned by another MPI process, so prepare to update recv and recv_halo.
375  // Only update them if the vertex is actually visible by *this* MPI process,
376  // i.e. if at least one of its neighbours is owned by *this* process.
377  bool visible = false;
378  for(typename std::vector<index_t>::const_iterator neigh=_mesh->NNList[vert->id].begin(); neigh!=_mesh->NNList[vert->id].end(); ++neigh){
379  if(_mesh->is_owned_node(*neigh)){
380  visible = true;
381  DirectedEdge<index_t> gnn_edge(_mesh->lnn2gnn[vert->edge.first], _mesh->lnn2gnn[vert->edge.second], vert->id);
382  recv_additional[_mesh->node_owner[vert->id]].insert(gnn_edge);
383  break;
384  }
385  }
386  }else{
387  // Vertex is owned by *this* MPI process, so check whether it is visible by other MPI processes.
388  // The latter is true only if both vertices of the original edge were halo vertices.
389  if(_mesh->is_halo_node(vert->edge.first) && _mesh->is_halo_node(vert->edge.second)){
390  // Find which processes see this vertex
391  std::set<int> processes;
392  for(typename std::vector<index_t>::const_iterator neigh=_mesh->NNList[vert->id].begin(); neigh!=_mesh->NNList[vert->id].end(); ++neigh)
393  processes.insert(_mesh->node_owner[*neigh]);
394 
395  processes.erase(rank);
396 
397  for(typename std::set<int>::const_iterator proc=processes.begin(); proc!=processes.end(); ++proc){
398  DirectedEdge<index_t> gnn_edge(_mesh->lnn2gnn[vert->edge.first], _mesh->lnn2gnn[vert->edge.second], vert->id);
399  send_additional[*proc].insert(gnn_edge);
400  }
401  }
402  }
403  }
404 
405  // Append vertices in recv_additional and send_additional to recv and send.
406  // Mark how many vertices are added to each of these vectors.
407  std::vector<size_t> recv_cnt(nprocs, 0), send_cnt(nprocs, 0);
408 
409  for(int i=0;i<nprocs;++i){
410  recv_cnt[i] = recv_additional[i].size();
411  for(typename std::set< DirectedEdge<index_t> >::const_iterator it=recv_additional[i].begin();it!=recv_additional[i].end();++it){
412  _mesh->recv[i].push_back(it->id);
413  _mesh->recv_halo.insert(it->id);
414  }
415 
416  send_cnt[i] = send_additional[i].size();
417  for(typename std::set< DirectedEdge<index_t> >::const_iterator it=send_additional[i].begin();it!=send_additional[i].end();++it){
418  _mesh->send[i].push_back(it->id);
419  _mesh->send_halo.insert(it->id);
420  }
421  }
422 
423  // Additional code for centroidal vertices.
424  if(dim==3){
425  for(int i=0;i<nprocs;++i){
426  recv_cnt[i] += cidRecv_additional[i].size();
427  for(typename std::set<Wedge>::const_iterator it=cidRecv_additional[i].begin();it!=cidRecv_additional[i].end();++it){
428  _mesh->recv[i].push_back(it->cid);
429  _mesh->recv_halo.insert(it->cid);
430  }
431 
432  send_cnt[i] += cidSend_additional[i].size();
433  for(typename std::set<Wedge>::const_iterator it=cidSend_additional[i].begin();it!=cidSend_additional[i].end();++it){
434  _mesh->send[i].push_back(it->cid);
435  _mesh->send_halo.insert(it->cid);
436  }
437  }
438  }
439 
440  // Update global numbering
441  _mesh->update_gappy_global_numbering(recv_cnt, send_cnt);
442 
443  // Now that the global numbering has been updated, update send_map and recv_map.
444  for(int i=0;i<nprocs;++i){
445  for(typename std::set< DirectedEdge<index_t> >::const_iterator it=recv_additional[i].begin();it!=recv_additional[i].end();++it)
446  _mesh->recv_map[i][_mesh->lnn2gnn[it->id]] = it->id;
447 
448  for(typename std::set< DirectedEdge<index_t> >::const_iterator it=send_additional[i].begin();it!=send_additional[i].end();++it)
449  _mesh->send_map[i][_mesh->lnn2gnn[it->id]] = it->id;
450 
451  // Additional code for centroidals.
452  if(dim==3){
453  for(typename std::set<Wedge>::const_iterator it=cidRecv_additional[i].begin();it!=cidRecv_additional[i].end();++it)
454  _mesh->recv_map[i][_mesh->lnn2gnn[it->cid]] = it->cid;
455 
456  for(typename std::set<Wedge>::const_iterator it=cidSend_additional[i].begin();it!=cidSend_additional[i].end();++it)
457  _mesh->send_map[i][_mesh->lnn2gnn[it->cid]] = it->cid;
458 
459  cidRecv_additional[i].clear();
460  cidSend_additional[i].clear();
461  }
462  }
463 
464  _mesh->trim_halo();
465  }
466  }
467 #endif
468 
469 #if !defined NDEBUG
470  if(dim==2){
471 #pragma omp barrier
472  // Fix orientations of new elements.
473  size_t NElements = _mesh->get_number_elements();
474 
475 #pragma omp for schedule(guided)
476  for(size_t i=0;i<NElements;i++){
477  index_t n0 = _mesh->_ENList[i*nloc];
478  if(n0<0)
479  continue;
480 
481  index_t n1 = _mesh->_ENList[i*nloc + 1];
482  index_t n2 = _mesh->_ENList[i*nloc + 2];
483 
484  const real_t *x0 = &_mesh->_coords[n0*ndims];
485  const real_t *x1 = &_mesh->_coords[n1*ndims];
486  const real_t *x2 = &_mesh->_coords[n2*ndims];
487 
488  real_t av = property->area(x0, x1, x2);
489 
490  if(av<=0){
491 #pragma omp critical
492  std::cerr<<"ERROR: inverted element in refinement"<<std::endl
493  <<"element = "<<n0<<", "<<n1<<", "<<n2<<std::endl;
494  exit(-1);
495  }
496  }
497  }
498 #endif
499  }
500  }
501 
502  private:
503 
504  void refine_edge(index_t n0, index_t n1, int tid){
505  if(_mesh->lnn2gnn[n0] > _mesh->lnn2gnn[n1]){
506  // Needs to be swapped because we want the lesser gnn first.
507  index_t tmp_n0=n0;
508  n0=n1;
509  n1=tmp_n0;
510  }
511  newVertices[tid].push_back(DirectedEdge<index_t>(n0, n1));
512 
513  // Calculate the position of the new point. From equation 16 in
514  // Li et al, Comp Methods Appl Mech Engrg 194 (2005) 4915-4950.
515  real_t x, m;
516  const real_t *x0 = _mesh->get_coords(n0);
517  const double *m0 = _mesh->get_metric(n0);
518 
519  const real_t *x1 = _mesh->get_coords(n1);
520  const double *m1 = _mesh->get_metric(n1);
521 
522  real_t weight = 1.0/(1.0 + sqrt(property->template length<dim>(x0, x1, m0)/
523  property->template length<dim>(x0, x1, m1)));
524 
525  // Calculate position of new vertex and append it to OMP thread's temp storage
526  for(size_t i=0;i<ndims;i++){
527  x = x0[i]+weight*(x1[i] - x0[i]);
528  newCoords[tid].push_back(x);
529  }
530 
531  // Interpolate new metric and append it to OMP thread's temp storage
532  for(size_t i=0;i<msize;i++){
533  m = m0[i]+weight*(m1[i] - m0[i]);
534  newMetric[tid].push_back(m);
535  if(pragmatic_isnan(m))
536  std::cerr<<"ERROR: metric health is bad in "<<__FILE__<<std::endl
537  <<"m0[i] = "<<m0[i]<<std::endl
538  <<"m1[i] = "<<m1[i]<<std::endl
539  <<"property->length(x0, x1, m0) = "<<property->template length<dim>(x0, x1, m0)<<std::endl
540  <<"property->length(x0, x1, m1) = "<<property->template length<dim>(x0, x1, m1)<<std::endl
541  <<"weight = "<<weight<<std::endl;
542  }
543  }
544 
545  void refine_facet(index_t eid, const index_t *facet, int tid){
546  const index_t *n=_mesh->get_element(eid);
547 
548  index_t newVertex[3] = {-1, -1, -1};
549  newVertex[0] = new_vertices_per_element[nedge*eid+edgeNumber(eid, facet[1], facet[2])];
550  newVertex[1] = new_vertices_per_element[nedge*eid+edgeNumber(eid, facet[0], facet[2])];
551  newVertex[2] = new_vertices_per_element[nedge*eid+edgeNumber(eid, facet[0], facet[1])];
552 
553  int refine_cnt=0;
554  for(size_t i=0; i<3; ++i)
555  if(newVertex[i]!=-1)
556  ++refine_cnt;
557 
558  switch(refine_cnt){
559  case 0:
560  // Do nothing
561  break;
562  case 1:
563  // 1:2 facet bisection
564  for(int j=0; j<3; j++)
565  if(newVertex[j] >= 0){
566  def_ops->addNN(newVertex[j], facet[j], tid);
567  def_ops->addNN(facet[j], newVertex[j], tid);
568  break;
569  }
570  break;
571  case 2:
572  // 1:3 refinement with trapezoid split
573  for(int j=0; j<3; j++){
574  if(newVertex[j] < 0){
575  def_ops->addNN(newVertex[(j+1)%3], newVertex[(j+2)%3], tid);
576  def_ops->addNN(newVertex[(j+2)%3], newVertex[(j+1)%3], tid);
577 
578  real_t ldiag1 = _mesh->calc_edge_length(newVertex[(j+1)%3], facet[(j+1)%3]);
579  real_t ldiag2 = _mesh->calc_edge_length(newVertex[(j+2)%3], facet[(j+2)%3]);
580  const int offset = ldiag1 < ldiag2 ? (j+1)%3 : (j+2)%3;
581 
582  def_ops->addNN(newVertex[offset], facet[offset], tid);
583  def_ops->addNN(facet[offset], newVertex[offset], tid);
584 
585  break;
586  }
587  }
588  break;
589  case 3:
590  // 1:4 regular refinement
591  for(int j=0; j<3; j++){
592  def_ops->addNN(newVertex[j], newVertex[(j+1)%3], tid);
593  def_ops->addNN(newVertex[(j+1)%3], newVertex[j], tid);
594  }
595  break;
596  default:
597  break;
598  }
599  }
600 
601 #ifdef HAVE_BOOST_UNORDERED_MAP_HPP
602  typedef boost::unordered_map<index_t, int> boundary_t;
603 #else
604  typedef std::map<index_t, int> boundary_t;
605 #endif
606 
607  void refine_element(size_t eid, int tid){
608  if(dim==2){
609  /*
610  *************************
611  * 2D Element Refinement *
612  *************************
613  */
614 
615  const int *n=_mesh->get_element(eid);
616 
617  // Note the order of the edges - the i'th edge is opposite the i'th node in the element.
618  index_t newVertex[3] = {-1, -1, -1};
619  newVertex[0] = new_vertices_per_element[nedge*eid];
620  newVertex[1] = new_vertices_per_element[nedge*eid+1];
621  newVertex[2] = new_vertices_per_element[nedge*eid+2];
622 
623  int refine_cnt=0;
624  for(size_t i=0; i<3; ++i)
625  if(newVertex[i]!=-1)
626  ++refine_cnt;
627 
628  if(refine_cnt > 0)
629  (this->*refineMode2D[refine_cnt-1])(newVertex, eid, tid);
630 
631  }else{
632  /*
633  *************************
634  * 3D Element Refinement *
635  *************************
636  */
637 
638  const int *n=_mesh->get_element(eid);
639 
640  int refine_cnt;
641  std::vector< DirectedEdge<index_t> > splitEdges;
642  for(int j=0, pos=0; j<4; j++)
643  for(int k=j+1; k<4; k++){
644  index_t vertexID = new_vertices_per_element[nedge*eid+pos];
645  if(vertexID >= 0){
646  splitEdges.push_back(DirectedEdge<index_t>(n[j], n[k], vertexID));
647  }
648  ++pos;
649  }
650  refine_cnt=splitEdges.size();
651 
652  if(refine_cnt > 0)
653  (this->*refineMode3D[refine_cnt-1])(splitEdges, eid, tid);
654  }
655  }
656 
657  void refine2D_1(const index_t *newVertex, int eid, int tid){
658  // Single edge split.
659 
660  const int *n=_mesh->get_element(eid);
661  const int *boundary=&(_mesh->boundary[eid*nloc]);
662 
663  int rotated_ele[3];
664  int rotated_boundary[3];
665  index_t vertexID = -1;
666  for(int j=0;j<3;j++)
667  if(newVertex[j] >= 0){
668  vertexID = newVertex[j];
669 
670  rotated_ele[0] = n[j];
671  rotated_ele[1] = n[(j+1)%3];
672  rotated_ele[2] = n[(j+2)%3];
673 
674  rotated_boundary[0] = boundary[j];
675  rotated_boundary[1] = boundary[(j+1)%3];
676  rotated_boundary[2] = boundary[(j+2)%3];
677 
678  break;
679  }
680  assert(vertexID!=-1);
681 
682  const index_t ele0[] = {rotated_ele[0], rotated_ele[1], vertexID};
683  const index_t ele1[] = {rotated_ele[0], vertexID, rotated_ele[2]};
684 
685  const index_t ele0_boundary[] = {rotated_boundary[0], 0, rotated_boundary[2]};
686  const index_t ele1_boundary[] = {rotated_boundary[0], rotated_boundary[1], 0};
687 
688  index_t ele1ID;
689  ele1ID = splitCnt[tid];
690 
691  // Add rotated_ele[0] to vertexID's NNList
692  def_ops->addNN(vertexID, rotated_ele[0], tid);
693  // Add vertexID to rotated_ele[0]'s NNList
694  def_ops->addNN(rotated_ele[0], vertexID, tid);
695 
696  // ele1ID is a new ID which isn't correct yet, it has to be
697  // updated once each thread has calculated how many new elements
698  // it created, so put ele1ID into addNE_fix instead of addNE.
699  // Put ele1 in rotated_ele[0]'s NEList
700  def_ops->addNE_fix(rotated_ele[0], ele1ID, tid);
701 
702  // Put eid and ele1 in vertexID's NEList
703  def_ops->addNE(vertexID, eid, tid);
704  def_ops->addNE_fix(vertexID, ele1ID, tid);
705 
706  // Replace eid with ele1 in rotated_ele[2]'s NEList
707  def_ops->remNE(rotated_ele[2], eid, tid);
708  def_ops->addNE_fix(rotated_ele[2], ele1ID, tid);
709 
710  assert(ele0[0]>=0 && ele0[1]>=0 && ele0[2]>=0);
711  assert(ele1[0]>=0 && ele1[1]>=0 && ele1[2]>=0);
712 
713  replace_element(eid, ele0, ele0_boundary);
714  append_element(ele1, ele1_boundary, tid);
715  splitCnt[tid] += 1;
716  }
717 
718  void refine2D_2(const index_t *newVertex, int eid, int tid){
719  const int *n=_mesh->get_element(eid);
720  const int *boundary=&(_mesh->boundary[eid*nloc]);
721 
722  int rotated_ele[3];
723  int rotated_boundary[3];
724  index_t vertexID[2];
725  for(int j=0;j<3;j++){
726  if(newVertex[j] < 0){
727  vertexID[0] = newVertex[(j+1)%3];
728  vertexID[1] = newVertex[(j+2)%3];
729 
730  rotated_ele[0] = n[j];
731  rotated_ele[1] = n[(j+1)%3];
732  rotated_ele[2] = n[(j+2)%3];
733 
734  rotated_boundary[0] = boundary[j];
735  rotated_boundary[1] = boundary[(j+1)%3];
736  rotated_boundary[2] = boundary[(j+2)%3];
737 
738  break;
739  }
740  }
741 
742  real_t ldiag0 = _mesh->calc_edge_length(rotated_ele[1], vertexID[0]);
743  real_t ldiag1 = _mesh->calc_edge_length(rotated_ele[2], vertexID[1]);
744 
745  const int offset = ldiag0 < ldiag1 ? 0 : 1;
746 
747  const index_t ele0[] = {rotated_ele[0], vertexID[1], vertexID[0]};
748  const index_t ele1[] = {vertexID[offset], rotated_ele[1], rotated_ele[2]};
749  const index_t ele2[] = {vertexID[0], vertexID[1], rotated_ele[offset+1]};
750 
751  const index_t ele0_boundary[] = {0, rotated_boundary[1], rotated_boundary[2]};
752  const index_t ele1_boundary[] = {rotated_boundary[0], (offset==0)?rotated_boundary[1]:0, (offset==0)?0:rotated_boundary[2]};
753  const index_t ele2_boundary[] = {(offset==0)?rotated_boundary[2]:0, (offset==0)?0:rotated_boundary[1], 0};
754 
755  index_t ele0ID, ele2ID;
756  ele0ID = splitCnt[tid];
757  ele2ID = ele0ID+1;
758 
759 
760  // NNList: Connect vertexID[0] and vertexID[1] with each other
761  def_ops->addNN(vertexID[0], vertexID[1], tid);
762  def_ops->addNN(vertexID[1], vertexID[0], tid);
763 
764  // vertexID[offset] and rotated_ele[offset+1] are the vertices on the diagonal
765  def_ops->addNN(vertexID[offset], rotated_ele[offset+1], tid);
766  def_ops->addNN(rotated_ele[offset+1], vertexID[offset], tid);
767 
768  // rotated_ele[offset+1] is the old vertex which is on the diagonal
769  // Add ele2 in rotated_ele[offset+1]'s NEList
770  def_ops->addNE_fix(rotated_ele[offset+1], ele2ID, tid);
771 
772  // Replace eid with ele0 in NEList[rotated_ele[0]]
773  def_ops->remNE(rotated_ele[0], eid, tid);
774  def_ops->addNE_fix(rotated_ele[0], ele0ID, tid);
775 
776  // Put ele0, ele1 and ele2 in vertexID[offset]'s NEList
777  def_ops->addNE(vertexID[offset], eid, tid);
778  def_ops->addNE_fix(vertexID[offset], ele0ID, tid);
779  def_ops->addNE_fix(vertexID[offset], ele2ID, tid);
780 
781  // vertexID[(offset+1)%2] is the new vertex which is not on the diagonal
782  // Put ele0 and ele2 in vertexID[(offset+1)%2]'s NEList
783  def_ops->addNE_fix(vertexID[(offset+1)%2], ele0ID, tid);
784  def_ops->addNE_fix(vertexID[(offset+1)%2], ele2ID, tid);
785 
786  assert(ele0[0]>=0 && ele0[1]>=0 && ele0[2]>=0);
787  assert(ele1[0]>=0 && ele1[1]>=0 && ele1[2]>=0);
788  assert(ele2[0]>=0 && ele2[1]>=0 && ele2[2]>=0);
789 
790  replace_element(eid, ele1, ele1_boundary);
791  append_element(ele0, ele0_boundary, tid);
792  append_element(ele2, ele2_boundary, tid);
793  splitCnt[tid] += 2;
794  }
795 
796  void refine2D_3(const index_t *newVertex, int eid, int tid){
797  const int *n=_mesh->get_element(eid);
798  const int *boundary=&(_mesh->boundary[eid*nloc]);
799 
800  const index_t ele0[] = {n[0], newVertex[2], newVertex[1]};
801  const index_t ele1[] = {n[1], newVertex[0], newVertex[2]};
802  const index_t ele2[] = {n[2], newVertex[1], newVertex[0]};
803  const index_t ele3[] = {newVertex[0], newVertex[1], newVertex[2]};
804 
805  const int ele0_boundary[] = {0, boundary[1], boundary[2]};
806  const int ele1_boundary[] = {0, boundary[2], boundary[0]};
807  const int ele2_boundary[] = {0, boundary[0], boundary[1]};
808  const int ele3_boundary[] = {0, 0, 0};
809 
810  index_t ele1ID, ele2ID, ele3ID;
811  ele1ID = splitCnt[tid];
812  ele2ID = ele1ID+1;
813  ele3ID = ele1ID+2;
814 
815  // Update NNList
816  def_ops->addNN(newVertex[0], newVertex[1], tid);
817  def_ops->addNN(newVertex[0], newVertex[2], tid);
818  def_ops->addNN(newVertex[1], newVertex[0], tid);
819  def_ops->addNN(newVertex[1], newVertex[2], tid);
820  def_ops->addNN(newVertex[2], newVertex[0], tid);
821  def_ops->addNN(newVertex[2], newVertex[1], tid);
822 
823  // Update NEList
824  def_ops->remNE(n[1], eid, tid);
825  def_ops->addNE_fix(n[1], ele1ID, tid);
826  def_ops->remNE(n[2], eid, tid);
827  def_ops->addNE_fix(n[2], ele2ID, tid);
828 
829  def_ops->addNE_fix(newVertex[0], ele1ID, tid);
830  def_ops->addNE_fix(newVertex[0], ele2ID, tid);
831  def_ops->addNE_fix(newVertex[0], ele3ID, tid);
832 
833  def_ops->addNE(newVertex[1], eid, tid);
834  def_ops->addNE_fix(newVertex[1], ele2ID, tid);
835  def_ops->addNE_fix(newVertex[1], ele3ID, tid);
836 
837  def_ops->addNE(newVertex[2], eid, tid);
838  def_ops->addNE_fix(newVertex[2], ele1ID, tid);
839  def_ops->addNE_fix(newVertex[2], ele3ID, tid);
840 
841  assert(ele0[0]>=0 && ele0[1]>=0 && ele0[2]>=0);
842  assert(ele1[0]>=0 && ele1[1]>=0 && ele1[2]>=0);
843  assert(ele2[0]>=0 && ele2[1]>=0 && ele2[2]>=0);
844  assert(ele3[0]>=0 && ele3[1]>=0 && ele3[2]>=0);
845 
846  replace_element(eid, ele0, ele0_boundary);
847  append_element(ele1, ele1_boundary, tid);
848  append_element(ele2, ele2_boundary, tid);
849  append_element(ele3, ele3_boundary, tid);
850  splitCnt[tid] += 3;
851  }
852 
853  void refine3D_1(std::vector< DirectedEdge<index_t> >& splitEdges, int eid, int tid){
854  const int *n=_mesh->get_element(eid);
855  const int *boundary=&(_mesh->boundary[eid*nloc]);
856 
857  boundary_t b;
858  for(int j=0; j<nloc; ++j)
859  b[n[j]] = boundary[j];
860 
861  // Find the opposite edge
862  index_t oe[2];
863  for(int j=0, pos=0;j<4;j++)
864  if(!splitEdges[0].contains(n[j]))
865  oe[pos++] = n[j];
866 
867  // Form and add two new edges.
868  const int ele0[] = {splitEdges[0].edge.first, splitEdges[0].id, oe[0], oe[1]};
869  const int ele1[] = {splitEdges[0].edge.second, splitEdges[0].id, oe[0], oe[1]};
870 
871  const int ele0_boundary[] = {0, b[splitEdges[0].edge.second], b[oe[0]], b[oe[1]]};
872  const int ele1_boundary[] = {0, b[splitEdges[0].edge.first], b[oe[0]], b[oe[1]]};
873 
874  index_t ele1ID;
875  ele1ID = splitCnt[tid];
876 
877  // ele1ID is a new ID which isn't correct yet, it has to be
878  // updated once each thread has calculated how many new elements
879  // it created, so put ele1ID into addNE_fix instead of addNE.
880  // Put ele1 in oe[0] and oe[1]'s NEList
881  def_ops->addNE_fix(oe[0], ele1ID, tid);
882  def_ops->addNE_fix(oe[1], ele1ID, tid);
883 
884  // Put eid and ele1 in newVertex[0]'s NEList
885  def_ops->addNE(splitEdges[0].id, eid, tid);
886  def_ops->addNE_fix(splitEdges[0].id, ele1ID, tid);
887 
888  // Replace eid with ele1 in splitEdges[0].edge.second's NEList
889  def_ops->remNE(splitEdges[0].edge.second, eid, tid);
890  def_ops->addNE_fix(splitEdges[0].edge.second, ele1ID, tid);
891 
892  replace_element(eid, ele0, ele0_boundary);
893  append_element(ele1, ele1_boundary, tid);
894  splitCnt[tid] += 1;
895  }
896 
897  void refine3D_2(std::vector< DirectedEdge<index_t> >& splitEdges, int eid, int tid){
898  const int *n=_mesh->get_element(eid);
899  const int *boundary=&(_mesh->boundary[eid*nloc]);
900 
901  boundary_t b;
902  for(int j=0; j<nloc; ++j)
903  b[n[j]] = boundary[j];
904 
905  /* Here there are two possibilities. Either the two split
906  * edges share a vertex (case 2(a)) or they are opposite edges
907  * (case 2(b)). Case 2(a) results in a 1:3 subdivision, case 2(b)
908  * results in a 1:4.
909  */
910 
911  int n0=splitEdges[0].connected(splitEdges[1]);
912  if(n0>=0){
913  /*
914  *************
915  * Case 2(a) *
916  *************
917  */
918  int n1 = (n0 == splitEdges[0].edge.first) ? splitEdges[0].edge.second : splitEdges[0].edge.first;
919  int n2 = (n0 == splitEdges[1].edge.first) ? splitEdges[1].edge.second : splitEdges[1].edge.first;
920 
921  // Opposite vertex
922  int n3;
923  for(int j=0; j<nloc; ++j)
924  if(n[j] != n0 && n[j] != n1 && n[j] != n2){
925  n3 = n[j];
926  break;
927  }
928 
929  // Find the diagonal which has bisected the trapezoid.
930  DirectedEdge<index_t> diagonal, offdiagonal;
931  std::vector<index_t>::const_iterator p = std::find(_mesh->NNList[splitEdges[0].id].begin(),
932  _mesh->NNList[splitEdges[0].id].end(), n2);
933  if(p != _mesh->NNList[splitEdges[0].id].end()){
934  diagonal.edge.first = splitEdges[0].id;
935  diagonal.edge.second = n2;
936  offdiagonal.edge.first = splitEdges[1].id;
937  offdiagonal.edge.second = n1;
938  }else{
939  assert(std::find(_mesh->NNList[splitEdges[1].id].begin(),
940  _mesh->NNList[splitEdges[1].id].end(), n1) != _mesh->NNList[splitEdges[1].id].end());
941  diagonal.edge.first = splitEdges[1].id;
942  diagonal.edge.second = n1;
943  offdiagonal.edge.first = splitEdges[0].id;
944  offdiagonal.edge.second = n2;
945  }
946 
947  const int ele0[] = {n0, splitEdges[0].id, splitEdges[1].id, n3};
948  const int ele1[] = {diagonal.edge.first, offdiagonal.edge.first, diagonal.edge.second, n3};
949  const int ele2[] = {diagonal.edge.first, diagonal.edge.second, offdiagonal.edge.second, n3};
950 
951  const int ele0_boundary[] = {0, b[n1], b[n2], b[n3]};
952  const int ele1_boundary[] = {b[offdiagonal.edge.second], 0, 0, b[n3]};
953  const int ele2_boundary[] = {b[n0], b[diagonal.edge.second], 0, b[n3]};
954 
955  index_t ele1ID, ele2ID;
956  ele1ID = splitCnt[tid];
957  ele2ID = ele1ID+1;
958 
959  def_ops->addNE(diagonal.edge.first, eid, tid);
960  def_ops->addNE_fix(diagonal.edge.first, ele1ID, tid);
961  def_ops->addNE_fix(diagonal.edge.first, ele2ID, tid);
962 
963  def_ops->remNE(diagonal.edge.second, eid, tid);
964  def_ops->addNE_fix(diagonal.edge.second, ele1ID, tid);
965  def_ops->addNE_fix(diagonal.edge.second, ele2ID, tid);
966 
967  def_ops->addNE(offdiagonal.edge.first, eid, tid);
968  def_ops->addNE_fix(offdiagonal.edge.first, ele1ID, tid);
969 
970  def_ops->remNE(offdiagonal.edge.second, eid, tid);
971  def_ops->addNE_fix(offdiagonal.edge.second, ele2ID, tid);
972 
973  def_ops->addNE_fix(n3, ele1ID, tid);
974  def_ops->addNE_fix(n3, ele2ID, tid);
975 
976  replace_element(eid, ele0, ele0_boundary);
977  append_element(ele1, ele1_boundary, tid);
978  append_element(ele2, ele2_boundary, tid);
979  splitCnt[tid] += 2;
980  }else{
981  /*
982  *************
983  * Case 2(b) *
984  *************
985  */
986  const int ele0[] = {splitEdges[0].edge.first, splitEdges[0].id, splitEdges[1].edge.first, splitEdges[1].id};
987  const int ele1[] = {splitEdges[0].edge.first, splitEdges[0].id, splitEdges[1].edge.second, splitEdges[1].id};
988  const int ele2[] = {splitEdges[0].edge.second, splitEdges[0].id, splitEdges[1].edge.first, splitEdges[1].id};
989  const int ele3[] = {splitEdges[0].edge.second, splitEdges[0].id, splitEdges[1].edge.second, splitEdges[1].id};
990 
991  const int ele0_boundary[] = {0, b[splitEdges[0].edge.second], 0, b[splitEdges[1].edge.second]};
992  const int ele1_boundary[] = {0, b[splitEdges[0].edge.second], 0, b[splitEdges[1].edge.first]};
993  const int ele2_boundary[] = {0, b[splitEdges[0].edge.first], 0, b[splitEdges[1].edge.second]};
994  const int ele3_boundary[] = {0, b[splitEdges[0].edge.first], 0, b[splitEdges[1].edge.first]};
995 
996  index_t ele1ID, ele2ID, ele3ID;
997  ele1ID = splitCnt[tid];
998  ele2ID = ele1ID+1;
999  ele3ID = ele1ID+2;
1000 
1001  def_ops->addNN(splitEdges[0].id, splitEdges[1].id, tid);
1002  def_ops->addNN(splitEdges[1].id, splitEdges[0].id, tid);
1003 
1004  def_ops->addNE(splitEdges[0].id, eid, tid);
1005  def_ops->addNE_fix(splitEdges[0].id, ele1ID, tid);
1006  def_ops->addNE_fix(splitEdges[0].id, ele2ID, tid);
1007  def_ops->addNE_fix(splitEdges[0].id, ele3ID, tid);
1008 
1009  def_ops->addNE(splitEdges[1].id, eid, tid);
1010  def_ops->addNE_fix(splitEdges[1].id, ele1ID, tid);
1011  def_ops->addNE_fix(splitEdges[1].id, ele2ID, tid);
1012  def_ops->addNE_fix(splitEdges[1].id, ele3ID, tid);
1013 
1014  def_ops->addNE_fix(splitEdges[0].edge.first, ele1ID, tid);
1015 
1016  def_ops->remNE(splitEdges[0].edge.second, eid, tid);
1017  def_ops->addNE_fix(splitEdges[0].edge.second, ele2ID, tid);
1018  def_ops->addNE_fix(splitEdges[0].edge.second, ele3ID, tid);
1019 
1020  def_ops->addNE_fix(splitEdges[1].edge.first, ele2ID, tid);
1021 
1022  def_ops->remNE(splitEdges[1].edge.second, eid, tid);
1023  def_ops->addNE_fix(splitEdges[1].edge.second, ele1ID, tid);
1024  def_ops->addNE_fix(splitEdges[1].edge.second, ele3ID, tid);
1025 
1026  replace_element(eid, ele0, ele0_boundary);
1027  append_element(ele1, ele1_boundary, tid);
1028  append_element(ele2, ele2_boundary, tid);
1029  append_element(ele3, ele3_boundary, tid);
1030  splitCnt[tid] += 3;
1031  }
1032  }
1033 
1034  void refine3D_3(std::vector< DirectedEdge<index_t> >& splitEdges, int eid, int tid){
1035  const int *n=_mesh->get_element(eid);
1036  const int *boundary=&(_mesh->boundary[eid*nloc]);
1037 
1038  boundary_t b;
1039  for(int j=0; j<nloc; ++j)
1040  b[n[j]] = boundary[j];
1041 
1042  /* There are 3 cases that need to be considered. They can
1043  * be distinguished by the total number of nodes that are
1044  * common between any pair of edges.
1045  * Case 3(a): there are 3 different nodes common between pairs
1046  * of split edges, i.e the three new vertices are on the
1047  * same triangle.
1048  * Case 3(b): The three new vertices are around the same
1049  * original vertex.
1050  * Case 3(c): There are 2 different nodes common between pairs
1051  * of split edges.
1052  */
1053  std::set<index_t> shared;
1054  for(int j=0;j<3;j++){
1055  for(int k=j+1;k<3;k++){
1056  index_t nid = splitEdges[j].connected(splitEdges[k]);
1057  if(nid>=0)
1058  shared.insert(nid);
1059  }
1060  }
1061  size_t nshared = shared.size();
1062 
1063  if(nshared==3){
1064  /*
1065  *************
1066  * Case 3(a) *
1067  *************
1068  */
1069  index_t m[] = {-1, -1, -1, -1, -1, -1, -1};
1070 
1071  m[0] = splitEdges[0].edge.first;
1072  m[1] = splitEdges[0].id;
1073  m[2] = splitEdges[0].edge.second;
1074  if(splitEdges[1].contains(m[2])){
1075  m[3] = splitEdges[1].id;
1076  if(splitEdges[1].edge.first!=m[2])
1077  m[4] = splitEdges[1].edge.first;
1078  else
1079  m[4] = splitEdges[1].edge.second;
1080  m[5] = splitEdges[2].id;
1081  }else{
1082  m[3] = splitEdges[2].id;
1083  if(splitEdges[2].edge.first!=m[2])
1084  m[4] = splitEdges[2].edge.first;
1085  else
1086  m[4] = splitEdges[2].edge.second;
1087  m[5] = splitEdges[1].id;
1088  }
1089  for(int j=0;j<4;j++){
1090  if((n[j]!=m[0])&&(n[j]!=m[2])&&(n[j]!=m[4])){
1091  m[6] = n[j];
1092  break;
1093  }
1094  }
1095 
1096  const int ele0[] = {m[0], m[1], m[5], m[6]};
1097  const int ele1[] = {m[1], m[2], m[3], m[6]};
1098  const int ele2[] = {m[5], m[3], m[4], m[6]};
1099  const int ele3[] = {m[1], m[3], m[5], m[6]};
1100 
1101  const int ele0_boundary[] = {0, b[m[2]], b[m[4]], b[m[6]]};
1102  const int ele1_boundary[] = {b[m[0]], 0, b[m[4]], b[m[6]]};
1103  const int ele2_boundary[] = {b[m[0]], b[m[2]], 0, b[m[6]]};
1104  const int ele3_boundary[] = {0, 0, 0, b[m[6]]};
1105 
1106  index_t ele1ID, ele2ID, ele3ID;
1107  ele1ID = splitCnt[tid];
1108  ele2ID = ele1ID+1;
1109  ele3ID = ele1ID+2;
1110 
1111  def_ops->addNE(m[1], eid, tid);
1112  def_ops->addNE_fix(m[1], ele1ID, tid);
1113  def_ops->addNE_fix(m[1], ele3ID, tid);
1114 
1115  def_ops->addNE(m[5], eid, tid);
1116  def_ops->addNE_fix(m[5], ele2ID, tid);
1117  def_ops->addNE_fix(m[5], ele3ID, tid);
1118 
1119  def_ops->addNE_fix(m[3], ele1ID, tid);
1120  def_ops->addNE_fix(m[3], ele2ID, tid);
1121  def_ops->addNE_fix(m[3], ele3ID, tid);
1122 
1123  def_ops->addNE_fix(m[6], ele1ID, tid);
1124  def_ops->addNE_fix(m[6], ele2ID, tid);
1125  def_ops->addNE_fix(m[6], ele3ID, tid);
1126 
1127  def_ops->remNE(m[2], eid, tid);
1128  def_ops->addNE_fix(m[2], ele1ID, tid);
1129 
1130  def_ops->remNE(m[4], eid, tid);
1131  def_ops->addNE_fix(m[4], ele2ID, tid);
1132 
1133  replace_element(eid, ele0, ele0_boundary);
1134  append_element(ele1, ele1_boundary, tid);
1135  append_element(ele2, ele2_boundary, tid);
1136  append_element(ele3, ele3_boundary, tid);
1137  splitCnt[tid] += 3;
1138  }else if(nshared==1){
1139  /*
1140  *************
1141  * Case 3(b) *
1142  *************
1143  */
1144 
1145  // Find the three bottom vertices, i.e. vertices of
1146  // the original elements which are part of the wedge.
1147  index_t top_vertex = *shared.begin();
1148  index_t bottom_triangle[3], top_triangle[3];
1149  for(int j=0; j<3; ++j){
1150  if(splitEdges[j].edge.first != top_vertex){
1151  bottom_triangle[j] = splitEdges[j].edge.first;
1152  }else{
1153  bottom_triangle[j] = splitEdges[j].edge.second;
1154  }
1155  top_triangle[j] = splitEdges[j].id;
1156  }
1157 
1158  // Boundary values of each wedge side
1159  int bwedge[] = {b[bottom_triangle[2]], b[bottom_triangle[0]], b[bottom_triangle[1]], 0, b[top_vertex]};
1160  refine_wedge(top_triangle, bottom_triangle, bwedge, NULL, eid, tid);
1161 
1162  const int ele0[] = {top_vertex, splitEdges[0].id, splitEdges[1].id, splitEdges[2].id};
1163  const int ele0_boundary[] = {0, b[bottom_triangle[0]], b[bottom_triangle[1]], b[bottom_triangle[2]]};
1164 
1165  def_ops->remNE(bottom_triangle[0], eid, tid);
1166  def_ops->remNE(bottom_triangle[1], eid, tid);
1167  def_ops->remNE(bottom_triangle[2], eid, tid);
1168  def_ops->addNE(splitEdges[0].id, eid, tid);
1169  def_ops->addNE(splitEdges[1].id, eid, tid);
1170  def_ops->addNE(splitEdges[2].id, eid, tid);
1171 
1172  replace_element(eid, ele0, ele0_boundary);
1173  }else{
1174  /*
1175  *************
1176  * Case 3(c) *
1177  *************
1178  */
1179  assert(shared.size() == 2);
1180  /*
1181  * This case results in a 1:4 or 1:5 subdivision. There are three
1182  * split edges, connected in a Z-like way (cf. connection of
1183  * diagonals in 1:3 wedge split). By convention, the topOfZ edge is
1184  * the one connected to middleOfZ.edge.first, bottomOfZ is the one
1185  * connected to middleOfZ.edge.second. Top and bottom edges are
1186  * flipped if necessary so that the first vertex of top and the
1187  * second vertex of bottom are the ones connected to the middle edge.
1188  */
1189 
1190  DirectedEdge<index_t> *topZ, *middleZ, *bottomZ;
1191  // Middle split edge
1192  for(int j=0; j<3; ++j){
1193  if(splitEdges[j].contains(*shared.begin()) && splitEdges[j].contains(*shared.rbegin())){
1194  middleZ = &splitEdges[j];
1195 
1196  if(splitEdges[(j+1)%3].contains(splitEdges[j].edge.first)){
1197  topZ = &splitEdges[(j+1)%3];
1198  bottomZ = &splitEdges[(j+2)%3];
1199  }else{
1200  topZ = &splitEdges[(j+2)%3];
1201  bottomZ = &splitEdges[(j+1)%3];
1202  }
1203 
1204  break;
1205  }
1206  }
1207 
1208  // Flip vertices of top and bottom edges if necessary
1209  if(topZ->edge.first != middleZ->edge.first){
1210  topZ->edge.second = topZ->edge.first;
1211  topZ->edge.first = middleZ->edge.first;
1212  }
1213  if(bottomZ->edge.second != middleZ->edge.second){
1214  bottomZ->edge.first = bottomZ->edge.second;
1215  bottomZ->edge.second = middleZ->edge.second;
1216  }
1217 
1218  /*
1219  * There are 3 sub-cases, depending on the way the trapezoids on the
1220  * facets between topZ-middleZ and middleZ-bottomZ were bisected.
1221  *
1222  * Case 3(c)(1): Both diagonals involve middleZ->id.
1223  * Case 3(c)(2): Only one diagonal involves middleZ->id.
1224  * Case 3(c)(3): No diagonal involves middleZ->id.
1225  */
1226 
1227  std::vector< DirectedEdge<index_t> > diagonals;
1228  for(std::vector<index_t>::const_iterator it=_mesh->NNList[middleZ->id].begin();
1229  it!=_mesh->NNList[middleZ->id].end(); ++it){
1230  if(*it == topZ->edge.second || *it == bottomZ->edge.first){
1231  diagonals.push_back(DirectedEdge<index_t>(middleZ->id, *it));
1232  }
1233  }
1234 
1235  switch(diagonals.size()){
1236  case 0:{
1237  // Case 3(c)(2)
1238  const int ele0[] = {middleZ->edge.first, topZ->id, bottomZ->id, bottomZ->edge.first};
1239  const int ele1[] = {middleZ->id, middleZ->edge.first, topZ->id, bottomZ->id};
1240  const int ele2[] = {topZ->id, topZ->edge.second, bottomZ->id, bottomZ->edge.first};
1241  const int ele3[] = {topZ->id, topZ->edge.second, bottomZ->edge.second, bottomZ->id};
1242  const int ele4[] = {middleZ->id, topZ->id, bottomZ->edge.second, bottomZ->id};
1243 
1244  const int ele0_boundary[] = {0, b[topZ->edge.second], b[middleZ->edge.second], 0};
1245  const int ele1_boundary[] = {0, 0, b[topZ->edge.second], b[bottomZ->edge.first]};
1246  const int ele2_boundary[] = {b[middleZ->edge.first], 0, b[middleZ->edge.second], 0};
1247  const int ele3_boundary[] = {b[middleZ->edge.first], 0, 0, b[bottomZ->edge.first]};
1248  const int ele4_boundary[] = {0, b[topZ->edge.second], 0, b[bottomZ->edge.first]};
1249 
1250  index_t ele1ID, ele2ID, ele3ID, ele4ID;
1251  ele1ID = splitCnt[tid];
1252  ele2ID = ele1ID+1;
1253  ele3ID = ele1ID+2;
1254  ele4ID = ele1ID+3;
1255 
1256  def_ops->addNN(topZ->id, bottomZ->id, tid);
1257  def_ops->addNN(bottomZ->id, topZ->id, tid);
1258 
1259  def_ops->addNE_fix(middleZ->edge.first, ele1ID, tid);
1260 
1261  def_ops->remNE(middleZ->edge.second, eid, tid);
1262  def_ops->addNE_fix(middleZ->edge.second, ele3ID, tid);
1263  def_ops->addNE_fix(middleZ->edge.second, ele4ID, tid);
1264 
1265  def_ops->remNE(topZ->edge.second, eid, tid);
1266  def_ops->addNE_fix(topZ->edge.second, ele2ID, tid);
1267  def_ops->addNE_fix(topZ->edge.second, ele3ID, tid);
1268 
1269  def_ops->addNE_fix(bottomZ->edge.first, ele2ID, tid);
1270 
1271  def_ops->addNE_fix(middleZ->id, ele1ID, tid);
1272  def_ops->addNE_fix(middleZ->id, ele4ID, tid);
1273 
1274  def_ops->addNE(topZ->id, eid, tid);
1275  def_ops->addNE_fix(topZ->id, ele1ID, tid);
1276  def_ops->addNE_fix(topZ->id, ele2ID, tid);
1277  def_ops->addNE_fix(topZ->id, ele3ID, tid);
1278  def_ops->addNE_fix(topZ->id, ele4ID, tid);
1279 
1280  def_ops->addNE(bottomZ->id, eid, tid);
1281  def_ops->addNE_fix(bottomZ->id, ele1ID, tid);
1282  def_ops->addNE_fix(bottomZ->id, ele2ID, tid);
1283  def_ops->addNE_fix(bottomZ->id, ele3ID, tid);
1284  def_ops->addNE_fix(bottomZ->id, ele4ID, tid);
1285 
1286  replace_element(eid, ele0, ele0_boundary);
1287  append_element(ele1, ele1_boundary, tid);
1288  append_element(ele2, ele2_boundary, tid);
1289  append_element(ele3, ele3_boundary, tid);
1290  append_element(ele4, ele4_boundary, tid);
1291  splitCnt[tid] += 4;
1292  break;
1293  }
1294  case 1:{
1295  // Case 3(c)(3)
1296 
1297  // Re-arrange topZ and bottomZ if necessary; make topZ point to the
1298  // splitEdge for which edge.second is connected to middleZ->top.
1299  if(topZ->edge.second != diagonals[0].edge.second){
1300  assert(diagonals[0].edge.second == bottomZ->edge.first);
1301  DirectedEdge<index_t> *p = topZ;
1302  topZ = bottomZ;
1303  bottomZ = p;
1304 
1305  // Flip topZ, middleZ, bottomZ
1306  index_t v = middleZ->edge.first;
1307  middleZ->edge.first = middleZ->edge.second;
1308  middleZ->edge.second = v;
1309 
1310  v = topZ->edge.first;
1311  topZ->edge.first = topZ->edge.second;
1312  topZ->edge.second = v;
1313 
1314  v = bottomZ->edge.first;
1315  bottomZ->edge.first = bottomZ->edge.second;
1316  bottomZ->edge.second = v;
1317  }
1318 
1319  const int ele0[] = {middleZ->edge.first, topZ->id, bottomZ->id, bottomZ->edge.first};
1320  const int ele1[] = {middleZ->id, middleZ->edge.first, topZ->id, bottomZ->id};
1321  const int ele2[] = {topZ->id, topZ->edge.second, bottomZ->id, bottomZ->edge.first};
1322  const int ele3[] = {middleZ->id, topZ->id, topZ->edge.second, bottomZ->id};
1323  const int ele4[] = {middleZ->id, topZ->edge.second, middleZ->edge.second, bottomZ->id};
1324 
1325  const int ele0_boundary[] = {0, b[topZ->edge.second], b[middleZ->edge.second], 0};
1326  const int ele1_boundary[] = {0, 0, b[topZ->edge.second], b[bottomZ->edge.first]};
1327  const int ele2_boundary[] = {b[middleZ->edge.first], 0, b[middleZ->edge.second], 0};
1328  const int ele3_boundary[] = {0, 0, 0, b[bottomZ->edge.first]};
1329  const int ele4_boundary[] = {b[middleZ->edge.first], b[topZ->edge.second], 0, b[bottomZ->edge.first]};
1330 
1331  index_t ele1ID, ele2ID, ele3ID, ele4ID;
1332  ele1ID = splitCnt[tid];
1333  ele2ID = ele1ID+1;
1334  ele3ID = ele1ID+2;
1335  ele4ID = ele1ID+3;
1336 
1337  def_ops->addNN(topZ->id, bottomZ->id, tid);
1338  def_ops->addNN(bottomZ->id, topZ->id, tid);
1339 
1340  def_ops->addNE_fix(middleZ->edge.first, ele1ID, tid);
1341 
1342  def_ops->remNE(middleZ->edge.second, eid, tid);
1343  def_ops->addNE_fix(middleZ->edge.second, ele4ID, tid);
1344 
1345  def_ops->remNE(topZ->edge.second, eid, tid);
1346  def_ops->addNE_fix(topZ->edge.second, ele2ID, tid);
1347  def_ops->addNE_fix(topZ->edge.second, ele3ID, tid);
1348  def_ops->addNE_fix(topZ->edge.second, ele4ID, tid);
1349 
1350  def_ops->addNE_fix(bottomZ->edge.first, ele2ID, tid);
1351 
1352  def_ops->addNE_fix(middleZ->id, ele1ID, tid);
1353  def_ops->addNE_fix(middleZ->id, ele3ID, tid);
1354  def_ops->addNE_fix(middleZ->id, ele4ID, tid);
1355 
1356  def_ops->addNE(topZ->id, eid, tid);
1357  def_ops->addNE_fix(topZ->id, ele1ID, tid);
1358  def_ops->addNE_fix(topZ->id, ele2ID, tid);
1359  def_ops->addNE_fix(topZ->id, ele3ID, tid);
1360 
1361  def_ops->addNE(bottomZ->id, eid, tid);
1362  def_ops->addNE_fix(bottomZ->id, ele1ID, tid);
1363  def_ops->addNE_fix(bottomZ->id, ele2ID, tid);
1364  def_ops->addNE_fix(bottomZ->id, ele3ID, tid);
1365  def_ops->addNE_fix(bottomZ->id, ele4ID, tid);
1366 
1367  replace_element(eid, ele0, ele0_boundary);
1368  append_element(ele1, ele1_boundary, tid);
1369  append_element(ele2, ele2_boundary, tid);
1370  append_element(ele3, ele3_boundary, tid);
1371  append_element(ele4, ele4_boundary, tid);
1372  splitCnt[tid] += 4;
1373  break;
1374  }
1375  case 2:{
1376  // Case 3(c)(1)
1377  const int ele0[] = {middleZ->id, bottomZ->edge.first, middleZ->edge.first, topZ->id};
1378  const int ele1[] = {middleZ->id, bottomZ->edge.first, topZ->id, topZ->edge.second};
1379  const int ele2[] = {middleZ->id, bottomZ->id, bottomZ->edge.first, topZ->edge.second};
1380  const int ele3[] = {middleZ->id, middleZ->edge.second, bottomZ->id, topZ->edge.second};
1381 
1382  const int ele0_boundary[] = {b[middleZ->edge.second], b[bottomZ->edge.first], 0, b[topZ->edge.second]};
1383  const int ele1_boundary[] = {b[middleZ->edge.second], b[bottomZ->edge.first], 0, 0};
1384  const int ele2_boundary[] = {b[middleZ->edge.first], 0, 0, b[topZ->edge.second]};
1385  const int ele3_boundary[] = {b[middleZ->edge.first], 0, b[bottomZ->edge.first], b[topZ->edge.second]};
1386 
1387  index_t ele1ID, ele2ID, ele3ID;
1388  ele1ID = splitCnt[tid];
1389  ele2ID = ele1ID+1;
1390  ele3ID = ele1ID+2;
1391 
1392  def_ops->addNE(middleZ->id, eid, tid);
1393  def_ops->addNE_fix(middleZ->id, ele1ID, tid);
1394  def_ops->addNE_fix(middleZ->id, ele2ID, tid);
1395  def_ops->addNE_fix(middleZ->id, ele3ID, tid);
1396 
1397  def_ops->remNE(middleZ->edge.second, eid, tid);
1398  def_ops->addNE_fix(middleZ->edge.second, ele3ID, tid);
1399 
1400  def_ops->remNE(topZ->edge.second, eid, tid);
1401  def_ops->addNE_fix(topZ->edge.second, ele1ID, tid);
1402  def_ops->addNE_fix(topZ->edge.second, ele2ID, tid);
1403  def_ops->addNE_fix(topZ->edge.second, ele3ID, tid);
1404 
1405  def_ops->addNE_fix(bottomZ->edge.first, ele1ID, tid);
1406  def_ops->addNE_fix(bottomZ->edge.first, ele2ID, tid);
1407 
1408  def_ops->addNE(topZ->id, eid, tid);
1409  def_ops->addNE_fix(topZ->id, ele1ID, tid);
1410 
1411  def_ops->addNE_fix(bottomZ->id, ele2ID, tid);
1412  def_ops->addNE_fix(bottomZ->id, ele3ID, tid);
1413 
1414  replace_element(eid, ele0, ele0_boundary);
1415  append_element(ele1, ele1_boundary, tid);
1416  append_element(ele2, ele2_boundary, tid);
1417  append_element(ele3, ele3_boundary, tid);
1418  splitCnt[tid] += 3;
1419  break;
1420  }
1421  default:
1422  break;
1423  }
1424  }
1425  }
1426 
1427  void refine3D_4(std::vector< DirectedEdge<index_t> >& splitEdges, int eid, int tid){
1428  const int *n=_mesh->get_element(eid);
1429  const int *boundary=&(_mesh->boundary[eid*nloc]);
1430 
1431  boundary_t b;
1432  for(int j=0; j<nloc; ++j)
1433  b[n[j]] = boundary[j];
1434 
1435  /*
1436  * There are 2 cases here:
1437  *
1438  * Case 4(a): Three split edges are on the same triangle.
1439  * Case 4(b): Each of the four triangles has exactly two split edges.
1440  */
1441 
1442  std::set<index_t> shared;
1443  for(int j=0; j<4; ++j){
1444  for(int k=j+1; k<4; ++k){
1445  index_t nid = splitEdges[j].connected(splitEdges[k]);
1446  if(nid>=0)
1447  shared.insert(nid);
1448  }
1449  }
1450  size_t nshared = shared.size();
1451  assert(nshared==3 || nshared==4);
1452 
1453  if(nshared==3){
1454  /*
1455  *************
1456  * Case 4(a) *
1457  *************
1458  */
1459  DirectedEdge<index_t>* p[4];
1460  int pos = 0;
1461  for(int j=0; j<4; ++j)
1462  if(shared.count(splitEdges[j].edge.first)>0 && shared.count(splitEdges[j].edge.second)>0)
1463  p[pos++] = &splitEdges[j];
1464  else
1465  p[3] = &splitEdges[j];
1466 
1467  assert(pos==3);
1468 
1469  // p[0], p[1] and p[2] point to the three split edges which
1470  // are on the same facet, p[3] points to the other split edge.
1471 
1472  // Re-arrange p[] so that p[0] points to the
1473  // split edge which is not connected to p[3].
1474  if(p[3]->connected(*p[0]) >= 0){
1475  for(int j=1; j<3; ++j){
1476  if(p[3]->connected(*p[j]) < 0){
1477  DirectedEdge<index_t> *swap = p[j];
1478  p[j] = p[0];
1479  p[0] = swap;
1480  break;
1481  }
1482  }
1483  }
1484 
1485  // Re-arrange p[3] if necessary so that edge.first
1486  // is the vertex on the triangle with the 3 split edges.
1487  if(shared.count(p[3]->edge.first)==0){
1488  index_t v = p[3]->edge.first;
1489  p[3]->edge.first = p[3]->edge.second;
1490  p[3]->edge.second = v;
1491  }
1492 
1493  // Same for p[1] and p[2]; make edge.first = p[3]->edge.first.
1494  for(int j=1; j<=2; ++j)
1495  if(p[j]->edge.first != p[3]->edge.first){
1496  assert(p[j]->edge.second == p[3]->edge.first);
1497  p[j]->edge.second = p[j]->edge.first;
1498  p[j]->edge.first = p[3]->edge.first;
1499  }
1500 
1501  /*
1502  * There are 3 sub-cases, depending on the way the trapezoids
1503  * on the facets between p[1]-p[3] and p[2]-p[3] were bisected.
1504  *
1505  * Case 4(a)(1): No diagonal involves p[3].
1506  * Case 4(a)(2): Only one diagonal involves p[3].
1507  * Case 4(a)(3): Both diagonals involve p[3].
1508  */
1509 
1510  std::vector< DirectedEdge<index_t> > diagonals;
1511  for(std::vector<index_t>::const_iterator it=_mesh->NNList[p[3]->id].begin();
1512  it!=_mesh->NNList[p[3]->id].end(); ++it){
1513  if(*it == p[1]->edge.second || *it == p[2]->edge.second){
1514  diagonals.push_back(DirectedEdge<index_t>(p[3]->id, *it));
1515  }
1516  }
1517 
1518  switch(diagonals.size()){
1519  case 0:{
1520  // Case 4(a)(1)
1521  const int ele0[] = {p[0]->id, p[1]->edge.second, p[1]->id, p[3]->edge.second};
1522  const int ele1[] = {p[0]->id, p[1]->id, p[2]->id, p[3]->edge.second};
1523  const int ele2[] = {p[0]->id, p[2]->id, p[2]->edge.second, p[3]->edge.second};
1524  const int ele3[] = {p[1]->id, p[3]->id, p[2]->id, p[3]->edge.second};
1525  const int ele4[] = {p[1]->id, p[2]->id, p[3]->id, p[3]->edge.first};
1526 
1527  const int ele0_boundary[] = {b[p[2]->edge.second], 0, b[p[3]->edge.first], b[p[3]->edge.second]};
1528  const int ele1_boundary[] = {0, 0, 0, b[p[3]->edge.second]};
1529  const int ele2_boundary[] = {b[p[1]->edge.second], b[p[2]->edge.first], 0, b[p[3]->edge.second]};
1530  const int ele3_boundary[] = {b[p[1]->edge.second], 0, b[p[2]->edge.second], 0};
1531  const int ele4_boundary[] = {b[p[1]->edge.second], b[p[2]->edge.second], b[p[3]->edge.second], 0};
1532 
1533  index_t ele1ID, ele2ID, ele3ID, ele4ID;
1534  ele1ID = splitCnt[tid];
1535  ele2ID = ele1ID+1;
1536  ele3ID = ele1ID+2;
1537  ele4ID = ele1ID+3;
1538 
1539  def_ops->remNE(p[2]->edge.first, eid, tid);
1540  def_ops->addNE_fix(p[2]->edge.first, ele4ID, tid);
1541 
1542  def_ops->remNE(p[2]->edge.second, eid, tid);
1543  def_ops->addNE_fix(p[2]->edge.second, ele2ID, tid);
1544 
1545  def_ops->addNE_fix(p[3]->edge.second, ele1ID, tid);
1546  def_ops->addNE_fix(p[3]->edge.second, ele2ID, tid);
1547  def_ops->addNE_fix(p[3]->edge.second, ele3ID, tid);
1548 
1549  def_ops->addNE(p[0]->id, eid, tid);
1550  def_ops->addNE_fix(p[0]->id, ele1ID, tid);
1551  def_ops->addNE_fix(p[0]->id, ele2ID, tid);
1552 
1553  def_ops->addNE(p[1]->id, eid, tid);
1554  def_ops->addNE_fix(p[1]->id, ele1ID, tid);
1555  def_ops->addNE_fix(p[1]->id, ele3ID, tid);
1556  def_ops->addNE_fix(p[1]->id, ele4ID, tid);
1557 
1558  def_ops->addNE_fix(p[2]->id, ele1ID, tid);
1559  def_ops->addNE_fix(p[2]->id, ele2ID, tid);
1560  def_ops->addNE_fix(p[2]->id, ele3ID, tid);
1561  def_ops->addNE_fix(p[2]->id, ele4ID, tid);
1562 
1563  def_ops->addNE_fix(p[3]->id, ele3ID, tid);
1564  def_ops->addNE_fix(p[3]->id, ele4ID, tid);
1565 
1566  replace_element(eid, ele0, ele0_boundary);
1567  append_element(ele1, ele1_boundary, tid);
1568  append_element(ele2, ele2_boundary, tid);
1569  append_element(ele3, ele3_boundary, tid);
1570  append_element(ele4, ele4_boundary, tid);
1571  splitCnt[tid] += 4;
1572  break;
1573  }
1574  case 1:{
1575  // Case 4(a)(2)
1576 
1577  // Swap p[1] and p[2] if necessary so that p[2]->edge.second
1578  // is the ending point of the diagonal bisecting the trapezoid.
1579  if(p[2]->edge.second != diagonals[0].edge.second){
1580  DirectedEdge<index_t> *swap = p[1];
1581  p[1] = p[2];
1582  p[2] = swap;
1583  }
1584  assert(p[2]->edge.second == diagonals[0].edge.second);
1585 
1586  const int ele0[] = {p[0]->id, p[1]->edge.second, p[1]->id, p[3]->edge.second};
1587  const int ele1[] = {p[0]->id, p[3]->id, p[2]->edge.second, p[3]->edge.second};
1588  const int ele2[] = {p[0]->id, p[1]->id, p[3]->id, p[3]->edge.second};
1589  const int ele3[] = {p[0]->id, p[3]->id, p[2]->id, p[2]->edge.second};
1590  const int ele4[] = {p[0]->id, p[1]->id, p[2]->id, p[3]->id};
1591  const int ele5[] = {p[2]->id, p[3]->id, p[1]->id, p[3]->edge.first};
1592 
1593  const int ele0_boundary[] = {b[p[2]->edge.second], 0, b[p[1]->edge.first], b[p[3]->edge.second]};
1594  const int ele1_boundary[] = {b[p[1]->edge.second], b[p[3]->edge.first], 0, 0};
1595  const int ele2_boundary[] = {b[p[2]->edge.second], 0, 0, 0};
1596  const int ele3_boundary[] = {b[p[1]->edge.second], b[p[3]->edge.second], 0, 0};
1597  const int ele4_boundary[] = {0, 0, 0, b[p[3]->edge.second]};
1598  const int ele5_boundary[] = {b[p[2]->edge.second], b[p[3]->edge.second], b[p[1]->edge.second], 0};
1599 
1600  index_t ele1ID, ele2ID, ele3ID, ele4ID, ele5ID;
1601  ele1ID = splitCnt[tid];
1602  ele2ID = ele1ID+1;
1603  ele3ID = ele1ID+2;
1604  ele4ID = ele1ID+3;
1605  ele5ID = ele1ID+4;
1606 
1607  def_ops->addNN(p[0]->id, p[3]->id, tid);
1608  def_ops->addNN(p[3]->id, p[0]->id, tid);
1609 
1610  def_ops->remNE(p[2]->edge.first, eid, tid);
1611  def_ops->addNE_fix(p[2]->edge.first, ele5ID, tid);
1612 
1613  def_ops->remNE(p[2]->edge.second, eid, tid);
1614  def_ops->addNE_fix(p[2]->edge.second, ele1ID, tid);
1615  def_ops->addNE_fix(p[2]->edge.second, ele3ID, tid);
1616 
1617  def_ops->addNE_fix(p[3]->edge.second, ele1ID, tid);
1618  def_ops->addNE_fix(p[3]->edge.second, ele2ID, tid);
1619 
1620  def_ops->addNE(p[0]->id, eid, tid);
1621  def_ops->addNE_fix(p[0]->id, ele1ID, tid);
1622  def_ops->addNE_fix(p[0]->id, ele2ID, tid);
1623  def_ops->addNE_fix(p[0]->id, ele3ID, tid);
1624  def_ops->addNE_fix(p[0]->id, ele4ID, tid);
1625 
1626  def_ops->addNE(p[1]->id, eid, tid);
1627  def_ops->addNE_fix(p[1]->id, ele2ID, tid);
1628  def_ops->addNE_fix(p[1]->id, ele4ID, tid);
1629  def_ops->addNE_fix(p[1]->id, ele5ID, tid);
1630 
1631  def_ops->addNE_fix(p[2]->id, ele3ID, tid);
1632  def_ops->addNE_fix(p[2]->id, ele4ID, tid);
1633  def_ops->addNE_fix(p[2]->id, ele5ID, tid);
1634 
1635  def_ops->addNE_fix(p[3]->id, ele1ID, tid);
1636  def_ops->addNE_fix(p[3]->id, ele2ID, tid);
1637  def_ops->addNE_fix(p[3]->id, ele3ID, tid);
1638  def_ops->addNE_fix(p[3]->id, ele4ID, tid);
1639  def_ops->addNE_fix(p[3]->id, ele5ID, tid);
1640 
1641  replace_element(eid, ele0, ele0_boundary);
1642  append_element(ele1, ele1_boundary, tid);
1643  append_element(ele2, ele2_boundary, tid);
1644  append_element(ele3, ele3_boundary, tid);
1645  append_element(ele4, ele4_boundary, tid);
1646  append_element(ele5, ele5_boundary, tid);
1647  splitCnt[tid] += 5;
1648  break;
1649  }
1650  case 2:{
1651  // Case 4(a)(3)
1652  const int ele0[] = {p[1]->edge.first, p[1]->id, p[2]->id, p[3]->id};
1653  const int ele1[] = {p[3]->id, p[1]->edge.second, p[0]->id, p[3]->edge.second};
1654  const int ele2[] = {p[3]->id, p[0]->id, p[2]->edge.second, p[3]->edge.second};
1655  const int ele3[] = {p[1]->id, p[1]->edge.second, p[0]->id, p[3]->id};
1656  const int ele4[] = {p[1]->id, p[0]->id, p[2]->id, p[3]->id};
1657  const int ele5[] = {p[2]->id, p[3]->id, p[0]->id, p[2]->edge.second};
1658 
1659  const int ele0_boundary[] = {0, b[p[1]->edge.second], b[p[2]->edge.second], b[p[3]->edge.second]};
1660  const int ele1_boundary[] = {b[p[3]->edge.first], 0, b[p[2]->edge.second], 0};
1661  const int ele2_boundary[] = {b[p[3]->edge.first], b[p[1]->edge.second], 0, 0};
1662  const int ele3_boundary[] = {0, 0, b[p[2]->edge.second], b[p[3]->edge.second]};
1663  const int ele4_boundary[] = {0, 0, 0, b[p[3]->edge.second]};
1664  const int ele5_boundary[] = {0, b[p[3]->edge.second], b[p[1]->edge.second], 0};
1665 
1666  index_t ele1ID, ele2ID, ele3ID, ele4ID, ele5ID;
1667  ele1ID = splitCnt[tid];
1668  ele2ID = ele1ID+1;
1669  ele3ID = ele1ID+2;
1670  ele4ID = ele1ID+3;
1671  ele5ID = ele1ID+4;
1672 
1673  def_ops->addNN(p[0]->id, p[3]->id, tid);
1674  def_ops->addNN(p[3]->id, p[0]->id, tid);
1675 
1676 
1677  def_ops->remNE(p[1]->edge.second, eid, tid);
1678  def_ops->addNE_fix(p[1]->edge.second, ele1ID, tid);
1679  def_ops->addNE_fix(p[1]->edge.second, ele3ID, tid);
1680 
1681  def_ops->remNE(p[2]->edge.second, eid, tid);
1682  def_ops->addNE_fix(p[2]->edge.second, ele2ID, tid);
1683  def_ops->addNE_fix(p[2]->edge.second, ele5ID, tid);
1684 
1685  def_ops->remNE(p[3]->edge.second, eid, tid);
1686  def_ops->addNE_fix(p[3]->edge.second, ele1ID, tid);
1687  def_ops->addNE_fix(p[3]->edge.second, ele2ID, tid);
1688 
1689  def_ops->addNE_fix(p[0]->id, ele1ID, tid);
1690  def_ops->addNE_fix(p[0]->id, ele2ID, tid);
1691  def_ops->addNE_fix(p[0]->id, ele3ID, tid);
1692  def_ops->addNE_fix(p[0]->id, ele4ID, tid);
1693  def_ops->addNE_fix(p[0]->id, ele5ID, tid);
1694 
1695  def_ops->addNE(p[1]->id, eid, tid);
1696  def_ops->addNE_fix(p[1]->id, ele3ID, tid);
1697  def_ops->addNE_fix(p[1]->id, ele4ID, tid);
1698 
1699  def_ops->addNE(p[2]->id, eid, tid);
1700  def_ops->addNE_fix(p[2]->id, ele4ID, tid);
1701  def_ops->addNE_fix(p[2]->id, ele5ID, tid);
1702 
1703  def_ops->addNE(p[3]->id, eid, tid);
1704  def_ops->addNE_fix(p[3]->id, ele1ID, tid);
1705  def_ops->addNE_fix(p[3]->id, ele2ID, tid);
1706  def_ops->addNE_fix(p[3]->id, ele3ID, tid);
1707  def_ops->addNE_fix(p[3]->id, ele4ID, tid);
1708  def_ops->addNE_fix(p[3]->id, ele5ID, tid);
1709 
1710  replace_element(eid, ele0, ele0_boundary);
1711  append_element(ele1, ele1_boundary, tid);
1712  append_element(ele2, ele2_boundary, tid);
1713  append_element(ele3, ele3_boundary, tid);
1714  append_element(ele4, ele4_boundary, tid);
1715  append_element(ele5, ele5_boundary, tid);
1716  splitCnt[tid] += 5;
1717  break;
1718  }
1719  default:
1720  break;
1721  }
1722  }else{
1723  /*
1724  *************
1725  * Case 4(b) *
1726  *************
1727  */
1728 
1729  // In this case, the element is split into two wedges.
1730 
1731  // Find the top left, top right, bottom left and
1732  // bottom right split edges, as depicted in the paper.
1733  DirectedEdge<index_t> *tr, *tl, *br, *bl;
1734  tl = &splitEdges[0];
1735 
1736  // Find top right
1737  for(int j=1; j<4; ++j){
1738  if(splitEdges[j].contains(tl->edge.first)){
1739  tr = &splitEdges[j];
1740  break;
1741  }
1742  }
1743  // Re-arrange tr so that tl->edge.first == tr->edge.first
1744  if(tr->edge.first != tl->edge.first){
1745  assert(tr->edge.second == tl->edge.first);
1746  tr->edge.second = tr->edge.first;
1747  tr->edge.first = tl->edge.first;
1748  }
1749 
1750  // Find bottom left
1751  for(int j=1; j<4; ++j){
1752  if(splitEdges[j].contains(tl->edge.second)){
1753  bl = &splitEdges[j];
1754  break;
1755  }
1756  }
1757  // Re-arrange bl so that tl->edge.second == bl->edge.second
1758  if(bl->edge.second != tl->edge.second){
1759  assert(bl->edge.first == tl->edge.second);
1760  bl->edge.first = bl->edge.second;
1761  bl->edge.second = tl->edge.second;
1762  }
1763 
1764  // Find bottom right
1765  for(int j=1; j<4; ++j){
1766  if(splitEdges[j].contains(bl->edge.first) && splitEdges[j].contains(tr->edge.second)){
1767  br = &splitEdges[j];
1768  break;
1769  }
1770  }
1771  // Re-arrange br so that bl->edge.first == br->edge.first
1772  if(br->edge.first != bl->edge.first){
1773  assert(br->edge.second == bl->edge.first);
1774  br->edge.second = br->edge.first;
1775  br->edge.first = bl->edge.first;
1776  }
1777 
1778  assert(tr->edge.second == br->edge.second);
1779 
1780  // Find how the trapezoids have been split
1781  DirectedEdge<index_t> bw1, bw2, tw1, tw2;
1782  std::vector<index_t>::const_iterator p;
1783 
1784  // For the bottom wedge:
1785  // 1) From tl->id to tr->edge.second or from tr->id to tl->edge.second?
1786  // 2) From bl->id to br->edge.second or from br->id to bl->edge.second?
1787  // For the top wedge:
1788  // 1) From tl->id to bl->edge.first or from bl->id to tl->edge.first?
1789  // 2) From tr->id to br->edge.first or from br->id to tr->edge.first?
1790 
1791  p = std::find(_mesh->NNList[tl->id].begin(), _mesh->NNList[tl->id].end(), tr->edge.second);
1792  if(p != _mesh->NNList[tl->id].end()){
1793  bw1.edge.first = tl->id;
1794  bw1.edge.second = tr->edge.second;
1795  }else{
1796  bw1.edge.first = tr->id;
1797  bw1.edge.second = tl->edge.second;
1798  }
1799 
1800  p = std::find(_mesh->NNList[bl->id].begin(), _mesh->NNList[bl->id].end(), br->edge.second);
1801  if(p != _mesh->NNList[bl->id].end()){
1802  bw2.edge.first = bl->id;
1803  bw2.edge.second = br->edge.second;
1804  }else{
1805  bw2.edge.first = br->id;
1806  bw2.edge.second = bl->edge.second;
1807  }
1808 
1809  p = std::find(_mesh->NNList[tl->id].begin(), _mesh->NNList[tl->id].end(), bl->edge.first);
1810  if(p != _mesh->NNList[tl->id].end()){
1811  tw1.edge.first = tl->id;
1812  tw1.edge.second = bl->edge.first;
1813  }else{
1814  tw1.edge.first = bl->id;
1815  tw1.edge.second = tl->edge.first;
1816  }
1817 
1818  p = std::find(_mesh->NNList[tr->id].begin(), _mesh->NNList[tr->id].end(), br->edge.first);
1819  if(p != _mesh->NNList[tr->id].end()){
1820  tw2.edge.first = tr->id;
1821  tw2.edge.second = br->edge.first;
1822  }else{
1823  tw2.edge.first = br->id;
1824  tw2.edge.second = tr->edge.first;
1825  }
1826 
1827  // If bw1 and bw2 are connected, then the third quadrilateral
1828  // can be split whichever way we like. Otherwise, we want to
1829  // choose the diagonal which will lead to a 1:3 wedge split.
1830  // Same for tw1 and tw2.
1831 
1832  DirectedEdge<index_t> bw, tw;
1833  bool flex_bottom, flex_top;
1834 
1835  if(bw1.connected(bw2) >= 0){
1836  flex_bottom = true;
1837  }else{
1838  flex_bottom = false;
1839  bw.edge.first = bw1.edge.first;
1840  bw.edge.second = bw2.edge.first;
1841  assert(bw.connected(bw1) >= 0 && bw.connected(bw2) >= 0);
1842  }
1843  if(tw1.connected(tw2) >= 0){
1844  flex_top = true;
1845  }else{
1846  flex_top = false;
1847  tw.edge.first = tw1.edge.first;
1848  tw.edge.second = tw2.edge.first;
1849  assert(tw.connected(tw1) >= 0 && tw.connected(tw2) >= 0);
1850  }
1851 
1852  DirectedEdge<index_t> diag;
1853  if(flex_top && !flex_bottom){
1854  // Choose the diagonal which is the preferred one for the bottom wedge
1855  diag.edge.first = bw.edge.first;
1856  diag.edge.second = bw.edge.second;
1857  }else if(!flex_top && flex_bottom){
1858  // Choose the diagonal which is the preferred one for the top wedge
1859  diag.edge.first = tw.edge.first;
1860  diag.edge.second = tw.edge.second;
1861  }else{
1862  if(flex_top && flex_bottom){
1863  // Choose the shortest diagonal
1864  real_t ldiag1 = _mesh->calc_edge_length(tl->id, br->id);
1865  real_t ldiag2 = _mesh->calc_edge_length(bl->id, tr->id);
1866 
1867  if(ldiag1 < ldiag2){
1868  diag.edge.first = tl->id;
1869  diag.edge.second = br->id;
1870  }else{
1871  diag.edge.first = bl->id;
1872  diag.edge.second = tr->id;
1873  }
1874  }else{
1875  // If we reach this point, it means we are not
1876  // flexible in any of the diagonals. If we are
1877  // lucky enough and bw==tw, then diag=bw.
1878  if(bw.contains(tw.edge.first) && bw.contains(tw.edge.second)){
1879  diag.edge.first = bw.edge.first;
1880  diag.edge.second = bw.edge.second;
1881  }else{
1882  // Choose the shortest diagonal
1883  real_t ldiag1 = _mesh->calc_edge_length(tl->id, br->id);
1884  real_t ldiag2 = _mesh->calc_edge_length(bl->id, tr->id);
1885 
1886  if(ldiag1 < ldiag2){
1887  diag.edge.first = tl->id;
1888  diag.edge.second = br->id;
1889  }else{
1890  diag.edge.first = bl->id;
1891  diag.edge.second = tr->id;
1892  }
1893  }
1894  }
1895  }
1896 
1897  def_ops->addNN(diag.edge.first, diag.edge.second, tid);
1898  def_ops->addNN(diag.edge.second, diag.edge.first, tid);
1899 
1900  // At this point, we have identified the wedges and how their sides
1901  // have been split, so we can proceed to the actual refinement.
1902  index_t top_triangle[3];
1903  index_t bottom_triangle[3];
1904  int bwedge[5];
1905 
1906  // Bottom wedge
1907  top_triangle[0] = tr->id;
1908  top_triangle[1] = tr->edge.second;
1909  top_triangle[2] = br->id;
1910  bottom_triangle[0] = tl->id;
1911  bottom_triangle[1] = tl->edge.second;
1912  bottom_triangle[2] = bl->id;
1913  bwedge[0] = b[bl->edge.first];
1914  bwedge[1] = b[tl->edge.first];
1915  bwedge[2] = 0;
1916  bwedge[3] = b[tl->edge.second];
1917  bwedge[4] = b[tr->edge.second];
1918  refine_wedge(top_triangle, bottom_triangle, bwedge, &diag, eid, tid);
1919 
1920  // Top wedge
1921  top_triangle[0] = tl->id;
1922  top_triangle[1] = tl->edge.first;
1923  top_triangle[2] = tr->id;
1924  bottom_triangle[0] = bl->id;
1925  bottom_triangle[1] = bl->edge.first;
1926  bottom_triangle[2] = br->id;
1927  bwedge[0] = b[tr->edge.second];
1928  bwedge[1] = b[tl->edge.second];
1929  bwedge[2] = 0;
1930  bwedge[3] = b[bl->edge.first];
1931  bwedge[4] = b[tl->edge.first];
1932  // Flip diag
1933  index_t swap = diag.edge.first;
1934  diag.edge.first = diag.edge.second;
1935  diag.edge.second = swap;
1936  refine_wedge(top_triangle, bottom_triangle, bwedge, &diag, eid, tid);
1937 
1938  // Remove parent element
1939  for(size_t j=0; j<nloc; ++j)
1940  def_ops->remNE(n[j], eid, tid);
1941  _mesh->_ENList[eid*nloc] = -1;
1942  }
1943  }
1944 
1945  void refine3D_5(std::vector< DirectedEdge<index_t> >& splitEdges, int eid, int tid){
1946  const int *n=_mesh->get_element(eid);
1947  const int *boundary=&(_mesh->boundary[eid*nloc]);
1948 
1949  boundary_t b;
1950  for(int j=0; j<nloc; ++j)
1951  b[n[j]] = boundary[j];
1952 
1953  // Find the unsplit edge
1954  int adj_cnt[] = {0, 0, 0, 0};
1955  for(int j=0; j<nloc; ++j){
1956  for(int k=0; k<5; ++k)
1957  if(splitEdges[k].contains(n[j]))
1958  ++adj_cnt[j];
1959  }
1960 
1961  // Vertices of the unsplit edge are adjacent to 2 split edges;
1962  // the other vertices are adjacent to 3 split edges.
1963  index_t ue[2];
1964  int pos=0;
1965  for(int j=0; j<nloc; ++j)
1966  if(adj_cnt[j] == 2)
1967  ue[pos++] = n[j];
1968 
1969  // Find the opposite edge
1971  for(int k=0; k<5; ++k)
1972  if(!splitEdges[k].contains(ue[0]) && !splitEdges[k].contains(ue[1])){
1973  // Swap splitEdges[k] with splitEdges[4]
1974  if(k!=4){
1975  DirectedEdge<index_t> swap = splitEdges[4];
1976  splitEdges[4] = splitEdges[k];
1977  splitEdges[k] = swap;
1978  }
1979  oe = &splitEdges[4];
1980  break;
1981  }
1982 
1983  // Like in 4(b), find tl, tr, bl and br
1984  DirectedEdge<index_t> *tr, *tl, *br, *bl;
1985  tl = &splitEdges[0];
1986 
1987  // Flip tl if necessary so that tl->edge.first is part of the unsplit edge
1988  if(oe->contains(tl->edge.first)){
1989  index_t swap = tl->edge.second;
1990  tl->edge.second = tl->edge.first;
1991  tl->edge.first = swap;
1992  }
1993 
1994  // Find top right
1995  for(int j=1; j<4; ++j){
1996  if(splitEdges[j].contains(tl->edge.first)){
1997  tr = &splitEdges[j];
1998  break;
1999  }
2000  }
2001  // Re-arrange tr so that tl->edge.first == tr->edge.first
2002  if(tr->edge.first != tl->edge.first){
2003  assert(tr->edge.second == tl->edge.first);
2004  tr->edge.second = tr->edge.first;
2005  tr->edge.first = tl->edge.first;
2006  }
2007 
2008  // Find bottom left
2009  for(int j=1; j<4; ++j){
2010  if(splitEdges[j].contains(tl->edge.second)){
2011  bl = &splitEdges[j];
2012  break;
2013  }
2014  }
2015  // Re-arrange bl so that tl->edge.second == bl->edge.second
2016  if(bl->edge.second != tl->edge.second){
2017  assert(bl->edge.first == tl->edge.second);
2018  bl->edge.first = bl->edge.second;
2019  bl->edge.second = tl->edge.second;
2020  }
2021 
2022  // Find bottom right
2023  for(int j=1; j<4; ++j){
2024  if(splitEdges[j].contains(bl->edge.first) && splitEdges[j].contains(tr->edge.second)){
2025  br = &splitEdges[j];
2026  break;
2027  }
2028  }
2029  // Re-arrange br so that bl->edge.first == br->edge.first
2030  if(br->edge.first != bl->edge.first){
2031  assert(br->edge.second == bl->edge.first);
2032  br->edge.second = br->edge.first;
2033  br->edge.first = bl->edge.first;
2034  }
2035 
2036  assert(tr->edge.second == br->edge.second);
2037  assert(oe->contains(tl->edge.second) && oe->contains(tr->edge.second));
2038 
2039  // Find how the trapezoids have been split:
2040  // 1) From tl->id to bl->edge.first or from bl->id to tl->edge.first?
2041  // 2) From tr->id to br->edge.first or from br->id to tr->edge.first?
2042  DirectedEdge<index_t> q1, q2;
2043  std::vector<index_t>::const_iterator p;
2044 
2045  p = std::find(_mesh->NNList[tl->id].begin(), _mesh->NNList[tl->id].end(), bl->edge.first);
2046  if(p != _mesh->NNList[tl->id].end()){
2047  q1.edge.first = tl->id;
2048  q1.edge.second = bl->edge.first;
2049  }else{
2050  q1.edge.first = bl->id;
2051  q1.edge.second = tl->edge.first;
2052  }
2053 
2054  p = std::find(_mesh->NNList[tr->id].begin(), _mesh->NNList[tr->id].end(), br->edge.first);
2055  if(p != _mesh->NNList[tr->id].end()){
2056  q2.edge.first = tr->id;
2057  q2.edge.second = br->edge.first;
2058  }else{
2059  q2.edge.first = br->id;
2060  q2.edge.second = tr->edge.first;
2061  }
2062 
2063  DirectedEdge<index_t> diag, cross_diag;
2064  if(q1.connected(q2) >= 0){
2065  // We are flexible in choosing how the third quadrilateral
2066  // will be split and we will choose the shortest diagonal.
2067  real_t ldiag1 = _mesh->calc_edge_length(tl->id, br->id);
2068  real_t ldiag2 = _mesh->calc_edge_length(bl->id, tr->id);
2069 
2070  if(ldiag1 < ldiag2){
2071  diag.edge.first = br->id;
2072  diag.edge.second = tl->id;
2073  cross_diag.edge.first = tr->id;
2074  cross_diag.edge.second = bl->id;
2075  }else{
2076  diag.edge.first = bl->id;
2077  diag.edge.second = tr->id;
2078  cross_diag.edge.first = tl->id;
2079  cross_diag.edge.second = br->id;
2080  }
2081  }else{
2082  // We will choose the diagonal which leads to a 1:3 wedge refinement.
2083  if(q1.edge.first == bl->id){
2084  assert(q2.edge.first == tr->id);
2085  diag.edge.first = q1.edge.first;
2086  diag.edge.second = q2.edge.first;
2087  }else{
2088  assert(q2.edge.first == br->id);
2089  diag.edge.first = q2.edge.first;
2090  diag.edge.second = q1.edge.first;
2091  }
2092  assert((diag.edge.first==bl->id && diag.edge.second==tr->id)
2093  || (diag.edge.first==br->id && diag.edge.second==tl->id));
2094 
2095  cross_diag.edge.first = (diag.edge.second == tr->id ? tl->id : tr->id);
2096  cross_diag.edge.second = (diag.edge.first == br->id ? bl->id : br->id);
2097  assert((cross_diag.edge.first==tl->id && cross_diag.edge.second==br->id)
2098  || (cross_diag.edge.first==tr->id && cross_diag.edge.second==bl->id));
2099  }
2100 
2101  index_t bottom_triangle[] = {br->id, br->edge.first, bl->id};
2102  index_t top_triangle[] = {tr->id, tr->edge.first, tl->id};
2103  int bwedge[] = {b[bl->edge.second], b[br->edge.second], 0, b[bl->edge.first], b[tl->edge.first]};
2104  refine_wedge(top_triangle, bottom_triangle, bwedge, &diag, eid, tid);
2105 
2106  const int ele0[] = {tl->edge.second, bl->id, tl->id, oe->id};
2107  const int ele1[] = {tr->edge.second, tr->id, br->id, oe->id};
2108  const int ele2[] = {diag.edge.first, cross_diag.edge.first, diag.edge.second, oe->id};
2109  const int ele3[] = {diag.edge.first, diag.edge.second, cross_diag.edge.second, oe->id};
2110 
2111  const int ele0_boundary[] = {0, b[bl->edge.first], b[tl->edge.first], b[tr->edge.second]};
2112  const int ele1_boundary[] = {0, b[tl->edge.first], b[bl->edge.first], b[tl->edge.second]};
2113  const int ele2_boundary[] = {b[bl->edge.first], 0, 0, 0};
2114  const int ele3_boundary[] = {0, b[tl->edge.first], 0, 0};
2115 
2116  index_t ele1ID, ele2ID, ele3ID;
2117  ele1ID = splitCnt[tid];
2118  ele2ID = ele1ID+1;
2119  ele3ID = ele1ID+2;
2120 
2121  def_ops->addNN(diag.edge.first, diag.edge.second, tid);
2122  def_ops->addNN(diag.edge.second, diag.edge.first, tid);
2123 
2124  def_ops->remNE(tr->edge.first, eid, tid);
2125  def_ops->remNE(br->edge.first, eid, tid);
2126  def_ops->remNE(tr->edge.second, eid, tid);
2127 
2128  def_ops->addNE(tl->id, eid, tid);
2129  def_ops->addNE(bl->id, eid, tid);
2130  def_ops->addNE(oe->id, eid, tid);
2131 
2132  def_ops->addNE_fix(tr->edge.second, ele1ID, tid);
2133  def_ops->addNE_fix(tr->id, ele1ID, tid);
2134  def_ops->addNE_fix(br->id, ele1ID, tid);
2135  def_ops->addNE_fix(oe->id, ele1ID, tid);
2136 
2137  def_ops->addNE_fix(diag.edge.first, ele2ID, tid);
2138  def_ops->addNE_fix(diag.edge.second, ele2ID, tid);
2139  def_ops->addNE_fix(cross_diag.edge.first, ele2ID, tid);
2140  def_ops->addNE_fix(oe->id, ele2ID, tid);
2141 
2142  def_ops->addNE_fix(diag.edge.first, ele3ID, tid);
2143  def_ops->addNE_fix(diag.edge.second, ele3ID, tid);
2144  def_ops->addNE_fix(cross_diag.edge.second, ele3ID, tid);
2145  def_ops->addNE_fix(oe->id, ele3ID, tid);
2146 
2147  replace_element(eid, ele0, ele0_boundary);
2148  append_element(ele1, ele1_boundary, tid);
2149  append_element(ele2, ele2_boundary, tid);
2150  append_element(ele3, ele3_boundary, tid);
2151  splitCnt[tid] += 3;
2152  }
2153 
2154  void refine3D_6(std::vector< DirectedEdge<index_t> >& splitEdges, int eid, int tid){
2155  const int *n=_mesh->get_element(eid);
2156  const int *boundary=&(_mesh->boundary[eid*nloc]);
2157 
2158  boundary_t b;
2159  for(int j=0; j<nloc; ++j)
2160  b[n[j]] = boundary[j];
2161 
2162  /*
2163  * There is an internal edge in this case. We choose the shortest among:
2164  * a) newVertex[0] - newVertex[5]
2165  * b) newVertex[1] - newVertex[4]
2166  * c) newVertex[2] - newVertex[3]
2167  */
2168 
2169  real_t ldiag0 = _mesh->calc_edge_length(splitEdges[0].id, splitEdges[5].id);
2170  real_t ldiag1 = _mesh->calc_edge_length(splitEdges[1].id, splitEdges[4].id);
2171  real_t ldiag2 = _mesh->calc_edge_length(splitEdges[2].id, splitEdges[3].id);
2172 
2173  std::vector<index_t> internal(2);
2174  std::vector<index_t> opposite(4);
2175  std::vector<int> bndr(4);
2176  if(ldiag0 < ldiag1 && ldiag0 < ldiag2){
2177  // 0-5
2178  internal[0] = splitEdges[5].id;
2179  internal[1] = splitEdges[0].id;
2180  opposite[0] = splitEdges[3].id;
2181  opposite[1] = splitEdges[4].id;
2182  opposite[2] = splitEdges[2].id;
2183  opposite[3] = splitEdges[1].id;
2184  bndr[0] = boundary[0];
2185  bndr[1] = boundary[2];
2186  bndr[2] = boundary[1];
2187  bndr[3] = boundary[3];
2188  }else if(ldiag1 < ldiag2){
2189  // 1-4
2190  internal[0] = splitEdges[1].id;
2191  internal[1] = splitEdges[4].id;
2192  opposite[0] = splitEdges[0].id;
2193  opposite[1] = splitEdges[3].id;
2194  opposite[2] = splitEdges[5].id;
2195  opposite[3] = splitEdges[2].id;
2196  bndr[0] = boundary[3];
2197  bndr[1] = boundary[0];
2198  bndr[2] = boundary[1];
2199  bndr[3] = boundary[2];
2200  }else{
2201  // 2-3
2202  internal[0] = splitEdges[3].id;
2203  internal[1] = splitEdges[2].id;
2204  opposite[0] = splitEdges[4].id;
2205  opposite[1] = splitEdges[5].id;
2206  opposite[2] = splitEdges[1].id;
2207  opposite[3] = splitEdges[0].id;
2208  bndr[0] = boundary[0];
2209  bndr[1] = boundary[1];
2210  bndr[2] = boundary[3];
2211  bndr[3] = boundary[2];
2212  }
2213 
2214  const int ele0[] = {n[0], splitEdges[0].id, splitEdges[1].id, splitEdges[2].id};
2215  const int ele1[] = {n[1], splitEdges[3].id, splitEdges[0].id, splitEdges[4].id};
2216  const int ele2[] = {n[2], splitEdges[1].id, splitEdges[3].id, splitEdges[5].id};
2217  const int ele3[] = {n[3], splitEdges[2].id, splitEdges[4].id, splitEdges[5].id};
2218  const int ele4[] = {internal[0], opposite[0], opposite[1], internal[1]};
2219  const int ele5[] = {internal[0], opposite[1], opposite[2], internal[1]};
2220  const int ele6[] = {internal[0], opposite[2], opposite[3], internal[1]};
2221  const int ele7[] = {internal[0], opposite[3], opposite[0], internal[1]};
2222 
2223  const int ele0_boundary[] = {0, boundary[1], boundary[2], boundary[3]};
2224  const int ele1_boundary[] = {0, boundary[2], boundary[0], boundary[3]};
2225  const int ele2_boundary[] = {0, boundary[0], boundary[1], boundary[3]};
2226  const int ele3_boundary[] = {0, boundary[0], boundary[1], boundary[2]};
2227  const int ele4_boundary[] = {0, 0, 0, bndr[0]};
2228  const int ele5_boundary[] = {bndr[1], 0, 0, 0};
2229  const int ele6_boundary[] = {0, 0, 0, bndr[2]};
2230  const int ele7_boundary[] = {bndr[3], 0, 0, 0};
2231 
2232  index_t ele1ID, ele2ID, ele3ID, ele4ID, ele5ID, ele6ID, ele7ID;
2233  ele1ID = splitCnt[tid];
2234  ele2ID = ele1ID+1;
2235  ele3ID = ele1ID+2;
2236  ele4ID = ele1ID+3;
2237  ele5ID = ele1ID+4;
2238  ele6ID = ele1ID+5;
2239  ele7ID = ele1ID+6;
2240 
2241  def_ops->addNN(internal[0], internal[1], tid);
2242  def_ops->addNN(internal[1], internal[0], tid);
2243 
2244  def_ops->remNE(n[1], eid, tid);
2245  def_ops->addNE_fix(n[1], ele1ID, tid);
2246  def_ops->remNE(n[2], eid, tid);
2247  def_ops->addNE_fix(n[2], ele2ID, tid);
2248  def_ops->remNE(n[3], eid, tid);
2249  def_ops->addNE_fix(n[3], ele3ID, tid);
2250 
2251  def_ops->addNE(splitEdges[0].id, eid, tid);
2252  def_ops->addNE_fix(splitEdges[0].id, ele1ID, tid);
2253 
2254  def_ops->addNE(splitEdges[1].id, eid, tid);
2255  def_ops->addNE_fix(splitEdges[1].id, ele2ID, tid);
2256 
2257  def_ops->addNE(splitEdges[2].id, eid, tid);
2258  def_ops->addNE_fix(splitEdges[2].id, ele3ID, tid);
2259 
2260  def_ops->addNE_fix(splitEdges[3].id, ele1ID, tid);
2261  def_ops->addNE_fix(splitEdges[3].id, ele2ID, tid);
2262 
2263  def_ops->addNE_fix(splitEdges[4].id, ele1ID, tid);
2264  def_ops->addNE_fix(splitEdges[4].id, ele3ID, tid);
2265 
2266  def_ops->addNE_fix(splitEdges[5].id, ele2ID, tid);
2267  def_ops->addNE_fix(splitEdges[5].id, ele3ID, tid);
2268 
2269  def_ops->addNE_fix(internal[0], ele4ID, tid);
2270  def_ops->addNE_fix(internal[0], ele5ID, tid);
2271  def_ops->addNE_fix(internal[0], ele6ID, tid);
2272  def_ops->addNE_fix(internal[0], ele7ID, tid);
2273  def_ops->addNE_fix(internal[1], ele4ID, tid);
2274  def_ops->addNE_fix(internal[1], ele5ID, tid);
2275  def_ops->addNE_fix(internal[1], ele6ID, tid);
2276  def_ops->addNE_fix(internal[1], ele7ID, tid);
2277 
2278  def_ops->addNE_fix(opposite[0], ele4ID, tid);
2279  def_ops->addNE_fix(opposite[1], ele4ID, tid);
2280  def_ops->addNE_fix(opposite[1], ele5ID, tid);
2281  def_ops->addNE_fix(opposite[2], ele5ID, tid);
2282  def_ops->addNE_fix(opposite[2], ele6ID, tid);
2283  def_ops->addNE_fix(opposite[3], ele6ID, tid);
2284  def_ops->addNE_fix(opposite[3], ele7ID, tid);
2285  def_ops->addNE_fix(opposite[0], ele7ID, tid);
2286 
2287  replace_element(eid, ele0, ele0_boundary);
2288  append_element(ele1, ele1_boundary, tid);
2289  append_element(ele2, ele2_boundary, tid);
2290  append_element(ele3, ele3_boundary, tid);
2291  append_element(ele4, ele4_boundary, tid);
2292  append_element(ele5, ele5_boundary, tid);
2293  append_element(ele6, ele6_boundary, tid);
2294  append_element(ele7, ele7_boundary, tid);
2295  splitCnt[tid] += 7;
2296  }
2297 
2298  void refine_wedge(const index_t top_triangle[], const index_t bottom_triangle[],
2299  const int bndr[], DirectedEdge<index_t>* third_diag, int eid, int tid){
2300  /*
2301  * bndr[] must contain the boundary values for each side of the wedge:
2302  * bndr[0], bndr[1] and bndr[2]: Boundary values of Side0, Side1 and Side2
2303  * bndr[3]: Boundary value of top triangle
2304  * bndr[4]: Boundary value of bottom triangle
2305  */
2306 
2307  /*
2308  * third_diag is used optionally if we need to define manually what the
2309  * third diagonal is (used in cases 4(b) and 5). It needs to be a directed
2310  * edge from the bottom triangle to the top triangle and be on Side2.
2311  */
2312  if(third_diag != NULL){
2313  for(int j=0; j<3; ++j){
2314  if(third_diag->edge.first == bottom_triangle[j] || third_diag->edge.second == top_triangle[j]){
2315  break;
2316  }else if(third_diag->edge.first == top_triangle[j] || third_diag->edge.second == bottom_triangle[j]){
2317  index_t swap = third_diag->edge.first;
2318  third_diag->edge.first = third_diag->edge.second;
2319  third_diag->edge.second = swap;
2320  break;
2321  }
2322  }
2323  assert((third_diag->edge.first == bottom_triangle[2] && third_diag->edge.second == top_triangle[0]) ||
2324  (third_diag->edge.first == bottom_triangle[0] && third_diag->edge.second == top_triangle[2]));
2325  }
2326 
2327  /*
2328  * For each quadrilateral side of the wedge find
2329  * the diagonal which has bisected the wedge side.
2330  * Side0: bottom[0] - bottom[1] - top[1] - top[0]
2331  * Side1: bottom[1] - bottom[2] - top[2] - top[1]
2332  * Side2: bottom[2] - bottom[0] - top[0] - top[2]
2333  */
2334  std::vector< DirectedEdge<index_t> > diagonals, ghostDiagonals;
2335  for(int j=0; j<3; ++j){
2336  bool fwd_connected;
2337  if(j==2 && third_diag != NULL){
2338  fwd_connected = (bottom_triangle[j] == third_diag->edge.first ? true : false);
2339  }else{
2340  std::vector<index_t>::const_iterator p = std::find(_mesh->NNList[bottom_triangle[j]].begin(),
2341  _mesh->NNList[bottom_triangle[j]].end(), top_triangle[(j+1)%3]);
2342  fwd_connected = (p != _mesh->NNList[bottom_triangle[j]].end() ? true : false);
2343  }
2344  if(fwd_connected){
2345  diagonals.push_back(DirectedEdge<index_t>(bottom_triangle[j], top_triangle[(j+1)%3]));
2346  ghostDiagonals.push_back(DirectedEdge<index_t>(bottom_triangle[(j+1)%3], top_triangle[j]));
2347  }else{
2348  diagonals.push_back(DirectedEdge<index_t>(bottom_triangle[(j+1)%3], top_triangle[j]));
2349  ghostDiagonals.push_back(DirectedEdge<index_t>(bottom_triangle[j], top_triangle[(j+1)%3]));
2350  }
2351  }
2352 
2353  // Determine how the wedge will be split
2354  std::vector<index_t> diag_shared;
2355  for(int j=0;j<3;j++){
2356  index_t nid = diagonals[j].connected(diagonals[(j+1)%3]);
2357  if(nid>=0)
2358  diag_shared.push_back(nid);
2359  }
2360 
2361  if(!diag_shared.empty()){
2362  /*
2363  ***************
2364  * Case 1-to-3 *
2365  ***************
2366  */
2367 
2368  assert(diag_shared.size() == 2);
2369 
2370  // Here we can subdivide the wedge into 3 tetrahedra.
2371 
2372  // Find the "middle" diagonal, i.e. the one which
2373  // consists of the two vertices in diag_shared.
2374  int middle;
2375  index_t non_shared_top=-1, non_shared_bottom=-1;
2376  for(int j=0; j<3; ++j){
2377  if(diagonals[j].contains(diag_shared[0]) && diagonals[j].contains(diag_shared[1])){
2378  middle = j;
2379  for(int k=0; k<2; ++k){
2380  if(diagonals[(j+k+1)%3].edge.first != diag_shared[0] && diagonals[(j+k+1)%3].edge.first != diag_shared[1])
2381  non_shared_bottom = diagonals[(j+k+1)%3].edge.first;
2382  else
2383  non_shared_top = diagonals[(j+k+1)%3].edge.second;
2384  }
2385  break;
2386  }
2387  }
2388  assert(non_shared_top >= 0 && non_shared_bottom >= 0);
2389 
2390  /*
2391  * 2 elements are formed by the three vertices of two connected
2392  * diagonals plus a fourth vertex which is the one vertex of top/
2393  * bottom triangle which does not belong to any diagonal.
2394  *
2395  * 1 element is formed by the four vertices of two disjoint diagonals.
2396  */
2397  index_t v_top, v_bottom;
2398 
2399  // diagonals[middle].edge.first is always one of the bottom vertices
2400  // diagonals[middle].edge.second is always one of the top vertices
2401 
2402  for(int j=0; j<3; ++j){
2403  if(top_triangle[j]!=diagonals[middle].edge.second && top_triangle[j]!=non_shared_top){
2404  v_top = top_triangle[j];
2405  assert(bottom_triangle[j] == diagonals[middle].edge.first);
2406  break;
2407  }
2408  }
2409 
2410  for(int j=0; j<3; ++j){
2411  if(bottom_triangle[j]!=diagonals[middle].edge.first && bottom_triangle[j]!=non_shared_bottom){
2412  v_bottom = bottom_triangle[j];
2413  assert(top_triangle[j] == diagonals[middle].edge.second);
2414  break;
2415  }
2416  }
2417 
2418  const int ele1[] = {diagonals[middle].edge.first, diagonals[middle].edge.second, non_shared_top, v_top};
2419  const int ele2[] = {diagonals[middle].edge.first, diagonals[middle].edge.second, v_bottom, non_shared_bottom};
2420  const int ele3[] = {diagonals[middle].edge.first, diagonals[middle].edge.second, non_shared_top, non_shared_bottom};
2421 
2422  int bv_bottom, bnsb, bfirst;
2423  for(int j=0; j<3; ++j){
2424  if(v_bottom == bottom_triangle[j]){
2425  bv_bottom = bndr[(j+1)%3];
2426  }else if(non_shared_bottom == bottom_triangle[j]){
2427  bnsb = bndr[(j+1)%3];
2428  }else{
2429  bfirst = bndr[(j+1)%3];
2430  }
2431  }
2432 
2433  const int ele1_boundary[] = {bndr[3], bv_bottom, bnsb, 0};
2434  const int ele2_boundary[] = {bfirst, bndr[4], 0, bnsb};
2435  const int ele3_boundary[] = {bfirst, bv_bottom, 0, 0};
2436 
2437  index_t ele1ID, ele2ID, ele3ID;
2438  ele1ID = splitCnt[tid];
2439  ele2ID = ele1ID+1;
2440  ele3ID = ele1ID+2;
2441 
2442  def_ops->addNE_fix(diagonals[middle].edge.first, ele1ID, tid);
2443  def_ops->addNE_fix(diagonals[middle].edge.first, ele2ID, tid);
2444  def_ops->addNE_fix(diagonals[middle].edge.first, ele3ID, tid);
2445 
2446  def_ops->addNE_fix(diagonals[middle].edge.second, ele1ID, tid);
2447  def_ops->addNE_fix(diagonals[middle].edge.second, ele2ID, tid);
2448  def_ops->addNE_fix(diagonals[middle].edge.second, ele3ID, tid);
2449 
2450  def_ops->addNE_fix(non_shared_bottom, ele2ID, tid);
2451  def_ops->addNE_fix(non_shared_bottom, ele3ID, tid);
2452 
2453  def_ops->addNE_fix(non_shared_top, ele1ID, tid);
2454  def_ops->addNE_fix(non_shared_top, ele3ID, tid);
2455 
2456  def_ops->addNE_fix(v_bottom, ele2ID, tid);
2457 
2458  def_ops->addNE_fix(v_top, ele1ID, tid);
2459 
2460  append_element(ele1, ele1_boundary, tid);
2461  append_element(ele2, ele2_boundary, tid);
2462  append_element(ele3, ele3_boundary, tid);
2463  splitCnt[tid] += 3;
2464  }else{
2465  /*
2466  ***************
2467  * Case 1-to-8 *
2468  ***************
2469  */
2470 
2471  /*
2472  * The wedge must by split into 8 tetrahedra with the introduction of
2473  * a new centroidal vertex. Each tetrahedron is formed by the three
2474  * vertices of a triangular facet (there are 8 triangular facets: 6 are
2475  * formed via the bisection of the 3 quadrilaterals of the wedge, 2 are
2476  * the top and bottom triangles and the centroidal vertex.
2477  */
2478 
2479  // Allocate space for the centroidal vertex
2480  index_t cid = pragmatic_omp_atomic_capture(&_mesh->NNodes, 1);
2481 
2482  const int ele1[] = {diagonals[0].edge.first, ghostDiagonals[0].edge.first, diagonals[0].edge.second, cid};
2483  const int ele2[] = {diagonals[0].edge.first, diagonals[0].edge.second, ghostDiagonals[0].edge.second, cid};
2484  const int ele3[] = {diagonals[1].edge.first, ghostDiagonals[1].edge.first, diagonals[1].edge.second, cid};
2485  const int ele4[] = {diagonals[1].edge.first, diagonals[1].edge.second, ghostDiagonals[1].edge.second, cid};
2486  const int ele5[] = {diagonals[2].edge.first, ghostDiagonals[2].edge.first, diagonals[2].edge.second, cid};
2487  const int ele6[] = {diagonals[2].edge.first, diagonals[2].edge.second, ghostDiagonals[2].edge.second, cid};
2488  const int ele7[] = {top_triangle[0], top_triangle[1], top_triangle[2], cid};
2489  const int ele8[] = {bottom_triangle[0], bottom_triangle[2], bottom_triangle[1], cid};
2490 
2491  const int ele1_boundary[] = {0, 0, 0, bndr[0]};
2492  const int ele2_boundary[] = {0, 0, 0, bndr[0]};
2493  const int ele3_boundary[] = {0, 0, 0, bndr[1]};
2494  const int ele4_boundary[] = {0, 0, 0, bndr[1]};
2495  const int ele5_boundary[] = {0, 0, 0, bndr[2]};
2496  const int ele6_boundary[] = {0, 0, 0, bndr[2]};
2497  const int ele7_boundary[] = {0, 0, 0, bndr[3]};
2498  const int ele8_boundary[] = {0, 0, 0, bndr[4]};
2499 
2500  index_t ele1ID, ele2ID, ele3ID, ele4ID, ele5ID, ele6ID, ele7ID, ele8ID;
2501  ele1ID = splitCnt[tid];
2502  ele2ID = ele1ID+1;
2503  ele3ID = ele1ID+2;
2504  ele4ID = ele1ID+3;
2505  ele5ID = ele1ID+4;
2506  ele6ID = ele1ID+5;
2507  ele7ID = ele1ID+6;
2508  ele8ID = ele1ID+7;
2509 
2510  for(int j=0; j<3; ++j){
2511  _mesh->NNList[cid].push_back(top_triangle[j]);
2512  _mesh->NNList[cid].push_back(bottom_triangle[j]);
2513  def_ops->addNN(top_triangle[j], cid, tid);
2514  def_ops->addNN(bottom_triangle[j], cid, tid);
2515  }
2516 
2517  def_ops->addNE_fix(cid, ele1ID, tid);
2518  def_ops->addNE_fix(cid, ele2ID, tid);
2519  def_ops->addNE_fix(cid, ele3ID, tid);
2520  def_ops->addNE_fix(cid, ele4ID, tid);
2521  def_ops->addNE_fix(cid, ele5ID, tid);
2522  def_ops->addNE_fix(cid, ele6ID, tid);
2523  def_ops->addNE_fix(cid, ele7ID, tid);
2524  def_ops->addNE_fix(cid, ele8ID, tid);
2525 
2526  def_ops->addNE_fix(bottom_triangle[0], ele8ID, tid);
2527  def_ops->addNE_fix(bottom_triangle[1], ele8ID, tid);
2528  def_ops->addNE_fix(bottom_triangle[2], ele8ID, tid);
2529 
2530  def_ops->addNE_fix(top_triangle[0], ele7ID, tid);
2531  def_ops->addNE_fix(top_triangle[1], ele7ID, tid);
2532  def_ops->addNE_fix(top_triangle[2], ele7ID, tid);
2533 
2534  def_ops->addNE_fix(diagonals[0].edge.first, ele1ID, tid);
2535  def_ops->addNE_fix(diagonals[0].edge.first, ele2ID, tid);
2536  def_ops->addNE_fix(diagonals[0].edge.second, ele1ID, tid);
2537  def_ops->addNE_fix(diagonals[0].edge.second, ele2ID, tid);
2538  def_ops->addNE_fix(ghostDiagonals[0].edge.first, ele1ID, tid);
2539  def_ops->addNE_fix(ghostDiagonals[0].edge.second, ele2ID, tid);
2540 
2541  def_ops->addNE_fix(diagonals[1].edge.first, ele3ID, tid);
2542  def_ops->addNE_fix(diagonals[1].edge.first, ele4ID, tid);
2543  def_ops->addNE_fix(diagonals[1].edge.second, ele3ID, tid);
2544  def_ops->addNE_fix(diagonals[1].edge.second, ele4ID, tid);
2545  def_ops->addNE_fix(ghostDiagonals[1].edge.first, ele3ID, tid);
2546  def_ops->addNE_fix(ghostDiagonals[1].edge.second, ele4ID, tid);
2547 
2548  def_ops->addNE_fix(diagonals[2].edge.first, ele5ID, tid);
2549  def_ops->addNE_fix(diagonals[2].edge.first, ele6ID, tid);
2550  def_ops->addNE_fix(diagonals[2].edge.second, ele5ID, tid);
2551  def_ops->addNE_fix(diagonals[2].edge.second, ele6ID, tid);
2552  def_ops->addNE_fix(ghostDiagonals[2].edge.first, ele5ID, tid);
2553  def_ops->addNE_fix(ghostDiagonals[2].edge.second, ele6ID, tid);
2554 
2555  // Sort all 6 vertices of the wedge by their coordinates.
2556  // Need to do so to enforce consistency across MPI processes.
2557  std::map<Coords_t, index_t> coords_map;
2558  for(int j=0; j<3; ++j){
2559  Coords_t cb(_mesh->get_coords(bottom_triangle[j]));
2560  coords_map[cb] = bottom_triangle[j];
2561  Coords_t ct(_mesh->get_coords(top_triangle[j]));
2562  coords_map[ct] = top_triangle[j];
2563  }
2564 
2565  real_t nc[] = {0.0, 0.0, 0.0}; // new coordinates
2566  double nm[msize]; // new metric
2567  const index_t* n = _mesh->get_element(eid);
2568 
2569  {
2570  // Calculate the coordinates of the centroidal vertex.
2571  // We start with a temporary location at the euclidean barycentre of the wedge.
2572  for(typename std::map<Coords_t, index_t>::const_iterator it=coords_map.begin(); it!=coords_map.end(); ++it){
2573  const real_t *x = _mesh->get_coords(it->second);
2574  for(int j=0; j<ndims; ++j)
2575  nc[j] += x[j];
2576  }
2577  for(int j=0; j<ndims; ++j){
2578  nc[j] /= coords_map.size();
2579  _mesh->_coords[cid*ndims+j] = nc[j];
2580  }
2581 
2582  // Interpolate metric at temporary location using the parent element's basis functions
2583  std::map<Coords_t, index_t> parent_coords;
2584  for(int j=0; j<nloc; ++j){
2585  Coords_t cn(_mesh->get_coords(n[j]));
2586  parent_coords[cn] = n[j];
2587  }
2588 
2589  std::vector<const real_t *> x;
2590  std::vector<index_t> sorted_n;
2591  for(typename std::map<Coords_t, index_t>::const_iterator it=parent_coords.begin(); it!=parent_coords.end(); ++it){
2592  x.push_back(_mesh->get_coords(it->second));
2593  sorted_n.push_back(it->second);
2594  }
2595 
2596  // Order of parent element's vertices has changed, so volume might be negative.
2597  real_t L = fabs(property->volume(x[0], x[1], x[2], x[3]));
2598 
2599  real_t ll[4];
2600  ll[0] = fabs(property->volume(nc , x[1], x[2], x[3])/L);
2601  ll[1] = fabs(property->volume(x[0], nc , x[2], x[3])/L);
2602  ll[2] = fabs(property->volume(x[0], x[1], nc , x[3])/L);
2603  ll[3] = fabs(property->volume(x[0], x[1], x[2], nc )/L);
2604 
2605  for(int i=0; i<msize; i++){
2606  nm[i] = ll[0] * _mesh->metric[sorted_n[0]*msize+i]+
2607  ll[1] * _mesh->metric[sorted_n[1]*msize+i]+
2608  ll[2] * _mesh->metric[sorted_n[2]*msize+i]+
2609  ll[3] * _mesh->metric[sorted_n[3]*msize+i];
2610  _mesh->metric[cid*msize+i] = nm[i];
2611  }
2612  }
2613 
2614  // Use the 3D laplacian smoothing kernel to find the barycentre of the wedge in metric space.
2615  Eigen::Matrix<real_t, Eigen::Dynamic, Eigen::Dynamic> A =
2616  Eigen::Matrix<real_t, Eigen::Dynamic, Eigen::Dynamic>::Zero(3, 3);
2617  Eigen::Matrix<real_t, Eigen::Dynamic, 1> q = Eigen::Matrix<real_t, Eigen::Dynamic, 1>::Zero(3);
2618 
2619  for(typename std::map<Coords_t, index_t>::const_iterator it=coords_map.begin(); it!=coords_map.end(); ++it){
2620  const real_t *il = _mesh->get_coords(it->second);
2621  real_t x = il[0]-nc[0];
2622  real_t y = il[1]-nc[1];
2623  real_t z = il[2]-nc[2];
2624 
2625  q[0] += nm[0]*x + nm[1]*y + nm[2]*z;
2626  q[1] += nm[1]*x + nm[3]*y + nm[4]*z;
2627  q[2] += nm[2]*x + nm[4]*y + nm[5]*z;
2628 
2629  A[0] += nm[0]; A[1] += nm[1]; A[2] += nm[2];
2630  A[4] += nm[3]; A[5] += nm[4];
2631  A[8] += nm[5];
2632  }
2633  A[3] = A[1];
2634  A[6] = A[2];
2635  A[7] = A[5];
2636 
2637  // Want to solve the system Ap=q to find the new position, p.
2638  Eigen::Matrix<real_t, Eigen::Dynamic, 1> b = Eigen::Matrix<real_t, Eigen::Dynamic, 1>::Zero(3);
2639  A.svd().solve(q, &b);
2640 
2641  for(int i=0; i<3; ++i){
2642  nc[i] += b[i];
2643  }
2644 
2645  // Interpolate metric at new location
2646  real_t l[]={-1, -1, -1, -1};
2647  real_t tol=-1;
2648  std::vector<index_t> sorted_best_e(nloc);
2649  const index_t *welements[] = {ele1, ele2, ele3, ele4, ele5, ele6, ele7, ele8};
2650 
2651  for(int ele=0; ele<8; ++ele){
2652  std::map<Coords_t, index_t> local_coords;
2653  for(int j=0; j<nloc; ++j){
2654  Coords_t cl(_mesh->get_coords(welements[ele][j]));
2655  local_coords[cl] = welements[ele][j];
2656  }
2657 
2658  std::vector<const real_t *> x;
2659  std::vector<index_t> sorted_n;
2660  for(typename std::map<Coords_t, index_t>::const_iterator it=local_coords.begin(); it!=local_coords.end(); ++it){
2661  x.push_back(_mesh->get_coords(it->second));
2662  sorted_n.push_back(it->second);
2663  }
2664 
2665  real_t L = fabs(property->volume(x[0], x[1], x[2], x[3]));
2666 
2667  assert(L>0);
2668 
2669  real_t ll[4];
2670  ll[0] = fabs(property->volume(nc , x[1], x[2], x[3])/L);
2671  ll[1] = fabs(property->volume(x[0], nc , x[2], x[3])/L);
2672  ll[2] = fabs(property->volume(x[0], x[1], nc , x[3])/L);
2673  ll[3] = fabs(property->volume(x[0], x[1], x[2], nc )/L);
2674 
2675  real_t min_l = std::min(std::min(ll[0], ll[1]), std::min(ll[2], ll[3]));
2676  if(min_l>tol){
2677  tol = min_l;
2678  for(int j=0;j<nloc;++j){
2679  l[j] = ll[j];
2680  sorted_best_e[j] = sorted_n[j];
2681  }
2682  }
2683  }
2684 
2685  for(int i=0; i<ndims; ++i){
2686  _mesh->_coords[cid*ndims+i] = nc[i];
2687  }
2688 
2689  for(int i=0; i<msize; ++i)
2690  _mesh->metric[cid*msize+i] = l[0]*_mesh->metric[sorted_best_e[0]*msize+i]+
2691  l[1]*_mesh->metric[sorted_best_e[1]*msize+i]+
2692  l[2]*_mesh->metric[sorted_best_e[2]*msize+i]+
2693  l[3]*_mesh->metric[sorted_best_e[3]*msize+i];
2694 
2695  append_element(ele1, ele1_boundary, tid);
2696  append_element(ele2, ele2_boundary, tid);
2697  append_element(ele3, ele3_boundary, tid);
2698  append_element(ele4, ele4_boundary, tid);
2699  append_element(ele5, ele5_boundary, tid);
2700  append_element(ele6, ele6_boundary, tid);
2701  append_element(ele7, ele7_boundary, tid);
2702  append_element(ele8, ele8_boundary, tid);
2703  splitCnt[tid] += 8;
2704 
2705  // Finally, assign a gnn and owner
2706  if(nprocs == 1){
2707  _mesh->node_owner[cid] = 0;
2708  _mesh->lnn2gnn[cid] = cid;
2709  }else{
2710  int owner = nprocs;
2711  for(int j=0; j<nloc; ++j)
2712  owner = std::min(owner, _mesh->node_owner[n[j]]);
2713 
2714  _mesh->node_owner[cid] = owner;
2715 
2716  if(_mesh->node_owner[cid] != rank){
2717  // Vertex is owned by another MPI process, so prepare to update recv and recv_halo.
2718  Wedge wedge(cid, _mesh->get_coords(cid));
2719 #pragma omp critical
2720  cidRecv_additional[_mesh->node_owner[cid]].insert(wedge);
2721  }else{
2722  // Vertex is owned by *this* MPI process, so check whether it is visible by other MPI processes.
2723  // The latter is true only if all vertices of the original element were halo vertices.
2724  if(_mesh->is_halo_node(n[0]) && _mesh->is_halo_node(n[1]) && _mesh->is_halo_node(n[2]) && _mesh->is_halo_node(n[3])){
2725  // Find which processes see this vertex
2726  std::set<int> processes;
2727  for(int j=0; j<nloc; ++j)
2728  processes.insert(_mesh->node_owner[n[j]]);
2729 
2730  processes.erase(rank);
2731 
2732  Wedge wedge(cid, _mesh->get_coords(cid));
2733  for(typename std::set<int>::const_iterator proc=processes.begin(); proc!=processes.end(); ++proc){
2734 #pragma omp critical
2735  cidSend_additional[*proc].insert(wedge);
2736  }
2737  }
2738 
2739  // Finally, assign a gnn
2740  _mesh->lnn2gnn[cid] = _mesh->gnn_offset+cid;
2741  }
2742  }
2743  }
2744  }
2745 
2746  inline void append_element(const index_t *elem, const int *boundary, const size_t tid){
2747  if(dim==3){
2748  // Fix orientation of new element.
2749  const real_t *x0 = &(_mesh->_coords[elem[0]*ndims]);
2750  const real_t *x1 = &(_mesh->_coords[elem[1]*ndims]);
2751  const real_t *x2 = &(_mesh->_coords[elem[2]*ndims]);
2752  const real_t *x3 = &(_mesh->_coords[elem[3]*ndims]);
2753 
2754  real_t av = property->volume(x0, x1, x2, x3);
2755 
2756  if(av<0){
2757  index_t *e = const_cast<index_t *>(elem);
2758  int *b = const_cast<int *>(boundary);
2759 
2760  // Flip element
2761  index_t e0 = e[0];
2762  e[0] = e[1];
2763  e[1] = e0;
2764 
2765  // and boundary
2766  int b0 = b[0];
2767  b[0] = b[1];
2768  b[1] = b0;
2769  }
2770  }
2771 
2772  for(size_t i=0; i<nloc; ++i){
2773  newElements[tid].push_back(elem[i]);
2774  newBoundaries[tid].push_back(boundary[i]);
2775  }
2776  }
2777 
2778  inline void replace_element(const index_t eid, const index_t *n, const int *boundary){
2779  if(dim==3){
2780  // Fix orientation of new element.
2781  const real_t *x0 = &(_mesh->_coords[n[0]*ndims]);
2782  const real_t *x1 = &(_mesh->_coords[n[1]*ndims]);
2783  const real_t *x2 = &(_mesh->_coords[n[2]*ndims]);
2784  const real_t *x3 = &(_mesh->_coords[n[3]*ndims]);
2785 
2786  real_t av = property->volume(x0, x1, x2, x3);
2787 
2788  if(av<0){
2789  index_t *e = const_cast<index_t *>(n);
2790  int *b = const_cast<int *>(boundary);
2791 
2792  // Flip element
2793  index_t e0 = e[0];
2794  e[0] = e[1];
2795  e[1] = e0;
2796 
2797  // and boundary
2798  int b0 = b[0];
2799  b[0] = b[1];
2800  b[1] = b0;
2801  }
2802  }
2803 
2804  for(size_t i=0;i<nloc;i++){
2805  _mesh->_ENList[eid*nloc+i]=n[i];
2806  _mesh->boundary[eid*nloc+i]=boundary[i];
2807  }
2808  }
2809 
2810  inline size_t edgeNumber(index_t eid, index_t v1, index_t v2) const{
2811  const int *n=_mesh->get_element(eid);
2812 
2813  if(dim==2){
2814  /* In 2D:
2815  * Edge 0 is the edge (n[1],n[2]).
2816  * Edge 1 is the edge (n[0],n[2]).
2817  * Edge 2 is the edge (n[0],n[1]).
2818  */
2819  if(n[1]==v1 || n[1]==v2){
2820  if(n[2]==v1 || n[2]==v2)
2821  return 0;
2822  else
2823  return 2;
2824  }
2825  else
2826  return 1;
2827  }else{ //if(dim=3)
2828  /*
2829  * In 3D:
2830  * Edge 0 is the edge (n[0],n[1]).
2831  * Edge 1 is the edge (n[0],n[2]).
2832  * Edge 2 is the edge (n[0],n[3]).
2833  * Edge 3 is the edge (n[1],n[2]).
2834  * Edge 4 is the edge (n[1],n[3]).
2835  * Edge 5 is the edge (n[2],n[3]).
2836  */
2837  if(n[0]==v1 || n[0]==v2){
2838  if(n[1]==v1 || n[1]==v2)
2839  return 0;
2840  else if(n[2]==v1 || n[2]==v2)
2841  return 1;
2842  else
2843  return 2;
2844  }else if(n[1]==v1 || n[1]==v2){
2845  if(n[2]==v1 || n[2]==v2)
2846  return 3;
2847  else
2848  return 4;
2849  }else
2850  return 5;
2851  }
2852  }
2853 
2854  // Struct used for sorting vertices by their coordinates. It's
2855  // meant to be used by the 1:8 wedge refinement code to enforce
2856  // consistent order of floating point arithmetic across MPI processes.
2857  struct Coords_t{
2858  real_t coords[3];
2859 
2860  Coords_t(const real_t *x){
2861  coords[0] = x[0];
2862  coords[1] = x[1];
2863  coords[2] = x[2];
2864  }
2865 
2867  bool operator<(const Coords_t& in) const{
2868  bool isLess;
2869 
2870  for(int i=0; i<3; ++i){
2871  if(coords[i] < in.coords[i]){
2872  isLess=true;
2873  break;
2874  }else if(coords[i] > in.coords[i]){
2875  isLess = false;
2876  break;
2877  }
2878  }
2879 
2880  return isLess;
2881  }
2882  };
2883 
2884  // Struct containing gnn's of the six vertices comprising a wedge. It is only
2885  // to be used for consistent sorting of centroidal vertices across MPI processes.
2886  struct Wedge{
2887  const Coords_t coords;
2888  const index_t cid;
2889 
2890  Wedge(const index_t id, const real_t *cid_coords) : coords(cid_coords), cid(id){}
2891 
2893  bool operator<(const Wedge& in) const{
2894  return coords < in.coords;
2895  }
2896  };
2897 
2898  std::vector< std::vector< DirectedEdge<index_t> > > newVertices;
2899  std::vector< std::vector<real_t> > newCoords;
2900  std::vector< std::vector<double> > newMetric;
2901  std::vector< std::vector<index_t> > newElements;
2902  std::vector< std::vector<int> > newBoundaries;
2903  std::vector<index_t> new_vertices_per_element;
2904 
2905  std::vector<size_t> threadIdx, splitCnt;
2906  std::vector< DirectedEdge<index_t> > allNewVertices;
2907  std::vector< std::set<Wedge> > cidRecv_additional, cidSend_additional;
2908 
2909  DeferredOperations<real_t>* def_ops;
2910  static const int defOp_scaling_factor = 32;
2911 
2912  Mesh<real_t> *_mesh;
2913  ElementProperty<real_t> *property;
2914 
2915  static const size_t ndims=dim, nloc=(dim+1), msize=(dim==2?3:6), nedge=(dim==2?3:6);
2916  int nprocs, rank, nthreads;
2917 
2918  void (Refine<real_t,dim>::* refineMode2D[nedge])(const index_t *, int, int);
2919  void (Refine<real_t,dim>::* refineMode3D[nedge])(std::vector< DirectedEdge<index_t> >&, int, int);
2920 };
2921 
2922 
2923 #endif
void refine(real_t L_max)
Definition: Refine.h:132
Refine(Mesh< real_t > &mesh)
Default constructor.
Definition: Refine.h:59
int pragmatic_process_id(MPI_Comm comm)
bool contains(index_t nid) const
Definition: Edge.h:169
int index_t
Calculates a number of element properties.
Manages mesh data.
Definition: Mesh.h:70
int pragmatic_nthreads()
index_t connected(const DirectedEdge &in) const
Definition: Edge.h:161
#define pragmatic_isnan
~Refine()
Default destructor.
Definition: Refine.h:120
int pragmatic_thread_id()
Performs 2D mesh refinement.
Definition: Refine.h:56
size_t pragmatic_omp_atomic_capture(size_t *shared, size_t inc)
int pragmatic_nprocesses(MPI_Comm comm)