Files
mfem/fem/ceed/solvers/solvers-atpmg.cpp
T
2025-08-15 09:09:50 -06:00

796 lines
33 KiB
C++

// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "solvers-atpmg.hpp"
#include "../interface/ceed.hpp"
#include "../interface/util.hpp"
#ifdef MFEM_USE_CEED
#include <ceed/backend.h>
#include <math.h>
// todo: should probably use Ceed memory wrappers instead of calloc/free?
#include <stdlib.h>
namespace mfem
{
namespace ceed
{
// In one dimension, return corresponding coarse edof index for
// given fine index, with -1 meaning the edof disappears on the
// coarse grid
int coarse_1d_edof(int i, int P1d, int coarse_P1d)
{
int coarse_i = (i < coarse_P1d - 1) ? i : -1;
if (i == P1d - 1)
{
coarse_i = coarse_P1d - 1;
}
return coarse_i;
}
int reverse_coarse_1d_edof(int i, int P1d, int coarse_P1d)
{
int coarse_i;
if (i > P1d - coarse_P1d)
{
coarse_i = i - (P1d - coarse_P1d);
}
else
{
coarse_i = -1;
}
if (i == 0)
{
coarse_i = 0;
}
return coarse_i;
}
int min4(int a, int b, int c, int d)
{
if (a <= b && a <= c && a <= d)
{
return a;
}
else if (b <= a && b <= c && b <= d)
{
return b;
}
else if (c <= a && c <= b && c <= d)
{
return c;
}
else
{
return d;
}
}
int CeedATPMGElemRestriction(int order,
int order_reduction,
CeedElemRestriction er_in,
CeedElemRestriction* er_out,
CeedInt *&dof_map)
{
int ierr;
Ceed ceed;
ierr = CeedElemRestrictionGetCeed(er_in, &ceed); PCeedChk(ierr);
CeedInt numelem, numcomp, elemsize;
CeedSize numnodes;
ierr = CeedElemRestrictionGetNumElements(er_in, &numelem); PCeedChk(ierr);
ierr = CeedElemRestrictionGetLVectorSize(er_in, &numnodes); PCeedChk(ierr);
ierr = CeedElemRestrictionGetElementSize(er_in, &elemsize); PCeedChk(ierr);
ierr = CeedElemRestrictionGetNumComponents(er_in, &numcomp); PCeedChk(ierr);
if (numcomp != 1)
{
// todo: multi-component will require more thought
return CeedError(ceed, 1, "Algebraic element restriction not "
"implemented for multiple components.");
}
int P1d = order + 1;
int coarse_P1d = P1d - order_reduction;
int dim = (log((double) elemsize) / log((double) P1d)) + 1.e-3;
CeedVector in_lvec, in_evec;
ierr = CeedElemRestrictionCreateVector(er_in, &in_lvec, &in_evec);
PCeedChk(ierr);
// Create the elem_dof array from the given high-order ElemRestriction
// by using it to map the L-vector indices to an E-vector
CeedScalar * lvec_data;
ierr = CeedVectorGetArrayWrite(in_lvec, CEED_MEM_HOST, &lvec_data);
PCeedChk(ierr);
for (CeedSize i = 0; i < numnodes; ++i)
{
lvec_data[i] = (CeedScalar) i;
}
ierr = CeedVectorRestoreArray(in_lvec, &lvec_data); PCeedChk(ierr);
CeedInt in_layout[3];
#if CEED_VERSION_GE(0, 13, 0)
ierr = CeedElemRestrictionGetELayout(er_in, in_layout); PCeedChk(ierr);
#else
ierr = CeedElemRestrictionGetELayout(er_in, &in_layout); PCeedChk(ierr);
#endif
if (in_layout[0] == 0 && in_layout[1] == 0 && in_layout[2] == 0)
{
return CeedError(ceed, 1, "Cannot interpret e-vector ordering of given"
"CeedElemRestriction!");
}
ierr = CeedElemRestrictionApply(er_in, CEED_NOTRANSPOSE, in_lvec, in_evec,
CEED_REQUEST_IMMEDIATE); PCeedChk(ierr);
ierr = CeedVectorDestroy(&in_lvec); PCeedChk(ierr);
const CeedScalar * in_elem_dof;
ierr = CeedVectorGetArrayRead(in_evec, CEED_MEM_HOST, &in_elem_dof);
PCeedChk(ierr);
// Create a map (dof_map) that maps high-order ldof indices to
// low-order ldof indices, with -1 indicating no correspondence
// (NOTE: it is the caller's responsibility to free dof_map)
dof_map = new CeedInt[numnodes];
for (CeedSize i = 0; i < numnodes; ++i)
{
dof_map[i] = -1;
}
CeedInt coarse_elemsize = pow(coarse_P1d, dim);
CeedInt * out_elem_dof = new CeedInt[coarse_elemsize * numelem];
const double rounding_guard = 1.e-10;
int running_out_ldof_count = 0;
if (dim == 2)
{
for (int e = 0; e < numelem; ++e)
{
// Loop over edofs in element
for (int i = 0; i < P1d; ++i)
{
for (int j = 0; j < P1d; ++j)
{
// Determine topology; is this edof on the outside of the element
// in the i or j direction?
int in_edof = i*P1d + j;
const int edof_index = in_edof*in_layout[0] + e*in_layout[2];
int in_ldof = in_elem_dof[edof_index] + rounding_guard;
bool i_edge = (i == 0 || i == P1d - 1);
bool j_edge = (j == 0 || j == P1d - 1);
// Determine corresponding coarse 1D edof indices
// We do this systematically, orienting edges and faces based on ldof
// orientation, so that the choices are consistent when we visit a
// shared dof multiple times
int coarse_i, coarse_j;
if (i_edge == j_edge) // edof is a vertex or interior
{
// note that interiors could be done with elements in parallel
// (you'd have to rethink numbering but it could be done in advance)
coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
}
else // edof is on an edge but not a vertex
{
// Orient coarse_i, coarse_j based on numbering of ldofs on vertices
int left_in_edof, left_in_ldof, right_in_edof, right_in_ldof;
if (i_edge)
{
left_in_edof = i*P1d + 0;
right_in_edof = i*P1d + (P1d - 1);
left_in_ldof = in_elem_dof[left_in_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
right_in_ldof = in_elem_dof[right_in_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
coarse_j = (left_in_ldof < right_in_ldof) ?
coarse_1d_edof(j, P1d, coarse_P1d) : reverse_coarse_1d_edof(j, P1d, coarse_P1d);
}
else
{
left_in_edof = 0*P1d + j;
right_in_edof = (P1d - 1)*P1d + j;
left_in_ldof = in_elem_dof[left_in_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
right_in_ldof = in_elem_dof[right_in_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
coarse_i = (left_in_ldof < right_in_ldof) ?
coarse_1d_edof(i, P1d, coarse_P1d) : reverse_coarse_1d_edof(i, P1d, coarse_P1d);
coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
}
}
// Select edof to be on coarse grid and assign numbering and maps
if (coarse_i >= 0 && coarse_j >= 0)
{
int out_edof = coarse_i*coarse_P1d + coarse_j;
if (dof_map[in_ldof] >= 0)
{
out_elem_dof[e*coarse_elemsize + out_edof] = dof_map[in_ldof];
}
else
{
out_elem_dof[e*coarse_elemsize + out_edof] = running_out_ldof_count;
dof_map[in_ldof] = running_out_ldof_count;
running_out_ldof_count++;
}
}
}
}
}
}
else if (dim == 3)
{
// The 3D code is perhaps overly complicated and could be optimized
for (int e = 0; e < numelem; ++e)
{
// Loop over edofs in element
for (int i = 0; i < P1d; ++i)
{
for (int j = 0; j < P1d; ++j)
{
for (int k = 0; k < P1d; ++k)
{
// Determine topology; is this edof on the outside of the element
// in the i, j, or k direction?
int in_edof = i*P1d*P1d + j*P1d + k;
int in_ldof = in_elem_dof[in_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
bool i_edge = (i == 0 || i == P1d - 1);
bool j_edge = (j == 0 || j == P1d - 1);
bool k_edge = (k == 0 || k == P1d - 1);
int topo = 0;
if (i_edge) { topo++; }
if (j_edge) { topo++; }
if (k_edge) { topo++; }
// Determine corresponding coarse 1D edof indices
// We do this systematically, orienting edges and faces based on ldof
// orientation, so that the choices are consistent when we visit a
// shared dof multiple times
int coarse_i, coarse_j, coarse_k;
if (topo == 0 || topo == 3)
{
// edof is a vertex or interior
coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
coarse_k = coarse_1d_edof(k, P1d, coarse_P1d);
}
else if (topo == 2)
{
// edof is on an edge, not a vertex
// Orient based on ldof numbering of vertices that define edge
int left_in_edof, left_in_ldof, right_in_edof, right_in_ldof;
if (!i_edge)
{
left_in_edof = 0*P1d*P1d + j*P1d + k;
right_in_edof = (P1d - 1)*P1d*P1d + j*P1d + k;
left_in_ldof = in_elem_dof[left_in_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
right_in_ldof = in_elem_dof[right_in_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
coarse_i = (left_in_ldof < right_in_ldof) ?
coarse_1d_edof(i, P1d, coarse_P1d) : reverse_coarse_1d_edof(i, P1d, coarse_P1d);
coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
coarse_k = coarse_1d_edof(k, P1d, coarse_P1d);
}
else if (!j_edge)
{
left_in_edof = i*P1d*P1d + 0*P1d + k;
right_in_edof = i*P1d*P1d + (P1d - 1)*P1d + k;
left_in_ldof = in_elem_dof[left_in_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
right_in_ldof = in_elem_dof[right_in_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
coarse_j = (left_in_ldof < right_in_ldof) ?
coarse_1d_edof(j, P1d, coarse_P1d) : reverse_coarse_1d_edof(j, P1d, coarse_P1d);
coarse_k = coarse_1d_edof(k, P1d, coarse_P1d);
}
else
{
if (k_edge)
{
return CeedError(ceed, 1,
"Element connectivity does not make sense!");
}
left_in_edof = i*P1d*P1d + j*P1d + 0;
right_in_edof = i*P1d*P1d + j*P1d + (P1d - 1);
left_in_ldof = in_elem_dof[left_in_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
right_in_ldof = in_elem_dof[right_in_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
coarse_k = (left_in_ldof < right_in_ldof) ?
coarse_1d_edof(k, P1d, coarse_P1d) : reverse_coarse_1d_edof(k, P1d, coarse_P1d);
}
}
else
{
// edof is on a face, not an edge
// Orient based on four vertices that define the face
if (topo != 1)
{
return CeedError(ceed, 1,
"Element connectivity does not match topology!");
}
int bottom_left_edof, bottom_right_edof, top_left_edof, top_right_edof;
int bottom_left_ldof, bottom_right_ldof, top_left_ldof, top_right_ldof;
if (i_edge)
{
bottom_left_edof = i*P1d*P1d + 0*P1d + 0;
bottom_right_edof = i*P1d*P1d + 0*P1d + (P1d - 1);
top_right_edof = i*P1d*P1d + (P1d - 1)*P1d + (P1d - 1);
top_left_edof = i*P1d*P1d + (P1d - 1)*P1d + 0;
bottom_left_ldof = in_elem_dof[bottom_left_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
bottom_right_ldof = in_elem_dof[bottom_right_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
top_right_ldof = in_elem_dof[top_right_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
top_left_ldof = in_elem_dof[top_left_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
int m = min4(bottom_left_ldof, bottom_right_ldof, top_right_ldof,
top_left_ldof);
coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
if (m == bottom_left_ldof)
{
coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
coarse_k = coarse_1d_edof(k, P1d, coarse_P1d);
}
else if (m == bottom_right_ldof) // j=0, k=P1d-1
{
coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
coarse_k = reverse_coarse_1d_edof(k, P1d, coarse_P1d);
}
else if (m == top_right_ldof)
{
coarse_j = reverse_coarse_1d_edof(j, P1d, coarse_P1d);
coarse_k = reverse_coarse_1d_edof(k, P1d, coarse_P1d);
}
else // j=P1d-1, k=0
{
coarse_j = reverse_coarse_1d_edof(j, P1d, coarse_P1d);
coarse_k = coarse_1d_edof(k, P1d, coarse_P1d);
}
}
else if (j_edge)
{
bottom_left_edof = 0*P1d*P1d + j*P1d + 0;
bottom_right_edof = 0*P1d*P1d + j*P1d + (P1d - 1);
top_right_edof = (P1d - 1)*P1d*P1d + j*P1d + (P1d - 1);
top_left_edof = (P1d - 1)*P1d*P1d + j*P1d + 0;
bottom_left_ldof = in_elem_dof[bottom_left_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
bottom_right_ldof = in_elem_dof[bottom_right_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
top_right_ldof = in_elem_dof[top_right_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
top_left_ldof = in_elem_dof[top_left_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
int m = min4(bottom_left_ldof, bottom_right_ldof, top_right_ldof,
top_left_ldof);
coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
if (m == bottom_left_ldof)
{
coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
coarse_k = coarse_1d_edof(k, P1d, coarse_P1d);
}
else if (m == bottom_right_ldof) // i=0, k=P1d-1
{
coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
coarse_k = reverse_coarse_1d_edof(k, P1d, coarse_P1d);
}
else if (m == top_right_ldof)
{
coarse_i = reverse_coarse_1d_edof(i, P1d, coarse_P1d);
coarse_k = reverse_coarse_1d_edof(k, P1d, coarse_P1d);
}
else // i=P1d-1, k=0
{
coarse_i = reverse_coarse_1d_edof(i, P1d, coarse_P1d);
coarse_k = coarse_1d_edof(k, P1d, coarse_P1d);
}
}
else
{
if (!k_edge)
{
return CeedError(ceed, 1,
"Element connectivity does not make sense!");
}
bottom_left_edof = 0*P1d*P1d + 0*P1d + k;
bottom_right_edof = 0*P1d*P1d + (P1d - 1)*P1d + k;
top_right_edof = (P1d - 1)*P1d*P1d + (P1d - 1)*P1d + k;
top_left_edof = (P1d - 1)*P1d*P1d + 0*P1d + k;
bottom_left_ldof = in_elem_dof[bottom_left_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
bottom_right_ldof = in_elem_dof[bottom_right_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
top_right_ldof = in_elem_dof[top_right_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
top_left_ldof = in_elem_dof[top_left_edof*in_layout[0]+e*in_layout[2]]
+ rounding_guard;
int m = min4(bottom_left_ldof, bottom_right_ldof,
top_right_ldof, top_left_ldof);
coarse_k = coarse_1d_edof(k, P1d, coarse_P1d);
if (m == bottom_left_ldof)
{
coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
}
else if (m == bottom_right_ldof) // i=0, j=P1d-1
{
coarse_i = coarse_1d_edof(i, P1d, coarse_P1d);
coarse_j = reverse_coarse_1d_edof(j, P1d, coarse_P1d);
}
else if (m == top_right_ldof)
{
coarse_i = reverse_coarse_1d_edof(i, P1d, coarse_P1d);
coarse_j = reverse_coarse_1d_edof(j, P1d, coarse_P1d);
}
else // i=P1d-1, j=0
{
coarse_i = reverse_coarse_1d_edof(i, P1d, coarse_P1d);
coarse_j = coarse_1d_edof(j, P1d, coarse_P1d);
}
}
}
// Select edof to be on coarse grid and assign numbering and maps
if (coarse_i >= 0 && coarse_j >= 0 && coarse_k >= 0)
{
int out_edof = coarse_i*coarse_P1d*coarse_P1d + coarse_j*coarse_P1d + coarse_k;
if (dof_map[in_ldof] >= 0)
{
out_elem_dof[e*coarse_elemsize + out_edof] = dof_map[in_ldof];
}
else
{
out_elem_dof[e*coarse_elemsize + out_edof] = running_out_ldof_count;
dof_map[in_ldof] = running_out_ldof_count;
running_out_ldof_count++;
}
}
}
}
}
}
}
else
{
return CeedError(ceed, 1,
"CeedATPMGElemRestriction does not yet support this dimension.");
}
ierr = CeedVectorRestoreArrayRead(in_evec, &in_elem_dof); PCeedChk(ierr);
ierr = CeedVectorDestroy(&in_evec); PCeedChk(ierr);
ierr = CeedElemRestrictionCreate(ceed, numelem, coarse_elemsize, numcomp,
0, running_out_ldof_count,
CEED_MEM_HOST, CEED_COPY_VALUES, out_elem_dof,
er_out); PCeedChk(ierr);
delete [] out_elem_dof;
return 0;
}
int CeedBasisATPMGCoarseToFine(Ceed ceed, int P1d, int dim, int order_reduction,
CeedBasis *basisc2f)
{
// this assumes Lobatto nodes on fine and coarse again
// (not so hard to generalize, but we would have to write it ourselves instead of
// calling the following Ceed function)
int ierr;
ierr = CeedBasisCreateTensorH1Lagrange(ceed, dim, 1, P1d - order_reduction, P1d,
CEED_GAUSS_LOBATTO, basisc2f); PCeedChk(ierr);
return 0;
}
int CeedBasisATPMGCoarseToFine(CeedBasis basisin,
CeedBasis *basisc2f,
int order_reduction)
{
int ierr;
Ceed ceed;
ierr = CeedBasisGetCeed(basisin, &ceed); PCeedChk(ierr);
CeedInt dim, P1d;
ierr = CeedBasisGetDimension(basisin, &dim); PCeedChk(ierr);
ierr = CeedBasisGetNumNodes1D(basisin, &P1d); PCeedChk(ierr);
ierr = CeedBasisATPMGCoarseToFine(ceed, P1d, dim, order_reduction,
basisc2f); PCeedChk(ierr);
return 0;
}
int CeedBasisATPMGCoarsen(CeedBasis basisin,
CeedBasis basisc2f,
CeedBasis* basisout,
int order_reduction)
{
int ierr;
Ceed ceed;
ierr = CeedBasisGetCeed(basisin, &ceed); PCeedChk(ierr);
CeedInt dim, ncomp, P1d, Q1d;
ierr = CeedBasisGetDimension(basisin, &dim); PCeedChk(ierr);
ierr = CeedBasisGetNumComponents(basisin, &ncomp); PCeedChk(ierr);
ierr = CeedBasisGetNumNodes1D(basisin, &P1d); PCeedChk(ierr);
ierr = CeedBasisGetNumQuadraturePoints1D(basisin, &Q1d); PCeedChk(ierr);
CeedInt coarse_P1d = P1d - order_reduction;
const CeedScalar *interp1d;
ierr = CeedBasisGetInterp1D(basisin, &interp1d); PCeedChk(ierr);
const CeedScalar * grad1d;
ierr = CeedBasisGetGrad1D(basisin, &grad1d); PCeedChk(ierr);
CeedScalar * coarse_interp1d = new CeedScalar[coarse_P1d * Q1d];
CeedScalar * coarse_grad1d = new CeedScalar[coarse_P1d * Q1d];
CeedScalar * fine_nodal_points = new CeedScalar[P1d];
// these things are in [-1, 1], not [0, 1], which matters
// (todo: how can we determine this or something related, algebraically?)
/* one way you might be able to tell is to just run this algorithm
with coarse_P1d = 2 (i.e., linear) and look for symmetry in the coarse
basis matrix? */
ierr = CeedLobattoQuadrature(P1d, fine_nodal_points, NULL); PCeedChk(ierr);
for (int i = 0; i < P1d; ++i)
{
fine_nodal_points[i] = 0.5 * fine_nodal_points[i] + 0.5; // cheating
}
const CeedScalar *interp_ctof;
ierr = CeedBasisGetInterp1D(basisc2f, &interp_ctof); PCeedChk(ierr);
for (int i = 0; i < Q1d; ++i)
{
for (int j = 0; j < coarse_P1d; ++j)
{
coarse_interp1d[i * coarse_P1d + j] = 0.0;
coarse_grad1d[i * coarse_P1d + j] = 0.0;
for (int k = 0; k < P1d; ++k)
{
coarse_interp1d[i * coarse_P1d + j] += interp_ctof[k * coarse_P1d + j] *
interp1d[i * P1d + k];
coarse_grad1d[i * coarse_P1d + j] += interp_ctof[k * coarse_P1d + j] *
grad1d[i * P1d + k];
}
}
}
const CeedScalar * qref1d;
ierr = CeedBasisGetQRef(basisin, &qref1d); PCeedChk(ierr);
const CeedScalar * qweight1d;
ierr = CeedBasisGetQWeights(basisin, &qweight1d); PCeedChk(ierr);
ierr = CeedBasisCreateTensorH1(ceed, dim, ncomp,
coarse_P1d, Q1d, coarse_interp1d, coarse_grad1d,
qref1d, qweight1d, basisout); PCeedChk(ierr);
delete [] fine_nodal_points;
delete [] coarse_interp1d;
delete [] coarse_grad1d;
return 0;
}
int CeedATPMGOperator(CeedOperator oper, int order_reduction,
CeedElemRestriction coarse_er,
CeedBasis coarse_basis_in,
CeedBasis basis_ctof_in,
CeedOperator* out)
{
(void)order_reduction;
(void)basis_ctof_in;
int ierr;
Ceed ceed;
ierr = CeedOperatorGetCeed(oper, &ceed); PCeedChk(ierr);
CeedQFunction qf;
ierr = CeedOperatorGetQFunction(oper, &qf); PCeedChk(ierr);
CeedInt numinputfields, numoutputfields;
CeedQFunctionField *inputqfields, *outputqfields;
ierr = CeedQFunctionGetFields(qf, &numinputfields, &inputqfields,
&numoutputfields, &outputqfields);
PCeedChk(ierr);
CeedOperatorField *inputfields, *outputfields;
ierr = CeedOperatorGetFields(oper, &numinputfields, &inputfields,
&numoutputfields, &outputfields);
PCeedChk(ierr);
CeedElemRestriction * er_input = new CeedElemRestriction[numinputfields];
CeedElemRestriction * er_output = new CeedElemRestriction[numoutputfields];
CeedVector * if_vector = new CeedVector[numinputfields];
CeedVector * of_vector = new CeedVector[numoutputfields];
CeedBasis * basis_input = new CeedBasis[numinputfields];
CeedBasis * basis_output = new CeedBasis[numoutputfields];
CeedBasis cbasis = coarse_basis_in;
int active_input_basis = -1;
for (int i = 0; i < numinputfields; ++i)
{
ierr = CeedOperatorFieldGetElemRestriction(inputfields[i],
&er_input[i]); PCeedChk(ierr);
ierr = CeedOperatorFieldGetVector(inputfields[i], &if_vector[i]);
PCeedChk(ierr);
ierr = CeedOperatorFieldGetBasis(inputfields[i], &basis_input[i]);
PCeedChk(ierr);
if (if_vector[i] == CEED_VECTOR_ACTIVE)
{
if (active_input_basis < 0)
{
active_input_basis = i;
}
else if (basis_input[i] != basis_input[active_input_basis])
{
return CeedError(ceed, 1, "Two different active input basis!");
}
}
}
for (int i = 0; i < numoutputfields; ++i)
{
ierr = CeedOperatorFieldGetElemRestriction(outputfields[i],
&er_output[i]); PCeedChk(ierr);
ierr = CeedOperatorFieldGetVector(outputfields[i], &of_vector[i]);
PCeedChk(ierr);
ierr = CeedOperatorFieldGetBasis(outputfields[i], &basis_output[i]);
PCeedChk(ierr);
if (of_vector[i] == CEED_VECTOR_ACTIVE)
{
// should already be coarsened
if (basis_output[i] != basis_input[active_input_basis])
{
return CeedError(ceed, 1, "Input and output basis do not match!");
}
if (er_output[i] != er_input[active_input_basis])
{
return CeedError(ceed, 1, "Input and output elem-restriction do not match!");
}
}
}
CeedOperator coper;
ierr = CeedOperatorCreate(ceed, qf, CEED_QFUNCTION_NONE, CEED_QFUNCTION_NONE,
&coper); PCeedChk(ierr);
for (int i = 0; i < numinputfields; ++i)
{
#if CEED_VERSION_GE(0, 13, 0)
const char * fieldname;
#else
char * fieldname;
#endif
ierr = CeedQFunctionFieldGetName(inputqfields[i], &fieldname); PCeedChk(ierr);
if (if_vector[i] == CEED_VECTOR_ACTIVE)
{
ierr = CeedOperatorSetField(coper, fieldname, coarse_er, cbasis,
if_vector[i]); PCeedChk(ierr);
}
else
{
ierr = CeedOperatorSetField(coper, fieldname, er_input[i], basis_input[i],
if_vector[i]); PCeedChk(ierr);
}
#if CEED_VERSION_GE(0, 13, 0)
ierr = CeedVectorDestroy(&if_vector[i]); PCeedChk(ierr);
ierr = CeedElemRestrictionDestroy(&er_input[i]); PCeedChk(ierr);
ierr = CeedBasisDestroy(&basis_input[i]); PCeedChk(ierr);
#endif
}
for (int i = 0; i < numoutputfields; ++i)
{
#if CEED_VERSION_GE(0, 13, 0)
const char * fieldname;
#else
char * fieldname;
#endif
ierr = CeedQFunctionFieldGetName(outputqfields[i], &fieldname); PCeedChk(ierr);
if (of_vector[i] == CEED_VECTOR_ACTIVE)
{
ierr = CeedOperatorSetField(coper, fieldname, coarse_er, cbasis,
of_vector[i]); PCeedChk(ierr);
}
else
{
ierr = CeedOperatorSetField(coper, fieldname, er_output[i], basis_output[i],
of_vector[i]); PCeedChk(ierr);
}
#if CEED_VERSION_GE(0, 13, 0)
ierr = CeedVectorDestroy(&of_vector[i]); PCeedChk(ierr);
ierr = CeedElemRestrictionDestroy(&er_output[i]); PCeedChk(ierr);
ierr = CeedBasisDestroy(&basis_output[i]); PCeedChk(ierr);
#endif
}
delete [] er_input;
delete [] er_output;
delete [] if_vector;
delete [] of_vector;
delete [] basis_input;
delete [] basis_output;
*out = coper;
return 0;
}
int CeedATPMGOperator(CeedOperator oper, int order_reduction,
CeedElemRestriction coarse_er,
CeedBasis *coarse_basis_out,
CeedBasis *basis_ctof_out,
CeedOperator *out)
{
int ierr;
CeedQFunction qf;
ierr = CeedOperatorGetQFunction(oper, &qf); PCeedChk(ierr);
CeedInt numinputfields, numoutputfields;
CeedOperatorField *inputfields;
ierr = CeedOperatorGetFields(oper, &numinputfields, &inputfields,
&numoutputfields, NULL);
PCeedChk(ierr);
CeedBasis basis;
ierr = CeedOperatorGetActiveBasis(oper, &basis); PCeedChk(ierr);
ierr = CeedBasisATPMGCoarseToFine(basis, basis_ctof_out, order_reduction);
PCeedChk(ierr);
ierr = CeedBasisATPMGCoarsen(basis, *basis_ctof_out, coarse_basis_out,
order_reduction); PCeedChk(ierr);
ierr = CeedATPMGOperator(oper, order_reduction, coarse_er, *coarse_basis_out,
*basis_ctof_out, out); PCeedChk(ierr);
return 0;
}
int CeedOperatorGetOrder(CeedOperator oper, CeedInt * order)
{
int ierr;
CeedOperatorField active_field;
ierr = CeedOperatorGetActiveField(oper, &active_field); PCeedChk(ierr);
CeedBasis basis;
ierr = CeedOperatorFieldGetBasis(active_field, &basis); PCeedChk(ierr);
int P1d;
ierr = CeedBasisGetNumNodes1D(basis, &P1d); PCeedChk(ierr);
*order = P1d - 1;
#if CEED_VERSION_GE(0, 13, 0)
ierr = CeedBasisDestroy(&basis); PCeedChk(ierr);
#endif
return 0;
}
int CeedATPMGBundle(CeedOperator oper, int order_reduction,
CeedBasis* coarse_basis_out,
CeedBasis* basis_ctof_out,
CeedElemRestriction* er_out,
CeedOperator* coarse_oper,
CeedInt *&dof_map)
{
int ierr;
CeedInt order;
ierr = CeedOperatorGetOrder(oper, &order); PCeedChk(ierr);
CeedElemRestriction ho_er;
ierr = CeedOperatorGetActiveElemRestriction(oper, &ho_er); PCeedChk(ierr);
ierr = CeedATPMGElemRestriction(order, order_reduction, ho_er, er_out, dof_map);
PCeedChk(ierr);
ierr = CeedATPMGOperator(oper, order_reduction, *er_out, coarse_basis_out,
basis_ctof_out, coarse_oper); PCeedChk(ierr);
return 0;
}
} // namespace ceed
} // namespace mfem
#endif // MFEM_USE_CEED