xref: /libCEED/examples/petsc/src/petscutils.c (revision edb2538e3dd6743c029967fc4e89c6fcafedb8c2)
1 #include "../include/petscutils.h"
2 
3 // -----------------------------------------------------------------------------
4 // Convert PETSc MemType to libCEED MemType
5 // -----------------------------------------------------------------------------
6 CeedMemType MemTypeP2C(PetscMemType mem_type) { return PetscMemTypeDevice(mem_type) ? CEED_MEM_DEVICE : CEED_MEM_HOST; }
7 
8 // -----------------------------------------------------------------------------
9 // Apply 3D Kershaw mesh transformation
10 // -----------------------------------------------------------------------------
11 // Transition from a value of "a" for x=0, to a value of "b" for x=1.  Optionally
12 // smooth -- see the commented versions at the end.
13 static double step(const double a, const double b, double x) {
14   if (x <= 0) return a;
15   if (x >= 1) return b;
16   return a + (b - a) * (x);
17 }
18 
19 // 1D transformation at the right boundary
20 static double right(const double eps, const double x) { return (x <= 0.5) ? (2 - eps) * x : 1 + eps * (x - 1); }
21 
22 // 1D transformation at the left boundary
23 static double left(const double eps, const double x) { return 1 - right(eps, 1 - x); }
24 
25 // Apply 3D Kershaw mesh transformation
26 // The eps parameters are in (0, 1]
27 // Uniform mesh is recovered for eps=1
28 PetscErrorCode Kershaw(DM dm_orig, PetscScalar eps) {
29   Vec          coord;
30   PetscInt     ncoord;
31   PetscScalar *c;
32 
33   PetscFunctionBeginUser;
34 
35   PetscCall(DMGetCoordinatesLocal(dm_orig, &coord));
36   PetscCall(VecGetLocalSize(coord, &ncoord));
37   PetscCall(VecGetArray(coord, &c));
38 
39   for (PetscInt i = 0; i < ncoord; i += 3) {
40     PetscScalar x = c[i], y = c[i + 1], z = c[i + 2];
41     PetscInt    layer  = x * 6;
42     PetscScalar lambda = (x - layer / 6.0) * 6;
43     c[i]               = x;
44 
45     switch (layer) {
46       case 0:
47         c[i + 1] = left(eps, y);
48         c[i + 2] = left(eps, z);
49         break;
50       case 1:
51       case 4:
52         c[i + 1] = step(left(eps, y), right(eps, y), lambda);
53         c[i + 2] = step(left(eps, z), right(eps, z), lambda);
54         break;
55       case 2:
56         c[i + 1] = step(right(eps, y), left(eps, y), lambda / 2);
57         c[i + 2] = step(right(eps, z), left(eps, z), lambda / 2);
58         break;
59       case 3:
60         c[i + 1] = step(right(eps, y), left(eps, y), (1 + lambda) / 2);
61         c[i + 2] = step(right(eps, z), left(eps, z), (1 + lambda) / 2);
62         break;
63       default:
64         c[i + 1] = right(eps, y);
65         c[i + 2] = right(eps, z);
66     }
67   }
68   PetscCall(VecRestoreArray(coord, &c));
69   PetscFunctionReturn(PETSC_SUCCESS);
70 }
71 
72 // -----------------------------------------------------------------------------
73 // Create BC label
74 // -----------------------------------------------------------------------------
75 static PetscErrorCode CreateBCLabel(DM dm, const char name[]) {
76   DMLabel label;
77 
78   PetscFunctionBeginUser;
79 
80   PetscCall(DMCreateLabel(dm, name));
81   PetscCall(DMGetLabel(dm, name, &label));
82   PetscCall(DMPlexMarkBoundaryFaces(dm, PETSC_DETERMINE, label));
83   PetscCall(DMPlexLabelComplete(dm, label));
84 
85   PetscFunctionReturn(PETSC_SUCCESS);
86 }
87 
88 // -----------------------------------------------------------------------------
89 // This function sets up a DM for a given degree
90 // -----------------------------------------------------------------------------
91 PetscErrorCode SetupDMByDegree(DM dm, PetscInt p_degree, PetscInt q_extra, PetscInt num_comp_u, PetscInt dim, bool enforce_bc) {
92   PetscInt  marker_ids[1] = {1};
93   PetscInt  q_degree      = p_degree + q_extra;
94   PetscFE   fe;
95   MPI_Comm  comm;
96   PetscBool is_simplex = PETSC_TRUE;
97 
98   PetscFunctionBeginUser;
99 
100   // Check if simplex or tensor-product mesh
101   PetscCall(DMPlexIsSimplex(dm, &is_simplex));
102   // Setup FE
103   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
104   PetscCall(PetscFECreateLagrange(comm, dim, num_comp_u, is_simplex, p_degree, q_degree, &fe));
105   PetscCall(DMAddField(dm, NULL, (PetscObject)fe));
106   PetscCall(DMCreateDS(dm));
107 
108   {
109     // create FE field for coordinates
110     PetscFE  fe_coords;
111     PetscInt num_comp_coord;
112     PetscCall(DMGetCoordinateDim(dm, &num_comp_coord));
113     PetscCall(PetscFECreateLagrange(comm, dim, num_comp_coord, is_simplex, 1, q_degree, &fe_coords));
114     PetscCall(DMProjectCoordinates(dm, fe_coords));
115     PetscCall(PetscFEDestroy(&fe_coords));
116   }
117 
118   // Setup Dirichlet BC
119   // Note bp1, bp2 are projection and we don't need to apply BC
120   // For bp3,bp4, the target function is zero on the boundaries
121   // So we pass bcFunc = NULL in DMAddBoundary function
122   if (enforce_bc) {
123     PetscBool has_label;
124     DMHasLabel(dm, "marker", &has_label);
125     if (!has_label) {
126       CreateBCLabel(dm, "marker");
127     }
128     DMLabel label;
129     PetscCall(DMGetLabel(dm, "marker", &label));
130     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "wall", label, 1, marker_ids, 0, 0, NULL, NULL, NULL, NULL, NULL));
131     PetscCall(DMSetOptionsPrefix(dm, "final_"));
132     PetscCall(DMViewFromOptions(dm, NULL, "-dm_view"));
133   }
134 
135   if (!is_simplex) {
136     DM dm_coord;
137     PetscCall(DMGetCoordinateDM(dm, &dm_coord));
138     PetscCall(DMPlexSetClosurePermutationTensor(dm, PETSC_DETERMINE, NULL));
139     PetscCall(DMPlexSetClosurePermutationTensor(dm_coord, PETSC_DETERMINE, NULL));
140   }
141   PetscCall(PetscFEDestroy(&fe));
142 
143   PetscFunctionReturn(PETSC_SUCCESS);
144 }
145 
146 // -----------------------------------------------------------------------------
147 // Get CEED restriction data from DMPlex
148 // -----------------------------------------------------------------------------
149 PetscErrorCode CreateRestrictionFromPlex(Ceed ceed, DM dm, CeedInt height, DMLabel domain_label, CeedInt value, CeedElemRestriction *elem_restr) {
150   PetscInt num_elem, elem_size, num_dof, num_comp, *elem_restr_offsets;
151 
152   PetscFunctionBeginUser;
153 
154   PetscCall(DMPlexGetLocalOffsets(dm, domain_label, value, height, 0, &num_elem, &elem_size, &num_comp, &num_dof, &elem_restr_offsets));
155 
156   CeedElemRestrictionCreate(ceed, num_elem, elem_size, num_comp, 1, num_dof, CEED_MEM_HOST, CEED_COPY_VALUES, elem_restr_offsets, elem_restr);
157   PetscCall(PetscFree(elem_restr_offsets));
158 
159   PetscFunctionReturn(PETSC_SUCCESS);
160 }
161 
162 // -----------------------------------------------------------------------------
163 // Utility function - convert from DMPolytopeType to CeedElemTopology
164 // -----------------------------------------------------------------------------
165 CeedElemTopology ElemTopologyP2C(DMPolytopeType cell_type) {
166   switch (cell_type) {
167     case DM_POLYTOPE_TRIANGLE:
168       return CEED_TOPOLOGY_TRIANGLE;
169     case DM_POLYTOPE_QUADRILATERAL:
170       return CEED_TOPOLOGY_QUAD;
171     case DM_POLYTOPE_TETRAHEDRON:
172       return CEED_TOPOLOGY_TET;
173     case DM_POLYTOPE_HEXAHEDRON:
174       return CEED_TOPOLOGY_HEX;
175     default:
176       return 0;
177   }
178 }
179 
180 // -----------------------------------------------------------------------------
181 // Convert DM field to DS field
182 // -----------------------------------------------------------------------------
183 PetscErrorCode DMFieldToDSField(DM dm, DMLabel domain_label, PetscInt dm_field, PetscInt *ds_field) {
184   PetscDS         ds;
185   IS              field_is;
186   const PetscInt *fields;
187   PetscInt        num_fields;
188 
189   PetscFunctionBeginUser;
190 
191   // Translate dm_field to ds_field
192   PetscCall(DMGetRegionDS(dm, domain_label, &field_is, &ds, NULL));
193   PetscCall(ISGetIndices(field_is, &fields));
194   PetscCall(ISGetSize(field_is, &num_fields));
195   for (PetscInt i = 0; i < num_fields; i++) {
196     if (dm_field == fields[i]) {
197       *ds_field = i;
198       break;
199     }
200   }
201   PetscCall(ISRestoreIndices(field_is, &fields));
202 
203   if (*ds_field == -1) SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Could not find dm_field %" PetscInt_FMT " in DS", dm_field);
204 
205   PetscFunctionReturn(PETSC_SUCCESS);
206 }
207 
208 // -----------------------------------------------------------------------------
209 // Create libCEED Basis from PetscTabulation
210 // -----------------------------------------------------------------------------
211 PetscErrorCode BasisCreateFromTabulation(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height, PetscInt face, PetscFE fe,
212                                          PetscTabulation basis_tabulation, PetscQuadrature quadrature, CeedBasis *basis) {
213   PetscInt           first_point;
214   PetscInt           ids[1] = {label_value};
215   DMLabel            depth_label;
216   DMPolytopeType     cell_type;
217   CeedElemTopology   elem_topo;
218   PetscScalar       *q_points, *interp, *grad;
219   const PetscScalar *q_weights;
220   PetscDualSpace     dual_space;
221   PetscInt           num_dual_basis_vectors;
222   PetscInt           dim, num_comp, P, Q;
223 
224   PetscFunctionBeginUser;
225 
226   // General basis information
227   PetscCall(PetscFEGetSpatialDimension(fe, &dim));
228   PetscCall(PetscFEGetNumComponents(fe, &num_comp));
229   PetscCall(PetscFEGetDualSpace(fe, &dual_space));
230   PetscCall(PetscDualSpaceGetDimension(dual_space, &num_dual_basis_vectors));
231   P = num_dual_basis_vectors / num_comp;
232 
233   // Use depth label if no domain label present
234   if (!domain_label) {
235     PetscInt depth;
236 
237     PetscCall(DMPlexGetDepth(dm, &depth));
238     PetscCall(DMPlexGetDepthLabel(dm, &depth_label));
239     ids[0] = depth - height;
240   }
241 
242   // Get cell interp, grad, and quadrature data
243   PetscCall(DMGetFirstLabeledPoint(dm, dm, domain_label ? domain_label : depth_label, 1, ids, height, &first_point, NULL));
244   PetscCall(DMPlexGetCellType(dm, first_point, &cell_type));
245   elem_topo = ElemTopologyP2C(cell_type);
246   if (!elem_topo) SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "DMPlex topology not supported");
247   {
248     size_t             q_points_size;
249     const PetscScalar *q_points_petsc;
250     PetscInt           q_dim;
251 
252     PetscCall(PetscQuadratureGetData(quadrature, &q_dim, NULL, &Q, &q_points_petsc, &q_weights));
253     q_points_size = Q * dim * sizeof(CeedScalar);
254     PetscCall(PetscCalloc(q_points_size, &q_points));
255     for (PetscInt q = 0; q < Q; q++) {
256       for (PetscInt d = 0; d < q_dim; d++) q_points[q * dim + d] = q_points_petsc[q * q_dim + d];
257     }
258   }
259 
260   // Convert to libCEED orientation
261   {
262     PetscBool       is_simplex  = PETSC_FALSE;
263     IS              permutation = NULL;
264     const PetscInt *permutation_indices;
265 
266     PetscCall(DMPlexIsSimplex(dm, &is_simplex));
267     if (!is_simplex) {
268       PetscSection section;
269 
270       // -- Get permutation
271       PetscCall(DMGetLocalSection(dm, &section));
272       PetscCall(PetscSectionGetClosurePermutation(section, (PetscObject)dm, dim, num_comp * P, &permutation));
273       PetscCall(ISGetIndices(permutation, &permutation_indices));
274     }
275 
276     // -- Copy interp, grad matrices
277     PetscCall(PetscCalloc(P * Q * sizeof(CeedScalar), &interp));
278     PetscCall(PetscCalloc(P * Q * dim * sizeof(CeedScalar), &grad));
279     const CeedInt c = 0;
280     for (CeedInt q = 0; q < Q; q++) {
281       for (CeedInt p_ceed = 0; p_ceed < P; p_ceed++) {
282         CeedInt p_petsc = is_simplex ? (p_ceed * num_comp) : permutation_indices[p_ceed * num_comp];
283 
284         interp[q * P + p_ceed] = basis_tabulation->T[0][((face * Q + q) * P * num_comp + p_petsc) * num_comp + c];
285         for (CeedInt d = 0; d < dim; d++) {
286           grad[(d * Q + q) * P + p_ceed] = basis_tabulation->T[1][(((face * Q + q) * P * num_comp + p_petsc) * num_comp + c) * dim + d];
287         }
288       }
289     }
290 
291     // -- Cleanup
292     if (permutation) PetscCall(ISRestoreIndices(permutation, &permutation_indices));
293     PetscCall(ISDestroy(&permutation));
294   }
295 
296   // Finally, create libCEED basis
297   CeedBasisCreateH1(ceed, elem_topo, num_comp, P, Q, interp, grad, q_points, q_weights, basis);
298   PetscCall(PetscFree(q_points));
299   PetscCall(PetscFree(interp));
300   PetscCall(PetscFree(grad));
301 
302   PetscFunctionReturn(PETSC_SUCCESS);
303 }
304 
305 // -----------------------------------------------------------------------------
306 // Get CEED Basis from DMPlex
307 // -----------------------------------------------------------------------------
308 PetscErrorCode CreateBasisFromPlex(Ceed ceed, DM dm, DMLabel domain_label, CeedInt label_value, CeedInt height, CeedInt dm_field, BPData bp_data,
309                                    CeedBasis *basis) {
310   PetscDS         ds;
311   PetscFE         fe;
312   PetscQuadrature quadrature;
313   PetscBool       is_simplex = PETSC_TRUE;
314   PetscInt        ds_field   = -1;
315 
316   PetscFunctionBeginUser;
317 
318   // Get element information
319   PetscCall(DMGetRegionDS(dm, domain_label, NULL, &ds, NULL));
320   PetscCall(DMFieldToDSField(dm, domain_label, dm_field, &ds_field));
321   PetscCall(PetscDSGetDiscretization(ds, ds_field, (PetscObject *)&fe));
322   PetscCall(PetscFEGetHeightSubspace(fe, height, &fe));
323   PetscCall(PetscFEGetQuadrature(fe, &quadrature));
324 
325   // Check if simplex or tensor-product mesh
326   PetscCall(DMPlexIsSimplex(dm, &is_simplex));
327 
328   // Build libCEED basis
329   if (is_simplex) {
330     PetscTabulation basis_tabulation;
331     PetscInt        num_derivatives = 1, face = 0;
332 
333     PetscCall(PetscFEGetCellTabulation(fe, num_derivatives, &basis_tabulation));
334     PetscCall(BasisCreateFromTabulation(ceed, dm, domain_label, label_value, height, face, fe, basis_tabulation, quadrature, basis));
335   } else {
336     PetscDualSpace dual_space;
337     PetscInt       num_dual_basis_vectors;
338     PetscInt       dim, num_comp, P, Q;
339 
340     PetscCall(PetscFEGetSpatialDimension(fe, &dim));
341     PetscCall(PetscFEGetNumComponents(fe, &num_comp));
342     PetscCall(PetscFEGetDualSpace(fe, &dual_space));
343     PetscCall(PetscDualSpaceGetDimension(dual_space, &num_dual_basis_vectors));
344     P = num_dual_basis_vectors / num_comp;
345     PetscCall(PetscQuadratureGetData(quadrature, NULL, NULL, &Q, NULL, NULL));
346 
347     CeedInt P_1d = (CeedInt)round(pow(P, 1.0 / dim));
348     CeedInt Q_1d = (CeedInt)round(pow(Q, 1.0 / dim));
349 
350     CeedBasisCreateTensorH1Lagrange(ceed, dim, num_comp, P_1d, Q_1d, bp_data.q_mode, basis);
351   }
352 
353   PetscFunctionReturn(PETSC_SUCCESS);
354 }
355 
356 // -----------------------------------------------------------------------------
357 // Utilities
358 // -----------------------------------------------------------------------------
359 
360 // Utility function, compute three factors of an integer
361 static void Split3(PetscInt size, PetscInt m[3], bool reverse) {
362   for (PetscInt d = 0, size_left = size; d < 3; d++) {
363     PetscInt try = (PetscInt)PetscCeilReal(PetscPowReal(size_left, 1. / (3 - d)));
364     while (try * (size_left / try) != size_left) try++;
365     m[reverse ? 2 - d : d] = try;
366     size_left /= try;
367   }
368 }
369 
370 static int Max3(const PetscInt a[3]) { return PetscMax(a[0], PetscMax(a[1], a[2])); }
371 
372 static int Min3(const PetscInt a[3]) { return PetscMin(a[0], PetscMin(a[1], a[2])); }
373 
374 // -----------------------------------------------------------------------------
375 // Create distribute dm
376 // -----------------------------------------------------------------------------
377 PetscErrorCode CreateDistributedDM(RunParams rp, DM *dm) {
378   PetscFunctionBeginUser;
379   // Setup DM
380   if (rp->read_mesh) {
381     PetscCall(DMPlexCreateFromFile(PETSC_COMM_WORLD, rp->filename, NULL, PETSC_TRUE, dm));
382   } else {
383     if (rp->user_l_nodes) {
384       // Find a nicely composite number of elements no less than global nodes
385       PetscMPIInt size;
386       PetscCall(MPI_Comm_size(rp->comm, &size));
387       for (PetscInt g_elem = PetscMax(1, size * rp->local_nodes / PetscPowInt(rp->degree, rp->dim));; g_elem++) {
388         Split3(g_elem, rp->mesh_elem, true);
389         if (Max3(rp->mesh_elem) / Min3(rp->mesh_elem) <= 2) break;
390       }
391     }
392 
393     PetscCall(DMPlexCreateBoxMesh(PETSC_COMM_WORLD, rp->dim, rp->simplex, rp->mesh_elem, NULL, NULL, NULL, PETSC_TRUE, dm));
394   }
395 
396   PetscCall(DMSetFromOptions(*dm));
397   PetscCall(DMViewFromOptions(*dm, NULL, "-dm_view"));
398 
399   PetscFunctionReturn(PETSC_SUCCESS);
400 }
401 
402 // -----------------------------------------------------------------------------
403