MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
solvers-atpmg.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, 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 "solvers-atpmg.hpp"
13
14#include "../interface/ceed.hpp"
15#include "../interface/util.hpp"
16
17#ifdef MFEM_USE_CEED
18
19#include <ceed/backend.h>
20#include <math.h>
21// todo: should probably use Ceed memory wrappers instead of calloc/free?
22#include <stdlib.h>
23
24namespace mfem
25{
26
27namespace ceed
28{
29
30// In one dimension, return corresponding coarse edof index for
31// given fine index, with -1 meaning the edof disappears on the
32// coarse grid
33int coarse_1d_edof(int i, int P1d, int coarse_P1d)
34{
35 int coarse_i = (i < coarse_P1d - 1) ? i : -1;
36 if (i == P1d - 1)
37 {
38 coarse_i = coarse_P1d - 1;
39 }
40 return coarse_i;
41}
42
43int reverse_coarse_1d_edof(int i, int P1d, int coarse_P1d)
44{
45 int coarse_i;
46 if (i > P1d - coarse_P1d)
47 {
48 coarse_i = i - (P1d - coarse_P1d);
49 }
50 else
51 {
52 coarse_i = -1;
53 }
54 if (i == 0)
55 {
56 coarse_i = 0;
57 }
58 return coarse_i;
59}
60
61int min4(int a, int b, int c, int d)
62{
63 if (a <= b && a <= c && a <= d)
64 {
65 return a;
66 }
67 else if (b <= a && b <= c && b <= d)
68 {
69 return b;
70 }
71 else if (c <= a && c <= b && c <= d)
72 {
73 return c;
74 }
75 else
76 {
77 return d;
78 }
79}
80
82 int order_reduction,
83 CeedElemRestriction er_in,
84 CeedElemRestriction* er_out,
85 CeedInt *&dof_map)
86{
87 int ierr;
88 Ceed ceed;
89 ierr = CeedElemRestrictionGetCeed(er_in, &ceed); PCeedChk(ierr);
90
91 CeedInt numelem, numcomp, elemsize;
92 CeedSize numnodes;
93 ierr = CeedElemRestrictionGetNumElements(er_in, &numelem); PCeedChk(ierr);
94 ierr = CeedElemRestrictionGetLVectorSize(er_in, &numnodes); PCeedChk(ierr);
95 ierr = CeedElemRestrictionGetElementSize(er_in, &elemsize); PCeedChk(ierr);
96 ierr = CeedElemRestrictionGetNumComponents(er_in, &numcomp); PCeedChk(ierr);
97 if (numcomp != 1)
98 {
99 // todo: multi-component will require more thought
100 return CeedError(ceed, 1, "Algebraic element restriction not "
101 "implemented for multiple components.");
102 }
103
104 int P1d = order + 1;
105 int coarse_P1d = P1d - order_reduction;
106 int dim = (log((double) elemsize) / log((double) P1d)) + 1.e-3;
107
108 CeedVector in_lvec, in_evec;
109 ierr = CeedElemRestrictionCreateVector(er_in, &in_lvec, &in_evec);
110 PCeedChk(ierr);
111
112 // Create the elem_dof array from the given high-order ElemRestriction
113 // by using it to map the L-vector indices to an E-vector
114 CeedScalar * lvec_data;
115 ierr = CeedVectorGetArrayWrite(in_lvec, CEED_MEM_HOST, &lvec_data);
116 PCeedChk(ierr);
117 for (CeedSize i = 0; i < numnodes; ++i)
118 {
119 lvec_data[i] = (CeedScalar) i;
120 }
121 ierr = CeedVectorRestoreArray(in_lvec, &lvec_data); PCeedChk(ierr);
122 CeedInt in_layout[3];
123#if CEED_VERSION_GE(0, 13, 0)
124 ierr = CeedElemRestrictionGetELayout(er_in, in_layout); PCeedChk(ierr);
125#else
126 ierr = CeedElemRestrictionGetELayout(er_in, &in_layout); PCeedChk(ierr);
127#endif
128 if (in_layout[0] == 0 && in_layout[1] == 0 && in_layout[2] == 0)
129 {
130 return CeedError(ceed, 1, "Cannot interpret e-vector ordering of given"
131 "CeedElemRestriction!");
132 }
133 ierr = CeedElemRestrictionApply(er_in, CEED_NOTRANSPOSE, in_lvec, in_evec,
134 CEED_REQUEST_IMMEDIATE); PCeedChk(ierr);
135 ierr = CeedVectorDestroy(&in_lvec); PCeedChk(ierr);
136 const CeedScalar * in_elem_dof;
137 ierr = CeedVectorGetArrayRead(in_evec, CEED_MEM_HOST, &in_elem_dof);
138 PCeedChk(ierr);
139
140 // Create a map (dof_map) that maps high-order ldof indices to
141 // low-order ldof indices, with -1 indicating no correspondence
142 // (NOTE: it is the caller's responsibility to free dof_map)
143 dof_map = new CeedInt[numnodes];
144 for (CeedSize i = 0; i < numnodes; ++i)
145 {
146 dof_map[i] = -1;
147 }
148 CeedInt coarse_elemsize = pow(coarse_P1d, dim);
149 CeedInt * out_elem_dof = new CeedInt[coarse_elemsize * numelem];
150 const double rounding_guard = 1.e-10;
151 int running_out_ldof_count = 0;
152 if (dim == 2)
153 {
154 for (int e = 0; e < numelem; ++e)
155 {
156 // Loop over edofs in element
157 for (int i = 0; i < P1d; ++i)
158 {
159 for (int j = 0; j < P1d; ++j)
160 {
161 // Determine topology; is this edof on the outside of the element
162 // in the i or j direction?
163 int in_edof = i*P1d + j;
164 const int edof_index = in_edof*in_layout[0] + e*in_layout[2];
165 int in_ldof = in_elem_dof[edof_index] + rounding_guard;
166 bool i_edge = (i == 0 || i == P1d - 1);
167 bool j_edge = (j == 0 || j == P1d - 1);
168
169 // Determine corresponding coarse 1D edof indices
170 // We do this systematically, orienting edges and faces based on ldof
171 // orientation, so that the choices are consistent when we visit a
172 // shared dof multiple times
173 int coarse_i, coarse_j;
174 if (i_edge == j_edge) // edof is a vertex or interior
175 {
176 // note that interiors could be done with elements in parallel
177 // (you'd have to rethink numbering but it could be done in advance)
178 coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
179 coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
180 }
181 else // edof is on an edge but not a vertex
182 {
183 // Orient coarse_i, coarse_j based on numbering of ldofs on vertices
184 int left_in_edof, left_in_ldof, right_in_edof, right_in_ldof;
185 if (i_edge)
186 {
187 left_in_edof = i*P1d + 0;
188 right_in_edof = i*P1d + (P1d - 1);
189 left_in_ldof = in_elem_dof[left_in_edof*in_layout[0]+e*in_layout[2]]
190 + rounding_guard;
191 right_in_ldof = in_elem_dof[right_in_edof*in_layout[0]+e*in_layout[2]]
192 + rounding_guard;
193 coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
194 coarse_j = (left_in_ldof < right_in_ldof) ?
195 coarse_1d_edof(j, P1d, coarse_P1d) : reverse_coarse_1d_edof(j, P1d, coarse_P1d);
196 }
197 else
198 {
199 left_in_edof = 0*P1d + j;
200 right_in_edof = (P1d - 1)*P1d + j;
201 left_in_ldof = in_elem_dof[left_in_edof*in_layout[0]+e*in_layout[2]]
202 + rounding_guard;
203 right_in_ldof = in_elem_dof[right_in_edof*in_layout[0]+e*in_layout[2]]
204 + rounding_guard;
205 coarse_i = (left_in_ldof < right_in_ldof) ?
206 coarse_1d_edof(i, P1d, coarse_P1d) : reverse_coarse_1d_edof(i, P1d, coarse_P1d);
207 coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
208 }
209 }
210
211 // Select edof to be on coarse grid and assign numbering and maps
212 if (coarse_i >= 0 && coarse_j >= 0)
213 {
214 int out_edof = coarse_i*coarse_P1d + coarse_j;
215 if (dof_map[in_ldof] >= 0)
216 {
217 out_elem_dof[e*coarse_elemsize + out_edof] = dof_map[in_ldof];
218 }
219 else
220 {
221 out_elem_dof[e*coarse_elemsize + out_edof] = running_out_ldof_count;
222 dof_map[in_ldof] = running_out_ldof_count;
223 running_out_ldof_count++;
224 }
225 }
226 }
227 }
228 }
229 }
230 else if (dim == 3)
231 {
232 // The 3D code is perhaps overly complicated and could be optimized
233 for (int e = 0; e < numelem; ++e)
234 {
235 // Loop over edofs in element
236 for (int i = 0; i < P1d; ++i)
237 {
238 for (int j = 0; j < P1d; ++j)
239 {
240 for (int k = 0; k < P1d; ++k)
241 {
242 // Determine topology; is this edof on the outside of the element
243 // in the i, j, or k direction?
244 int in_edof = i*P1d*P1d + j*P1d + k;
245 int in_ldof = in_elem_dof[in_edof*in_layout[0]+e*in_layout[2]]
246 + rounding_guard;
247 bool i_edge = (i == 0 || i == P1d - 1);
248 bool j_edge = (j == 0 || j == P1d - 1);
249 bool k_edge = (k == 0 || k == P1d - 1);
250 int topo = 0;
251 if (i_edge) { topo++; }
252 if (j_edge) { topo++; }
253 if (k_edge) { topo++; }
254
255 // Determine corresponding coarse 1D edof indices
256 // We do this systematically, orienting edges and faces based on ldof
257 // orientation, so that the choices are consistent when we visit a
258 // shared dof multiple times
259 int coarse_i, coarse_j, coarse_k;
260 if (topo == 0 || topo == 3)
261 {
262 // edof is a vertex or interior
263 coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
264 coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
265 coarse_k = coarse_1d_edof(k, P1d, coarse_P1d);
266 }
267 else if (topo == 2)
268 {
269 // edof is on an edge, not a vertex
270 // Orient based on ldof numbering of vertices that define edge
271 int left_in_edof, left_in_ldof, right_in_edof, right_in_ldof;
272 if (!i_edge)
273 {
274 left_in_edof = 0*P1d*P1d + j*P1d + k;
275 right_in_edof = (P1d - 1)*P1d*P1d + j*P1d + k;
276 left_in_ldof = in_elem_dof[left_in_edof*in_layout[0]+e*in_layout[2]]
277 + rounding_guard;
278 right_in_ldof = in_elem_dof[right_in_edof*in_layout[0]+e*in_layout[2]]
279 + rounding_guard;
280 coarse_i = (left_in_ldof < right_in_ldof) ?
281 coarse_1d_edof(i, P1d, coarse_P1d) : reverse_coarse_1d_edof(i, P1d, coarse_P1d);
282 coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
283 coarse_k = coarse_1d_edof(k, P1d, coarse_P1d);
284 }
285 else if (!j_edge)
286 {
287 left_in_edof = i*P1d*P1d + 0*P1d + k;
288 right_in_edof = i*P1d*P1d + (P1d - 1)*P1d + k;
289 left_in_ldof = in_elem_dof[left_in_edof*in_layout[0]+e*in_layout[2]]
290 + rounding_guard;
291 right_in_ldof = in_elem_dof[right_in_edof*in_layout[0]+e*in_layout[2]]
292 + rounding_guard;
293 coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
294 coarse_j = (left_in_ldof < right_in_ldof) ?
295 coarse_1d_edof(j, P1d, coarse_P1d) : reverse_coarse_1d_edof(j, P1d, coarse_P1d);
296 coarse_k = coarse_1d_edof(k, P1d, coarse_P1d);
297 }
298 else
299 {
300 if (k_edge)
301 {
302 return CeedError(ceed, 1,
303 "Element connectivity does not make sense!");
304 }
305 left_in_edof = i*P1d*P1d + j*P1d + 0;
306 right_in_edof = i*P1d*P1d + j*P1d + (P1d - 1);
307 left_in_ldof = in_elem_dof[left_in_edof*in_layout[0]+e*in_layout[2]]
308 + rounding_guard;
309 right_in_ldof = in_elem_dof[right_in_edof*in_layout[0]+e*in_layout[2]]
310 + rounding_guard;
311 coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
312 coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
313 coarse_k = (left_in_ldof < right_in_ldof) ?
314 coarse_1d_edof(k, P1d, coarse_P1d) : reverse_coarse_1d_edof(k, P1d, coarse_P1d);
315 }
316 }
317 else
318 {
319 // edof is on a face, not an edge
320 // Orient based on four vertices that define the face
321 if (topo != 1)
322 {
323 return CeedError(ceed, 1,
324 "Element connectivity does not match topology!");
325 }
326 int bottom_left_edof, bottom_right_edof, top_left_edof, top_right_edof;
327 int bottom_left_ldof, bottom_right_ldof, top_left_ldof, top_right_ldof;
328 if (i_edge)
329 {
330 bottom_left_edof = i*P1d*P1d + 0*P1d + 0;
331 bottom_right_edof = i*P1d*P1d + 0*P1d + (P1d - 1);
332 top_right_edof = i*P1d*P1d + (P1d - 1)*P1d + (P1d - 1);
333 top_left_edof = i*P1d*P1d + (P1d - 1)*P1d + 0;
334 bottom_left_ldof = in_elem_dof[bottom_left_edof*in_layout[0]+e*in_layout[2]]
335 + rounding_guard;
336 bottom_right_ldof = in_elem_dof[bottom_right_edof*in_layout[0]+e*in_layout[2]]
337 + rounding_guard;
338 top_right_ldof = in_elem_dof[top_right_edof*in_layout[0]+e*in_layout[2]]
339 + rounding_guard;
340 top_left_ldof = in_elem_dof[top_left_edof*in_layout[0]+e*in_layout[2]]
341 + rounding_guard;
342 int m = min4(bottom_left_ldof, bottom_right_ldof, top_right_ldof,
343 top_left_ldof);
344 coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
345 if (m == bottom_left_ldof)
346 {
347 coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
348 coarse_k = coarse_1d_edof(k, P1d, coarse_P1d);
349 }
350 else if (m == bottom_right_ldof) // j=0, k=P1d-1
351 {
352 coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
353 coarse_k = reverse_coarse_1d_edof(k, P1d, coarse_P1d);
354 }
355 else if (m == top_right_ldof)
356 {
357 coarse_j = reverse_coarse_1d_edof(j, P1d, coarse_P1d);
358 coarse_k = reverse_coarse_1d_edof(k, P1d, coarse_P1d);
359 }
360 else // j=P1d-1, k=0
361 {
362 coarse_j = reverse_coarse_1d_edof(j, P1d, coarse_P1d);
363 coarse_k = coarse_1d_edof(k, P1d, coarse_P1d);
364 }
365 }
366 else if (j_edge)
367 {
368 bottom_left_edof = 0*P1d*P1d + j*P1d + 0;
369 bottom_right_edof = 0*P1d*P1d + j*P1d + (P1d - 1);
370 top_right_edof = (P1d - 1)*P1d*P1d + j*P1d + (P1d - 1);
371 top_left_edof = (P1d - 1)*P1d*P1d + j*P1d + 0;
372 bottom_left_ldof = in_elem_dof[bottom_left_edof*in_layout[0]+e*in_layout[2]]
373 + rounding_guard;
374 bottom_right_ldof = in_elem_dof[bottom_right_edof*in_layout[0]+e*in_layout[2]]
375 + rounding_guard;
376 top_right_ldof = in_elem_dof[top_right_edof*in_layout[0]+e*in_layout[2]]
377 + rounding_guard;
378 top_left_ldof = in_elem_dof[top_left_edof*in_layout[0]+e*in_layout[2]]
379 + rounding_guard;
380 int m = min4(bottom_left_ldof, bottom_right_ldof, top_right_ldof,
381 top_left_ldof);
382 coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
383 if (m == bottom_left_ldof)
384 {
385 coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
386 coarse_k = coarse_1d_edof(k, P1d, coarse_P1d);
387 }
388 else if (m == bottom_right_ldof) // i=0, k=P1d-1
389 {
390 coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
391 coarse_k = reverse_coarse_1d_edof(k, P1d, coarse_P1d);
392 }
393 else if (m == top_right_ldof)
394 {
395 coarse_i = reverse_coarse_1d_edof(i, P1d, coarse_P1d);
396 coarse_k = reverse_coarse_1d_edof(k, P1d, coarse_P1d);
397 }
398 else // i=P1d-1, k=0
399 {
400 coarse_i = reverse_coarse_1d_edof(i, P1d, coarse_P1d);
401 coarse_k = coarse_1d_edof(k, P1d, coarse_P1d);
402 }
403 }
404 else
405 {
406 if (!k_edge)
407 {
408 return CeedError(ceed, 1,
409 "Element connectivity does not make sense!");
410 }
411 bottom_left_edof = 0*P1d*P1d + 0*P1d + k;
412 bottom_right_edof = 0*P1d*P1d + (P1d - 1)*P1d + k;
413 top_right_edof = (P1d - 1)*P1d*P1d + (P1d - 1)*P1d + k;
414 top_left_edof = (P1d - 1)*P1d*P1d + 0*P1d + k;
415 bottom_left_ldof = in_elem_dof[bottom_left_edof*in_layout[0]+e*in_layout[2]]
416 + rounding_guard;
417 bottom_right_ldof = in_elem_dof[bottom_right_edof*in_layout[0]+e*in_layout[2]]
418 + rounding_guard;
419 top_right_ldof = in_elem_dof[top_right_edof*in_layout[0]+e*in_layout[2]]
420 + rounding_guard;
421 top_left_ldof = in_elem_dof[top_left_edof*in_layout[0]+e*in_layout[2]]
422 + rounding_guard;
423 int m = min4(bottom_left_ldof, bottom_right_ldof,
424 top_right_ldof, top_left_ldof);
425 coarse_k = coarse_1d_edof(k, P1d, coarse_P1d);
426 if (m == bottom_left_ldof)
427 {
428 coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
429 coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
430 }
431 else if (m == bottom_right_ldof) // i=0, j=P1d-1
432 {
433 coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
434 coarse_j = reverse_coarse_1d_edof(j, P1d, coarse_P1d);
435 }
436 else if (m == top_right_ldof)
437 {
438 coarse_i = reverse_coarse_1d_edof(i, P1d, coarse_P1d);
439 coarse_j = reverse_coarse_1d_edof(j, P1d, coarse_P1d);
440 }
441 else // i=P1d-1, j=0
442 {
443 coarse_i = reverse_coarse_1d_edof(i, P1d, coarse_P1d);
444 coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
445 }
446 }
447 }
448
449 // Select edof to be on coarse grid and assign numbering and maps
450 if (coarse_i >= 0 && coarse_j >= 0 && coarse_k >= 0)
451 {
452 int out_edof = coarse_i*coarse_P1d*coarse_P1d + coarse_j*coarse_P1d + coarse_k;
453 if (dof_map[in_ldof] >= 0)
454 {
455 out_elem_dof[e*coarse_elemsize + out_edof] = dof_map[in_ldof];
456 }
457 else
458 {
459 out_elem_dof[e*coarse_elemsize + out_edof] = running_out_ldof_count;
460 dof_map[in_ldof] = running_out_ldof_count;
461 running_out_ldof_count++;
462 }
463 }
464 }
465 }
466 }
467 }
468
469 }
470 else
471 {
472 return CeedError(ceed, 1,
473 "CeedATPMGElemRestriction does not yet support this dimension.");
474 }
475
476 ierr = CeedVectorRestoreArrayRead(in_evec, &in_elem_dof); PCeedChk(ierr);
477 ierr = CeedVectorDestroy(&in_evec); PCeedChk(ierr);
478
479 ierr = CeedElemRestrictionCreate(ceed, numelem, coarse_elemsize, numcomp,
480 0, running_out_ldof_count,
481 CEED_MEM_HOST, CEED_COPY_VALUES, out_elem_dof,
482 er_out); PCeedChk(ierr);
483
484 delete [] out_elem_dof;
485
486 return 0;
487}
488
489
490int CeedBasisATPMGCoarseToFine(Ceed ceed, int P1d, int dim, int order_reduction,
491 CeedBasis *basisc2f)
492{
493 // this assumes Lobatto nodes on fine and coarse again
494 // (not so hard to generalize, but we would have to write it ourselves instead of
495 // calling the following Ceed function)
496 int ierr;
497 ierr = CeedBasisCreateTensorH1Lagrange(ceed, dim, 1, P1d - order_reduction, P1d,
498 CEED_GAUSS_LOBATTO, basisc2f); PCeedChk(ierr);
499 return 0;
500}
501
502int CeedBasisATPMGCoarseToFine(CeedBasis basisin,
503 CeedBasis *basisc2f,
504 int order_reduction)
505{
506 int ierr;
507 Ceed ceed;
508 ierr = CeedBasisGetCeed(basisin, &ceed); PCeedChk(ierr);
509
510 CeedInt dim, P1d;
511 ierr = CeedBasisGetDimension(basisin, &dim); PCeedChk(ierr);
512 ierr = CeedBasisGetNumNodes1D(basisin, &P1d); PCeedChk(ierr);
513 ierr = CeedBasisATPMGCoarseToFine(ceed, P1d, dim, order_reduction,
514 basisc2f); PCeedChk(ierr);
515 return 0;
516}
517
518int CeedBasisATPMGCoarsen(CeedBasis basisin,
519 CeedBasis basisc2f,
520 CeedBasis* basisout,
521 int order_reduction)
522{
523 int ierr;
524 Ceed ceed;
525 ierr = CeedBasisGetCeed(basisin, &ceed); PCeedChk(ierr);
526
527 CeedInt dim, ncomp, P1d, Q1d;
528 ierr = CeedBasisGetDimension(basisin, &dim); PCeedChk(ierr);
529 ierr = CeedBasisGetNumComponents(basisin, &ncomp); PCeedChk(ierr);
530 ierr = CeedBasisGetNumNodes1D(basisin, &P1d); PCeedChk(ierr);
531 ierr = CeedBasisGetNumQuadraturePoints1D(basisin, &Q1d); PCeedChk(ierr);
532
533 CeedInt coarse_P1d = P1d - order_reduction;
534
535 const CeedScalar *interp1d;
536 ierr = CeedBasisGetInterp1D(basisin, &interp1d); PCeedChk(ierr);
537 const CeedScalar * grad1d;
538 ierr = CeedBasisGetGrad1D(basisin, &grad1d); PCeedChk(ierr);
539
540 CeedScalar * coarse_interp1d = new CeedScalar[coarse_P1d * Q1d];
541 CeedScalar * coarse_grad1d = new CeedScalar[coarse_P1d * Q1d];
542 CeedScalar * fine_nodal_points = new CeedScalar[P1d];
543
544 // these things are in [-1, 1], not [0, 1], which matters
545 // (todo: how can we determine this or something related, algebraically?)
546 /* one way you might be able to tell is to just run this algorithm
547 with coarse_P1d = 2 (i.e., linear) and look for symmetry in the coarse
548 basis matrix? */
549 ierr = CeedLobattoQuadrature(P1d, fine_nodal_points, NULL); PCeedChk(ierr);
550 for (int i = 0; i < P1d; ++i)
551 {
552 fine_nodal_points[i] = 0.5 * fine_nodal_points[i] + 0.5; // cheating
553 }
554
555 const CeedScalar *interp_ctof;
556 ierr = CeedBasisGetInterp1D(basisc2f, &interp_ctof); PCeedChk(ierr);
557
558 for (int i = 0; i < Q1d; ++i)
559 {
560 for (int j = 0; j < coarse_P1d; ++j)
561 {
562 coarse_interp1d[i * coarse_P1d + j] = 0.0;
563 coarse_grad1d[i * coarse_P1d + j] = 0.0;
564 for (int k = 0; k < P1d; ++k)
565 {
566 coarse_interp1d[i * coarse_P1d + j] += interp_ctof[k * coarse_P1d + j] *
567 interp1d[i * P1d + k];
568 coarse_grad1d[i * coarse_P1d + j] += interp_ctof[k * coarse_P1d + j] *
569 grad1d[i * P1d + k];
570 }
571 }
572 }
573
574 const CeedScalar * qref1d;
575 ierr = CeedBasisGetQRef(basisin, &qref1d); PCeedChk(ierr);
576 const CeedScalar * qweight1d;
577 ierr = CeedBasisGetQWeights(basisin, &qweight1d); PCeedChk(ierr);
578 ierr = CeedBasisCreateTensorH1(ceed, dim, ncomp,
579 coarse_P1d, Q1d, coarse_interp1d, coarse_grad1d,
580 qref1d, qweight1d, basisout); PCeedChk(ierr);
581
582 delete [] fine_nodal_points;
583 delete [] coarse_interp1d;
584 delete [] coarse_grad1d;
585
586 return 0;
587}
588
589int CeedATPMGOperator(CeedOperator oper, int order_reduction,
590 CeedElemRestriction coarse_er,
591 CeedBasis coarse_basis_in,
592 CeedBasis basis_ctof_in,
593 CeedOperator* out)
594{
595 (void)order_reduction;
596 (void)basis_ctof_in;
597
598 int ierr;
599 Ceed ceed;
600 ierr = CeedOperatorGetCeed(oper, &ceed); PCeedChk(ierr);
601
602 CeedQFunction qf;
603 ierr = CeedOperatorGetQFunction(oper, &qf); PCeedChk(ierr);
604 CeedInt numinputfields, numoutputfields;
605 CeedQFunctionField *inputqfields, *outputqfields;
606 ierr = CeedQFunctionGetFields(qf, &numinputfields, &inputqfields,
607 &numoutputfields, &outputqfields);
608 PCeedChk(ierr);
609 CeedOperatorField *inputfields, *outputfields;
610 ierr = CeedOperatorGetFields(oper, &numinputfields, &inputfields,
611 &numoutputfields, &outputfields);
612 PCeedChk(ierr);
613
614 CeedElemRestriction * er_input = new CeedElemRestriction[numinputfields];
615 CeedElemRestriction * er_output = new CeedElemRestriction[numoutputfields];
616 CeedVector * if_vector = new CeedVector[numinputfields];
617 CeedVector * of_vector = new CeedVector[numoutputfields];
618 CeedBasis * basis_input = new CeedBasis[numinputfields];
619 CeedBasis * basis_output = new CeedBasis[numoutputfields];
620 CeedBasis cbasis = coarse_basis_in;
621
622 int active_input_basis = -1;
623 for (int i = 0; i < numinputfields; ++i)
624 {
625 ierr = CeedOperatorFieldGetElemRestriction(inputfields[i],
626 &er_input[i]); PCeedChk(ierr);
627 ierr = CeedOperatorFieldGetVector(inputfields[i], &if_vector[i]);
628 PCeedChk(ierr);
629 ierr = CeedOperatorFieldGetBasis(inputfields[i], &basis_input[i]);
630 PCeedChk(ierr);
631 if (if_vector[i] == CEED_VECTOR_ACTIVE)
632 {
633 if (active_input_basis < 0)
634 {
635 active_input_basis = i;
636 }
637 else if (basis_input[i] != basis_input[active_input_basis])
638 {
639 return CeedError(ceed, 1, "Two different active input basis!");
640 }
641 }
642 }
643 for (int i = 0; i < numoutputfields; ++i)
644 {
645 ierr = CeedOperatorFieldGetElemRestriction(outputfields[i],
646 &er_output[i]); PCeedChk(ierr);
647 ierr = CeedOperatorFieldGetVector(outputfields[i], &of_vector[i]);
648 PCeedChk(ierr);
649 ierr = CeedOperatorFieldGetBasis(outputfields[i], &basis_output[i]);
650 PCeedChk(ierr);
651 if (of_vector[i] == CEED_VECTOR_ACTIVE)
652 {
653 // should already be coarsened
654 if (basis_output[i] != basis_input[active_input_basis])
655 {
656 return CeedError(ceed, 1, "Input and output basis do not match!");
657 }
658 if (er_output[i] != er_input[active_input_basis])
659 {
660 return CeedError(ceed, 1, "Input and output elem-restriction do not match!");
661 }
662 }
663 }
664
665 CeedOperator coper;
666 ierr = CeedOperatorCreate(ceed, qf, CEED_QFUNCTION_NONE, CEED_QFUNCTION_NONE,
667 &coper); PCeedChk(ierr);
668
669 for (int i = 0; i < numinputfields; ++i)
670 {
671#if CEED_VERSION_GE(0, 13, 0)
672 const char * fieldname;
673#else
674 char * fieldname;
675#endif
676 ierr = CeedQFunctionFieldGetName(inputqfields[i], &fieldname); PCeedChk(ierr);
677 if (if_vector[i] == CEED_VECTOR_ACTIVE)
678 {
679 ierr = CeedOperatorSetField(coper, fieldname, coarse_er, cbasis,
680 if_vector[i]); PCeedChk(ierr);
681 }
682 else
683 {
684 ierr = CeedOperatorSetField(coper, fieldname, er_input[i], basis_input[i],
685 if_vector[i]); PCeedChk(ierr);
686 }
687#if CEED_VERSION_GE(0, 13, 0)
688 ierr = CeedVectorDestroy(&if_vector[i]); PCeedChk(ierr);
689 ierr = CeedElemRestrictionDestroy(&er_input[i]); PCeedChk(ierr);
690 ierr = CeedBasisDestroy(&basis_input[i]); PCeedChk(ierr);
691#endif
692 }
693 for (int i = 0; i < numoutputfields; ++i)
694 {
695#if CEED_VERSION_GE(0, 13, 0)
696 const char * fieldname;
697#else
698 char * fieldname;
699#endif
700 ierr = CeedQFunctionFieldGetName(outputqfields[i], &fieldname); PCeedChk(ierr);
701 if (of_vector[i] == CEED_VECTOR_ACTIVE)
702 {
703 ierr = CeedOperatorSetField(coper, fieldname, coarse_er, cbasis,
704 of_vector[i]); PCeedChk(ierr);
705 }
706 else
707 {
708 ierr = CeedOperatorSetField(coper, fieldname, er_output[i], basis_output[i],
709 of_vector[i]); PCeedChk(ierr);
710 }
711#if CEED_VERSION_GE(0, 13, 0)
712 ierr = CeedVectorDestroy(&of_vector[i]); PCeedChk(ierr);
713 ierr = CeedElemRestrictionDestroy(&er_output[i]); PCeedChk(ierr);
714 ierr = CeedBasisDestroy(&basis_output[i]); PCeedChk(ierr);
715#endif
716 }
717 delete [] er_input;
718 delete [] er_output;
719 delete [] if_vector;
720 delete [] of_vector;
721 delete [] basis_input;
722 delete [] basis_output;
723
724 *out = coper;
725 return 0;
726}
727
728int CeedATPMGOperator(CeedOperator oper, int order_reduction,
729 CeedElemRestriction coarse_er,
730 CeedBasis *coarse_basis_out,
731 CeedBasis *basis_ctof_out,
732 CeedOperator *out)
733{
734 int ierr;
735
736 CeedQFunction qf;
737 ierr = CeedOperatorGetQFunction(oper, &qf); PCeedChk(ierr);
738 CeedInt numinputfields, numoutputfields;
739 CeedOperatorField *inputfields;
740 ierr = CeedOperatorGetFields(oper, &numinputfields, &inputfields,
741 &numoutputfields, NULL);
742 PCeedChk(ierr);
743
744 CeedBasis basis;
745 ierr = CeedOperatorGetActiveBasis(oper, &basis); PCeedChk(ierr);
746 ierr = CeedBasisATPMGCoarseToFine(basis, basis_ctof_out, order_reduction);
747 PCeedChk(ierr);
748 ierr = CeedBasisATPMGCoarsen(basis, *basis_ctof_out, coarse_basis_out,
749 order_reduction); PCeedChk(ierr);
750 ierr = CeedATPMGOperator(oper, order_reduction, coarse_er, *coarse_basis_out,
751 *basis_ctof_out, out); PCeedChk(ierr);
752 return 0;
753}
754
755int CeedOperatorGetOrder(CeedOperator oper, CeedInt * order)
756{
757 int ierr;
758
759 CeedOperatorField active_field;
760 ierr = CeedOperatorGetActiveField(oper, &active_field); PCeedChk(ierr);
761 CeedBasis basis;
762 ierr = CeedOperatorFieldGetBasis(active_field, &basis); PCeedChk(ierr);
763 int P1d;
764 ierr = CeedBasisGetNumNodes1D(basis, &P1d); PCeedChk(ierr);
765 *order = P1d - 1;
766#if CEED_VERSION_GE(0, 13, 0)
767 ierr = CeedBasisDestroy(&basis); PCeedChk(ierr);
768#endif
769 return 0;
770}
771
772int CeedATPMGBundle(CeedOperator oper, int order_reduction,
773 CeedBasis* coarse_basis_out,
774 CeedBasis* basis_ctof_out,
775 CeedElemRestriction* er_out,
776 CeedOperator* coarse_oper,
777 CeedInt *&dof_map)
778{
779 int ierr;
780 CeedInt order;
781 ierr = CeedOperatorGetOrder(oper, &order); PCeedChk(ierr);
782 CeedElemRestriction ho_er;
783 ierr = CeedOperatorGetActiveElemRestriction(oper, &ho_er); PCeedChk(ierr);
784 ierr = CeedATPMGElemRestriction(order, order_reduction, ho_er, er_out, dof_map);
785 PCeedChk(ierr);
786 ierr = CeedATPMGOperator(oper, order_reduction, *er_out, coarse_basis_out,
787 basis_ctof_out, coarse_oper); PCeedChk(ierr);
788 return 0;
789}
790
791} // namespace ceed
792
793} // namespace mfem
794
795#endif // MFEM_USE_CEED
int dim
Definition ex24.cpp:53
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
int min4(int a, int b, int c, int d)
int reverse_coarse_1d_edof(int i, int P1d, int coarse_P1d)
int CeedBasisATPMGCoarseToFine(Ceed ceed, int P1d, int dim, int order_reduction, CeedBasis *basisc2f)
Create coarse-to-fine basis, given number of input nodes and order reduction.
int coarse_1d_edof(int i, int P1d, int coarse_P1d)
int CeedOperatorGetOrder(CeedOperator oper, CeedInt *order)
int CeedATPMGOperator(CeedOperator oper, int order_reduction, CeedElemRestriction coarse_er, CeedBasis coarse_basis_in, CeedBasis basis_ctof_in, CeedOperator *out)
int CeedBasisATPMGCoarsen(CeedBasis basisin, CeedBasis basisc2f, CeedBasis *basisout, int order_reduction)
int CeedOperatorGetActiveField(CeedOperator oper, CeedOperatorField *field)
Definition util.cpp:131
int CeedATPMGBundle(CeedOperator oper, int order_reduction, CeedBasis *coarse_basis_out, CeedBasis *basis_ctof_out, CeedElemRestriction *er_out, CeedOperator *coarse_oper, CeedInt *&dof_map)
Given (fine) CeedOperator, produces everything you need for a coarse level (operator and interpolatio...
int CeedATPMGElemRestriction(int order, int order_reduction, CeedElemRestriction er_in, CeedElemRestriction *er_out, CeedInt *&dof_map)
Take given (high-order) CeedElemRestriction and make a new CeedElemRestriction, which corresponds to ...
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