// Copyright (c) 2010-2023, 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 #include // todo: should probably use Ceed memory wrappers instead of calloc/free? #include 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); CeedChk(ierr); CeedInt numelem, numcomp, elemsize; CeedSize numnodes; ierr = CeedElemRestrictionGetNumElements(er_in, &numelem); CeedChk(ierr); ierr = CeedElemRestrictionGetLVectorSize(er_in, &numnodes); CeedChk(ierr); ierr = CeedElemRestrictionGetElementSize(er_in, &elemsize); CeedChk(ierr); ierr = CeedElemRestrictionGetNumComponents(er_in, &numcomp); CeedChk(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); CeedChk(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); CeedChk(ierr); for (CeedSize i = 0; i < numnodes; ++i) { lvec_data[i] = (CeedScalar) i; } ierr = CeedVectorRestoreArray(in_lvec, &lvec_data); CeedChk(ierr); CeedInt in_layout[3]; ierr = CeedElemRestrictionGetELayout(er_in, &in_layout); CeedChk(ierr); 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); CeedChk(ierr); ierr = CeedVectorDestroy(&in_lvec); CeedChk(ierr); const CeedScalar * in_elem_dof; ierr = CeedVectorGetArrayRead(in_evec, CEED_MEM_HOST, &in_elem_dof); CeedChk(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); CeedChk(ierr); ierr = CeedVectorDestroy(&in_evec); CeedChk(ierr); ierr = CeedElemRestrictionCreate(ceed, numelem, coarse_elemsize, numcomp, 0, running_out_ldof_count, CEED_MEM_HOST, CEED_COPY_VALUES, out_elem_dof, er_out); CeedChk(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); CeedChk(ierr); return 0; } int CeedBasisATPMGCoarseToFine(CeedBasis basisin, CeedBasis *basisc2f, int order_reduction) { int ierr; Ceed ceed; ierr = CeedBasisGetCeed(basisin, &ceed); CeedChk(ierr); CeedInt dim, P1d; ierr = CeedBasisGetDimension(basisin, &dim); CeedChk(ierr); ierr = CeedBasisGetNumNodes1D(basisin, &P1d); CeedChk(ierr); ierr = CeedBasisATPMGCoarseToFine(ceed, P1d, dim, order_reduction, basisc2f); CeedChk(ierr); return 0; } int CeedBasisATPMGCoarsen(CeedBasis basisin, CeedBasis basisc2f, CeedBasis* basisout, int order_reduction) { int ierr; Ceed ceed; ierr = CeedBasisGetCeed(basisin, &ceed); CeedChk(ierr); CeedInt dim, ncomp, P1d, Q1d; ierr = CeedBasisGetDimension(basisin, &dim); CeedChk(ierr); ierr = CeedBasisGetNumComponents(basisin, &ncomp); CeedChk(ierr); ierr = CeedBasisGetNumNodes1D(basisin, &P1d); CeedChk(ierr); ierr = CeedBasisGetNumQuadraturePoints1D(basisin, &Q1d); CeedChk(ierr); CeedInt coarse_P1d = P1d - order_reduction; const CeedScalar *interp1d; ierr = CeedBasisGetInterp1D(basisin, &interp1d); CeedChk(ierr); const CeedScalar * grad1d; ierr = CeedBasisGetGrad1D(basisin, &grad1d); CeedChk(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); CeedChk(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); CeedChk(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); CeedChk(ierr); const CeedScalar * qweight1d; ierr = CeedBasisGetQWeights(basisin, &qweight1d); CeedChk(ierr); ierr = CeedBasisCreateTensorH1(ceed, dim, ncomp, coarse_P1d, Q1d, coarse_interp1d, coarse_grad1d, qref1d, qweight1d, basisout); CeedChk(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); CeedChk(ierr); CeedQFunction qf; ierr = CeedOperatorGetQFunction(oper, &qf); CeedChk(ierr); CeedInt numinputfields, numoutputfields; CeedQFunctionField *inputqfields, *outputqfields; ierr = CeedQFunctionGetFields(qf, &numinputfields, &inputqfields, &numoutputfields, &outputqfields); CeedChk(ierr); CeedOperatorField *inputfields, *outputfields; ierr = CeedOperatorGetFields(oper, &numinputfields, &inputfields, &numoutputfields, &outputfields); CeedChk(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]); CeedChk(ierr); ierr = CeedOperatorFieldGetVector(inputfields[i], &if_vector[i]); CeedChk(ierr); ierr = CeedOperatorFieldGetBasis(inputfields[i], &basis_input[i]); CeedChk(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]); CeedChk(ierr); ierr = CeedOperatorFieldGetVector(outputfields[i], &of_vector[i]); CeedChk(ierr); ierr = CeedOperatorFieldGetBasis(outputfields[i], &basis_output[i]); CeedChk(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); CeedChk(ierr); for (int i = 0; i < numinputfields; ++i) { char * fieldname; ierr = CeedQFunctionFieldGetName(inputqfields[i], &fieldname); CeedChk(ierr); if (if_vector[i] == CEED_VECTOR_ACTIVE) { ierr = CeedOperatorSetField(coper, fieldname, coarse_er, cbasis, if_vector[i]); CeedChk(ierr); } else { ierr = CeedOperatorSetField(coper, fieldname, er_input[i], basis_input[i], if_vector[i]); CeedChk(ierr); } } for (int i = 0; i < numoutputfields; ++i) { char * fieldname; ierr = CeedQFunctionFieldGetName(outputqfields[i], &fieldname); CeedChk(ierr); if (of_vector[i] == CEED_VECTOR_ACTIVE) { ierr = CeedOperatorSetField(coper, fieldname, coarse_er, cbasis, of_vector[i]); CeedChk(ierr); } else { ierr = CeedOperatorSetField(coper, fieldname, er_output[i], basis_output[i], of_vector[i]); CeedChk(ierr); } } 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); CeedChk(ierr); CeedInt numinputfields, numoutputfields; CeedOperatorField *inputfields; ierr = CeedOperatorGetFields(oper, &numinputfields, &inputfields, &numoutputfields, NULL); CeedChk(ierr); CeedBasis basis; ierr = CeedOperatorGetActiveBasis(oper, &basis); CeedChk(ierr); ierr = CeedBasisATPMGCoarseToFine(basis, basis_ctof_out, order_reduction); CeedChk(ierr); ierr = CeedBasisATPMGCoarsen(basis, *basis_ctof_out, coarse_basis_out, order_reduction); CeedChk(ierr); ierr = CeedATPMGOperator(oper, order_reduction, coarse_er, *coarse_basis_out, *basis_ctof_out, out); CeedChk(ierr); return 0; } int CeedOperatorGetOrder(CeedOperator oper, CeedInt * order) { int ierr; CeedOperatorField active_field; ierr = CeedOperatorGetActiveField(oper, &active_field); CeedChk(ierr); CeedBasis basis; ierr = CeedOperatorFieldGetBasis(active_field, &basis); CeedChk(ierr); int P1d; ierr = CeedBasisGetNumNodes1D(basis, &P1d); CeedChk(ierr); *order = P1d - 1; 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); CeedChk(ierr); CeedElemRestriction ho_er; ierr = CeedOperatorGetActiveElemRestriction(oper, &ho_er); CeedChk(ierr); ierr = CeedATPMGElemRestriction(order, order_reduction, ho_er, er_out, dof_map); CeedChk(ierr); ierr = CeedATPMGOperator(oper, order_reduction, *er_out, coarse_basis_out, basis_ctof_out, coarse_oper); CeedChk(ierr); return 0; } } // namespace ceed } // namespace mfem #endif // MFEM_USE_CEED