MFEM  v4.3.0
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
pumi.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-806117.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability visit https://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 #include "pumi.hpp"
13 
14 #ifdef MFEM_USE_PUMI
15 #ifdef MFEM_USE_MPI
16 
17 #include "mesh_headers.hpp"
18 #include "../fem/fem.hpp"
19 #include "../general/sort_pairs.hpp"
20 #include "../general/text.hpp"
21 #include "../general/sets.hpp"
22 
23 #include <iostream>
24 #include <sstream>
25 #include <fstream>
26 #include <limits>
27 #include <cmath>
28 #include <cstring>
29 #include <ctime>
30 
31 using namespace std;
32 
33 namespace mfem
34 {
35 
36 static void ReadPumiElement(apf::MeshEntity* Ent, /* ptr to pumi entity */
37  apf::Downward Verts,
38  const int Attr, apf::Numbering* vert_num,
39  Element* el /* ptr to mfem entity being created */
40  )
41 {
42  int nv, *v;
43 
44  // Create element in MFEM
45  nv = el->GetNVertices();
46  v = el->GetVertices();
47 
48  // Fill the connectivity
49  for (int i = 0; i < nv; ++i)
50  {
51  v[i] = apf::getNumber(vert_num, Verts[i], 0, 0);
52  }
53 
54  // Assign attribute
55  el->SetAttribute(Attr);
56 }
57 
58 // 12 possible rotations of a tet
59 static int const tet_rotation[12][4]=
60 {
61  {0,1,2,3},
62  {0,2,3,1},
63  {0,3,1,2},
64  {1,0,3,2},
65  {1,3,2,0},
66  {1,2,0,3},
67  {2,0,1,3},
68  {2,1,3,0},
69  {2,3,0,1},
70  {3,0,2,1},
71  {3,2,1,0},
72  {3,1,0,2}
73 };
74 
75 // inverse of tet_rotation
76 static int const tet_inv_rotation[12][4]=
77 {
78  {0,1,2,3}, //{0,1,2,3}
79  {0,3,1,2}, //{0,2,3,1}
80  {0,2,3,1}, //{0,3,1,2}
81  {1,0,3,2}, //{1,0,3,2}
82  {3,0,2,1}, //{1,3,2,0}
83  {2,0,1,3}, //{1,2,0,3}
84  {1,2,0,3}, //{2,0,1,3}
85  {3,1,0,2}, //{2,1,3,0}
86  {2,3,0,1}, //{2,3,0,1}
87  {1,3,2,0}, //{3,0,2,1}
88  {3,2,1,0}, //{3,2,1,0}
89  {2,1,3,0} //{3,1,0,2}
90 };
91 
92 // 6 possible rotations of a tri (including the flips)
93 static int const tri_rotation[6][3]=
94 {
95  {0,1,2},
96  {0,2,1},
97  {1,0,2},
98  {1,2,0},
99  {2,0,1},
100  {2,1,0}
101 };
102 
103 // inverse of tri_rotation
104 static int const tri_inv_rotation[6][3]=
105 {
106  {0,1,2}, //{0,1,2}
107  {0,2,1}, //{0,2,1}
108  {1,0,2}, //{1,0,2}
109  {2,0,1}, //{1,2,0}
110  {1,2,0}, //{2,0,1}
111  {2,1,0} //{2,1,0}
112 };
113 
114 
115 
116 static bool same(int n,
117  apf::MeshEntity** a,
118  apf::MeshEntity** b)
119 {
120  for (int i = 0; i < n; i++)
121  {
122  if (a[i] != b[i])
123  {
124  return false;
125  }
126  }
127  return true;
128 }
129 
130 static void rotateSimplex(int type,
131  int r,
132  apf::MeshEntity** in,
133  apf::MeshEntity** out)
134 {
135  int n;
136  if (type == apf::Mesh::TRIANGLE) // triangles
137  {
138  MFEM_ASSERT(r>=0 && r<6, "incorrect rotation");
139  n = 3;
140  }
141  else if (type == apf::Mesh::TET) // tets
142  {
143  MFEM_ASSERT(r>=0 && r<12, "incorrect rotation");
144  n = 4;
145  }
146  else
147  {
148  MFEM_ASSERT(0, "unsupported case!");
149  }
150 
151  for (int i = 0; i < n; i++)
152  if (n == 3)
153  {
154  out[i] = in[tri_rotation[r][i]];
155  }
156  else
157  {
158  out[i] = in[tet_rotation[r][i]];
159  }
160 }
161 
162 
163 static int findSimplexRotation(apf::Mesh2* apf_mesh,
164  apf::MeshEntity* simplex,
165  apf::MeshEntity** vs)
166 {
167  int type = apf_mesh->getType(simplex);
168  int dim;
169  if (type == apf::Mesh::TET)
170  {
171  dim = 3;
172  }
173  else if (type == apf::Mesh::TRIANGLE)
174  {
175  dim = 2;
176  }
177  else
178  {
179  MFEM_ASSERT(0, "unsupported entity type");
180  }
181 
182  apf::MeshEntity* dvs[12];
183  apf::MeshEntity* rotated_dvs[12];
184  int nd = apf_mesh->getDownward(simplex, 0, dvs);
185 
186  int first = apf::findIn(dvs, nd, vs[0]);
187  int begin = first*dim;
188  int end = first*dim + dim;
189  for (int r = begin; r < end; r++)
190  {
191  rotateSimplex(type, r, dvs, rotated_dvs);
192  if (same(nd, rotated_dvs, vs))
193  {
194  return r;
195  }
196  }
197  return -1;
198 }
199 
200 static void rotateSimplexXi(apf::Vector3& xi, int dim, int rot)
201 {
202  double a[4];
203  a[0] = 1.;
204  for (int i = 0; i < dim; i++)
205  {
206  a[0] -= xi[i];
207  }
208  a[1] = xi[0];
209  a[2] = xi[1];
210  a[3] = xi[2];
211  int const* inverseIdx = dim == 2 ? tri_inv_rotation[rot] :
212  tet_inv_rotation[rot];
213  double b[4];
214  for (int i = 0; i <= dim; i++)
215  {
216  b[inverseIdx[i]] = a[i];
217  }
218  xi[0] = b[1];
219  xi[1] = b[2];
220  xi[2] = dim == 2 ? 1.-xi[0]-xi[1] : b[3];
221 }
222 
223 static void unrotateSimplexXi(apf::Vector3& xi, int dim, int rot)
224 {
225  double a[4];
226  a[0] = 1.;
227  for (int i = 0; i < dim; i++)
228  {
229  a[0] -= xi[i];
230  }
231  a[1] = xi[0];
232  a[2] = xi[1];
233  a[3] = xi[2];
234  int const* originalIdx = dim == 2 ? tri_rotation[rot] : tet_rotation[rot];
235  double b[4];
236  for (int i = 0; i <= dim; i++)
237  {
238  b[originalIdx[i]] = a[i];
239  }
240  xi[0] = b[1];
241  xi[1] = b[2];
242  xi[2] = dim == 2 ? 1.-xi[0]-xi[1] : b[3];
243 }
244 
245 PumiMesh::PumiMesh(apf::Mesh2* apf_mesh, int generate_edges, int refine,
246  bool fix_orientation)
247 {
248  Load(apf_mesh, generate_edges, refine, fix_orientation);
249 }
250 
251 
252 
253 void PumiMesh::CountBoundaryEntity(apf::Mesh2* apf_mesh, const int BcDim,
254  int &NumBc)
255 {
256  apf::MeshEntity* ent;
257  apf::MeshIterator* itr = apf_mesh->begin(BcDim);
258 
259  while ((ent=apf_mesh->iterate(itr)))
260  {
261  apf::ModelEntity* mdEnt = apf_mesh->toModel(ent);
262  if (apf_mesh->getModelType(mdEnt) == BcDim)
263  {
264  NumBc++;
265  }
266  }
267  apf_mesh->end(itr);
268 
269  // Check if any boundary is detected
270  if (NumBc==0)
271  {
272  MFEM_ABORT("no boundary detected!");
273  }
274 }
275 
276 void PumiMesh::Load(apf::Mesh2* apf_mesh, int generate_edges, int refine,
277  bool fix_orientation)
278 {
279  int curved = 0, read_gf = 1;
280 
281  // Add a check on apf_mesh just in case
282  Clear();
283 
284  // First number vertices
285  apf::Field* apf_field_crd = apf_mesh->getCoordinateField();
286  apf::FieldShape* crd_shape = apf::getShape(apf_field_crd);
287  apf::Numbering* v_num_loc = apf::createNumbering(apf_mesh, "VertexNumbering",
288  crd_shape, 1);
289  // Check if it is a curved mesh
290  curved = (crd_shape->getOrder() > 1) ? 1 : 0;
291 
292  // Read mesh
293  ReadSCORECMesh(apf_mesh, v_num_loc, curved);
294 #ifdef MFEM_DEBUG
295  mfem::out << "After ReadSCORECMesh" << endl;
296 #endif
297  // at this point the following should be defined:
298  // 1) Dim
299  // 2) NumOfElements, elements
300  // 3) NumOfBdrElements, boundary
301  // 4) NumOfVertices, with allocated space in vertices
302  // 5) curved
303  // 5a) if curved == 0, vertices must be defined
304  // 5b) if curved != 0 and read_gf != 0,
305  // 'input' must point to a GridFunction
306  // 5c) if curved != 0 and read_gf == 0,
307  // vertices and Nodes must be defined
308 
309  // FinalizeTopology() will:
310  // - assume that generate_edges is true
311  // - assume that refine is false
312  // - does not check the orientation of regular and boundary elements
313  FinalizeTopology();
314 
315  if (curved && read_gf)
316  {
317  // Check it to be only Quadratic if higher order
318  Nodes = new GridFunctionPumi(this, apf_mesh, v_num_loc,
319  crd_shape->getOrder());
320  edge_vertex = NULL;
321  own_nodes = 1;
322  spaceDim = Nodes->VectorDim();
323 
324  // Set the 'vertices' from the 'Nodes'
325  for (int i = 0; i < spaceDim; i++)
326  {
327  Vector vert_val;
328  Nodes->GetNodalValues(vert_val, i+1);
329  for (int j = 0; j < NumOfVertices; j++)
330  {
331  vertices[j](i) = vert_val(j);
332  }
333  }
334  }
335 
336  // Delete numbering
337  apf::destroyNumbering(v_num_loc);
338 
339  Finalize(refine, fix_orientation);
340 }
341 
342 void PumiMesh::ReadSCORECMesh(apf::Mesh2* apf_mesh, apf::Numbering* v_num_loc,
343  const int curved)
344 {
345  // Here fill the element table from SCOREC MESH
346  // The vector of element pointers is generated with attr and connectivity
347 
348  apf::MeshIterator* itr = apf_mesh->begin(0);
349  apf::MeshEntity* ent;
350  NumOfVertices = 0;
351  while ((ent = apf_mesh->iterate(itr)))
352  {
353  // IDs start from 0
354  apf::number(v_num_loc, ent, 0, 0, NumOfVertices);
355  NumOfVertices++;
356  }
357  apf_mesh->end(itr);
358 
359  Dim = apf_mesh->getDimension();
360  NumOfElements = countOwned(apf_mesh,Dim);
361  elements.SetSize(NumOfElements);
362 
363  // Read elements from SCOREC Mesh
364  itr = apf_mesh->begin(Dim);
365  unsigned int j=0;
366  while ((ent = apf_mesh->iterate(itr)))
367  {
368  // Get vertices
369  apf::Downward verts;
370  apf_mesh->getDownward(ent,0,verts); // num_vert
371  // Get attribute Tag vs Geometry
372  int attr = 1;
373 
374  int geom_type = apf_mesh->getType(ent);
375  elements[j] = NewElement(geom_type);
376  ReadPumiElement(ent, verts, attr, v_num_loc, elements[j]);
377  j++;
378  }
379  // End iterator
380  apf_mesh->end(itr);
381 
382  // Read Boundaries from SCOREC Mesh
383  // First we need to count them
384  int BCdim = Dim - 1;
385  NumOfBdrElements = 0;
386  CountBoundaryEntity(apf_mesh, BCdim, NumOfBdrElements);
387  boundary.SetSize(NumOfBdrElements);
388  j=0;
389 
390  // Read boundary from SCOREC mesh
391  itr = apf_mesh->begin(BCdim);
392  while ((ent = apf_mesh->iterate(itr)))
393  {
394  // Check if this mesh entity is on the model boundary
395  apf::ModelEntity* mdEnt = apf_mesh->toModel(ent);
396  if (apf_mesh->getModelType(mdEnt) == BCdim)
397  {
398  apf::Downward verts;
399  apf_mesh->getDownward(ent, 0, verts);
400  int attr = 1;
401  int geom_type = apf_mesh->getType(ent);
402  boundary[j] = NewElement(geom_type);
403  ReadPumiElement(ent, verts, attr, v_num_loc, boundary[j]);
404  j++;
405  }
406  }
407  apf_mesh->end(itr);
408 
409  // Fill vertices
410  vertices.SetSize(NumOfVertices);
411 
412  if (!curved)
413  {
414  apf::MeshIterator* itr = apf_mesh->begin(0);
415  spaceDim = Dim;
416 
417  while ((ent = apf_mesh->iterate(itr)))
418  {
419  unsigned int id = apf::getNumber(v_num_loc, ent, 0, 0);
420  apf::Vector3 Crds;
421  apf_mesh->getPoint(ent,0,Crds);
422 
423  for (int ii=0; ii<spaceDim; ii++)
424  {
425  vertices[id](ii) = Crds[ii];
426  }
427  }
428  apf_mesh->end(itr);
429  }
430 }
431 
432 // ParPumiMesh implementation
433 // This function loads a parallel PUMI mesh and returns the parallel MFEM mesh
434 // corresponding to it.
435 ParPumiMesh::ParPumiMesh(MPI_Comm comm, apf::Mesh2* apf_mesh,
436  int refine, bool fix_orientation)
437 {
438  // Set the communicator for gtopo
439  gtopo.SetComm(comm);
440 
441  MyComm = comm;
442  MPI_Comm_size(MyComm, &NRanks);
443  MPI_Comm_rank(MyComm, &MyRank);
444 
445  Dim = apf_mesh->getDimension();
446  spaceDim = Dim;// mesh.spaceDim;
447 
448  apf::MeshIterator* itr;
449  apf::MeshEntity* ent;
450 
451  // Global numbering of vertices. This is necessary to build a local numbering
452  // that has the same ordering in each process.
453  apf::Numbering* vLocNum =
454  apf::numberOwnedDimension(apf_mesh, "AuxVertexNumbering", 0);
455  apf::GlobalNumbering* VertexNumbering = apf::makeGlobal(vLocNum, true);
456  apf::synchronize(VertexNumbering);
457 
458  // Take this process global vertex IDs and sort
459  Array<Pair<long,int> > thisVertIds(apf_mesh->count(0));
460  itr = apf_mesh->begin(0);
461  for (int i = 0; (ent = apf_mesh->iterate(itr)); i++)
462  {
463  long id = apf::getNumber(VertexNumbering, ent, 0, 0);
464  thisVertIds[i] = Pair<long,int>(id, i);
465  }
466  apf_mesh->end(itr);
467  apf::destroyGlobalNumbering(VertexNumbering);
468  thisVertIds.Sort();
469  // Set thisVertIds[i].one = j where j is such that thisVertIds[j].two = i.
470  // Thus, the mapping i -> thisVertIds[i].one is the inverse of the mapping
471  // j -> thisVertIds[j].two.
472  for (int j = 0; j < thisVertIds.Size(); j++)
473  {
474  const int i = thisVertIds[j].two;
475  thisVertIds[i].one = j;
476  }
477 
478  // Create local numbering that respects the global ordering
479  apf::Field* apf_field_crd = apf_mesh->getCoordinateField();
480  apf::FieldShape* crd_shape = apf::getShape(apf_field_crd);
481  // v_num_loc might already be associated the mesh. In that case
482  // there is no need to create it again.
483  v_num_loc = apf_mesh->findNumbering("LocalVertexNumbering");
484  if (!v_num_loc)
485  v_num_loc = apf::createNumbering(apf_mesh,
486  "LocalVertexNumbering",
487  crd_shape, 1);
488 
489  // Construct the numbering v_num_loc and set the coordinates of the vertices.
490  NumOfVertices = thisVertIds.Size();
491  vertices.SetSize(NumOfVertices);
492  itr = apf_mesh->begin(0);
493  for (int i = 0; (ent = apf_mesh->iterate(itr)); i++)
494  {
495  const int id = thisVertIds[i].one;
496  // Assign as local number
497  apf::number(v_num_loc, ent, 0, 0, id);
498 
499  apf::Vector3 Crds;
500  apf_mesh->getPoint(ent,0,Crds);
501 
502  for (int ii=0; ii<spaceDim; ii++)
503  {
504  vertices[id](ii) = Crds[ii]; // Assuming the IDs are ordered and from 0
505  }
506  }
507  apf_mesh->end(itr);
508  thisVertIds.DeleteAll();
509 
510  // Fill the elements
511  NumOfElements = countOwned(apf_mesh,Dim);
512  elements.SetSize(NumOfElements);
513 
514  // Read elements from SCOREC Mesh
515  itr = apf_mesh->begin(Dim);
516  for (int j = 0; (ent = apf_mesh->iterate(itr)); j++)
517  {
518  // Get vertices
519  apf::Downward verts;
520  apf_mesh->getDownward(ent,0,verts);
521 
522  // Get attribute Tag vs Geometry
523  int attr = 1;
524  int geom_type = apf_mesh->getType(ent);
525  elements[j] = NewElement(geom_type);
526  ReadPumiElement(ent, verts, attr, v_num_loc, elements[j]);
527  }
528  // End iterator
529  apf_mesh->end(itr);
530 
531  // Count number of boundaries by classification
532  int BcDim = Dim - 1;
533  itr = apf_mesh->begin(BcDim);
534  NumOfBdrElements = 0;
535  while ((ent=apf_mesh->iterate(itr)))
536  {
537  apf::ModelEntity* mdEnt = apf_mesh->toModel(ent);
538  if (apf_mesh->getModelType(mdEnt) == BcDim)
539  {
540  NumOfBdrElements++;
541  }
542  }
543  apf_mesh->end(itr);
544 
545  boundary.SetSize(NumOfBdrElements);
546  // Read boundary from SCOREC mesh
547  itr = apf_mesh->begin(BcDim);
548  for (int bdr_ctr = 0; (ent = apf_mesh->iterate(itr)); )
549  {
550  // Check if this mesh entity is on the model boundary
551  apf::ModelEntity* mdEnt = apf_mesh->toModel(ent);
552  if (apf_mesh->getModelType(mdEnt) == BcDim)
553  {
554  apf::Downward verts;
555  apf_mesh->getDownward(ent, 0, verts);
556  int attr = 1 ;
557  int geom_type = apf_mesh->getType(ent);
558  boundary[bdr_ctr] = NewElement(geom_type);
559  ReadPumiElement(ent, verts, attr, v_num_loc, boundary[bdr_ctr]);
560  bdr_ctr++;
561  }
562  }
563  apf_mesh->end(itr);
564 
565  // The next two methods are called by FinalizeTopology() called below:
566  // Mesh::SetMeshGen();
567  // Mesh::SetAttributes();
568 
569  // This is called by the default Mesh constructor
570  // Mesh::InitTables();
571 
572  this->FinalizeTopology();
573 
574  ListOfIntegerSets groups;
575  IntegerSet group;
576 
577  // The first group is the local one
578  group.Recreate(1, &MyRank);
579  groups.Insert(group);
580 
581  MFEM_ASSERT(Dim >= 3 || GetNFaces() == 0,
582  "[proc " << MyRank << "]: invalid state");
583 
584  // Determine shared faces
586  // Initially sfaces[i].one holds the global face id.
587  // Then it is replaced by the group id of the shared face.
588  if (Dim > 2)
589  {
590  // Number the faces globally and enumerate the local shared faces
591  // following the global enumeration. This way we ensure that the ordering
592  // of the shared faces within each group (of processors) is the same in
593  // each processor in the group.
594  apf::Numbering* AuxFaceNum =
595  apf::numberOwnedDimension(apf_mesh, "AuxFaceNumbering", 2);
596  apf::GlobalNumbering* GlobalFaceNum = apf::makeGlobal(AuxFaceNum, true);
597  apf::synchronize(GlobalFaceNum);
598 
599  itr = apf_mesh->begin(2);
600  while ((ent = apf_mesh->iterate(itr)))
601  {
602  if (apf_mesh->isShared(ent))
603  {
604  long id = apf::getNumber(GlobalFaceNum, ent, 0, 0);
605  sfaces.Append(Pair<long,apf::MeshEntity*>(id, ent));
606  }
607  }
608  apf_mesh->end(itr);
609  sfaces.Sort();
610  apf::destroyGlobalNumbering(GlobalFaceNum);
611 
612  // Replace the global face id in sfaces[i].one with group id.
613  for (int i = 0; i < sfaces.Size(); i++)
614  {
615  ent = sfaces[i].two;
616 
617  const int thisNumAdjs = 2;
618  int eleRanks[thisNumAdjs];
619 
620  // Get the IDs
621  apf::Parts res;
622  apf_mesh->getResidence(ent, res);
623  int kk = 0;
624  for (std::set<int>::iterator itr = res.begin();
625  itr != res.end(); ++itr)
626  {
627  eleRanks[kk++] = *itr;
628  }
629 
630  group.Recreate(2, eleRanks);
631  sfaces[i].one = groups.Insert(group) - 1;
632  }
633  }
634 
635  // Determine shared edges
637  // Initially sedges[i].one holds the global edge id.
638  // Then it is replaced by the group id of the shared edge.
639  if (Dim > 1)
640  {
641  // Number the edges globally and enumerate the local shared edges
642  // following the global enumeration. This way we ensure that the ordering
643  // of the shared edges within each group (of processors) is the same in
644  // each processor in the group.
645  apf::Numbering* AuxEdgeNum =
646  apf::numberOwnedDimension(apf_mesh, "EdgeNumbering", 1);
647  apf::GlobalNumbering* GlobalEdgeNum = apf::makeGlobal(AuxEdgeNum, true);
648  apf::synchronize(GlobalEdgeNum);
649 
650  itr = apf_mesh->begin(1);
651  while ((ent = apf_mesh->iterate(itr)))
652  {
653  if (apf_mesh->isShared(ent))
654  {
655  long id = apf::getNumber(GlobalEdgeNum, ent, 0, 0);
656  sedges.Append(Pair<long,apf::MeshEntity*>(id, ent));
657  }
658  }
659  apf_mesh->end(itr);
660  sedges.Sort();
661  apf::destroyGlobalNumbering(GlobalEdgeNum);
662 
663  // Replace the global edge id in sedges[i].one with group id.
664  Array<int> eleRanks;
665  for (int i = 0; i < sedges.Size(); i++)
666  {
667  ent = sedges[i].two;
668 
669  // Number of adjacent element
670  apf::Parts res;
671  apf_mesh->getResidence(ent, res);
672  eleRanks.SetSize(res.size());
673 
674  // Get the IDs
675  int kk = 0;
676  for (std::set<int>::iterator itr = res.begin();
677  itr != res.end(); itr++)
678  {
679  eleRanks[kk++] = *itr;
680  }
681 
682  // Generate the group
683  group.Recreate(eleRanks.Size(), eleRanks);
684  sedges[i].one = groups.Insert(group) - 1;
685  }
686  }
687 
688  // Determine shared vertices
690  // The entries sverts[i].one hold the local vertex ids.
691  Array<int> svert_group;
692  {
693  itr = apf_mesh->begin(0);
694  while ((ent = apf_mesh->iterate(itr)))
695  {
696  if (apf_mesh->isShared(ent))
697  {
698  int vtId = apf::getNumber(v_num_loc, ent, 0, 0);
699  sverts.Append(Pair<int,apf::MeshEntity*>(vtId, ent));
700  }
701  }
702  apf_mesh->end(itr);
703  sverts.Sort();
704 
705  // Determine svert_group
706  svert_group.SetSize(sverts.Size());
707  Array<int> eleRanks;
708  for (int i = 0; i < sverts.Size(); i++)
709  {
710  ent = sverts[i].two;
711 
712  // Number of adjacent element
713  apf::Parts res;
714  apf_mesh->getResidence(ent, res);
715  eleRanks.SetSize(res.size());
716 
717  // Get the IDs
718  int kk = 0;
719  for (std::set<int>::iterator itr = res.begin();
720  itr != res.end(); itr++)
721  {
722  eleRanks[kk++] = *itr;
723  }
724 
725  group.Recreate(eleRanks.Size(), eleRanks);
726  svert_group[i] = groups.Insert(group) - 1;
727  }
728  }
729 
730  // Build group_stria and group_squad.
731  // Also allocate shared_trias, shared_quads, and sface_lface.
732  group_stria.MakeI(groups.Size()-1);
733  group_squad.MakeI(groups.Size()-1);
734  for (int i = 0; i < sfaces.Size(); i++)
735  {
736  apf::Mesh::Type ftype = apf_mesh->getType(sfaces[i].two);
737  if (ftype == apf::Mesh::TRIANGLE)
738  {
739  group_stria.AddAColumnInRow(sfaces[i].one);
740  }
741  else if (ftype == apf::Mesh::QUAD)
742  {
743  group_squad.AddAColumnInRow(sfaces[i].one);
744  }
745  }
746  group_stria.MakeJ();
747  group_squad.MakeJ();
748  {
749  int nst = 0;
750  for (int i = 0; i < sfaces.Size(); i++)
751  {
752  apf::Mesh::Type ftype = apf_mesh->getType(sfaces[i].two);
753  if (ftype == apf::Mesh::TRIANGLE)
754  {
755  group_stria.AddConnection(sfaces[i].one, nst++);
756  }
757  else if (ftype == apf::Mesh::QUAD)
758  {
759  group_squad.AddConnection(sfaces[i].one, i-nst);
760  }
761  }
762  shared_trias.SetSize(nst);
763  shared_quads.SetSize(sfaces.Size()-nst);
764  sface_lface.SetSize(sfaces.Size());
765  }
766  group_stria.ShiftUpI();
767  group_squad.ShiftUpI();
768 
769  // Build group_sedge
770  group_sedge.MakeI(groups.Size()-1);
771  for (int i = 0; i < sedges.Size(); i++)
772  {
773  group_sedge.AddAColumnInRow(sedges[i].one);
774  }
775  group_sedge.MakeJ();
776  for (int i = 0; i < sedges.Size(); i++)
777  {
778  group_sedge.AddConnection(sedges[i].one, i);
779  }
780  group_sedge.ShiftUpI();
781 
782  // Build group_svert
783  group_svert.MakeI(groups.Size()-1);
784  for (int i = 0; i < svert_group.Size(); i++)
785  {
786  group_svert.AddAColumnInRow(svert_group[i]);
787  }
788  group_svert.MakeJ();
789  for (int i = 0; i < svert_group.Size(); i++)
790  {
791  group_svert.AddConnection(svert_group[i], i);
792  }
793  group_svert.ShiftUpI();
794 
795  // Build shared_trias and shared_quads. They are allocated above.
796  {
797  int nst = 0;
798  for (int i = 0; i < sfaces.Size(); i++)
799  {
800  ent = sfaces[i].two;
801 
802  apf::Downward verts;
803  apf_mesh->getDownward(ent,0,verts);
804 
805  int *v, nv = 0;
806  apf::Mesh::Type ftype = apf_mesh->getType(ent);
807  if (ftype == apf::Mesh::TRIANGLE)
808  {
809  v = shared_trias[nst++].v;
810  nv = 3;
811  }
812  else if (ftype == apf::Mesh::QUAD)
813  {
814  v = shared_quads[i-nst].v;
815  nv = 4;
816  }
817  for (int j = 0; j < nv; ++j)
818  {
819  v[j] = apf::getNumber(v_num_loc, verts[j], 0, 0);
820  }
821  }
822  }
823 
824  // Build shared_edges and allocate sedge_ledge
825  shared_edges.SetSize(sedges.Size());
826  sedge_ledge. SetSize(sedges.Size());
827  for (int i = 0; i < sedges.Size(); i++)
828  {
829  ent = sedges[i].two;
830 
831  apf::Downward verts;
832  apf_mesh->getDownward(ent, 0, verts);
833  int id1, id2;
834  id1 = apf::getNumber(v_num_loc, verts[0], 0, 0);
835  id2 = apf::getNumber(v_num_loc, verts[1], 0, 0);
836  if (id1 > id2) { swap(id1,id2); }
837 
838  shared_edges[i] = new Segment(id1, id2, 1);
839  }
840 
841  // Build svert_lvert
842  svert_lvert.SetSize(sverts.Size());
843  for (int i = 0; i < sverts.Size(); i++)
844  {
845  svert_lvert[i] = sverts[i].one;
846  }
847 
848  // Build the group communication topology
849  gtopo.Create(groups, 822);
850 
851  // Determine sedge_ledge and sface_lface
852  FinalizeParTopo();
853 
854  // Set nodes for higher order mesh
855  int curved = (crd_shape->getOrder() > 1) ? 1 : 0;
856  if (curved) // curved mesh
857  {
858  GridFunctionPumi auxNodes(this, apf_mesh, v_num_loc,
859  crd_shape->getOrder());
860  Nodes = new ParGridFunction(this, &auxNodes);
861  Nodes->Vector::Swap(auxNodes);
862  this->edge_vertex = NULL;
863  own_nodes = 1;
864  }
865 
866  Finalize(refine, fix_orientation);
867 }
868 
869 // GridFunctionPumi Implementation needed for high order meshes
870 GridFunctionPumi::GridFunctionPumi(Mesh* m, apf::Mesh2* PumiM,
871  apf::Numbering* v_num_loc,
872  const int mesh_order)
873 {
874  int spDim = m->SpaceDimension();
875  // Note: default BasisType for 'fec' is GaussLobatto.
876  fec = new H1_FECollection(mesh_order, m->Dimension());
877  int ordering = Ordering::byVDIM; // x1y1z1/x2y2z2/...
878  fes = new FiniteElementSpace(m, fec, spDim, ordering);
879  int data_size = fes->GetVSize();
880 
881  // Read PUMI mesh data
882  this->SetSize(data_size);
883  double* PumiData = this->GetData();
884 
885  apf::MeshEntity* ent;
886  apf::MeshIterator* itr;
887 
888  // Assume all element type are the same i.e. tetrahedral
889  const FiniteElement* H1_elem = fes->GetFE(0);
890  const IntegrationRule &All_nodes = H1_elem->GetNodes();
891  int nnodes = All_nodes.Size();
892 
893  // Loop over elements
894  apf::Field* crd_field = PumiM->getCoordinateField();
895 
896  int nc = apf::countComponents(crd_field);
897  int iel = 0;
898  itr = PumiM->begin(m->Dimension());
899  while ((ent = PumiM->iterate(itr)))
900  {
901  Array<int> vdofs;
902  fes->GetElementVDofs(iel, vdofs);
903 
904  // Create PUMI element to interpolate
905  apf::MeshElement* mE = apf::createMeshElement(PumiM, ent);
906  apf::Element* elem = apf::createElement(crd_field, mE);
907 
908  // Vertices are already interpolated
909  for (int ip = 0; ip < nnodes; ip++)
910  {
911  // Take parametric coordinates of the node
912  apf::Vector3 param;
913  param[0] = All_nodes.IntPoint(ip).x;
914  param[1] = All_nodes.IntPoint(ip).y;
915  param[2] = All_nodes.IntPoint(ip).z;
916 
917  // Compute the interpolating coordinates
918  apf::DynamicVector phCrd(nc);
919  apf::getComponents(elem, param, &phCrd[0]);
920 
921  // Fill the nodes list
922  for (int kk = 0; kk < spDim; ++kk)
923  {
924  int dof_ctr = ip + kk * nnodes;
925  PumiData[vdofs[dof_ctr]] = phCrd[kk];
926  }
927  }
928  iel++;
929  apf::destroyElement(elem);
930  apf::destroyMeshElement(mE);
931  }
932  PumiM->end(itr);
933 
934  fes_sequence = 0;
935 }
936 
937 // Copy the adapted mesh to the original mesh and increase the sequence to be
938 // able to Call Update() methods of FESpace, Linear and Bilinear forms.
939 void ParPumiMesh::UpdateMesh(const ParMesh* AdaptedpMesh)
940 {
941  // Destroy the ParMesh data fields.
942  delete pncmesh;
943  pncmesh = NULL;
944 
945  DeleteFaceNbrData();
946 
947  for (int i = 0; i < shared_edges.Size(); i++)
948  {
949  FreeElement(shared_edges[i]);
950  }
951  shared_quads.DeleteAll();
952  shared_trias.DeleteAll();
953  shared_edges.DeleteAll();
954  group_svert.Clear();
955  group_sedge.Clear();
956  group_stria.Clear();
957  group_squad.Clear();
958  svert_lvert.DeleteAll();
959  sedge_ledge.DeleteAll();
960  sface_lface.DeleteAll();
961 
962  // Destroy the Mesh data fields.
963  Destroy();
964 
965  // Assuming Dim, spaceDim, geom type is unchanged
966  MFEM_ASSERT(Dim == AdaptedpMesh->Dim, "");
967  MFEM_ASSERT(spaceDim == AdaptedpMesh->spaceDim, "");
968  MFEM_ASSERT(meshgen == AdaptedpMesh->meshgen, "");
969 
970  NumOfVertices = AdaptedpMesh->GetNV();
971  NumOfElements = AdaptedpMesh->GetNE();
972  NumOfBdrElements = AdaptedpMesh->GetNBE();
973  NumOfEdges = AdaptedpMesh->GetNEdges();
974  NumOfFaces = AdaptedpMesh->GetNFaces();
975 
976  meshgen = AdaptedpMesh->meshgen;
977 
978  // Sequence is increased by one to trigger update in FEspace etc.
979  sequence++;
980  last_operation = Mesh::NONE;
981 
982  // Duplicate the elements
983  elements.SetSize(NumOfElements);
984  for (int i = 0; i < NumOfElements; i++)
985  {
986  elements[i] = AdaptedpMesh->GetElement(i)->Duplicate(this);
987  }
988 
989  // Copy the vertices
990  AdaptedpMesh->vertices.Copy(vertices);
991 
992  // Duplicate the boundary
993  boundary.SetSize(NumOfBdrElements);
994  for (int i = 0; i < NumOfBdrElements; i++)
995  {
996  boundary[i] = AdaptedpMesh->GetBdrElement(i)->Duplicate(this);
997  }
998 
999  // Copy the element-to-face Table, el_to_face
1000  el_to_face = (AdaptedpMesh->el_to_face) ?
1001  new Table(*(AdaptedpMesh->el_to_face)) : NULL;
1002 
1003  // Copy the boundary-to-face Array, be_to_face.
1004  AdaptedpMesh->be_to_face.Copy(be_to_face);
1005 
1006  // Copy the element-to-edge Table, el_to_edge
1007  el_to_edge = (AdaptedpMesh->el_to_edge) ?
1008  new Table(*(AdaptedpMesh->el_to_edge)) : NULL;
1009 
1010  // Copy the boudary-to-edge Table, bel_to_edge (3D)
1011  bel_to_edge = (AdaptedpMesh->bel_to_edge) ?
1012  new Table(*(AdaptedpMesh->bel_to_edge)) : NULL;
1013 
1014  // Copy the boudary-to-edge Array, be_to_edge (2D)
1015  AdaptedpMesh->be_to_edge.Copy(be_to_edge);
1016 
1017  // Duplicate the faces and faces_info.
1018  faces.SetSize(AdaptedpMesh->faces.Size());
1019  for (int i = 0; i < faces.Size(); i++)
1020  {
1021  Element *face = AdaptedpMesh->faces[i]; // in 1D the faces are NULL
1022  faces[i] = (face) ? face->Duplicate(this) : NULL;
1023  }
1024  AdaptedpMesh->faces_info.Copy(faces_info);
1025 
1026  // Do NOT copy the element-to-element Table, el_to_el
1027  el_to_el = NULL;
1028 
1029  // Do NOT copy the face-to-edge Table, face_edge
1030  face_edge = NULL;
1031 
1032  // Copy the edge-to-vertex Table, edge_vertex
1033  edge_vertex = (AdaptedpMesh->edge_vertex) ?
1034  new Table(*(AdaptedpMesh->edge_vertex)) : NULL;
1035 
1036  // Copy the attributes and bdr_attributes
1037  AdaptedpMesh->attributes.Copy(attributes);
1038  AdaptedpMesh->bdr_attributes.Copy(bdr_attributes);
1039 
1040  // PUMI meshes cannot use NURBS meshes.
1041  MFEM_VERIFY(AdaptedpMesh->NURBSext == NULL,
1042  "invalid adapted mesh: it is a NURBS mesh");
1043  NURBSext = NULL;
1044 
1045  // PUMI meshes cannot use NCMesh/ParNCMesh.
1046  MFEM_VERIFY(AdaptedpMesh->ncmesh == NULL && AdaptedpMesh->pncmesh == NULL,
1047  "invalid adapted mesh: it is a non-conforming mesh");
1048  ncmesh = NULL;
1049  pncmesh = NULL;
1050 
1051  // Parallel Implications
1052  AdaptedpMesh->group_svert.Copy(group_svert);
1053  AdaptedpMesh->group_sedge.Copy(group_sedge);
1054  group_stria = AdaptedpMesh->group_stria;
1055  group_squad = AdaptedpMesh->group_squad;
1056  AdaptedpMesh->gtopo.Copy(gtopo);
1057 
1058  MyComm = AdaptedpMesh->MyComm;
1059  NRanks = AdaptedpMesh->NRanks;
1060  MyRank = AdaptedpMesh->MyRank;
1061 
1062  // Duplicate the shared_edges
1063  shared_edges.SetSize(AdaptedpMesh->shared_edges.Size());
1064  for (int i = 0; i < shared_edges.Size(); i++)
1065  {
1066  shared_edges[i] = AdaptedpMesh->shared_edges[i]->Duplicate(this);
1067  }
1068 
1069  // Duplicate the shared_trias and shared_quads
1070  shared_trias = AdaptedpMesh->shared_trias;
1071  shared_quads = AdaptedpMesh->shared_quads;
1072 
1073  // Copy the shared-to-local index Arrays
1074  AdaptedpMesh->svert_lvert.Copy(svert_lvert);
1075  AdaptedpMesh->sedge_ledge.Copy(sedge_ledge);
1076  AdaptedpMesh->sface_lface.Copy(sface_lface);
1077 
1078  // Do not copy face-neighbor data (can be generated if needed)
1079  have_face_nbr_data = false;
1080 
1081  // Copy the Nodes as a ParGridFunction, including the FiniteElementCollection
1082  // and the FiniteElementSpace (as a ParFiniteElementSpace)
1083  if (AdaptedpMesh->Nodes)
1084  {
1085  FiniteElementSpace *fes = AdaptedpMesh->Nodes->FESpace();
1086  const FiniteElementCollection *fec = fes->FEColl();
1087  FiniteElementCollection *fec_copy =
1088  FiniteElementCollection::New(fec->Name());
1089  ParFiniteElementSpace *pfes_copy =
1090  new ParFiniteElementSpace(this, fec_copy, fes->GetVDim(),
1091  fes->GetOrdering());
1092  Nodes = new ParGridFunction(pfes_copy);
1093  Nodes->MakeOwner(fec_copy);
1094  *Nodes = *(AdaptedpMesh->Nodes);
1095  own_nodes = 1;
1096  }
1097 }
1098 
1099 int ParPumiMesh::RotationPUMItoMFEM(apf::Mesh2* apf_mesh,
1100  apf::MeshEntity* ent,
1101  int elemId)
1102 {
1103  int type = apf_mesh->getType(ent);
1104  MFEM_ASSERT(apf::isSimplex(type),
1105  "only implemented for simplex entity types");
1106  // get downward vertices of PUMI element
1107  apf::Downward vs;
1108  int nv = apf_mesh->getDownward(ent,0,vs);
1109  int pumi_vid[12];
1110  for (int i = 0; i < nv; i++)
1111  {
1112  pumi_vid[i] = apf::getNumber(v_num_loc, vs[i], 0, 0);
1113  }
1114 
1115  // get downward vertices of MFEM element
1116  mfem::Array<int> mfem_vid;
1117  this->GetElementVertices(elemId, mfem_vid);
1118 
1119  // get rotated indices of PUMI element
1120  int pumi_vid_rot[12];
1121  for (int i = 0; i < nv; i++)
1122  {
1123  pumi_vid_rot[i] = mfem_vid.Find(pumi_vid[i]);
1124  }
1125  apf::Downward vs_rot;
1126  for (int i = 0; i < nv; i++)
1127  {
1128  vs_rot[i] = vs[pumi_vid_rot[i]];
1129  }
1130  return findSimplexRotation(apf_mesh, ent, vs_rot);
1131 }
1132 
1133 // Convert parent coordinate form a PUMI tet to an MFEM tet
1134 IntegrationRule ParPumiMesh::ParentXisPUMItoMFEM(apf::Mesh2* apf_mesh,
1135  apf::MeshEntity* tet,
1136  int elemId,
1137  apf::NewArray<apf::Vector3>& pumi_xi,
1138  bool checkOrientation)
1139 {
1140  int type = apf_mesh->getType(tet);
1141  MFEM_ASSERT(apf::isSimplex(type),
1142  "only implemented for simplex entity types");
1143  int num_nodes = pumi_xi.size();
1144  IntegrationRule mfem_xi(num_nodes);
1145  int rotation = checkOrientation ? RotationPUMItoMFEM(apf_mesh, tet, elemId):0;
1146  for (int i = 0; i < num_nodes; i++)
1147  {
1148  // for non zero "rotation", rotate the xi
1149  if (rotation)
1150  {
1151  unrotateSimplexXi(pumi_xi[i], apf::Mesh::typeDimension[type], rotation);
1152  }
1153  IntegrationPoint& ip = mfem_xi.IntPoint(i);
1154  double tmp_xi[3];
1155  pumi_xi[i].toArray(tmp_xi);
1156  ip.Set(tmp_xi,3);
1157  }
1158  return mfem_xi;
1159 }
1160 
1161 // Convert parent coordinate from MFEM tet to PUMI tet
1162 void ParPumiMesh::ParentXisMFEMtoPUMI(apf::Mesh2* apf_mesh,
1163  int elemId,
1164  apf::MeshEntity* tet,
1165  const IntegrationRule& mfem_xi,
1166  apf::NewArray<apf::Vector3>& pumi_xi,
1167  bool checkOrientation)
1168 {
1169  int type = apf_mesh->getType(tet);
1170  MFEM_ASSERT(apf::isSimplex(type),
1171  "only implemented for simplex entity types");
1172  int num_nodes = mfem_xi.Size();
1173  if (!pumi_xi.allocated())
1174  {
1175  pumi_xi.allocate(num_nodes);
1176  }
1177  else
1178  {
1179  pumi_xi.resize(num_nodes);
1180  }
1181 
1182  int rotation = checkOrientation ? RotationPUMItoMFEM(apf_mesh, tet, elemId):0;
1183  for (int i = 0; i < num_nodes; i++)
1184  {
1185  IntegrationPoint ip = mfem_xi.IntPoint(i);
1186  pumi_xi[i] = apf::Vector3(ip.x, ip.y, ip.z);
1187 
1188  // for non zero "rotation", un-rotate the xi
1189  if (rotation)
1190  {
1191  rotateSimplexXi(pumi_xi[i], apf::Mesh::typeDimension[type], rotation);
1192  }
1193  }
1194 }
1195 
1196 
1197 // Transfer a mixed vector-scalar field (i.e. velocity,pressure) and the
1198 // magnitude of the vector field to use for mesh adaptation.
1199 void ParPumiMesh::FieldMFEMtoPUMI(apf::Mesh2* apf_mesh,
1200  ParGridFunction* grid_vel,
1201  ParGridFunction* grid_pr,
1202  apf::Field* vel_field,
1203  apf::Field* pr_field,
1204  apf::Field* vel_mag_field)
1205 {
1206  apf::FieldShape* field_shape = getShape(vel_field);
1207  int dim = apf_mesh->getDimension();
1208 
1209  apf::MeshEntity* ent;
1210  apf::MeshIterator* itr = apf_mesh->begin(dim);
1211  int iel = 0;
1212  while ((ent = apf_mesh->iterate(itr)))
1213  {
1214  apf::NewArray<apf::Vector3> pumi_nodes;
1215  apf::getElementNodeXis(field_shape, apf_mesh, ent, pumi_nodes);
1216  IntegrationRule mfem_nodes = ParentXisPUMItoMFEM(
1217  apf_mesh, ent, iel, pumi_nodes, true);
1218  // Get the solution
1219  ElementTransformation* eltr = this->GetElementTransformation(iel);
1220  DenseMatrix vel;
1221  grid_vel->GetVectorValues(*eltr, mfem_nodes, vel);
1222  Vector pr;
1223  grid_pr->GetValues(iel, mfem_nodes, pr, 1);
1224 
1225  int non = 0;
1226  for (int d = 0; d <= dim; d++)
1227  {
1228  if (!field_shape->hasNodesIn(d)) { continue; }
1229  apf::Downward a;
1230  int na = apf_mesh->getDownward(ent,d,a);
1231  for (int i = 0; i < na; i++)
1232  {
1233  int type = apf_mesh->getType(a[i]);
1234  int nan = field_shape->countNodesOn(type);
1235  for (int n = 0; n < nan; n++)
1236  {
1237  apf::Vector3 v(vel.GetColumn(non));
1238  apf::setVector(vel_field, a[i], n, v);
1239  apf::setScalar(pr_field, a[i], n, pr[non]);
1240  apf::setScalar(vel_mag_field, a[i], n, v.getLength());
1241  non++;
1242  }
1243  }
1244  }
1245  iel++;
1246  }
1247  apf_mesh->end(itr);
1248 }
1249 
1250 // Transfer a scalar field its magnitude to use for mesh adaptation.
1251 void ParPumiMesh::FieldMFEMtoPUMI(apf::Mesh2* apf_mesh,
1252  ParGridFunction* grid_pr,
1253  apf::Field* pr_field,
1254  apf::Field* pr_mag_field)
1255 {
1256  apf::FieldShape* field_shape = getShape(pr_field);
1257  int dim = apf_mesh->getDimension();
1258 
1259  apf::MeshEntity* ent;
1260  apf::MeshIterator* itr = apf_mesh->begin(dim);
1261  int iel = 0;
1262  while ((ent = apf_mesh->iterate(itr)))
1263  {
1264  apf::NewArray<apf::Vector3> pumi_nodes;
1265  apf::getElementNodeXis(field_shape, apf_mesh, ent, pumi_nodes);
1266  IntegrationRule mfem_nodes = ParentXisPUMItoMFEM(
1267  apf_mesh, ent, iel, pumi_nodes, true);
1268  // Get the solution
1269  Vector vals;
1270  grid_pr->GetValues(iel, mfem_nodes, vals, 1);
1271 
1272  int non = 0;
1273  for (int d = 0; d <= dim; d++)
1274  {
1275  if (!field_shape->hasNodesIn(d)) { continue; }
1276  apf::Downward a;
1277  int na = apf_mesh->getDownward(ent,d,a);
1278  for (int i = 0; i < na; i++)
1279  {
1280  int type = apf_mesh->getType(a[i]);
1281  int nan = field_shape->countNodesOn(type);
1282  for (int n = 0; n < nan; n++)
1283  {
1284  double pr = vals[non];
1285  double pr_mag = pr >= 0 ? pr : -pr;
1286  apf::setScalar(pr_field, a[i], n, pr);
1287  apf::setScalar(pr_mag_field, a[i], n, pr_mag);
1288  non++;
1289  }
1290  }
1291  }
1292  iel++;
1293  }
1294  apf_mesh->end(itr);
1295 }
1296 
1297 // Transfer a vector field and the magnitude of the vector field to use for mesh
1298 // adaptation
1299 void ParPumiMesh::VectorFieldMFEMtoPUMI(apf::Mesh2* apf_mesh,
1300  ParGridFunction* grid_vel,
1301  apf::Field* vel_field,
1302  apf::Field* vel_mag_field)
1303 {
1304  apf::FieldShape* field_shape = getShape(vel_field);
1305  int dim = apf_mesh->getDimension();
1306 
1307  apf::MeshEntity* ent;
1308  apf::MeshIterator* itr = apf_mesh->begin(dim);
1309  int iel = 0;
1310  while ((ent = apf_mesh->iterate(itr)))
1311  {
1312  apf::NewArray<apf::Vector3> pumi_nodes;
1313  apf::getElementNodeXis(field_shape, apf_mesh, ent, pumi_nodes);
1314  IntegrationRule mfem_nodes = ParentXisPUMItoMFEM(
1315  apf_mesh, ent, iel, pumi_nodes, true);
1316  // Get the solution
1317  ElementTransformation* eltr = this->GetElementTransformation(iel);
1318  DenseMatrix vel;
1319  grid_vel->GetVectorValues(*eltr, mfem_nodes, vel);
1320 
1321  int non = 0;
1322  for (int d = 0; d <= dim; d++)
1323  {
1324  if (!field_shape->hasNodesIn(d)) { continue; }
1325  apf::Downward a;
1326  int na = apf_mesh->getDownward(ent,d,a);
1327  for (int i = 0; i < na; i++)
1328  {
1329  int type = apf_mesh->getType(a[i]);
1330  int nan = field_shape->countNodesOn(type);
1331  for (int n = 0; n < nan; n++)
1332  {
1333  apf::Vector3 v(vel.GetColumn(non));
1334  apf::setScalar(vel_mag_field, a[i], n, v.getLength());
1335  apf::setVector(vel_field, a[i], n, v);
1336  non++;
1337  }
1338  }
1339  }
1340  iel++;
1341  }
1342  apf_mesh->end(itr);
1343 }
1344 
1345 void ParPumiMesh::NedelecFieldMFEMtoPUMI(apf::Mesh2* apf_mesh,
1346  ParGridFunction* gf,
1347  apf::Field* nedelec_field)
1348 {
1349  apf::FieldShape* nedelecFieldShape = nedelec_field->getShape();
1350  int dim = apf_mesh->getDimension();
1351 
1352  // loop over all elements
1353  size_t elemNo = 0;
1354  apf::MeshEntity* ent;
1355  apf::MeshIterator* it = apf_mesh->begin(dim);
1356  while ( (ent = apf_mesh->iterate(it)) )
1357  {
1358  // get all the pumi nodes and rotate them
1359  apf::NewArray<apf::Vector3> pumi_nodes;
1360  apf::getElementNodeXis(nedelecFieldShape, apf_mesh, ent, pumi_nodes);
1361  IntegrationRule mfem_nodes = ParentXisPUMItoMFEM(
1362  apf_mesh, ent, elemNo, pumi_nodes, true);
1363  // evaluate the vector field on the mfem nodes
1364  ElementTransformation* eltr = this->GetElementTransformation(elemNo);
1365  DenseMatrix mfem_field_vals;
1366  gf->GetVectorValues(*eltr, mfem_nodes, mfem_field_vals);
1367 
1368  // compute and store dofs on ND field
1369  int non = 0;
1370  for (int d = 0; d <= dim; d++)
1371  {
1372  if (!nedelecFieldShape->hasNodesIn(d)) { continue; }
1373  apf::Downward a;
1374  int na = apf_mesh->getDownward(ent,d,a);
1375  for (int i = 0; i < na; i++)
1376  {
1377  int type = apf_mesh->getType(a[i]);
1378  int nan = nedelecFieldShape->countNodesOn(type);
1379  apf::MeshElement* me = apf::createMeshElement(apf_mesh, a[i]);
1380  for (int n = 0; n < nan; n++)
1381  {
1382  apf::Vector3 xi, tangent;
1383  nedelecFieldShape->getNodeXi(type, n, xi);
1384  nedelecFieldShape->getNodeTangent(type, n, tangent);
1385  apf::Vector3 pumi_field_vector(mfem_field_vals.GetColumn(non));
1386  apf::Matrix3x3 J;
1387  apf::getJacobian(me, xi, J);
1388  double dof = (J * pumi_field_vector) * tangent;
1389  apf::setScalar(nedelec_field, a[i], n, dof);
1390  non++;
1391  }
1392  apf::destroyMeshElement(me);
1393  }
1394  }
1395  elemNo++;
1396  }
1397  apf_mesh->end(it); // end loop over all elements
1398 }
1399 
1400 void ParPumiMesh::FieldPUMItoMFEM(apf::Mesh2* apf_mesh,
1401  apf::Field* field,
1402  ParGridFunction* grid)
1403 {
1404  int nc = apf::countComponents(field);
1405  ParFiniteElementSpace* fes = grid->ParFESpace();
1406  ParMesh* pmesh = fes->GetParMesh();
1407 
1408  int dim = apf_mesh->getDimension();
1409 
1410  apf::MeshIterator* it = apf_mesh->begin(dim);
1411  for (int i = 0; i < pmesh->GetNE(); i++)
1412  {
1413  const FiniteElement* mfem_elem = fes->GetFE(i);
1414  const IntegrationRule &mfem_xi = mfem_elem->GetNodes();
1415  int non = mfem_xi.Size();
1416  apf::MeshEntity* ent = apf_mesh->iterate(it);
1417  apf::NewArray<apf::Vector3> pumi_xi(non);
1418  ParentXisMFEMtoPUMI(apf_mesh,
1419  i,
1420  ent,
1421  mfem_xi,
1422  pumi_xi,
1423  true);
1424  Array<int> vdofs;
1425  fes->GetElementVDofs(i, vdofs);
1426  apf::MeshElement* me = apf::createMeshElement(apf_mesh, ent);
1427  apf::Element* el = apf::createElement(field, me);
1428  for (int j = 0; j < non; j++)
1429  {
1430  apf::DynamicVector values(nc);
1431  apf::getComponents(el, pumi_xi[j], &values[0]);
1432  // Fill the nodes list
1433  for (int c = 0; c < nc; c++)
1434  {
1435  int dof_loc = j + c * non;
1436  (grid->GetData())[vdofs[dof_loc]] = values[c];
1437  }
1438  }
1439  apf::destroyElement(el);
1440  apf::destroyMeshElement(me);
1441  }
1442  apf_mesh->end(it);
1443 }
1444 
1445 }
1446 
1447 #endif // MFEM_USE_MPI
1448 #endif // MFEM_USE_SCOREC
Abstract class for all finite elements.
Definition: fe.hpp:243
void Set(const double *p, const int dim)
Definition: intrules.hpp:37
Ordering::Type GetOrdering() const
Return the ordering method.
Definition: fespace.hpp:552
int Size() const
Return the logical size of the array.
Definition: array.hpp:134
void Recreate(const int n, const int *p)
Create an integer set from C-array &#39;p&#39; of &#39;n&#39; integers. Overwrites any existing set data...
Definition: sets.cpp:59
Class for an integration rule - an Array of IntegrationPoint.
Definition: intrules.hpp:90
void GetVectorValues(int i, const IntegrationRule &ir, DenseMatrix &vals, DenseMatrix &tr) const
Definition: gridfunc.cpp:655
T * end()
STL-like end. Returns pointer after the last element of the array.
Definition: array.hpp:288
int NRanks
Definition: pmesh.hpp:36
virtual Element * Duplicate(Mesh *m) const =0
ParMesh * GetParMesh() const
Definition: pfespace.hpp:267
int GetNBE() const
Returns number of boundary elements.
Definition: mesh.hpp:849
void GetElementVDofs(int i, Array< int > &vdofs) const
Returns indexes of degrees of freedom in array dofs for i&#39;th element.
Definition: fespace.cpp:262
Array< int > sface_lface
Definition: pmesh.hpp:76
void Copy(Array &copy) const
Create a copy of the internal array to the provided copy.
Definition: array.hpp:851
Class for PUMI grid functions.
Definition: pumi.hpp:152
int VectorDim() const
Definition: gridfunc.cpp:311
Data type dense matrix using column-major storage.
Definition: densemat.hpp:23
GridFunction * Nodes
Definition: mesh.hpp:176
int GetNE() const
Returns number of elements.
Definition: mesh.hpp:846
Abstract parallel finite element space.
Definition: pfespace.hpp:28
Array< Vert3 > shared_trias
Definition: pmesh.hpp:63
constexpr Element::Type QUAD
double * GetData() const
Return a pointer to the beginning of the Vector data.
Definition: vector.hpp:199
Array< Element * > faces
Definition: mesh.hpp:92
Array< int > sedge_ledge
Definition: pmesh.hpp:74
Table group_stria
Definition: pmesh.hpp:69
Table * el_to_face
Definition: mesh.hpp:157
ParNCMesh * pncmesh
Definition: pmesh.hpp:313
IntegrationPoint & IntPoint(int i)
Returns a reference to the i-th integration point.
Definition: intrules.hpp:250
MPI_Comm MyComm
Definition: pmesh.hpp:35
virtual const FiniteElement * GetFE(int i) const
Definition: pfespace.cpp:511
Array< Element * > shared_edges
Definition: pmesh.hpp:59
int Append(const T &el)
Append element &#39;el&#39; to array, resize if necessary.
Definition: array.hpp:746
double b
Definition: lissajous.cpp:42
A pair of objects.
Definition: sort_pairs.hpp:23
const IntegrationRule & GetNodes() const
Get a const reference to the nodes of the element.
Definition: fe.hpp:390
int Insert(IntegerSet &s)
Check to see if set &#39;s&#39; is in the list. If not append it to the end of the list. Returns the index of...
Definition: sets.cpp:82
T * begin()
STL-like begin. Returns pointer to the first element of the array.
Definition: array.hpp:285
const Element * GetElement(int i) const
Definition: mesh.hpp:942
void Sort()
Sorts the array in ascending order. This requires operator&lt; to be defined for T.
Definition: array.hpp:244
void GetValues(int i, const IntegrationRule &ir, Vector &vals, int vdim=1) const
Definition: gridfunc.cpp:466
int GetVDim() const
Returns vector dimension.
Definition: fespace.hpp:534
FiniteElementSpace * FESpace()
Definition: gridfunc.hpp:629
int Dimension() const
Definition: mesh.hpp:911
Table * el_to_edge
Definition: mesh.hpp:156
int Size()
Return the number of integer sets in the list.
Definition: sets.hpp:67
void GetColumn(int c, Vector &col) const
Definition: densemat.cpp:1285
Array< Vert4 > shared_quads
Definition: pmesh.hpp:64
void Copy(GroupTopology &copy) const
Copy the internal data to the external &#39;copy&#39;.
int SpaceDimension() const
Definition: mesh.hpp:912
Array< int > bdr_attributes
A list of all unique boundary attributes used by the Mesh.
Definition: mesh.hpp:204
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition: fespace.hpp:87
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
Definition: fe_coll.hpp:26
int Find(const T &el) const
Return the first index where &#39;el&#39; is found; return -1 if not found.
Definition: array.hpp:805
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition: array.hpp:674
Array< Vertex > vertices
Definition: mesh.hpp:90
int meshgen
Definition: mesh.hpp:77
Table * bel_to_edge
Definition: mesh.hpp:160
double a
Definition: lissajous.cpp:41
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition: mesh.hpp:206
int GetNV() const
Returns number of vertices. Vertices are only at the corners of elements, where you would expect them...
Definition: mesh.hpp:843
Array< int > be_to_edge
Definition: mesh.hpp:159
virtual const char * Name() const
Definition: fe_coll.hpp:61
Class for integration point with weight.
Definition: intrules.hpp:25
Table group_sedge
Definition: pmesh.hpp:68
A set of integers.
Definition: sets.hpp:23
int dim
Definition: ex24.cpp:53
Table group_svert
Shared objects in each group.
Definition: pmesh.hpp:67
Table * edge_vertex
Definition: mesh.hpp:163
Array< FaceInfo > faces_info
Definition: mesh.hpp:153
NCMesh * ncmesh
Optional non-conforming mesh extension.
Definition: mesh.hpp:207
int Dim
Definition: mesh.hpp:66
int GetNEdges() const
Return the number of edges.
Definition: mesh.hpp:852
int GetNFaces() const
Return the number of faces in a 3D mesh.
Definition: mesh.hpp:855
Vector data type.
Definition: vector.hpp:60
const FiniteElementCollection * FEColl() const
Definition: fespace.hpp:554
Arbitrary order H1-conforming (continuous) finite elements.
Definition: fe_coll.hpp:216
Array< int > svert_lvert
Shared to local index mapping.
Definition: pmesh.hpp:73
int MyRank
Definition: pmesh.hpp:36
int spaceDim
Definition: mesh.hpp:67
Class for parallel grid function.
Definition: pgridfunc.hpp:32
List of integer sets.
Definition: sets.hpp:59
Array< int > be_to_face
Definition: mesh.hpp:161
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition: globals.hpp:66
GroupTopology gtopo
Definition: pmesh.hpp:300
Class for parallel meshes.
Definition: pmesh.hpp:32
Abstract data type element.
Definition: element.hpp:28
Data type line segment element.
Definition: segment.hpp:22
Table group_squad
Definition: pmesh.hpp:70
void Copy(Table &copy) const
Definition: table.cpp:390
Array< int > attributes
A list of all unique element attributes used by the Mesh.
Definition: mesh.hpp:202
const Element * GetBdrElement(int i) const
Definition: mesh.hpp:946
ParFiniteElementSpace * ParFESpace() const
Definition: pgridfunc.hpp:108