xref: /libCEED/examples/ceed/ex1-volume.c (revision 66087c0803b66a861339d30698fcb9988ebff34d)
1*66087c08SValeria Barra // Copyright (c) 2017-2018, Lawrence Livermore National Security, LLC.
2*66087c08SValeria Barra // Produced at the Lawrence Livermore National Laboratory. LLNL-CODE-734707.
3*66087c08SValeria Barra // All Rights reserved. See files LICENSE and NOTICE for details.
4*66087c08SValeria Barra //
5*66087c08SValeria Barra // This file is part of CEED, a collection of benchmarks, miniapps, software
6*66087c08SValeria Barra // libraries and APIs for efficient high-order finite element and spectral
7*66087c08SValeria Barra // element discretizations for exascale applications. For more information and
8*66087c08SValeria Barra // source code availability see http://github.com/ceed.
9*66087c08SValeria Barra //
10*66087c08SValeria Barra // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
11*66087c08SValeria Barra // a collaborative effort of two U.S. Department of Energy organizations (Office
12*66087c08SValeria Barra // of Science and the National Nuclear Security Administration) responsible for
13*66087c08SValeria Barra // the planning and preparation of a capable exascale ecosystem, including
14*66087c08SValeria Barra // software, applications, hardware, advanced system engineering and early
15*66087c08SValeria Barra // testbed platforms, in support of the nation's exascale computing imperative.
16*66087c08SValeria Barra 
17*66087c08SValeria Barra //                             libCEED Example 1
18*66087c08SValeria Barra //
19*66087c08SValeria Barra // This example illustrates a simple usage of libCEED to compute the volume of a
20*66087c08SValeria Barra // 3D body using matrix-free application of a mass operator.  Arbitrary mesh and
21*66087c08SValeria Barra // solution orders in 1D, 2D and 3D are supported from the same code.
22*66087c08SValeria Barra //
23*66087c08SValeria Barra // The example has no dependencies, and is designed to be self-contained. For
24*66087c08SValeria Barra // additional examples that use external discretization libraries (MFEM, PETSc,
25*66087c08SValeria Barra // etc.) see the subdirectories in libceed/examples.
26*66087c08SValeria Barra //
27*66087c08SValeria Barra // All libCEED objects use a Ceed device object constructed based on a command
28*66087c08SValeria Barra // line argument (-ceed).
29*66087c08SValeria Barra //
30*66087c08SValeria Barra // Build with:
31*66087c08SValeria Barra //
32*66087c08SValeria Barra //     make ex1-volume [CEED_DIR=</path/to/libceed>]
33*66087c08SValeria Barra //
34*66087c08SValeria Barra // Sample runs:
35*66087c08SValeria Barra //
36*66087c08SValeria Barra //     ./ex1-volume
37*66087c08SValeria Barra //     ./ex1-volume -ceed /cpu/self
38*66087c08SValeria Barra //     ./ex1-volume -ceed /gpu/occa
39*66087c08SValeria Barra //     ./ex1-volume -ceed /cpu/occa
40*66087c08SValeria Barra //     ./ex1-volume -ceed /omp/occa
41*66087c08SValeria Barra //     ./ex1-volume -ceed /ocl/occa
42*66087c08SValeria Barra //     ./ex1-volume -m ../../../mfem/data/fichera.mesh
43*66087c08SValeria Barra //     ./ex1-volume -m ../../../mfem/data/star.vtk -o 3
44*66087c08SValeria Barra //     ./ex1-volume -m ../../../mfem/data/inline-segment.mesh -o 8
45*66087c08SValeria Barra //
46*66087c08SValeria Barra // Next line is grep'd from tap.sh to set its arguments
47*66087c08SValeria Barra // Test in 1D-3D
48*66087c08SValeria Barra //TESTARGS -ceed {ceed_resource} -d 2 -t
49*66087c08SValeria Barra //TESTARGS -ceed {ceed_resource} -d 1 -t -g
50*66087c08SValeria Barra //TESTARGS -ceed {ceed_resource} -d 2 -t -g
51*66087c08SValeria Barra //TESTARGS -ceed {ceed_resource} -d 3 -t -g
52*66087c08SValeria Barra 
53*66087c08SValeria Barra /// @file
54*66087c08SValeria Barra /// libCEED example using mass operator to compute volume
55*66087c08SValeria Barra 
56*66087c08SValeria Barra #include <ceed.h>
57*66087c08SValeria Barra #include <stdlib.h>
58*66087c08SValeria Barra #include <math.h>
59*66087c08SValeria Barra #include <string.h>
60*66087c08SValeria Barra 
61*66087c08SValeria Barra #include "ex1-volume.h"
62*66087c08SValeria Barra 
63*66087c08SValeria Barra // Auxiliary functions.
64*66087c08SValeria Barra int GetCartesianMeshSize(int dim, int order, int prob_size, int nxyz[3]);
65*66087c08SValeria Barra int BuildCartesianRestriction(Ceed ceed, int dim, int nxyz[3], int order,
66*66087c08SValeria Barra                               int ncomp, CeedInt *size, CeedInt num_qpts,
67*66087c08SValeria Barra                               CeedElemRestriction *restr,
68*66087c08SValeria Barra                               CeedElemRestriction *restr_i);
69*66087c08SValeria Barra int SetCartesianMeshCoords(int dim, int nxyz[3], int mesh_order,
70*66087c08SValeria Barra                            CeedVector mesh_coords);
71*66087c08SValeria Barra CeedScalar TransformMeshCoords(int dim, int mesh_size, CeedVector mesh_coords);
72*66087c08SValeria Barra 
73*66087c08SValeria Barra 
74*66087c08SValeria Barra int main(int argc, const char *argv[]) {
75*66087c08SValeria Barra   const char *ceed_spec = "/cpu/self";
76*66087c08SValeria Barra   int dim        = 3;           // dimension of the mesh
77*66087c08SValeria Barra   int ncompx     = 3;           // number of x components
78*66087c08SValeria Barra   int mesh_order = 4;           // polynomial degree for the mesh
79*66087c08SValeria Barra   int sol_order  = 4;           // polynomial degree for the solution
80*66087c08SValeria Barra   int num_qpts   = sol_order+2; // number of 1D quadrature points
81*66087c08SValeria Barra   int prob_size  = -1;          // approximate problem size
82*66087c08SValeria Barra   int help = 0, test = 0, gallery = 0;
83*66087c08SValeria Barra 
84*66087c08SValeria Barra   // Process command line arguments.
85*66087c08SValeria Barra   for (int ia = 1; ia < argc; ia++) {
86*66087c08SValeria Barra     int next_arg = ((ia+1) < argc), parse_error = 0;
87*66087c08SValeria Barra     if (!strcmp(argv[ia],"-h")) {
88*66087c08SValeria Barra       help = 1;
89*66087c08SValeria Barra     } else if (!strcmp(argv[ia],"-c") || !strcmp(argv[ia],"-ceed")) {
90*66087c08SValeria Barra       parse_error = next_arg ? ceed_spec = argv[++ia], 0 : 1;
91*66087c08SValeria Barra     } else if (!strcmp(argv[ia],"-d")) {
92*66087c08SValeria Barra       parse_error = next_arg ? dim = atoi(argv[++ia]), 0 : 1;
93*66087c08SValeria Barra       ncompx = dim;
94*66087c08SValeria Barra     } else if (!strcmp(argv[ia],"-m")) {
95*66087c08SValeria Barra       parse_error = next_arg ? mesh_order = atoi(argv[++ia]), 0 : 1;
96*66087c08SValeria Barra     } else if (!strcmp(argv[ia],"-o")) {
97*66087c08SValeria Barra       parse_error = next_arg ? sol_order = atoi(argv[++ia]), 0 : 1;
98*66087c08SValeria Barra     } else if (!strcmp(argv[ia],"-q")) {
99*66087c08SValeria Barra       parse_error = next_arg ? num_qpts = atoi(argv[++ia]), 0 : 1;
100*66087c08SValeria Barra     } else if (!strcmp(argv[ia],"-s")) {
101*66087c08SValeria Barra       parse_error = next_arg ? prob_size = atoi(argv[++ia]), 0 : 1;
102*66087c08SValeria Barra     } else if (!strcmp(argv[ia],"-t")) {
103*66087c08SValeria Barra       test = 1;
104*66087c08SValeria Barra     } else if (!strcmp(argv[ia],"-g")) {
105*66087c08SValeria Barra       gallery = 1;
106*66087c08SValeria Barra     }
107*66087c08SValeria Barra     if (parse_error) {
108*66087c08SValeria Barra       printf("Error parsing command line options.\n");
109*66087c08SValeria Barra       return 1;
110*66087c08SValeria Barra     }
111*66087c08SValeria Barra   }
112*66087c08SValeria Barra   if (prob_size < 0) prob_size = test ? 8*16 : 256*1024;
113*66087c08SValeria Barra 
114*66087c08SValeria Barra   // Print the values of all options:
115*66087c08SValeria Barra   if (!test || help) {
116*66087c08SValeria Barra     printf("Selected options: [command line option] : <current value>\n");
117*66087c08SValeria Barra     printf("  Ceed specification [-c] : %s\n", ceed_spec);
118*66087c08SValeria Barra     printf("  Mesh dimension     [-d] : %d\n", dim);
119*66087c08SValeria Barra     printf("  Mesh order         [-m] : %d\n", mesh_order);
120*66087c08SValeria Barra     printf("  Solution order     [-o] : %d\n", sol_order);
121*66087c08SValeria Barra     printf("  Num. 1D quadr. pts [-q] : %d\n", num_qpts);
122*66087c08SValeria Barra     printf("  Approx. # unknowns [-s] : %d\n", prob_size);
123*66087c08SValeria Barra     printf("  QFunction source   [-g] : %s\n", gallery?"gallery":"header");
124*66087c08SValeria Barra     if (help) {
125*66087c08SValeria Barra       printf("Test/quiet mode is %s\n", (test?"ON":"OFF (use -t to enable)"));
126*66087c08SValeria Barra       return 0;
127*66087c08SValeria Barra     }
128*66087c08SValeria Barra     printf("\n");
129*66087c08SValeria Barra   }
130*66087c08SValeria Barra 
131*66087c08SValeria Barra   // Select appropriate backend and logical device based on the <ceed-spec>
132*66087c08SValeria Barra   // command line argument.
133*66087c08SValeria Barra   Ceed ceed;
134*66087c08SValeria Barra   CeedInit(ceed_spec, &ceed);
135*66087c08SValeria Barra 
136*66087c08SValeria Barra   // Construct the mesh and solution bases.
137*66087c08SValeria Barra   CeedBasis mesh_basis, sol_basis;
138*66087c08SValeria Barra   CeedBasisCreateTensorH1Lagrange(ceed, dim, ncompx, mesh_order+1, num_qpts,
139*66087c08SValeria Barra                                   CEED_GAUSS, &mesh_basis);
140*66087c08SValeria Barra   CeedBasisCreateTensorH1Lagrange(ceed, dim, 1, sol_order+1, num_qpts,
141*66087c08SValeria Barra                                   CEED_GAUSS, &sol_basis);
142*66087c08SValeria Barra 
143*66087c08SValeria Barra   // Determine the mesh size based on the given approximate problem size.
144*66087c08SValeria Barra   int nxyz[dim];
145*66087c08SValeria Barra   GetCartesianMeshSize(dim, sol_order, prob_size, nxyz);
146*66087c08SValeria Barra 
147*66087c08SValeria Barra   if (!test) {
148*66087c08SValeria Barra     printf("Mesh size: nx = %d", nxyz[0]);
149*66087c08SValeria Barra     if (dim > 1) { printf(", ny = %d", nxyz[1]); }
150*66087c08SValeria Barra     if (dim > 2) { printf(", nz = %d", nxyz[2]); }
151*66087c08SValeria Barra     printf("\n");
152*66087c08SValeria Barra   }
153*66087c08SValeria Barra 
154*66087c08SValeria Barra   // Build CeedElemRestriction objects describing the mesh and solution discrete
155*66087c08SValeria Barra   // representations.
156*66087c08SValeria Barra   CeedInt mesh_size, sol_size;
157*66087c08SValeria Barra   CeedElemRestriction mesh_restr, sol_restr, mesh_restr_i, sol_restr_i;
158*66087c08SValeria Barra   BuildCartesianRestriction(ceed, dim, nxyz, mesh_order, ncompx, &mesh_size,
159*66087c08SValeria Barra                             num_qpts, &mesh_restr, &mesh_restr_i);
160*66087c08SValeria Barra   BuildCartesianRestriction(ceed, dim, nxyz, sol_order, 1, &sol_size,
161*66087c08SValeria Barra                             num_qpts, &sol_restr, &sol_restr_i);
162*66087c08SValeria Barra   if (!test) {
163*66087c08SValeria Barra     printf("Number of mesh nodes     : %d\n", mesh_size/dim);
164*66087c08SValeria Barra     printf("Number of solution nodes : %d\n", sol_size);
165*66087c08SValeria Barra   }
166*66087c08SValeria Barra 
167*66087c08SValeria Barra   // Create a CeedVector with the mesh coordinates.
168*66087c08SValeria Barra   CeedVector mesh_coords;
169*66087c08SValeria Barra   CeedVectorCreate(ceed, mesh_size, &mesh_coords);
170*66087c08SValeria Barra   SetCartesianMeshCoords(dim, nxyz, mesh_order, mesh_coords);
171*66087c08SValeria Barra 
172*66087c08SValeria Barra   // Apply a transformation to the mesh.
173*66087c08SValeria Barra   CeedScalar exact_vol = TransformMeshCoords(dim, mesh_size, mesh_coords);
174*66087c08SValeria Barra 
175*66087c08SValeria Barra   // Context data to be passed to the 'f_build_mass' Q-function.
176*66087c08SValeria Barra   struct BuildContext build_ctx;
177*66087c08SValeria Barra   build_ctx.dim = build_ctx.space_dim = dim;
178*66087c08SValeria Barra 
179*66087c08SValeria Barra   // Create the Q-function that builds the mass operator (i.e. computes its
180*66087c08SValeria Barra   // quadrature data) and set its context data.
181*66087c08SValeria Barra   CeedQFunction build_qfunc;
182*66087c08SValeria Barra   switch (gallery) {
183*66087c08SValeria Barra   case 0:
184*66087c08SValeria Barra     // This creates the QFunction directly.
185*66087c08SValeria Barra     CeedQFunctionCreateInterior(ceed, 1, f_build_mass,
186*66087c08SValeria Barra                                 f_build_mass_loc, &build_qfunc);
187*66087c08SValeria Barra     CeedQFunctionAddInput(build_qfunc, "dx", ncompx*dim, CEED_EVAL_GRAD);
188*66087c08SValeria Barra     CeedQFunctionAddInput(build_qfunc, "weights", 1, CEED_EVAL_WEIGHT);
189*66087c08SValeria Barra     CeedQFunctionAddOutput(build_qfunc, "qdata", 1, CEED_EVAL_NONE);
190*66087c08SValeria Barra     CeedQFunctionSetContext(build_qfunc, &build_ctx, sizeof(build_ctx));
191*66087c08SValeria Barra     break;
192*66087c08SValeria Barra   case 1: {
193*66087c08SValeria Barra     // This creates the QFunction via the gallery.
194*66087c08SValeria Barra     char name[13] = "";
195*66087c08SValeria Barra     snprintf(name, sizeof name, "Mass%dDBuild", dim);
196*66087c08SValeria Barra     CeedQFunctionCreateInteriorByName(ceed, name, &build_qfunc);
197*66087c08SValeria Barra     break;
198*66087c08SValeria Barra   }
199*66087c08SValeria Barra   }
200*66087c08SValeria Barra 
201*66087c08SValeria Barra   // Create the operator that builds the quadrature data for the mass operator.
202*66087c08SValeria Barra   CeedOperator build_oper;
203*66087c08SValeria Barra   CeedOperatorCreate(ceed, build_qfunc, CEED_QFUNCTION_NONE,
204*66087c08SValeria Barra                      CEED_QFUNCTION_NONE, &build_oper);
205*66087c08SValeria Barra   CeedOperatorSetField(build_oper, "dx", mesh_restr, CEED_NOTRANSPOSE,
206*66087c08SValeria Barra                        mesh_basis,CEED_VECTOR_ACTIVE);
207*66087c08SValeria Barra   CeedOperatorSetField(build_oper, "weights", mesh_restr_i, CEED_NOTRANSPOSE,
208*66087c08SValeria Barra                        mesh_basis, CEED_VECTOR_NONE);
209*66087c08SValeria Barra   CeedOperatorSetField(build_oper, "qdata", sol_restr_i, CEED_NOTRANSPOSE,
210*66087c08SValeria Barra                        CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
211*66087c08SValeria Barra 
212*66087c08SValeria Barra   // Compute the quadrature data for the mass operator.
213*66087c08SValeria Barra   CeedVector qdata;
214*66087c08SValeria Barra   CeedInt elem_qpts = CeedIntPow(num_qpts, dim);
215*66087c08SValeria Barra   CeedInt num_elem = 1;
216*66087c08SValeria Barra   for (int d = 0; d < dim; d++)
217*66087c08SValeria Barra     num_elem *= nxyz[d];
218*66087c08SValeria Barra   CeedVectorCreate(ceed, num_elem*elem_qpts, &qdata);
219*66087c08SValeria Barra   if (!test) {
220*66087c08SValeria Barra     printf("Computing the quadrature data for the mass operator ...");
221*66087c08SValeria Barra     fflush(stdout);
222*66087c08SValeria Barra   }
223*66087c08SValeria Barra   CeedOperatorApply(build_oper, mesh_coords, qdata,
224*66087c08SValeria Barra                     CEED_REQUEST_IMMEDIATE);
225*66087c08SValeria Barra   if (!test) {
226*66087c08SValeria Barra     printf(" done.\n");
227*66087c08SValeria Barra   }
228*66087c08SValeria Barra 
229*66087c08SValeria Barra   // Create the Q-function that defines the action of the mass operator.
230*66087c08SValeria Barra   CeedQFunction apply_qfunc;
231*66087c08SValeria Barra   switch (gallery) {
232*66087c08SValeria Barra   case 0:
233*66087c08SValeria Barra     // This creates the QFunction directly.
234*66087c08SValeria Barra     CeedQFunctionCreateInterior(ceed, 1, f_apply_mass,
235*66087c08SValeria Barra                                 f_apply_mass_loc, &apply_qfunc);
236*66087c08SValeria Barra     CeedQFunctionAddInput(apply_qfunc, "u", 1, CEED_EVAL_INTERP);
237*66087c08SValeria Barra     CeedQFunctionAddInput(apply_qfunc, "qdata", 1, CEED_EVAL_NONE);
238*66087c08SValeria Barra     CeedQFunctionAddOutput(apply_qfunc, "v", 1, CEED_EVAL_INTERP);
239*66087c08SValeria Barra     break;
240*66087c08SValeria Barra   case 1:
241*66087c08SValeria Barra     // This creates the QFunction via the gallery.
242*66087c08SValeria Barra     CeedQFunctionCreateInteriorByName(ceed, "MassApply", &apply_qfunc);
243*66087c08SValeria Barra     break;
244*66087c08SValeria Barra   }
245*66087c08SValeria Barra 
246*66087c08SValeria Barra   // Create the mass operator.
247*66087c08SValeria Barra   CeedOperator oper;
248*66087c08SValeria Barra   CeedOperatorCreate(ceed, apply_qfunc, CEED_QFUNCTION_NONE,
249*66087c08SValeria Barra                      CEED_QFUNCTION_NONE, &oper);
250*66087c08SValeria Barra   CeedOperatorSetField(oper, "u", sol_restr, CEED_NOTRANSPOSE,
251*66087c08SValeria Barra                        sol_basis, CEED_VECTOR_ACTIVE);
252*66087c08SValeria Barra   CeedOperatorSetField(oper, "qdata", sol_restr_i, CEED_NOTRANSPOSE,
253*66087c08SValeria Barra                        CEED_BASIS_COLLOCATED, qdata);
254*66087c08SValeria Barra   CeedOperatorSetField(oper, "v", sol_restr, CEED_NOTRANSPOSE,
255*66087c08SValeria Barra                        sol_basis, CEED_VECTOR_ACTIVE);
256*66087c08SValeria Barra 
257*66087c08SValeria Barra   // Compute the mesh volume using the mass operator: vol = 1^T \cdot M \cdot 1
258*66087c08SValeria Barra   if (!test) {
259*66087c08SValeria Barra     printf("Computing the mesh volume using the formula: vol = 1^T.M.1 ...");
260*66087c08SValeria Barra     fflush(stdout);
261*66087c08SValeria Barra   }
262*66087c08SValeria Barra 
263*66087c08SValeria Barra   // Create auxiliary solution-size vectors.
264*66087c08SValeria Barra   CeedVector u, v;
265*66087c08SValeria Barra   CeedVectorCreate(ceed, sol_size, &u);
266*66087c08SValeria Barra   CeedVectorCreate(ceed, sol_size, &v);
267*66087c08SValeria Barra 
268*66087c08SValeria Barra   // Initialize 'u' and 'v' with ones.
269*66087c08SValeria Barra   CeedVectorSetValue(u, 1.0);
270*66087c08SValeria Barra 
271*66087c08SValeria Barra   // Apply the mass operator: 'u' -> 'v'.
272*66087c08SValeria Barra   CeedOperatorApply(oper, u, v, CEED_REQUEST_IMMEDIATE);
273*66087c08SValeria Barra 
274*66087c08SValeria Barra   // Compute and print the sum of the entries of 'v' giving the mesh volume.
275*66087c08SValeria Barra   const CeedScalar *v_host;
276*66087c08SValeria Barra   CeedVectorGetArrayRead(v, CEED_MEM_HOST, &v_host);
277*66087c08SValeria Barra   CeedScalar vol = 0.;
278*66087c08SValeria Barra   for (CeedInt i = 0; i < sol_size; i++) {
279*66087c08SValeria Barra     vol += v_host[i];
280*66087c08SValeria Barra   }
281*66087c08SValeria Barra   CeedVectorRestoreArrayRead(v, &v_host);
282*66087c08SValeria Barra   if (!test) {
283*66087c08SValeria Barra     printf(" done.\n");
284*66087c08SValeria Barra     printf("Exact mesh volume    : % .14g\n", exact_vol);
285*66087c08SValeria Barra     printf("Computed mesh volume : % .14g\n", vol);
286*66087c08SValeria Barra     printf("Volume error         : % .14g\n", vol-exact_vol);
287*66087c08SValeria Barra   } else {
288*66087c08SValeria Barra     CeedScalar tol = (dim==1? 1E-14 : dim==2? 1E-7 : 1E-5);
289*66087c08SValeria Barra     if (fabs(vol-exact_vol)>tol)
290*66087c08SValeria Barra       printf("Volume error : % .1e\n", vol-exact_vol);
291*66087c08SValeria Barra   }
292*66087c08SValeria Barra 
293*66087c08SValeria Barra   // Free dynamically allocated memory.
294*66087c08SValeria Barra   CeedVectorDestroy(&u);
295*66087c08SValeria Barra   CeedVectorDestroy(&v);
296*66087c08SValeria Barra   CeedVectorDestroy(&qdata);
297*66087c08SValeria Barra   CeedVectorDestroy(&mesh_coords);
298*66087c08SValeria Barra   CeedOperatorDestroy(&oper);
299*66087c08SValeria Barra   CeedQFunctionDestroy(&apply_qfunc);
300*66087c08SValeria Barra   CeedOperatorDestroy(&build_oper);
301*66087c08SValeria Barra   CeedQFunctionDestroy(&build_qfunc);
302*66087c08SValeria Barra   CeedElemRestrictionDestroy(&sol_restr);
303*66087c08SValeria Barra   CeedElemRestrictionDestroy(&mesh_restr);
304*66087c08SValeria Barra   CeedElemRestrictionDestroy(&sol_restr_i);
305*66087c08SValeria Barra   CeedElemRestrictionDestroy(&mesh_restr_i);
306*66087c08SValeria Barra   CeedBasisDestroy(&sol_basis);
307*66087c08SValeria Barra   CeedBasisDestroy(&mesh_basis);
308*66087c08SValeria Barra   CeedDestroy(&ceed);
309*66087c08SValeria Barra   return 0;
310*66087c08SValeria Barra }
311*66087c08SValeria Barra 
312*66087c08SValeria Barra 
313*66087c08SValeria Barra int GetCartesianMeshSize(int dim, int order, int prob_size, int nxyz[dim]) {
314*66087c08SValeria Barra   // Use the approximate formula:
315*66087c08SValeria Barra   //    prob_size ~ num_elem * order^dim
316*66087c08SValeria Barra   CeedInt num_elem = prob_size / CeedIntPow(order, dim);
317*66087c08SValeria Barra   CeedInt s = 0;  // find s: num_elem/2 < 2^s <= num_elem
318*66087c08SValeria Barra   while (num_elem > 1) {
319*66087c08SValeria Barra     num_elem /= 2;
320*66087c08SValeria Barra     s++;
321*66087c08SValeria Barra   }
322*66087c08SValeria Barra   CeedInt r = s%dim;
323*66087c08SValeria Barra   for (int d = 0; d < dim; d++) {
324*66087c08SValeria Barra     int sd = s/dim;
325*66087c08SValeria Barra     if (r > 0) { sd++; r--; }
326*66087c08SValeria Barra     nxyz[d] = 1 << sd;
327*66087c08SValeria Barra   }
328*66087c08SValeria Barra   return 0;
329*66087c08SValeria Barra }
330*66087c08SValeria Barra 
331*66087c08SValeria Barra int BuildCartesianRestriction(Ceed ceed, int dim, int nxyz[dim], int order,
332*66087c08SValeria Barra                               int ncomp, CeedInt *size, CeedInt num_qpts,
333*66087c08SValeria Barra                               CeedElemRestriction *restr,
334*66087c08SValeria Barra                               CeedElemRestriction *restr_i) {
335*66087c08SValeria Barra   CeedInt p = order, pp1 = p+1;
336*66087c08SValeria Barra   CeedInt nnodes = CeedIntPow(pp1, dim); // number of scal. nodes per element
337*66087c08SValeria Barra   CeedInt elem_qpts = CeedIntPow(num_qpts, dim); // number of qpts per element
338*66087c08SValeria Barra   CeedInt nd[3], num_elem = 1, scalar_size = 1;
339*66087c08SValeria Barra   for (int d = 0; d < dim; d++) {
340*66087c08SValeria Barra     num_elem *= nxyz[d];
341*66087c08SValeria Barra     nd[d] = nxyz[d]*p + 1;
342*66087c08SValeria Barra     scalar_size *= nd[d];
343*66087c08SValeria Barra   }
344*66087c08SValeria Barra   *size = scalar_size*ncomp;
345*66087c08SValeria Barra   // elem:         0             1                 n-1
346*66087c08SValeria Barra   //        |---*-...-*---|---*-...-*---|- ... -|--...--|
347*66087c08SValeria Barra   // nnodes:   0   1    p-1  p  p+1       2*p             n*p
348*66087c08SValeria Barra   CeedInt *el_nodes = malloc(sizeof(CeedInt)*num_elem*nnodes);
349*66087c08SValeria Barra   for (CeedInt e = 0; e < num_elem; e++) {
350*66087c08SValeria Barra     CeedInt exyz[3], re = e;
351*66087c08SValeria Barra     for (int d = 0; d < dim; d++) { exyz[d] = re%nxyz[d]; re /= nxyz[d]; }
352*66087c08SValeria Barra     CeedInt *loc_el_nodes = el_nodes + e*nnodes;
353*66087c08SValeria Barra     for (int lnodes = 0; lnodes < nnodes; lnodes++) {
354*66087c08SValeria Barra       CeedInt gnodes = 0, gnodes_stride = 1, rnodes = lnodes;
355*66087c08SValeria Barra       for (int d = 0; d < dim; d++) {
356*66087c08SValeria Barra         gnodes += (exyz[d]*p + rnodes%pp1) * gnodes_stride;
357*66087c08SValeria Barra         gnodes_stride *= nd[d];
358*66087c08SValeria Barra         rnodes /= pp1;
359*66087c08SValeria Barra       }
360*66087c08SValeria Barra       loc_el_nodes[lnodes] = gnodes;
361*66087c08SValeria Barra     }
362*66087c08SValeria Barra   }
363*66087c08SValeria Barra   CeedElemRestrictionCreate(ceed, num_elem, nnodes, scalar_size,
364*66087c08SValeria Barra                             ncomp, CEED_MEM_HOST,
365*66087c08SValeria Barra                             CEED_COPY_VALUES, el_nodes, restr);
366*66087c08SValeria Barra   CeedElemRestrictionCreateIdentity(ceed, num_elem, elem_qpts,
367*66087c08SValeria Barra                                     elem_qpts*num_elem,
368*66087c08SValeria Barra                                     ncomp, restr_i);
369*66087c08SValeria Barra   free(el_nodes);
370*66087c08SValeria Barra   return 0;
371*66087c08SValeria Barra }
372*66087c08SValeria Barra 
373*66087c08SValeria Barra int SetCartesianMeshCoords(int dim, int nxyz[dim], int mesh_order,
374*66087c08SValeria Barra                            CeedVector mesh_coords) {
375*66087c08SValeria Barra   CeedInt p = mesh_order;
376*66087c08SValeria Barra   CeedInt nd[3], num_elem = 1, scalar_size = 1;
377*66087c08SValeria Barra   for (int d = 0; d < dim; d++) {
378*66087c08SValeria Barra     num_elem *= nxyz[d];
379*66087c08SValeria Barra     nd[d] = nxyz[d]*p + 1;
380*66087c08SValeria Barra     scalar_size *= nd[d];
381*66087c08SValeria Barra   }
382*66087c08SValeria Barra   CeedScalar *coords;
383*66087c08SValeria Barra   CeedVectorGetArray(mesh_coords, CEED_MEM_HOST, &coords);
384*66087c08SValeria Barra   CeedScalar *nodes = malloc(sizeof(CeedScalar)*(p+1));
385*66087c08SValeria Barra   // The H1 basis uses Lobatto quadrature points as nodes.
386*66087c08SValeria Barra   CeedLobattoQuadrature(p+1, nodes, NULL); // nodes are in [-1,1]
387*66087c08SValeria Barra   for (CeedInt i = 0; i <= p; i++) { nodes[i] = 0.5+0.5*nodes[i]; }
388*66087c08SValeria Barra   for (CeedInt gsnodes = 0; gsnodes < scalar_size; gsnodes++) {
389*66087c08SValeria Barra     CeedInt rnodes = gsnodes;
390*66087c08SValeria Barra     for (int d = 0; d < dim; d++) {
391*66087c08SValeria Barra       CeedInt d1d = rnodes%nd[d];
392*66087c08SValeria Barra       coords[gsnodes+scalar_size*d] = ((d1d/p)+nodes[d1d%p]) / nxyz[d];
393*66087c08SValeria Barra       rnodes /= nd[d];
394*66087c08SValeria Barra     }
395*66087c08SValeria Barra   }
396*66087c08SValeria Barra   free(nodes);
397*66087c08SValeria Barra   CeedVectorRestoreArray(mesh_coords, &coords);
398*66087c08SValeria Barra   return 0;
399*66087c08SValeria Barra }
400*66087c08SValeria Barra 
401*66087c08SValeria Barra #ifndef M_PI
402*66087c08SValeria Barra #define M_PI    3.14159265358979323846
403*66087c08SValeria Barra #define M_PI_2  1.57079632679489661923
404*66087c08SValeria Barra #endif
405*66087c08SValeria Barra 
406*66087c08SValeria Barra CeedScalar TransformMeshCoords(int dim, int mesh_size, CeedVector mesh_coords) {
407*66087c08SValeria Barra   CeedScalar exact_volume;
408*66087c08SValeria Barra   CeedScalar *coords;
409*66087c08SValeria Barra   CeedVectorGetArray(mesh_coords, CEED_MEM_HOST, &coords);
410*66087c08SValeria Barra   if (dim == 1) {
411*66087c08SValeria Barra     for (CeedInt i = 0; i < mesh_size; i++) {
412*66087c08SValeria Barra       // map [0,1] to [0,1] varying the mesh density
413*66087c08SValeria Barra       coords[i] = 0.5+1./sqrt(3.)*sin((2./3.)*M_PI*(coords[i]-0.5));
414*66087c08SValeria Barra     }
415*66087c08SValeria Barra     exact_volume = 1.;
416*66087c08SValeria Barra   } else {
417*66087c08SValeria Barra     CeedInt num_nodes = mesh_size/dim;
418*66087c08SValeria Barra     for (CeedInt i = 0; i < num_nodes; i++) {
419*66087c08SValeria Barra       // map (x,y) from [0,1]x[0,1] to the quarter annulus with polar
420*66087c08SValeria Barra       // coordinates, (r,phi) in [1,2]x[0,pi/2] with area = 3/4*pi
421*66087c08SValeria Barra       CeedScalar u = coords[i], v = coords[i+num_nodes];
422*66087c08SValeria Barra       u = 1.+u;
423*66087c08SValeria Barra       v = M_PI_2*v;
424*66087c08SValeria Barra       coords[i] = u*cos(v);
425*66087c08SValeria Barra       coords[i+num_nodes] = u*sin(v);
426*66087c08SValeria Barra     }
427*66087c08SValeria Barra     exact_volume = 3./4.*M_PI;
428*66087c08SValeria Barra   }
429*66087c08SValeria Barra   CeedVectorRestoreArray(mesh_coords, &coords);
430*66087c08SValeria Barra   return exact_volume;
431*66087c08SValeria Barra }
432