xref: /libCEED/interface/ceed-operator.c (revision cd4dfc4886d0f227236be7081cdb958d91d495bd)
1d7b241e6Sjeremylt // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at
2d7b241e6Sjeremylt // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights
3d7b241e6Sjeremylt // reserved. See files LICENSE and NOTICE for details.
4d7b241e6Sjeremylt //
5d7b241e6Sjeremylt // This file is part of CEED, a collection of benchmarks, miniapps, software
6d7b241e6Sjeremylt // libraries and APIs for efficient high-order finite element and spectral
7d7b241e6Sjeremylt // element discretizations for exascale applications. For more information and
8d7b241e6Sjeremylt // source code availability see http://github.com/ceed.
9d7b241e6Sjeremylt //
10d7b241e6Sjeremylt // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
11d7b241e6Sjeremylt // a collaborative effort of two U.S. Department of Energy organizations (Office
12d7b241e6Sjeremylt // of Science and the National Nuclear Security Administration) responsible for
13d7b241e6Sjeremylt // the planning and preparation of a capable exascale ecosystem, including
14d7b241e6Sjeremylt // software, applications, hardware, advanced system engineering and early
15d7b241e6Sjeremylt // testbed platforms, in support of the nation's exascale computing imperative.
16d7b241e6Sjeremylt 
17ec3da8bcSJed Brown #include <ceed/ceed.h>
18ec3da8bcSJed Brown #include <ceed/backend.h>
193d576824SJeremy L Thompson #include <ceed-impl.h>
203bd813ffSjeremylt #include <math.h>
213d576824SJeremy L Thompson #include <stdbool.h>
223d576824SJeremy L Thompson #include <stdio.h>
233d576824SJeremy L Thompson #include <string.h>
24d7b241e6Sjeremylt 
25dfdf5a53Sjeremylt /// @file
267a982d89SJeremy L. Thompson /// Implementation of CeedOperator interfaces
277a982d89SJeremy L. Thompson 
287a982d89SJeremy L. Thompson /// ----------------------------------------------------------------------------
297a982d89SJeremy L. Thompson /// CeedOperator Library Internal Functions
307a982d89SJeremy L. Thompson /// ----------------------------------------------------------------------------
317a982d89SJeremy L. Thompson /// @addtogroup CeedOperatorDeveloper
327a982d89SJeremy L. Thompson /// @{
337a982d89SJeremy L. Thompson 
347a982d89SJeremy L. Thompson /**
357a982d89SJeremy L. Thompson   @brief Duplicate a CeedOperator with a reference Ceed to fallback for advanced
367a982d89SJeremy L. Thompson            CeedOperator functionality
377a982d89SJeremy L. Thompson 
387a982d89SJeremy L. Thompson   @param op  CeedOperator to create fallback for
397a982d89SJeremy L. Thompson 
407a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
417a982d89SJeremy L. Thompson 
427a982d89SJeremy L. Thompson   @ref Developer
437a982d89SJeremy L. Thompson **/
447a982d89SJeremy L. Thompson int CeedOperatorCreateFallback(CeedOperator op) {
457a982d89SJeremy L. Thompson   int ierr;
467a982d89SJeremy L. Thompson 
477a982d89SJeremy L. Thompson   // Fallback Ceed
48d1d35e2fSjeremylt   const char *resource, *fallback_resource;
497a982d89SJeremy L. Thompson   ierr = CeedGetResource(op->ceed, &resource); CeedChk(ierr);
50d1d35e2fSjeremylt   ierr = CeedGetOperatorFallbackResource(op->ceed, &fallback_resource);
517a982d89SJeremy L. Thompson   CeedChk(ierr);
52d1d35e2fSjeremylt   if (!strcmp(resource, fallback_resource))
537a982d89SJeremy L. Thompson     // LCOV_EXCL_START
54e15f9bd0SJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED,
55e15f9bd0SJeremy L Thompson                      "Backend %s cannot create an operator"
56d1d35e2fSjeremylt                      "fallback to resource %s", resource, fallback_resource);
577a982d89SJeremy L. Thompson   // LCOV_EXCL_STOP
587a982d89SJeremy L. Thompson 
597a982d89SJeremy L. Thompson   // Fallback Ceed
60d1d35e2fSjeremylt   Ceed ceed_ref;
61d1d35e2fSjeremylt   if (!op->ceed->op_fallback_ceed) {
62d1d35e2fSjeremylt     ierr = CeedInit(fallback_resource, &ceed_ref); CeedChk(ierr);
63d1d35e2fSjeremylt     ceed_ref->op_fallback_parent = op->ceed;
64d1d35e2fSjeremylt     ceed_ref->Error = op->ceed->Error;
65d1d35e2fSjeremylt     op->ceed->op_fallback_ceed = ceed_ref;
667a982d89SJeremy L. Thompson   }
67d1d35e2fSjeremylt   ceed_ref = op->ceed->op_fallback_ceed;
687a982d89SJeremy L. Thompson 
697a982d89SJeremy L. Thompson   // Clone Op
70d1d35e2fSjeremylt   CeedOperator op_ref;
71d1d35e2fSjeremylt   ierr = CeedCalloc(1, &op_ref); CeedChk(ierr);
72d1d35e2fSjeremylt   memcpy(op_ref, op, sizeof(*op_ref));
73d1d35e2fSjeremylt   op_ref->data = NULL;
74d1d35e2fSjeremylt   op_ref->interface_setup = false;
75d1d35e2fSjeremylt   op_ref->backend_setup = false;
76d1d35e2fSjeremylt   op_ref->ceed = ceed_ref;
77d1d35e2fSjeremylt   ierr = ceed_ref->OperatorCreate(op_ref); CeedChk(ierr);
78d1d35e2fSjeremylt   op->op_fallback = op_ref;
797a982d89SJeremy L. Thompson 
807a982d89SJeremy L. Thompson   // Clone QF
81d1d35e2fSjeremylt   CeedQFunction qf_ref;
82d1d35e2fSjeremylt   ierr = CeedCalloc(1, &qf_ref); CeedChk(ierr);
83d1d35e2fSjeremylt   memcpy(qf_ref, (op->qf), sizeof(*qf_ref));
84d1d35e2fSjeremylt   qf_ref->data = NULL;
85d1d35e2fSjeremylt   qf_ref->ceed = ceed_ref;
86d1d35e2fSjeremylt   ierr = ceed_ref->QFunctionCreate(qf_ref); CeedChk(ierr);
87d1d35e2fSjeremylt   op_ref->qf = qf_ref;
88d1d35e2fSjeremylt   op->qf_fallback = qf_ref;
89e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
90e15f9bd0SJeremy L Thompson }
917a982d89SJeremy L. Thompson 
92e15f9bd0SJeremy L Thompson /**
93e15f9bd0SJeremy L Thompson   @brief Check if a CeedOperator Field matches the QFunction Field
94e15f9bd0SJeremy L Thompson 
95e15f9bd0SJeremy L Thompson   @param[in] ceed      Ceed object for error handling
96d1d35e2fSjeremylt   @param[in] qf_field  QFunction Field matching Operator Field
97e15f9bd0SJeremy L Thompson   @param[in] r         Operator Field ElemRestriction
98e15f9bd0SJeremy L Thompson   @param[in] b         Operator Field Basis
99e15f9bd0SJeremy L Thompson 
100e15f9bd0SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
101e15f9bd0SJeremy L Thompson 
102e15f9bd0SJeremy L Thompson   @ref Developer
103e15f9bd0SJeremy L Thompson **/
104d1d35e2fSjeremylt static int CeedOperatorCheckField(Ceed ceed, CeedQFunctionField qf_field,
105e15f9bd0SJeremy L Thompson                                   CeedElemRestriction r, CeedBasis b) {
106e15f9bd0SJeremy L Thompson   int ierr;
107d1d35e2fSjeremylt   CeedEvalMode eval_mode = qf_field->eval_mode;
108d1d35e2fSjeremylt   CeedInt dim = 1, num_comp = 1, restr_num_comp = 1, size = qf_field->size;
109e15f9bd0SJeremy L Thompson   // Restriction
110e15f9bd0SJeremy L Thompson   if (r != CEED_ELEMRESTRICTION_NONE) {
111d1d35e2fSjeremylt     if (eval_mode == CEED_EVAL_WEIGHT) {
112e15f9bd0SJeremy L Thompson       // LCOV_EXCL_START
113e1e9e29dSJed Brown       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
114e1e9e29dSJed Brown                        "CEED_ELEMRESTRICTION_NONE should be used "
115e15f9bd0SJeremy L Thompson                        "for a field with eval mode CEED_EVAL_WEIGHT");
116e15f9bd0SJeremy L Thompson       // LCOV_EXCL_STOP
117e15f9bd0SJeremy L Thompson     }
118d1d35e2fSjeremylt     ierr = CeedElemRestrictionGetNumComponents(r, &restr_num_comp);
11978464608Sjeremylt     CeedChk(ierr);
120e1e9e29dSJed Brown   }
121d1d35e2fSjeremylt   if ((r == CEED_ELEMRESTRICTION_NONE) != (eval_mode == CEED_EVAL_WEIGHT)) {
122e1e9e29dSJed Brown     // LCOV_EXCL_START
123e1e9e29dSJed Brown     return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
124e1e9e29dSJed Brown                      "CEED_ELEMRESTRICTION_NONE and CEED_EVAL_WEIGHT "
125e1e9e29dSJed Brown                      "must be used together.");
126e1e9e29dSJed Brown     // LCOV_EXCL_STOP
127e1e9e29dSJed Brown   }
128e15f9bd0SJeremy L Thompson   // Basis
129e15f9bd0SJeremy L Thompson   if (b != CEED_BASIS_COLLOCATED) {
130d1d35e2fSjeremylt     if (eval_mode == CEED_EVAL_NONE)
131e1e9e29dSJed Brown       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
132d1d35e2fSjeremylt                        "Field '%s' configured with CEED_EVAL_NONE must "
133d1d35e2fSjeremylt                        "be used with CEED_BASIS_COLLOCATED",
134d1d35e2fSjeremylt                        qf_field->field_name);
135e15f9bd0SJeremy L Thompson     ierr = CeedBasisGetDimension(b, &dim); CeedChk(ierr);
136d1d35e2fSjeremylt     ierr = CeedBasisGetNumComponents(b, &num_comp); CeedChk(ierr);
137d1d35e2fSjeremylt     if (r != CEED_ELEMRESTRICTION_NONE && restr_num_comp != num_comp) {
138e15f9bd0SJeremy L Thompson       // LCOV_EXCL_START
139e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_DIMENSION,
140d1d35e2fSjeremylt                        "Field '%s' of size %d and EvalMode %s: ElemRestriction "
141d1d35e2fSjeremylt                        "has %d components, but Basis has %d components",
142d1d35e2fSjeremylt                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
143d1d35e2fSjeremylt                        restr_num_comp,
144d1d35e2fSjeremylt                        num_comp);
145e15f9bd0SJeremy L Thompson       // LCOV_EXCL_STOP
146e15f9bd0SJeremy L Thompson     }
147e15f9bd0SJeremy L Thompson   }
148e15f9bd0SJeremy L Thompson   // Field size
149d1d35e2fSjeremylt   switch(eval_mode) {
150e15f9bd0SJeremy L Thompson   case CEED_EVAL_NONE:
151d1d35e2fSjeremylt     if (size != restr_num_comp)
152e15f9bd0SJeremy L Thompson       // LCOV_EXCL_START
153e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_DIMENSION,
154e1e9e29dSJed Brown                        "Field '%s' of size %d and EvalMode %s: ElemRestriction has %d components",
155d1d35e2fSjeremylt                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
156d1d35e2fSjeremylt                        restr_num_comp);
157e15f9bd0SJeremy L Thompson     // LCOV_EXCL_STOP
158e15f9bd0SJeremy L Thompson     break;
159e15f9bd0SJeremy L Thompson   case CEED_EVAL_INTERP:
160d1d35e2fSjeremylt     if (size != num_comp)
161e15f9bd0SJeremy L Thompson       // LCOV_EXCL_START
162e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_DIMENSION,
163e1e9e29dSJed Brown                        "Field '%s' of size %d and EvalMode %s: ElemRestriction/Basis has %d components",
164d1d35e2fSjeremylt                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
165d1d35e2fSjeremylt                        num_comp);
166e15f9bd0SJeremy L Thompson     // LCOV_EXCL_STOP
167e15f9bd0SJeremy L Thompson     break;
168e15f9bd0SJeremy L Thompson   case CEED_EVAL_GRAD:
169d1d35e2fSjeremylt     if (size != num_comp * dim)
170e15f9bd0SJeremy L Thompson       // LCOV_EXCL_START
171e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_DIMENSION,
172d1d35e2fSjeremylt                        "Field '%s' of size %d and EvalMode %s in %d dimensions: "
173d1d35e2fSjeremylt                        "ElemRestriction/Basis has %d components",
174d1d35e2fSjeremylt                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode], dim,
175d1d35e2fSjeremylt                        num_comp);
176e15f9bd0SJeremy L Thompson     // LCOV_EXCL_STOP
177e15f9bd0SJeremy L Thompson     break;
178e15f9bd0SJeremy L Thompson   case CEED_EVAL_WEIGHT:
179d1d35e2fSjeremylt     // No additional checks required
180e15f9bd0SJeremy L Thompson     break;
181e15f9bd0SJeremy L Thompson   case CEED_EVAL_DIV:
182e15f9bd0SJeremy L Thompson     // Not implemented
183e15f9bd0SJeremy L Thompson     break;
184e15f9bd0SJeremy L Thompson   case CEED_EVAL_CURL:
185e15f9bd0SJeremy L Thompson     // Not implemented
186e15f9bd0SJeremy L Thompson     break;
187e15f9bd0SJeremy L Thompson   }
188e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1897a982d89SJeremy L. Thompson }
1907a982d89SJeremy L. Thompson 
1917a982d89SJeremy L. Thompson /**
1927a982d89SJeremy L. Thompson   @brief Check if a CeedOperator is ready to be used.
1937a982d89SJeremy L. Thompson 
1947a982d89SJeremy L. Thompson   @param[in] op  CeedOperator to check
1957a982d89SJeremy L. Thompson 
1967a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
1977a982d89SJeremy L. Thompson 
1987a982d89SJeremy L. Thompson   @ref Developer
1997a982d89SJeremy L. Thompson **/
200e2f04181SAndrew T. Barker static int CeedOperatorCheckReady(CeedOperator op) {
201e2f04181SAndrew T. Barker   int ierr;
202e2f04181SAndrew T. Barker   Ceed ceed;
203e2f04181SAndrew T. Barker   ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
204e2f04181SAndrew T. Barker 
205d1d35e2fSjeremylt   if (op->interface_setup)
206e15f9bd0SJeremy L Thompson     return CEED_ERROR_SUCCESS;
2077a982d89SJeremy L. Thompson 
208e15f9bd0SJeremy L Thompson   CeedQFunction qf = op->qf;
2097a982d89SJeremy L. Thompson   if (op->composite) {
210d1d35e2fSjeremylt     if (!op->num_suboperators)
2117a982d89SJeremy L. Thompson       // LCOV_EXCL_START
212d1d35e2fSjeremylt       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No sub_operators set");
2137a982d89SJeremy L. Thompson     // LCOV_EXCL_STOP
2147a982d89SJeremy L. Thompson   } else {
215d1d35e2fSjeremylt     if (op->num_fields == 0)
2167a982d89SJeremy L. Thompson       // LCOV_EXCL_START
217e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No operator fields set");
2187a982d89SJeremy L. Thompson     // LCOV_EXCL_STOP
219d1d35e2fSjeremylt     if (op->num_fields < qf->num_input_fields + qf->num_output_fields)
2207a982d89SJeremy L. Thompson       // LCOV_EXCL_START
221e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set");
2227a982d89SJeremy L. Thompson     // LCOV_EXCL_STOP
223d1d35e2fSjeremylt     if (!op->has_restriction)
2247a982d89SJeremy L. Thompson       // LCOV_EXCL_START
225e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
226e15f9bd0SJeremy L Thompson                        "At least one restriction required");
2277a982d89SJeremy L. Thompson     // LCOV_EXCL_STOP
228d1d35e2fSjeremylt     if (op->num_qpts == 0)
2297a982d89SJeremy L. Thompson       // LCOV_EXCL_START
230e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
231*cd4dfc48Sjeremylt                        "At least one non-collocated basis is required "
232*cd4dfc48Sjeremylt                        "or the number of quadrature points must be set");
2337a982d89SJeremy L. Thompson     // LCOV_EXCL_STOP
2347a982d89SJeremy L. Thompson   }
2357a982d89SJeremy L. Thompson 
236e15f9bd0SJeremy L Thompson   // Flag as immutable and ready
237d1d35e2fSjeremylt   op->interface_setup = true;
238e15f9bd0SJeremy L Thompson   if (op->qf && op->qf != CEED_QFUNCTION_NONE)
239e15f9bd0SJeremy L Thompson     // LCOV_EXCL_START
240d1d35e2fSjeremylt     op->qf->operators_set++;
241e15f9bd0SJeremy L Thompson   // LCOV_EXCL_STOP
242e15f9bd0SJeremy L Thompson   if (op->dqf && op->dqf != CEED_QFUNCTION_NONE)
243e15f9bd0SJeremy L Thompson     // LCOV_EXCL_START
244d1d35e2fSjeremylt     op->dqf->operators_set++;
245e15f9bd0SJeremy L Thompson   // LCOV_EXCL_STOP
246e15f9bd0SJeremy L Thompson   if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE)
247e15f9bd0SJeremy L Thompson     // LCOV_EXCL_START
248d1d35e2fSjeremylt     op->dqfT->operators_set++;
249e15f9bd0SJeremy L Thompson   // LCOV_EXCL_STOP
250e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
2517a982d89SJeremy L. Thompson }
2527a982d89SJeremy L. Thompson 
2537a982d89SJeremy L. Thompson /**
2547a982d89SJeremy L. Thompson   @brief View a field of a CeedOperator
2557a982d89SJeremy L. Thompson 
2567a982d89SJeremy L. Thompson   @param[in] field         Operator field to view
257d1d35e2fSjeremylt   @param[in] qf_field      QFunction field (carries field name)
258d1d35e2fSjeremylt   @param[in] field_number  Number of field being viewed
2594c4400c7SValeria Barra   @param[in] sub           true indicates sub-operator, which increases indentation; false for top-level operator
260d1d35e2fSjeremylt   @param[in] input         true for an input field; false for output field
2617a982d89SJeremy L. Thompson   @param[in] stream        Stream to view to, e.g., stdout
2627a982d89SJeremy L. Thompson 
2637a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
2647a982d89SJeremy L. Thompson 
2657a982d89SJeremy L. Thompson   @ref Utility
2667a982d89SJeremy L. Thompson **/
2677a982d89SJeremy L. Thompson static int CeedOperatorFieldView(CeedOperatorField field,
268d1d35e2fSjeremylt                                  CeedQFunctionField qf_field,
269d1d35e2fSjeremylt                                  CeedInt field_number, bool sub, bool input,
2707a982d89SJeremy L. Thompson                                  FILE *stream) {
2717a982d89SJeremy L. Thompson   const char *pre = sub ? "  " : "";
272d1d35e2fSjeremylt   const char *in_out = input ? "Input" : "Output";
2737a982d89SJeremy L. Thompson 
2747a982d89SJeremy L. Thompson   fprintf(stream, "%s    %s Field [%d]:\n"
2757a982d89SJeremy L. Thompson           "%s      Name: \"%s\"\n",
276d1d35e2fSjeremylt           pre, in_out, field_number, pre, qf_field->field_name);
2777a982d89SJeremy L. Thompson 
2787a982d89SJeremy L. Thompson   if (field->basis == CEED_BASIS_COLLOCATED)
2797a982d89SJeremy L. Thompson     fprintf(stream, "%s      Collocated basis\n", pre);
2807a982d89SJeremy L. Thompson 
2817a982d89SJeremy L. Thompson   if (field->vec == CEED_VECTOR_ACTIVE)
2827a982d89SJeremy L. Thompson     fprintf(stream, "%s      Active vector\n", pre);
2837a982d89SJeremy L. Thompson   else if (field->vec == CEED_VECTOR_NONE)
2847a982d89SJeremy L. Thompson     fprintf(stream, "%s      No vector\n", pre);
285e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
2867a982d89SJeremy L. Thompson }
2877a982d89SJeremy L. Thompson 
2887a982d89SJeremy L. Thompson /**
2897a982d89SJeremy L. Thompson   @brief View a single CeedOperator
2907a982d89SJeremy L. Thompson 
2917a982d89SJeremy L. Thompson   @param[in] op      CeedOperator to view
2927a982d89SJeremy L. Thompson   @param[in] sub     Boolean flag for sub-operator
2937a982d89SJeremy L. Thompson   @param[in] stream  Stream to write; typically stdout/stderr or a file
2947a982d89SJeremy L. Thompson 
2957a982d89SJeremy L. Thompson   @return Error code: 0 - success, otherwise - failure
2967a982d89SJeremy L. Thompson 
2977a982d89SJeremy L. Thompson   @ref Utility
2987a982d89SJeremy L. Thompson **/
2997a982d89SJeremy L. Thompson int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) {
3007a982d89SJeremy L. Thompson   int ierr;
3017a982d89SJeremy L. Thompson   const char *pre = sub ? "  " : "";
3027a982d89SJeremy L. Thompson 
30378464608Sjeremylt   CeedInt total_fields = 0;
30478464608Sjeremylt   ierr = CeedOperatorGetNumArgs(op, &total_fields); CeedChk(ierr);
3057a982d89SJeremy L. Thompson 
30678464608Sjeremylt   fprintf(stream, "%s  %d Field%s\n", pre, total_fields,
30778464608Sjeremylt           total_fields>1 ? "s" : "");
3087a982d89SJeremy L. Thompson 
309d1d35e2fSjeremylt   fprintf(stream, "%s  %d Input Field%s:\n", pre, op->qf->num_input_fields,
310d1d35e2fSjeremylt           op->qf->num_input_fields>1 ? "s" : "");
311d1d35e2fSjeremylt   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
312d1d35e2fSjeremylt     ierr = CeedOperatorFieldView(op->input_fields[i], op->qf->input_fields[i],
3137a982d89SJeremy L. Thompson                                  i, sub, 1, stream); CeedChk(ierr);
3147a982d89SJeremy L. Thompson   }
3157a982d89SJeremy L. Thompson 
316d1d35e2fSjeremylt   fprintf(stream, "%s  %d Output Field%s:\n", pre, op->qf->num_output_fields,
317d1d35e2fSjeremylt           op->qf->num_output_fields>1 ? "s" : "");
318d1d35e2fSjeremylt   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
319d1d35e2fSjeremylt     ierr = CeedOperatorFieldView(op->output_fields[i], op->qf->output_fields[i],
3207a982d89SJeremy L. Thompson                                  i, sub, 0, stream); CeedChk(ierr);
3217a982d89SJeremy L. Thompson   }
322e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
3237a982d89SJeremy L. Thompson }
3247a982d89SJeremy L. Thompson 
325d99fa3c5SJeremy L Thompson /**
326e2f04181SAndrew T. Barker   @brief Find the active vector ElemRestriction for a CeedOperator
327e2f04181SAndrew T. Barker 
328e2f04181SAndrew T. Barker   @param[in] op            CeedOperator to find active basis for
329d1d35e2fSjeremylt   @param[out] active_rstr  ElemRestriction for active input vector
330e2f04181SAndrew T. Barker 
331e2f04181SAndrew T. Barker   @return An error code: 0 - success, otherwise - failure
332e2f04181SAndrew T. Barker 
333e2f04181SAndrew T. Barker   @ref Utility
334e2f04181SAndrew T. Barker **/
335e2f04181SAndrew T. Barker static int CeedOperatorGetActiveElemRestriction(CeedOperator op,
336d1d35e2fSjeremylt     CeedElemRestriction *active_rstr) {
337d1d35e2fSjeremylt   *active_rstr = NULL;
338d1d35e2fSjeremylt   for (int i = 0; i < op->qf->num_input_fields; i++)
339d1d35e2fSjeremylt     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
340d1d35e2fSjeremylt       *active_rstr = op->input_fields[i]->elem_restr;
341e2f04181SAndrew T. Barker       break;
342e2f04181SAndrew T. Barker     }
343e2f04181SAndrew T. Barker 
344d1d35e2fSjeremylt   if (!*active_rstr) {
345e2f04181SAndrew T. Barker     // LCOV_EXCL_START
346e2f04181SAndrew T. Barker     int ierr;
347e2f04181SAndrew T. Barker     Ceed ceed;
348e2f04181SAndrew T. Barker     ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
349e2f04181SAndrew T. Barker     return CeedError(ceed, CEED_ERROR_INCOMPLETE,
350e2f04181SAndrew T. Barker                      "No active ElemRestriction found!");
351e2f04181SAndrew T. Barker     // LCOV_EXCL_STOP
352e2f04181SAndrew T. Barker   }
353e2f04181SAndrew T. Barker   return CEED_ERROR_SUCCESS;
354e2f04181SAndrew T. Barker }
355e2f04181SAndrew T. Barker 
356e2f04181SAndrew T. Barker /**
357d99fa3c5SJeremy L Thompson   @brief Find the active vector basis for a CeedOperator
358d99fa3c5SJeremy L Thompson 
359d99fa3c5SJeremy L Thompson   @param[in] op             CeedOperator to find active basis for
360d1d35e2fSjeremylt   @param[out] active_basis  Basis for active input vector
361d99fa3c5SJeremy L Thompson 
362d99fa3c5SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
363d99fa3c5SJeremy L Thompson 
364d99fa3c5SJeremy L Thompson   @ ref Developer
365d99fa3c5SJeremy L Thompson **/
366d99fa3c5SJeremy L Thompson static int CeedOperatorGetActiveBasis(CeedOperator op,
367d1d35e2fSjeremylt                                       CeedBasis *active_basis) {
368d1d35e2fSjeremylt   *active_basis = NULL;
369d1d35e2fSjeremylt   for (int i = 0; i < op->qf->num_input_fields; i++)
370d1d35e2fSjeremylt     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
371d1d35e2fSjeremylt       *active_basis = op->input_fields[i]->basis;
372d99fa3c5SJeremy L Thompson       break;
373d99fa3c5SJeremy L Thompson     }
374d99fa3c5SJeremy L Thompson 
375d1d35e2fSjeremylt   if (!*active_basis) {
376d99fa3c5SJeremy L Thompson     // LCOV_EXCL_START
377d99fa3c5SJeremy L Thompson     int ierr;
378d99fa3c5SJeremy L Thompson     Ceed ceed;
379d99fa3c5SJeremy L Thompson     ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
380e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_MINOR,
381d99fa3c5SJeremy L Thompson                      "No active basis found for automatic multigrid setup");
382d99fa3c5SJeremy L Thompson     // LCOV_EXCL_STOP
383d99fa3c5SJeremy L Thompson   }
384e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
385d99fa3c5SJeremy L Thompson }
386d99fa3c5SJeremy L Thompson 
387d99fa3c5SJeremy L Thompson /**
388d99fa3c5SJeremy L Thompson   @brief Common code for creating a multigrid coarse operator and level
389d99fa3c5SJeremy L Thompson            transfer operators for a CeedOperator
390d99fa3c5SJeremy L Thompson 
391d1d35e2fSjeremylt   @param[in] op_fine       Fine grid operator
392d1d35e2fSjeremylt   @param[in] p_mult_fine   L-vector multiplicity in parallel gather/scatter
393d1d35e2fSjeremylt   @param[in] rstr_coarse   Coarse grid restriction
394d1d35e2fSjeremylt   @param[in] basis_coarse  Coarse grid active vector basis
395d1d35e2fSjeremylt   @param[in] basis_c_to_f  Basis for coarse to fine interpolation
396d1d35e2fSjeremylt   @param[out] op_coarse    Coarse grid operator
397d1d35e2fSjeremylt   @param[out] op_prolong   Coarse to fine operator
398d1d35e2fSjeremylt   @param[out] op_restrict  Fine to coarse operator
399d99fa3c5SJeremy L Thompson 
400d99fa3c5SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
401d99fa3c5SJeremy L Thompson 
402d99fa3c5SJeremy L Thompson   @ref Developer
403d99fa3c5SJeremy L Thompson **/
404d1d35e2fSjeremylt static int CeedOperatorMultigridLevel_Core(CeedOperator op_fine,
405d1d35e2fSjeremylt     CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
406d1d35e2fSjeremylt     CeedBasis basis_c_to_f, CeedOperator *op_coarse, CeedOperator *op_prolong,
407d1d35e2fSjeremylt     CeedOperator *op_restrict) {
408d99fa3c5SJeremy L Thompson   int ierr;
409d99fa3c5SJeremy L Thompson   Ceed ceed;
410d1d35e2fSjeremylt   ierr = CeedOperatorGetCeed(op_fine, &ceed); CeedChk(ierr);
411d99fa3c5SJeremy L Thompson 
412d99fa3c5SJeremy L Thompson   // Check for composite operator
413d1d35e2fSjeremylt   bool is_composite;
414d1d35e2fSjeremylt   ierr = CeedOperatorIsComposite(op_fine, &is_composite); CeedChk(ierr);
415d1d35e2fSjeremylt   if (is_composite)
416d99fa3c5SJeremy L Thompson     // LCOV_EXCL_START
417e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
418d99fa3c5SJeremy L Thompson                      "Automatic multigrid setup for composite operators not supported");
419d99fa3c5SJeremy L Thompson   // LCOV_EXCL_STOP
420d99fa3c5SJeremy L Thompson 
421d99fa3c5SJeremy L Thompson   // Coarse Grid
422d1d35e2fSjeremylt   ierr = CeedOperatorCreate(ceed, op_fine->qf, op_fine->dqf, op_fine->dqfT,
423d1d35e2fSjeremylt                             op_coarse); CeedChk(ierr);
424d1d35e2fSjeremylt   CeedElemRestriction rstr_fine = NULL;
425d99fa3c5SJeremy L Thompson   // -- Clone input fields
426d1d35e2fSjeremylt   for (int i = 0; i < op_fine->qf->num_input_fields; i++) {
427d1d35e2fSjeremylt     if (op_fine->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
428d1d35e2fSjeremylt       rstr_fine = op_fine->input_fields[i]->elem_restr;
429d1d35e2fSjeremylt       ierr = CeedOperatorSetField(*op_coarse, op_fine->input_fields[i]->field_name,
430d1d35e2fSjeremylt                                   rstr_coarse, basis_coarse, CEED_VECTOR_ACTIVE);
431d99fa3c5SJeremy L Thompson       CeedChk(ierr);
432d99fa3c5SJeremy L Thompson     } else {
433d1d35e2fSjeremylt       ierr = CeedOperatorSetField(*op_coarse, op_fine->input_fields[i]->field_name,
434d1d35e2fSjeremylt                                   op_fine->input_fields[i]->elem_restr,
435d1d35e2fSjeremylt                                   op_fine->input_fields[i]->basis,
436d1d35e2fSjeremylt                                   op_fine->input_fields[i]->vec); CeedChk(ierr);
437d99fa3c5SJeremy L Thompson     }
438d99fa3c5SJeremy L Thompson   }
439d99fa3c5SJeremy L Thompson   // -- Clone output fields
440d1d35e2fSjeremylt   for (int i = 0; i < op_fine->qf->num_output_fields; i++) {
441d1d35e2fSjeremylt     if (op_fine->output_fields[i]->vec == CEED_VECTOR_ACTIVE) {
442d1d35e2fSjeremylt       ierr = CeedOperatorSetField(*op_coarse, op_fine->output_fields[i]->field_name,
443d1d35e2fSjeremylt                                   rstr_coarse, basis_coarse, CEED_VECTOR_ACTIVE);
444d99fa3c5SJeremy L Thompson       CeedChk(ierr);
445d99fa3c5SJeremy L Thompson     } else {
446d1d35e2fSjeremylt       ierr = CeedOperatorSetField(*op_coarse, op_fine->output_fields[i]->field_name,
447d1d35e2fSjeremylt                                   op_fine->output_fields[i]->elem_restr,
448d1d35e2fSjeremylt                                   op_fine->output_fields[i]->basis,
449d1d35e2fSjeremylt                                   op_fine->output_fields[i]->vec); CeedChk(ierr);
450d99fa3c5SJeremy L Thompson     }
451d99fa3c5SJeremy L Thompson   }
452d99fa3c5SJeremy L Thompson 
453d99fa3c5SJeremy L Thompson   // Multiplicity vector
454d1d35e2fSjeremylt   CeedVector mult_vec, mult_e_vec;
455d1d35e2fSjeremylt   ierr = CeedElemRestrictionCreateVector(rstr_fine, &mult_vec, &mult_e_vec);
456d99fa3c5SJeremy L Thompson   CeedChk(ierr);
457d1d35e2fSjeremylt   ierr = CeedVectorSetValue(mult_e_vec, 0.0); CeedChk(ierr);
458d1d35e2fSjeremylt   ierr = CeedElemRestrictionApply(rstr_fine, CEED_NOTRANSPOSE, p_mult_fine,
459d1d35e2fSjeremylt                                   mult_e_vec, CEED_REQUEST_IMMEDIATE); CeedChk(ierr);
460d1d35e2fSjeremylt   ierr = CeedVectorSetValue(mult_vec, 0.0); CeedChk(ierr);
461d1d35e2fSjeremylt   ierr = CeedElemRestrictionApply(rstr_fine, CEED_TRANSPOSE, mult_e_vec, mult_vec,
462d99fa3c5SJeremy L Thompson                                   CEED_REQUEST_IMMEDIATE); CeedChk(ierr);
463d1d35e2fSjeremylt   ierr = CeedVectorDestroy(&mult_e_vec); CeedChk(ierr);
464d1d35e2fSjeremylt   ierr = CeedVectorReciprocal(mult_vec); CeedChk(ierr);
465d99fa3c5SJeremy L Thompson 
466d99fa3c5SJeremy L Thompson   // Restriction
467d1d35e2fSjeremylt   CeedInt num_comp;
468d1d35e2fSjeremylt   ierr = CeedBasisGetNumComponents(basis_coarse, &num_comp); CeedChk(ierr);
469d1d35e2fSjeremylt   CeedQFunction qf_restrict;
470d1d35e2fSjeremylt   ierr = CeedQFunctionCreateInteriorByName(ceed, "Scale", &qf_restrict);
471d99fa3c5SJeremy L Thompson   CeedChk(ierr);
472d1d35e2fSjeremylt   CeedInt *num_comp_r_data;
473d1d35e2fSjeremylt   ierr = CeedCalloc(1, &num_comp_r_data); CeedChk(ierr);
474d1d35e2fSjeremylt   num_comp_r_data[0] = num_comp;
475d1d35e2fSjeremylt   CeedQFunctionContext ctx_r;
476d1d35e2fSjeremylt   ierr = CeedQFunctionContextCreate(ceed, &ctx_r); CeedChk(ierr);
477d1d35e2fSjeremylt   ierr = CeedQFunctionContextSetData(ctx_r, CEED_MEM_HOST, CEED_OWN_POINTER,
478d1d35e2fSjeremylt                                      sizeof(*num_comp_r_data), num_comp_r_data);
479777ff853SJeremy L Thompson   CeedChk(ierr);
480d1d35e2fSjeremylt   ierr = CeedQFunctionSetContext(qf_restrict, ctx_r); CeedChk(ierr);
481d1d35e2fSjeremylt   ierr = CeedQFunctionContextDestroy(&ctx_r); CeedChk(ierr);
482d1d35e2fSjeremylt   ierr = CeedQFunctionAddInput(qf_restrict, "input", num_comp, CEED_EVAL_NONE);
483d99fa3c5SJeremy L Thompson   CeedChk(ierr);
484d1d35e2fSjeremylt   ierr = CeedQFunctionAddInput(qf_restrict, "scale", num_comp, CEED_EVAL_NONE);
485d99fa3c5SJeremy L Thompson   CeedChk(ierr);
486d1d35e2fSjeremylt   ierr = CeedQFunctionAddOutput(qf_restrict, "output", num_comp,
487d1d35e2fSjeremylt                                 CEED_EVAL_INTERP); CeedChk(ierr);
488d99fa3c5SJeremy L Thompson 
489d1d35e2fSjeremylt   ierr = CeedOperatorCreate(ceed, qf_restrict, CEED_QFUNCTION_NONE,
490d1d35e2fSjeremylt                             CEED_QFUNCTION_NONE, op_restrict);
491d99fa3c5SJeremy L Thompson   CeedChk(ierr);
492d1d35e2fSjeremylt   ierr = CeedOperatorSetField(*op_restrict, "input", rstr_fine,
493d99fa3c5SJeremy L Thompson                               CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
494d99fa3c5SJeremy L Thompson   CeedChk(ierr);
495d1d35e2fSjeremylt   ierr = CeedOperatorSetField(*op_restrict, "scale", rstr_fine,
496d1d35e2fSjeremylt                               CEED_BASIS_COLLOCATED, mult_vec);
497d99fa3c5SJeremy L Thompson   CeedChk(ierr);
498d1d35e2fSjeremylt   ierr = CeedOperatorSetField(*op_restrict, "output", rstr_coarse, basis_c_to_f,
499d99fa3c5SJeremy L Thompson                               CEED_VECTOR_ACTIVE); CeedChk(ierr);
500d99fa3c5SJeremy L Thompson 
501d99fa3c5SJeremy L Thompson   // Prolongation
502d1d35e2fSjeremylt   CeedQFunction qf_prolong;
503d1d35e2fSjeremylt   ierr = CeedQFunctionCreateInteriorByName(ceed, "Scale", &qf_prolong);
504d99fa3c5SJeremy L Thompson   CeedChk(ierr);
505d1d35e2fSjeremylt   CeedInt *num_comp_p_data;
506d1d35e2fSjeremylt   ierr = CeedCalloc(1, &num_comp_p_data); CeedChk(ierr);
507d1d35e2fSjeremylt   num_comp_p_data[0] = num_comp;
508d1d35e2fSjeremylt   CeedQFunctionContext ctx_p;
509d1d35e2fSjeremylt   ierr = CeedQFunctionContextCreate(ceed, &ctx_p); CeedChk(ierr);
510d1d35e2fSjeremylt   ierr = CeedQFunctionContextSetData(ctx_p, CEED_MEM_HOST, CEED_OWN_POINTER,
511d1d35e2fSjeremylt                                      sizeof(*num_comp_p_data), num_comp_p_data);
512777ff853SJeremy L Thompson   CeedChk(ierr);
513d1d35e2fSjeremylt   ierr = CeedQFunctionSetContext(qf_prolong, ctx_p); CeedChk(ierr);
514d1d35e2fSjeremylt   ierr = CeedQFunctionContextDestroy(&ctx_p); CeedChk(ierr);
515d1d35e2fSjeremylt   ierr = CeedQFunctionAddInput(qf_prolong, "input", num_comp, CEED_EVAL_INTERP);
516d99fa3c5SJeremy L Thompson   CeedChk(ierr);
517d1d35e2fSjeremylt   ierr = CeedQFunctionAddInput(qf_prolong, "scale", num_comp, CEED_EVAL_NONE);
518d99fa3c5SJeremy L Thompson   CeedChk(ierr);
519d1d35e2fSjeremylt   ierr = CeedQFunctionAddOutput(qf_prolong, "output", num_comp, CEED_EVAL_NONE);
520d99fa3c5SJeremy L Thompson   CeedChk(ierr);
521d99fa3c5SJeremy L Thompson 
522d1d35e2fSjeremylt   ierr = CeedOperatorCreate(ceed, qf_prolong, CEED_QFUNCTION_NONE,
523d1d35e2fSjeremylt                             CEED_QFUNCTION_NONE, op_prolong);
524d99fa3c5SJeremy L Thompson   CeedChk(ierr);
525d1d35e2fSjeremylt   ierr = CeedOperatorSetField(*op_prolong, "input", rstr_coarse, basis_c_to_f,
526d99fa3c5SJeremy L Thompson                               CEED_VECTOR_ACTIVE); CeedChk(ierr);
527d1d35e2fSjeremylt   ierr = CeedOperatorSetField(*op_prolong, "scale", rstr_fine,
528d1d35e2fSjeremylt                               CEED_BASIS_COLLOCATED, mult_vec);
529d99fa3c5SJeremy L Thompson   CeedChk(ierr);
530d1d35e2fSjeremylt   ierr = CeedOperatorSetField(*op_prolong, "output", rstr_fine,
531d99fa3c5SJeremy L Thompson                               CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
532d99fa3c5SJeremy L Thompson   CeedChk(ierr);
533d99fa3c5SJeremy L Thompson 
534d99fa3c5SJeremy L Thompson   // Cleanup
535d1d35e2fSjeremylt   ierr = CeedVectorDestroy(&mult_vec); CeedChk(ierr);
536d1d35e2fSjeremylt   ierr = CeedBasisDestroy(&basis_c_to_f); CeedChk(ierr);
537d1d35e2fSjeremylt   ierr = CeedQFunctionDestroy(&qf_restrict); CeedChk(ierr);
538d1d35e2fSjeremylt   ierr = CeedQFunctionDestroy(&qf_prolong); CeedChk(ierr);
539e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
540d99fa3c5SJeremy L Thompson }
541d99fa3c5SJeremy L Thompson 
5427a982d89SJeremy L. Thompson /// @}
5437a982d89SJeremy L. Thompson 
5447a982d89SJeremy L. Thompson /// ----------------------------------------------------------------------------
5457a982d89SJeremy L. Thompson /// CeedOperator Backend API
5467a982d89SJeremy L. Thompson /// ----------------------------------------------------------------------------
5477a982d89SJeremy L. Thompson /// @addtogroup CeedOperatorBackend
5487a982d89SJeremy L. Thompson /// @{
5497a982d89SJeremy L. Thompson 
5507a982d89SJeremy L. Thompson /**
5517a982d89SJeremy L. Thompson   @brief Get the Ceed associated with a CeedOperator
5527a982d89SJeremy L. Thompson 
5537a982d89SJeremy L. Thompson   @param op         CeedOperator
5547a982d89SJeremy L. Thompson   @param[out] ceed  Variable to store Ceed
5557a982d89SJeremy L. Thompson 
5567a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
5577a982d89SJeremy L. Thompson 
5587a982d89SJeremy L. Thompson   @ref Backend
5597a982d89SJeremy L. Thompson **/
5607a982d89SJeremy L. Thompson 
5617a982d89SJeremy L. Thompson int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
5627a982d89SJeremy L. Thompson   *ceed = op->ceed;
563e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
5647a982d89SJeremy L. Thompson }
5657a982d89SJeremy L. Thompson 
5667a982d89SJeremy L. Thompson /**
5677a982d89SJeremy L. Thompson   @brief Get the number of elements associated with a CeedOperator
5687a982d89SJeremy L. Thompson 
5697a982d89SJeremy L. Thompson   @param op             CeedOperator
570d1d35e2fSjeremylt   @param[out] num_elem  Variable to store number of elements
5717a982d89SJeremy L. Thompson 
5727a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
5737a982d89SJeremy L. Thompson 
5747a982d89SJeremy L. Thompson   @ref Backend
5757a982d89SJeremy L. Thompson **/
5767a982d89SJeremy L. Thompson 
577d1d35e2fSjeremylt int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) {
5787a982d89SJeremy L. Thompson   if (op->composite)
5797a982d89SJeremy L. Thompson     // LCOV_EXCL_START
580e15f9bd0SJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_MINOR,
581e15f9bd0SJeremy L Thompson                      "Not defined for composite operator");
5827a982d89SJeremy L. Thompson   // LCOV_EXCL_STOP
5837a982d89SJeremy L. Thompson 
584d1d35e2fSjeremylt   *num_elem = op->num_elem;
585e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
5867a982d89SJeremy L. Thompson }
5877a982d89SJeremy L. Thompson 
5887a982d89SJeremy L. Thompson /**
5897a982d89SJeremy L. Thompson   @brief Get the number of quadrature points associated with a CeedOperator
5907a982d89SJeremy L. Thompson 
5917a982d89SJeremy L. Thompson   @param op             CeedOperator
592d1d35e2fSjeremylt   @param[out] num_qpts  Variable to store vector number of quadrature points
5937a982d89SJeremy L. Thompson 
5947a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
5957a982d89SJeremy L. Thompson 
5967a982d89SJeremy L. Thompson   @ref Backend
5977a982d89SJeremy L. Thompson **/
5987a982d89SJeremy L. Thompson 
599d1d35e2fSjeremylt int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) {
6007a982d89SJeremy L. Thompson   if (op->composite)
6017a982d89SJeremy L. Thompson     // LCOV_EXCL_START
602e15f9bd0SJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_MINOR,
603e15f9bd0SJeremy L Thompson                      "Not defined for composite operator");
6047a982d89SJeremy L. Thompson   // LCOV_EXCL_STOP
6057a982d89SJeremy L. Thompson 
606d1d35e2fSjeremylt   *num_qpts = op->num_qpts;
607e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
6087a982d89SJeremy L. Thompson }
6097a982d89SJeremy L. Thompson 
6107a982d89SJeremy L. Thompson /**
6117a982d89SJeremy L. Thompson   @brief Get the number of arguments associated with a CeedOperator
6127a982d89SJeremy L. Thompson 
6137a982d89SJeremy L. Thompson   @param op             CeedOperator
614d1d35e2fSjeremylt   @param[out] num_args  Variable to store vector number of arguments
6157a982d89SJeremy L. Thompson 
6167a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
6177a982d89SJeremy L. Thompson 
6187a982d89SJeremy L. Thompson   @ref Backend
6197a982d89SJeremy L. Thompson **/
6207a982d89SJeremy L. Thompson 
621d1d35e2fSjeremylt int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *num_args) {
6227a982d89SJeremy L. Thompson   if (op->composite)
6237a982d89SJeremy L. Thompson     // LCOV_EXCL_START
624e15f9bd0SJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_MINOR,
625e15f9bd0SJeremy L Thompson                      "Not defined for composite operators");
6267a982d89SJeremy L. Thompson   // LCOV_EXCL_STOP
6277a982d89SJeremy L. Thompson 
628d1d35e2fSjeremylt   *num_args = op->num_fields;
629e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
6307a982d89SJeremy L. Thompson }
6317a982d89SJeremy L. Thompson 
6327a982d89SJeremy L. Thompson /**
6337a982d89SJeremy L. Thompson   @brief Get the setup status of a CeedOperator
6347a982d89SJeremy L. Thompson 
6357a982d89SJeremy L. Thompson   @param op                  CeedOperator
636d1d35e2fSjeremylt   @param[out] is_setup_done  Variable to store setup status
6377a982d89SJeremy L. Thompson 
6387a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
6397a982d89SJeremy L. Thompson 
6407a982d89SJeremy L. Thompson   @ref Backend
6417a982d89SJeremy L. Thompson **/
6427a982d89SJeremy L. Thompson 
643d1d35e2fSjeremylt int CeedOperatorIsSetupDone(CeedOperator op, bool *is_setup_done) {
644d1d35e2fSjeremylt   *is_setup_done = op->backend_setup;
645e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
6467a982d89SJeremy L. Thompson }
6477a982d89SJeremy L. Thompson 
6487a982d89SJeremy L. Thompson /**
6497a982d89SJeremy L. Thompson   @brief Get the QFunction associated with a CeedOperator
6507a982d89SJeremy L. Thompson 
6517a982d89SJeremy L. Thompson   @param op       CeedOperator
6527a982d89SJeremy L. Thompson   @param[out] qf  Variable to store QFunction
6537a982d89SJeremy L. Thompson 
6547a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
6557a982d89SJeremy L. Thompson 
6567a982d89SJeremy L. Thompson   @ref Backend
6577a982d89SJeremy L. Thompson **/
6587a982d89SJeremy L. Thompson 
6597a982d89SJeremy L. Thompson int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
6607a982d89SJeremy L. Thompson   if (op->composite)
6617a982d89SJeremy L. Thompson     // LCOV_EXCL_START
662e15f9bd0SJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_MINOR,
663e15f9bd0SJeremy L Thompson                      "Not defined for composite operator");
6647a982d89SJeremy L. Thompson   // LCOV_EXCL_STOP
6657a982d89SJeremy L. Thompson 
6667a982d89SJeremy L. Thompson   *qf = op->qf;
667e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
6687a982d89SJeremy L. Thompson }
6697a982d89SJeremy L. Thompson 
6707a982d89SJeremy L. Thompson /**
671c04a41a7SJeremy L Thompson   @brief Get a boolean value indicating if the CeedOperator is composite
672c04a41a7SJeremy L Thompson 
673c04a41a7SJeremy L Thompson   @param op                 CeedOperator
674d1d35e2fSjeremylt   @param[out] is_composite  Variable to store composite status
675c04a41a7SJeremy L Thompson 
676c04a41a7SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
677c04a41a7SJeremy L Thompson 
678c04a41a7SJeremy L Thompson   @ref Backend
679c04a41a7SJeremy L Thompson **/
680c04a41a7SJeremy L Thompson 
681d1d35e2fSjeremylt int CeedOperatorIsComposite(CeedOperator op, bool *is_composite) {
682d1d35e2fSjeremylt   *is_composite = op->composite;
683e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
684c04a41a7SJeremy L Thompson }
685c04a41a7SJeremy L Thompson 
686c04a41a7SJeremy L Thompson /**
687d1d35e2fSjeremylt   @brief Get the number of sub_operators associated with a CeedOperator
6887a982d89SJeremy L. Thompson 
6897a982d89SJeremy L. Thompson   @param op                     CeedOperator
690d1d35e2fSjeremylt   @param[out] num_suboperators  Variable to store number of sub_operators
6917a982d89SJeremy L. Thompson 
6927a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
6937a982d89SJeremy L. Thompson 
6947a982d89SJeremy L. Thompson   @ref Backend
6957a982d89SJeremy L. Thompson **/
6967a982d89SJeremy L. Thompson 
697d1d35e2fSjeremylt int CeedOperatorGetNumSub(CeedOperator op, CeedInt *num_suboperators) {
6987a982d89SJeremy L. Thompson   if (!op->composite)
6997a982d89SJeremy L. Thompson     // LCOV_EXCL_START
700e15f9bd0SJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
7017a982d89SJeremy L. Thompson   // LCOV_EXCL_STOP
7027a982d89SJeremy L. Thompson 
703d1d35e2fSjeremylt   *num_suboperators = op->num_suboperators;
704e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
7057a982d89SJeremy L. Thompson }
7067a982d89SJeremy L. Thompson 
7077a982d89SJeremy L. Thompson /**
708d1d35e2fSjeremylt   @brief Get the list of sub_operators associated with a CeedOperator
7097a982d89SJeremy L. Thompson 
7107a982d89SJeremy L. Thompson   @param op                  CeedOperator
711d1d35e2fSjeremylt   @param[out] sub_operators  Variable to store list of sub_operators
7127a982d89SJeremy L. Thompson 
7137a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
7147a982d89SJeremy L. Thompson 
7157a982d89SJeremy L. Thompson   @ref Backend
7167a982d89SJeremy L. Thompson **/
7177a982d89SJeremy L. Thompson 
718d1d35e2fSjeremylt int CeedOperatorGetSubList(CeedOperator op, CeedOperator **sub_operators) {
7197a982d89SJeremy L. Thompson   if (!op->composite)
7207a982d89SJeremy L. Thompson     // LCOV_EXCL_START
721e15f9bd0SJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
7227a982d89SJeremy L. Thompson   // LCOV_EXCL_STOP
7237a982d89SJeremy L. Thompson 
724d1d35e2fSjeremylt   *sub_operators = op->sub_operators;
725e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
7267a982d89SJeremy L. Thompson }
7277a982d89SJeremy L. Thompson 
7287a982d89SJeremy L. Thompson /**
7297a982d89SJeremy L. Thompson   @brief Get the backend data of a CeedOperator
7307a982d89SJeremy L. Thompson 
7317a982d89SJeremy L. Thompson   @param op         CeedOperator
7327a982d89SJeremy L. Thompson   @param[out] data  Variable to store data
7337a982d89SJeremy L. Thompson 
7347a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
7357a982d89SJeremy L. Thompson 
7367a982d89SJeremy L. Thompson   @ref Backend
7377a982d89SJeremy L. Thompson **/
7387a982d89SJeremy L. Thompson 
739777ff853SJeremy L Thompson int CeedOperatorGetData(CeedOperator op, void *data) {
740777ff853SJeremy L Thompson   *(void **)data = op->data;
741e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
7427a982d89SJeremy L. Thompson }
7437a982d89SJeremy L. Thompson 
7447a982d89SJeremy L. Thompson /**
7457a982d89SJeremy L. Thompson   @brief Set the backend data of a CeedOperator
7467a982d89SJeremy L. Thompson 
7477a982d89SJeremy L. Thompson   @param[out] op  CeedOperator
7487a982d89SJeremy L. Thompson   @param data     Data to set
7497a982d89SJeremy L. Thompson 
7507a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
7517a982d89SJeremy L. Thompson 
7527a982d89SJeremy L. Thompson   @ref Backend
7537a982d89SJeremy L. Thompson **/
7547a982d89SJeremy L. Thompson 
755777ff853SJeremy L Thompson int CeedOperatorSetData(CeedOperator op, void *data) {
756777ff853SJeremy L Thompson   op->data = data;
757e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
7587a982d89SJeremy L. Thompson }
7597a982d89SJeremy L. Thompson 
7607a982d89SJeremy L. Thompson /**
76134359f16Sjeremylt   @brief Increment the reference counter for a CeedOperator
76234359f16Sjeremylt 
76334359f16Sjeremylt   @param op  CeedOperator to increment the reference counter
76434359f16Sjeremylt 
76534359f16Sjeremylt   @return An error code: 0 - success, otherwise - failure
76634359f16Sjeremylt 
76734359f16Sjeremylt   @ref Backend
76834359f16Sjeremylt **/
7699560d06aSjeremylt int CeedOperatorReference(CeedOperator op) {
77034359f16Sjeremylt   op->ref_count++;
77134359f16Sjeremylt   return CEED_ERROR_SUCCESS;
77234359f16Sjeremylt }
77334359f16Sjeremylt 
77434359f16Sjeremylt /**
7757a982d89SJeremy L. Thompson   @brief Set the setup flag of a CeedOperator to True
7767a982d89SJeremy L. Thompson 
7777a982d89SJeremy L. Thompson   @param op  CeedOperator
7787a982d89SJeremy L. Thompson 
7797a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
7807a982d89SJeremy L. Thompson 
7817a982d89SJeremy L. Thompson   @ref Backend
7827a982d89SJeremy L. Thompson **/
7837a982d89SJeremy L. Thompson 
7847a982d89SJeremy L. Thompson int CeedOperatorSetSetupDone(CeedOperator op) {
785d1d35e2fSjeremylt   op->backend_setup = true;
786e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
7877a982d89SJeremy L. Thompson }
7887a982d89SJeremy L. Thompson 
7897a982d89SJeremy L. Thompson /**
7907a982d89SJeremy L. Thompson   @brief Get the CeedOperatorFields of a CeedOperator
7917a982d89SJeremy L. Thompson 
7927a982d89SJeremy L. Thompson   @param op                  CeedOperator
793d1d35e2fSjeremylt   @param[out] input_fields   Variable to store input_fields
794d1d35e2fSjeremylt   @param[out] output_fields  Variable to store output_fields
7957a982d89SJeremy L. Thompson 
7967a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
7977a982d89SJeremy L. Thompson 
7987a982d89SJeremy L. Thompson   @ref Backend
7997a982d89SJeremy L. Thompson **/
8007a982d89SJeremy L. Thompson 
801d1d35e2fSjeremylt int CeedOperatorGetFields(CeedOperator op, CeedOperatorField **input_fields,
802d1d35e2fSjeremylt                           CeedOperatorField **output_fields) {
8037a982d89SJeremy L. Thompson   if (op->composite)
8047a982d89SJeremy L. Thompson     // LCOV_EXCL_START
805e15f9bd0SJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_MINOR,
806e15f9bd0SJeremy L Thompson                      "Not defined for composite operator");
8077a982d89SJeremy L. Thompson   // LCOV_EXCL_STOP
8087a982d89SJeremy L. Thompson 
809d1d35e2fSjeremylt   if (input_fields) *input_fields = op->input_fields;
810d1d35e2fSjeremylt   if (output_fields) *output_fields = op->output_fields;
811e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
8127a982d89SJeremy L. Thompson }
8137a982d89SJeremy L. Thompson 
8147a982d89SJeremy L. Thompson /**
8157a982d89SJeremy L. Thompson   @brief Get the CeedElemRestriction of a CeedOperatorField
8167a982d89SJeremy L. Thompson 
817d1d35e2fSjeremylt   @param op_field   CeedOperatorField
8187a982d89SJeremy L. Thompson   @param[out] rstr  Variable to store CeedElemRestriction
8197a982d89SJeremy L. Thompson 
8207a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
8217a982d89SJeremy L. Thompson 
8227a982d89SJeremy L. Thompson   @ref Backend
8237a982d89SJeremy L. Thompson **/
8247a982d89SJeremy L. Thompson 
825d1d35e2fSjeremylt int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field,
8267a982d89SJeremy L. Thompson                                         CeedElemRestriction *rstr) {
827d1d35e2fSjeremylt   *rstr = op_field->elem_restr;
828e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
8297a982d89SJeremy L. Thompson }
8307a982d89SJeremy L. Thompson 
8317a982d89SJeremy L. Thompson /**
8327a982d89SJeremy L. Thompson   @brief Get the CeedBasis of a CeedOperatorField
8337a982d89SJeremy L. Thompson 
834d1d35e2fSjeremylt   @param op_field    CeedOperatorField
8357a982d89SJeremy L. Thompson   @param[out] basis  Variable to store CeedBasis
8367a982d89SJeremy L. Thompson 
8377a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
8387a982d89SJeremy L. Thompson 
8397a982d89SJeremy L. Thompson   @ref Backend
8407a982d89SJeremy L. Thompson **/
8417a982d89SJeremy L. Thompson 
842d1d35e2fSjeremylt int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) {
843d1d35e2fSjeremylt   *basis = op_field->basis;
844e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
8457a982d89SJeremy L. Thompson }
8467a982d89SJeremy L. Thompson 
8477a982d89SJeremy L. Thompson /**
8487a982d89SJeremy L. Thompson   @brief Get the CeedVector of a CeedOperatorField
8497a982d89SJeremy L. Thompson 
850d1d35e2fSjeremylt   @param op_field  CeedOperatorField
8517a982d89SJeremy L. Thompson   @param[out] vec  Variable to store CeedVector
8527a982d89SJeremy L. Thompson 
8537a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
8547a982d89SJeremy L. Thompson 
8557a982d89SJeremy L. Thompson   @ref Backend
8567a982d89SJeremy L. Thompson **/
8577a982d89SJeremy L. Thompson 
858d1d35e2fSjeremylt int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) {
859d1d35e2fSjeremylt   *vec = op_field->vec;
860e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
8617a982d89SJeremy L. Thompson }
8627a982d89SJeremy L. Thompson 
8637a982d89SJeremy L. Thompson /// @}
8647a982d89SJeremy L. Thompson 
8657a982d89SJeremy L. Thompson /// ----------------------------------------------------------------------------
8667a982d89SJeremy L. Thompson /// CeedOperator Public API
8677a982d89SJeremy L. Thompson /// ----------------------------------------------------------------------------
8687a982d89SJeremy L. Thompson /// @addtogroup CeedOperatorUser
869dfdf5a53Sjeremylt /// @{
870d7b241e6Sjeremylt 
871d7b241e6Sjeremylt /**
8720219ea01SJeremy L Thompson   @brief Create a CeedOperator and associate a CeedQFunction. A CeedBasis and
8730219ea01SJeremy L Thompson            CeedElemRestriction can be associated with CeedQFunction fields with
8740219ea01SJeremy L Thompson            \ref CeedOperatorSetField.
875d7b241e6Sjeremylt 
876b11c1e72Sjeremylt   @param ceed     A Ceed object where the CeedOperator will be created
877d7b241e6Sjeremylt   @param qf       QFunction defining the action of the operator at quadrature points
87834138859Sjeremylt   @param dqf      QFunction defining the action of the Jacobian of @a qf (or
8794cc79fe7SJed Brown                     @ref CEED_QFUNCTION_NONE)
880d7b241e6Sjeremylt   @param dqfT     QFunction defining the action of the transpose of the Jacobian
8814cc79fe7SJed Brown                     of @a qf (or @ref CEED_QFUNCTION_NONE)
882b11c1e72Sjeremylt   @param[out] op  Address of the variable where the newly created
883b11c1e72Sjeremylt                     CeedOperator will be stored
884b11c1e72Sjeremylt 
885b11c1e72Sjeremylt   @return An error code: 0 - success, otherwise - failure
886dfdf5a53Sjeremylt 
8877a982d89SJeremy L. Thompson   @ref User
888d7b241e6Sjeremylt  */
889d7b241e6Sjeremylt int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf,
890d7b241e6Sjeremylt                        CeedQFunction dqfT, CeedOperator *op) {
891d7b241e6Sjeremylt   int ierr;
892d7b241e6Sjeremylt 
8935fe0d4faSjeremylt   if (!ceed->OperatorCreate) {
8945fe0d4faSjeremylt     Ceed delegate;
895aefd8378Sjeremylt     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
8965fe0d4faSjeremylt 
8975fe0d4faSjeremylt     if (!delegate)
898c042f62fSJeremy L Thompson       // LCOV_EXCL_START
899e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
900e15f9bd0SJeremy L Thompson                        "Backend does not support OperatorCreate");
901c042f62fSJeremy L Thompson     // LCOV_EXCL_STOP
9025fe0d4faSjeremylt 
9035fe0d4faSjeremylt     ierr = CeedOperatorCreate(delegate, qf, dqf, dqfT, op); CeedChk(ierr);
904e15f9bd0SJeremy L Thompson     return CEED_ERROR_SUCCESS;
9055fe0d4faSjeremylt   }
9065fe0d4faSjeremylt 
907b3b7035fSJeremy L Thompson   if (!qf || qf == CEED_QFUNCTION_NONE)
908b3b7035fSJeremy L Thompson     // LCOV_EXCL_START
909e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_MINOR,
910e15f9bd0SJeremy L Thompson                      "Operator must have a valid QFunction.");
911b3b7035fSJeremy L Thompson   // LCOV_EXCL_STOP
912d7b241e6Sjeremylt   ierr = CeedCalloc(1, op); CeedChk(ierr);
913d7b241e6Sjeremylt   (*op)->ceed = ceed;
9149560d06aSjeremylt   ierr = CeedReference(ceed); CeedChk(ierr);
915d1d35e2fSjeremylt   (*op)->ref_count = 1;
916d7b241e6Sjeremylt   (*op)->qf = qf;
9179560d06aSjeremylt   ierr = CeedQFunctionReference(qf); CeedChk(ierr);
918442e7f0bSjeremylt   if (dqf && dqf != CEED_QFUNCTION_NONE) {
919d7b241e6Sjeremylt     (*op)->dqf = dqf;
9209560d06aSjeremylt     ierr = CeedQFunctionReference(dqf); CeedChk(ierr);
921442e7f0bSjeremylt   }
922442e7f0bSjeremylt   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
923d7b241e6Sjeremylt     (*op)->dqfT = dqfT;
9249560d06aSjeremylt     ierr = CeedQFunctionReference(dqfT); CeedChk(ierr);
925442e7f0bSjeremylt   }
926d1d35e2fSjeremylt   ierr = CeedCalloc(16, &(*op)->input_fields); CeedChk(ierr);
927d1d35e2fSjeremylt   ierr = CeedCalloc(16, &(*op)->output_fields); CeedChk(ierr);
928d7b241e6Sjeremylt   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
929e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
930d7b241e6Sjeremylt }
931d7b241e6Sjeremylt 
932d7b241e6Sjeremylt /**
93352d6035fSJeremy L Thompson   @brief Create an operator that composes the action of several operators
93452d6035fSJeremy L Thompson 
93552d6035fSJeremy L Thompson   @param ceed     A Ceed object where the CeedOperator will be created
93652d6035fSJeremy L Thompson   @param[out] op  Address of the variable where the newly created
93752d6035fSJeremy L Thompson                     Composite CeedOperator will be stored
93852d6035fSJeremy L Thompson 
93952d6035fSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
94052d6035fSJeremy L Thompson 
9417a982d89SJeremy L. Thompson   @ref User
94252d6035fSJeremy L Thompson  */
94352d6035fSJeremy L Thompson int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
94452d6035fSJeremy L Thompson   int ierr;
94552d6035fSJeremy L Thompson 
94652d6035fSJeremy L Thompson   if (!ceed->CompositeOperatorCreate) {
94752d6035fSJeremy L Thompson     Ceed delegate;
948aefd8378Sjeremylt     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
94952d6035fSJeremy L Thompson 
950250756a7Sjeremylt     if (delegate) {
95152d6035fSJeremy L Thompson       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
952e15f9bd0SJeremy L Thompson       return CEED_ERROR_SUCCESS;
95352d6035fSJeremy L Thompson     }
954250756a7Sjeremylt   }
95552d6035fSJeremy L Thompson 
95652d6035fSJeremy L Thompson   ierr = CeedCalloc(1, op); CeedChk(ierr);
95752d6035fSJeremy L Thompson   (*op)->ceed = ceed;
9589560d06aSjeremylt   ierr = CeedReference(ceed); CeedChk(ierr);
95952d6035fSJeremy L Thompson   (*op)->composite = true;
960d1d35e2fSjeremylt   ierr = CeedCalloc(16, &(*op)->sub_operators); CeedChk(ierr);
961250756a7Sjeremylt 
962250756a7Sjeremylt   if (ceed->CompositeOperatorCreate) {
96352d6035fSJeremy L Thompson     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
964250756a7Sjeremylt   }
965e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
96652d6035fSJeremy L Thompson }
96752d6035fSJeremy L Thompson 
96852d6035fSJeremy L Thompson /**
9699560d06aSjeremylt   @brief Copy the pointer to a CeedOperator. Both pointers should
9709560d06aSjeremylt            be destroyed with `CeedOperatorDestroy()`;
9719560d06aSjeremylt            Note: If `*op_copy` is non-NULL, then it is assumed that
9729560d06aSjeremylt            `*op_copy` is a pointer to a CeedOperator. This
9739560d06aSjeremylt            CeedOperator will be destroyed if `*op_copy` is the only
9749560d06aSjeremylt            reference to this CeedOperator.
9759560d06aSjeremylt 
9769560d06aSjeremylt   @param op            CeedOperator to copy reference to
9779560d06aSjeremylt   @param[out] op_copy  Variable to store copied reference
9789560d06aSjeremylt 
9799560d06aSjeremylt   @return An error code: 0 - success, otherwise - failure
9809560d06aSjeremylt 
9819560d06aSjeremylt   @ref User
9829560d06aSjeremylt **/
9839560d06aSjeremylt int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) {
9849560d06aSjeremylt   int ierr;
9859560d06aSjeremylt 
9869560d06aSjeremylt   ierr = CeedOperatorReference(op); CeedChk(ierr);
9879560d06aSjeremylt   ierr = CeedOperatorDestroy(op_copy); CeedChk(ierr);
9889560d06aSjeremylt   *op_copy = op;
9899560d06aSjeremylt   return CEED_ERROR_SUCCESS;
9909560d06aSjeremylt }
9919560d06aSjeremylt 
9929560d06aSjeremylt /**
993b11c1e72Sjeremylt   @brief Provide a field to a CeedOperator for use by its CeedQFunction
994d7b241e6Sjeremylt 
995d7b241e6Sjeremylt   This function is used to specify both active and passive fields to a
996d7b241e6Sjeremylt   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
997d7b241e6Sjeremylt   fields can inputs or outputs (updated in-place when operator is applied).
998d7b241e6Sjeremylt 
999d7b241e6Sjeremylt   Active fields must be specified using this function, but their data (in a
1000d7b241e6Sjeremylt   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
1001d7b241e6Sjeremylt   input and at most one active output.
1002d7b241e6Sjeremylt 
10038c91a0c9SJeremy L Thompson   @param op          CeedOperator on which to provide the field
1004d1d35e2fSjeremylt   @param field_name  Name of the field (to be matched with the name used by
10058795c945Sjeremylt                        CeedQFunction)
1006b11c1e72Sjeremylt   @param r           CeedElemRestriction
10074cc79fe7SJed Brown   @param b           CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED
1008b11c1e72Sjeremylt                        if collocated with quadrature points
10094cc79fe7SJed Brown   @param v           CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE
10104cc79fe7SJed Brown                        if field is active or @ref CEED_VECTOR_NONE if using
10114cc79fe7SJed Brown                        @ref CEED_EVAL_WEIGHT in the QFunction
1012b11c1e72Sjeremylt 
1013b11c1e72Sjeremylt   @return An error code: 0 - success, otherwise - failure
1014dfdf5a53Sjeremylt 
10157a982d89SJeremy L. Thompson   @ref User
1016b11c1e72Sjeremylt **/
1017d1d35e2fSjeremylt int CeedOperatorSetField(CeedOperator op, const char *field_name,
1018a8d32208Sjeremylt                          CeedElemRestriction r, CeedBasis b, CeedVector v) {
1019d7b241e6Sjeremylt   int ierr;
102052d6035fSJeremy L Thompson   if (op->composite)
1021c042f62fSJeremy L Thompson     // LCOV_EXCL_START
1022e1e9e29dSJed Brown     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1023e15f9bd0SJeremy L Thompson                      "Cannot add field to composite operator.");
1024c042f62fSJeremy L Thompson   // LCOV_EXCL_STOP
10258b067b84SJed Brown   if (!r)
1026c042f62fSJeremy L Thompson     // LCOV_EXCL_START
1027e1e9e29dSJed Brown     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1028c042f62fSJeremy L Thompson                      "ElemRestriction r for field \"%s\" must be non-NULL.",
1029d1d35e2fSjeremylt                      field_name);
1030c042f62fSJeremy L Thompson   // LCOV_EXCL_STOP
10318b067b84SJed Brown   if (!b)
1032c042f62fSJeremy L Thompson     // LCOV_EXCL_START
1033e1e9e29dSJed Brown     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1034e15f9bd0SJeremy L Thompson                      "Basis b for field \"%s\" must be non-NULL.",
1035d1d35e2fSjeremylt                      field_name);
1036c042f62fSJeremy L Thompson   // LCOV_EXCL_STOP
10378b067b84SJed Brown   if (!v)
1038c042f62fSJeremy L Thompson     // LCOV_EXCL_START
1039e1e9e29dSJed Brown     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1040e15f9bd0SJeremy L Thompson                      "Vector v for field \"%s\" must be non-NULL.",
1041d1d35e2fSjeremylt                      field_name);
1042c042f62fSJeremy L Thompson   // LCOV_EXCL_STOP
104352d6035fSJeremy L Thompson 
1044d1d35e2fSjeremylt   CeedInt num_elem;
1045d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumElements(r, &num_elem); CeedChk(ierr);
1046d1d35e2fSjeremylt   if (r != CEED_ELEMRESTRICTION_NONE && op->has_restriction &&
1047d1d35e2fSjeremylt       op->num_elem != num_elem)
1048c042f62fSJeremy L Thompson     // LCOV_EXCL_START
1049e15f9bd0SJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_DIMENSION,
10501d102b48SJeremy L Thompson                      "ElemRestriction with %d elements incompatible with prior "
1051d1d35e2fSjeremylt                      "%d elements", num_elem, op->num_elem);
1052c042f62fSJeremy L Thompson   // LCOV_EXCL_STOP
1053d7b241e6Sjeremylt 
105478464608Sjeremylt   CeedInt num_qpts = 0;
1055e15f9bd0SJeremy L Thompson   if (b != CEED_BASIS_COLLOCATED) {
1056d1d35e2fSjeremylt     ierr = CeedBasisGetNumQuadraturePoints(b, &num_qpts); CeedChk(ierr);
1057d1d35e2fSjeremylt     if (op->num_qpts && op->num_qpts != num_qpts)
1058c042f62fSJeremy L Thompson       // LCOV_EXCL_START
1059e15f9bd0SJeremy L Thompson       return CeedError(op->ceed, CEED_ERROR_DIMENSION,
1060e15f9bd0SJeremy L Thompson                        "Basis with %d quadrature points "
1061d1d35e2fSjeremylt                        "incompatible with prior %d points", num_qpts,
1062d1d35e2fSjeremylt                        op->num_qpts);
1063c042f62fSJeremy L Thompson     // LCOV_EXCL_STOP
1064d7b241e6Sjeremylt   }
1065d1d35e2fSjeremylt   CeedQFunctionField qf_field;
1066d1d35e2fSjeremylt   CeedOperatorField *op_field;
1067d1d35e2fSjeremylt   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
1068d1d35e2fSjeremylt     if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) {
1069d1d35e2fSjeremylt       qf_field = op->qf->input_fields[i];
1070d1d35e2fSjeremylt       op_field = &op->input_fields[i];
1071d7b241e6Sjeremylt       goto found;
1072d7b241e6Sjeremylt     }
1073d7b241e6Sjeremylt   }
1074d1d35e2fSjeremylt   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
1075d1d35e2fSjeremylt     if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) {
1076d1d35e2fSjeremylt       qf_field = op->qf->output_fields[i];
1077d1d35e2fSjeremylt       op_field = &op->output_fields[i];
1078d7b241e6Sjeremylt       goto found;
1079d7b241e6Sjeremylt     }
1080d7b241e6Sjeremylt   }
1081c042f62fSJeremy L Thompson   // LCOV_EXCL_START
1082e15f9bd0SJeremy L Thompson   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE,
1083e15f9bd0SJeremy L Thompson                    "QFunction has no knowledge of field '%s'",
1084d1d35e2fSjeremylt                    field_name);
1085c042f62fSJeremy L Thompson   // LCOV_EXCL_STOP
1086d7b241e6Sjeremylt found:
1087d1d35e2fSjeremylt   ierr = CeedOperatorCheckField(op->ceed, qf_field, r, b); CeedChk(ierr);
1088d1d35e2fSjeremylt   ierr = CeedCalloc(1, op_field); CeedChk(ierr);
1089e15f9bd0SJeremy L Thompson 
1090d1d35e2fSjeremylt   (*op_field)->vec = v;
1091e15f9bd0SJeremy L Thompson   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE) {
10929560d06aSjeremylt     ierr = CeedVectorReference(v); CeedChk(ierr);
1093e15f9bd0SJeremy L Thompson   }
1094e15f9bd0SJeremy L Thompson 
1095d1d35e2fSjeremylt   (*op_field)->elem_restr = r;
10969560d06aSjeremylt   ierr = CeedElemRestrictionReference(r); CeedChk(ierr);
1097e15f9bd0SJeremy L Thompson   if (r != CEED_ELEMRESTRICTION_NONE) {
1098d1d35e2fSjeremylt     op->num_elem = num_elem;
1099d1d35e2fSjeremylt     op->has_restriction = true; // Restriction set, but num_elem may be 0
1100e15f9bd0SJeremy L Thompson   }
1101d99fa3c5SJeremy L Thompson 
1102d1d35e2fSjeremylt   (*op_field)->basis = b;
1103e15f9bd0SJeremy L Thompson   if (b != CEED_BASIS_COLLOCATED) {
1104*cd4dfc48Sjeremylt     if (!op->num_qpts) {
1105*cd4dfc48Sjeremylt       ierr = CeedOperatorSetNumQuadraturePoints(op, num_qpts); CeedChk(ierr);
1106*cd4dfc48Sjeremylt     }
11079560d06aSjeremylt     ierr = CeedBasisReference(b); CeedChk(ierr);
1108e15f9bd0SJeremy L Thompson   }
1109e15f9bd0SJeremy L Thompson 
1110d1d35e2fSjeremylt   op->num_fields += 1;
1111d1d35e2fSjeremylt   size_t len = strlen(field_name);
1112d99fa3c5SJeremy L Thompson   char *tmp;
1113d99fa3c5SJeremy L Thompson   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
1114d1d35e2fSjeremylt   memcpy(tmp, field_name, len+1);
1115d1d35e2fSjeremylt   (*op_field)->field_name = tmp;
1116e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1117d7b241e6Sjeremylt }
1118d7b241e6Sjeremylt 
1119d7b241e6Sjeremylt /**
112052d6035fSJeremy L Thompson   @brief Add a sub-operator to a composite CeedOperator
1121288c0443SJeremy L Thompson 
1122d1d35e2fSjeremylt   @param[out] composite_op  Composite CeedOperator
1123d1d35e2fSjeremylt   @param      sub_op        Sub-operator CeedOperator
112452d6035fSJeremy L Thompson 
112552d6035fSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
112652d6035fSJeremy L Thompson 
11277a982d89SJeremy L. Thompson   @ref User
112852d6035fSJeremy L Thompson  */
1129d1d35e2fSjeremylt int CeedCompositeOperatorAddSub(CeedOperator composite_op,
1130d1d35e2fSjeremylt                                 CeedOperator sub_op) {
113134359f16Sjeremylt   int ierr;
113234359f16Sjeremylt 
1133d1d35e2fSjeremylt   if (!composite_op->composite)
1134c042f62fSJeremy L Thompson     // LCOV_EXCL_START
1135d1d35e2fSjeremylt     return CeedError(composite_op->ceed, CEED_ERROR_MINOR,
1136e2f04181SAndrew T. Barker                      "CeedOperator is not a composite operator");
1137c042f62fSJeremy L Thompson   // LCOV_EXCL_STOP
113852d6035fSJeremy L Thompson 
1139d1d35e2fSjeremylt   if (composite_op->num_suboperators == CEED_COMPOSITE_MAX)
1140c042f62fSJeremy L Thompson     // LCOV_EXCL_START
1141d1d35e2fSjeremylt     return CeedError(composite_op->ceed, CEED_ERROR_UNSUPPORTED,
1142d1d35e2fSjeremylt                      "Cannot add additional sub_operators");
1143c042f62fSJeremy L Thompson   // LCOV_EXCL_STOP
114452d6035fSJeremy L Thompson 
1145d1d35e2fSjeremylt   composite_op->sub_operators[composite_op->num_suboperators] = sub_op;
11469560d06aSjeremylt   ierr = CeedOperatorReference(sub_op); CeedChk(ierr);
1147d1d35e2fSjeremylt   composite_op->num_suboperators++;
1148e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
114952d6035fSJeremy L Thompson }
115052d6035fSJeremy L Thompson 
115152d6035fSJeremy L Thompson /**
11521d102b48SJeremy L Thompson   @brief Assemble a linear CeedQFunction associated with a CeedOperator
11531d102b48SJeremy L Thompson 
11541d102b48SJeremy L Thompson   This returns a CeedVector containing a matrix at each quadrature point
11551d102b48SJeremy L Thompson     providing the action of the CeedQFunction associated with the CeedOperator.
11561d102b48SJeremy L Thompson     The vector 'assembled' is of shape
11571d102b48SJeremy L Thompson       [num_elements, num_input_fields, num_output_fields, num_quad_points]
11581d102b48SJeremy L Thompson     and contains column-major matrices representing the action of the
11591d102b48SJeremy L Thompson     CeedQFunction for a corresponding quadrature point on an element. Inputs and
11601d102b48SJeremy L Thompson     outputs are in the order provided by the user when adding CeedOperator fields.
11614cc79fe7SJed Brown     For example, a CeedQFunction with inputs 'u' and 'gradu' and outputs 'gradv' and
11621d102b48SJeremy L Thompson     'v', provided in that order, would result in an assembled QFunction that
11631d102b48SJeremy L Thompson     consists of (1 + dim) x (dim + 1) matrices at each quadrature point acting
11641d102b48SJeremy L Thompson     on the input [u, du_0, du_1] and producing the output [dv_0, dv_1, v].
11651d102b48SJeremy L Thompson 
11661d102b48SJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
11671d102b48SJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedQFunction at
11681d102b48SJeremy L Thompson                            quadrature points
11691d102b48SJeremy L Thompson   @param[out] rstr       CeedElemRestriction for CeedVector containing assembled
11701d102b48SJeremy L Thompson                            CeedQFunction
11711d102b48SJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
11724cc79fe7SJed Brown                            @ref CEED_REQUEST_IMMEDIATE
11731d102b48SJeremy L Thompson 
11741d102b48SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
11751d102b48SJeremy L Thompson 
11767a982d89SJeremy L. Thompson   @ref User
11771d102b48SJeremy L Thompson **/
117880ac2e43SJeremy L Thompson int CeedOperatorLinearAssembleQFunction(CeedOperator op, CeedVector *assembled,
11797f823360Sjeremylt                                         CeedElemRestriction *rstr,
11807f823360Sjeremylt                                         CeedRequest *request) {
11811d102b48SJeremy L Thompson   int ierr;
1182e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
11831d102b48SJeremy L Thompson 
11847172caadSjeremylt   // Backend version
118580ac2e43SJeremy L Thompson   if (op->LinearAssembleQFunction) {
1186e2f04181SAndrew T. Barker     return op->LinearAssembleQFunction(op, assembled, rstr, request);
11875107b09fSJeremy L Thompson   } else {
11885107b09fSJeremy L Thompson     // Fallback to reference Ceed
1189d1d35e2fSjeremylt     if (!op->op_fallback) {
11905107b09fSJeremy L Thompson       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
11915107b09fSJeremy L Thompson     }
11925107b09fSJeremy L Thompson     // Assemble
1193d1d35e2fSjeremylt     return CeedOperatorLinearAssembleQFunction(op->op_fallback, assembled,
1194e2f04181SAndrew T. Barker            rstr, request);
11955107b09fSJeremy L Thompson   }
11961d102b48SJeremy L Thompson }
11971d102b48SJeremy L Thompson 
11981d102b48SJeremy L Thompson /**
1199d965c7a7SJeremy L Thompson   @brief Assemble the diagonal of a square linear CeedOperator
1200b7ec98d8SJeremy L Thompson 
12019e9210b8SJeremy L Thompson   This overwrites a CeedVector with the diagonal of a linear CeedOperator.
1202b7ec98d8SJeremy L Thompson 
1203c04a41a7SJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and
1204c04a41a7SJeremy L Thompson           composite CeedOperators with single field sub-operators are supported.
1205d965c7a7SJeremy L Thompson 
1206b7ec98d8SJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
1207b7ec98d8SJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedOperator diagonal
1208b7ec98d8SJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
12094cc79fe7SJed Brown                            @ref CEED_REQUEST_IMMEDIATE
1210b7ec98d8SJeremy L Thompson 
1211b7ec98d8SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1212b7ec98d8SJeremy L Thompson 
12137a982d89SJeremy L. Thompson   @ref User
1214b7ec98d8SJeremy L Thompson **/
12152bba3ffaSJeremy L Thompson int CeedOperatorLinearAssembleDiagonal(CeedOperator op, CeedVector assembled,
12167f823360Sjeremylt                                        CeedRequest *request) {
1217b7ec98d8SJeremy L Thompson   int ierr;
1218e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1219b7ec98d8SJeremy L Thompson 
1220b7ec98d8SJeremy L Thompson   // Use backend version, if available
122180ac2e43SJeremy L Thompson   if (op->LinearAssembleDiagonal) {
1222e2f04181SAndrew T. Barker     return op->LinearAssembleDiagonal(op, assembled, request);
12239e9210b8SJeremy L Thompson   } else if (op->LinearAssembleAddDiagonal) {
12249e9210b8SJeremy L Thompson     ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
12259e9210b8SJeremy L Thompson     return CeedOperatorLinearAssembleAddDiagonal(op, assembled, request);
12269e9210b8SJeremy L Thompson   } else {
12279e9210b8SJeremy L Thompson     // Fallback to reference Ceed
1228d1d35e2fSjeremylt     if (!op->op_fallback) {
12299e9210b8SJeremy L Thompson       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
12309e9210b8SJeremy L Thompson     }
12319e9210b8SJeremy L Thompson     // Assemble
1232d1d35e2fSjeremylt     return CeedOperatorLinearAssembleDiagonal(op->op_fallback, assembled,
1233e2f04181SAndrew T. Barker            request);
12349e9210b8SJeremy L Thompson   }
12359e9210b8SJeremy L Thompson }
12369e9210b8SJeremy L Thompson 
12379e9210b8SJeremy L Thompson /**
12389e9210b8SJeremy L Thompson   @brief Assemble the diagonal of a square linear CeedOperator
12399e9210b8SJeremy L Thompson 
12409e9210b8SJeremy L Thompson   This sums into a CeedVector the diagonal of a linear CeedOperator.
12419e9210b8SJeremy L Thompson 
12429e9210b8SJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and
12439e9210b8SJeremy L Thompson           composite CeedOperators with single field sub-operators are supported.
12449e9210b8SJeremy L Thompson 
12459e9210b8SJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
12469e9210b8SJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedOperator diagonal
12479e9210b8SJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
12489e9210b8SJeremy L Thompson                            @ref CEED_REQUEST_IMMEDIATE
12499e9210b8SJeremy L Thompson 
12509e9210b8SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
12519e9210b8SJeremy L Thompson 
12529e9210b8SJeremy L Thompson   @ref User
12539e9210b8SJeremy L Thompson **/
12549e9210b8SJeremy L Thompson int CeedOperatorLinearAssembleAddDiagonal(CeedOperator op, CeedVector assembled,
12559e9210b8SJeremy L Thompson     CeedRequest *request) {
12569e9210b8SJeremy L Thompson   int ierr;
1257e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
12589e9210b8SJeremy L Thompson 
12599e9210b8SJeremy L Thompson   // Use backend version, if available
12609e9210b8SJeremy L Thompson   if (op->LinearAssembleAddDiagonal) {
1261e2f04181SAndrew T. Barker     return op->LinearAssembleAddDiagonal(op, assembled, request);
12627172caadSjeremylt   } else {
12637172caadSjeremylt     // Fallback to reference Ceed
1264d1d35e2fSjeremylt     if (!op->op_fallback) {
12657172caadSjeremylt       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1266b7ec98d8SJeremy L Thompson     }
12677172caadSjeremylt     // Assemble
1268d1d35e2fSjeremylt     return CeedOperatorLinearAssembleAddDiagonal(op->op_fallback, assembled,
1269e2f04181SAndrew T. Barker            request);
1270b7ec98d8SJeremy L Thompson   }
1271b7ec98d8SJeremy L Thompson }
1272b7ec98d8SJeremy L Thompson 
1273b7ec98d8SJeremy L Thompson /**
1274d965c7a7SJeremy L Thompson   @brief Assemble the point block diagonal of a square linear CeedOperator
1275d965c7a7SJeremy L Thompson 
12769e9210b8SJeremy L Thompson   This overwrites a CeedVector with the point block diagonal of a linear
1277d965c7a7SJeremy L Thompson     CeedOperator.
1278d965c7a7SJeremy L Thompson 
1279c04a41a7SJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and
1280c04a41a7SJeremy L Thompson           composite CeedOperators with single field sub-operators are supported.
1281d965c7a7SJeremy L Thompson 
1282d965c7a7SJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
1283d965c7a7SJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedOperator point block
1284d965c7a7SJeremy L Thompson                            diagonal, provided in row-major form with an
1285d1d35e2fSjeremylt                            @a num_comp * @a num_comp block at each node. The dimensions
1286d965c7a7SJeremy L Thompson                            of this vector are derived from the active vector
1287d965c7a7SJeremy L Thompson                            for the CeedOperator. The array has shape
1288d965c7a7SJeremy L Thompson                            [nodes, component out, component in].
1289d965c7a7SJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
1290d965c7a7SJeremy L Thompson                            CEED_REQUEST_IMMEDIATE
1291d965c7a7SJeremy L Thompson 
1292d965c7a7SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1293d965c7a7SJeremy L Thompson 
1294d965c7a7SJeremy L Thompson   @ref User
1295d965c7a7SJeremy L Thompson **/
129680ac2e43SJeremy L Thompson int CeedOperatorLinearAssemblePointBlockDiagonal(CeedOperator op,
12972bba3ffaSJeremy L Thompson     CeedVector assembled, CeedRequest *request) {
1298d965c7a7SJeremy L Thompson   int ierr;
1299e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1300d965c7a7SJeremy L Thompson 
1301d965c7a7SJeremy L Thompson   // Use backend version, if available
130280ac2e43SJeremy L Thompson   if (op->LinearAssemblePointBlockDiagonal) {
1303e2f04181SAndrew T. Barker     return op->LinearAssemblePointBlockDiagonal(op, assembled, request);
13049e9210b8SJeremy L Thompson   } else if (op->LinearAssembleAddPointBlockDiagonal) {
13059e9210b8SJeremy L Thompson     ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
13069e9210b8SJeremy L Thompson     return CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled,
13079e9210b8SJeremy L Thompson            request);
1308d965c7a7SJeremy L Thompson   } else {
1309d965c7a7SJeremy L Thompson     // Fallback to reference Ceed
1310d1d35e2fSjeremylt     if (!op->op_fallback) {
1311d965c7a7SJeremy L Thompson       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1312d965c7a7SJeremy L Thompson     }
1313d965c7a7SJeremy L Thompson     // Assemble
1314d1d35e2fSjeremylt     return CeedOperatorLinearAssemblePointBlockDiagonal(op->op_fallback,
1315e2f04181SAndrew T. Barker            assembled, request);
13169e9210b8SJeremy L Thompson   }
13179e9210b8SJeremy L Thompson }
13189e9210b8SJeremy L Thompson 
13199e9210b8SJeremy L Thompson /**
13209e9210b8SJeremy L Thompson   @brief Assemble the point block diagonal of a square linear CeedOperator
13219e9210b8SJeremy L Thompson 
13229e9210b8SJeremy L Thompson   This sums into a CeedVector with the point block diagonal of a linear
13239e9210b8SJeremy L Thompson     CeedOperator.
13249e9210b8SJeremy L Thompson 
13259e9210b8SJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and
13269e9210b8SJeremy L Thompson           composite CeedOperators with single field sub-operators are supported.
13279e9210b8SJeremy L Thompson 
13289e9210b8SJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
13299e9210b8SJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedOperator point block
13309e9210b8SJeremy L Thompson                            diagonal, provided in row-major form with an
1331d1d35e2fSjeremylt                            @a num_comp * @a num_comp block at each node. The dimensions
13329e9210b8SJeremy L Thompson                            of this vector are derived from the active vector
13339e9210b8SJeremy L Thompson                            for the CeedOperator. The array has shape
13349e9210b8SJeremy L Thompson                            [nodes, component out, component in].
13359e9210b8SJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
13369e9210b8SJeremy L Thompson                            CEED_REQUEST_IMMEDIATE
13379e9210b8SJeremy L Thompson 
13389e9210b8SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
13399e9210b8SJeremy L Thompson 
13409e9210b8SJeremy L Thompson   @ref User
13419e9210b8SJeremy L Thompson **/
13429e9210b8SJeremy L Thompson int CeedOperatorLinearAssembleAddPointBlockDiagonal(CeedOperator op,
13439e9210b8SJeremy L Thompson     CeedVector assembled, CeedRequest *request) {
13449e9210b8SJeremy L Thompson   int ierr;
1345e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
13469e9210b8SJeremy L Thompson 
13479e9210b8SJeremy L Thompson   // Use backend version, if available
13489e9210b8SJeremy L Thompson   if (op->LinearAssembleAddPointBlockDiagonal) {
1349e2f04181SAndrew T. Barker     return op->LinearAssembleAddPointBlockDiagonal(op, assembled, request);
13509e9210b8SJeremy L Thompson   } else {
13519e9210b8SJeremy L Thompson     // Fallback to reference Ceed
1352d1d35e2fSjeremylt     if (!op->op_fallback) {
13539e9210b8SJeremy L Thompson       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
13549e9210b8SJeremy L Thompson     }
13559e9210b8SJeremy L Thompson     // Assemble
1356d1d35e2fSjeremylt     return CeedOperatorLinearAssembleAddPointBlockDiagonal(op->op_fallback,
1357e2f04181SAndrew T. Barker            assembled, request);
1358d965c7a7SJeremy L Thompson   }
1359e2f04181SAndrew T. Barker }
1360e2f04181SAndrew T. Barker 
1361e2f04181SAndrew T. Barker /**
1362e2f04181SAndrew T. Barker    @brief Build nonzero pattern for non-composite operator.
1363e2f04181SAndrew T. Barker 
1364e2f04181SAndrew T. Barker    Users should generally use CeedOperatorLinearAssembleSymbolic()
1365e2f04181SAndrew T. Barker 
1366e2f04181SAndrew T. Barker    @ref Developer
1367e2f04181SAndrew T. Barker **/
1368e2f04181SAndrew T. Barker int CeedSingleOperatorAssembleSymbolic(CeedOperator op, CeedInt offset,
1369e2f04181SAndrew T. Barker                                        CeedInt *rows, CeedInt *cols) {
1370e2f04181SAndrew T. Barker   int ierr;
1371e2f04181SAndrew T. Barker   Ceed ceed = op->ceed;
1372e2f04181SAndrew T. Barker   if (op->composite)
1373e2f04181SAndrew T. Barker     // LCOV_EXCL_START
1374e2f04181SAndrew T. Barker     return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
1375e2f04181SAndrew T. Barker                      "Composite operator not supported");
1376e2f04181SAndrew T. Barker   // LCOV_EXCL_STOP
1377e2f04181SAndrew T. Barker 
1378d1d35e2fSjeremylt   CeedElemRestriction rstr_in;
1379d1d35e2fSjeremylt   ierr = CeedOperatorGetActiveElemRestriction(op, &rstr_in); CeedChk(ierr);
1380d1d35e2fSjeremylt   CeedInt num_elem, elem_size, num_nodes, num_comp;
1381d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumElements(rstr_in, &num_elem); CeedChk(ierr);
1382d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetElementSize(rstr_in, &elem_size); CeedChk(ierr);
1383d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetLVectorSize(rstr_in, &num_nodes); CeedChk(ierr);
1384d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumComponents(rstr_in, &num_comp); CeedChk(ierr);
1385e2f04181SAndrew T. Barker   CeedInt layout_er[3];
1386d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetELayout(rstr_in, &layout_er); CeedChk(ierr);
1387e2f04181SAndrew T. Barker 
1388d1d35e2fSjeremylt   CeedInt local_num_entries = elem_size*num_comp * elem_size*num_comp * num_elem;
1389e2f04181SAndrew T. Barker 
1390e2f04181SAndrew T. Barker   // Determine elem_dof relation
1391e2f04181SAndrew T. Barker   CeedVector index_vec;
1392d1d35e2fSjeremylt   ierr = CeedVectorCreate(ceed, num_nodes, &index_vec); CeedChk(ierr);
1393e2f04181SAndrew T. Barker   CeedScalar *array;
1394e2f04181SAndrew T. Barker   ierr = CeedVectorGetArray(index_vec, CEED_MEM_HOST, &array); CeedChk(ierr);
1395d1d35e2fSjeremylt   for (CeedInt i = 0; i < num_nodes; ++i) {
1396e2f04181SAndrew T. Barker     array[i] = i;
1397e2f04181SAndrew T. Barker   }
1398e2f04181SAndrew T. Barker   ierr = CeedVectorRestoreArray(index_vec, &array); CeedChk(ierr);
1399e2f04181SAndrew T. Barker   CeedVector elem_dof;
1400d1d35e2fSjeremylt   ierr = CeedVectorCreate(ceed, num_elem * elem_size * num_comp, &elem_dof);
1401e2f04181SAndrew T. Barker   CeedChk(ierr);
1402e2f04181SAndrew T. Barker   ierr = CeedVectorSetValue(elem_dof, 0.0); CeedChk(ierr);
1403d1d35e2fSjeremylt   CeedElemRestrictionApply(rstr_in, CEED_NOTRANSPOSE, index_vec,
1404e2f04181SAndrew T. Barker                            elem_dof, CEED_REQUEST_IMMEDIATE); CeedChk(ierr);
1405e2f04181SAndrew T. Barker   const CeedScalar *elem_dof_a;
1406e2f04181SAndrew T. Barker   ierr = CeedVectorGetArrayRead(elem_dof, CEED_MEM_HOST, &elem_dof_a);
1407e2f04181SAndrew T. Barker   CeedChk(ierr);
1408e2f04181SAndrew T. Barker   ierr = CeedVectorDestroy(&index_vec); CeedChk(ierr);
1409e2f04181SAndrew T. Barker 
1410e2f04181SAndrew T. Barker   // Determine i, j locations for element matrices
1411e2f04181SAndrew T. Barker   CeedInt count = 0;
1412d1d35e2fSjeremylt   for (int e = 0; e < num_elem; ++e) {
1413d1d35e2fSjeremylt     for (int comp_in = 0; comp_in < num_comp; ++comp_in) {
1414d1d35e2fSjeremylt       for (int comp_out = 0; comp_out < num_comp; ++comp_out) {
1415d1d35e2fSjeremylt         for (int i = 0; i < elem_size; ++i) {
1416d1d35e2fSjeremylt           for (int j = 0; j < elem_size; ++j) {
1417d1d35e2fSjeremylt             const CeedInt elem_dof_index_row = (i)*layout_er[0] +
1418d1d35e2fSjeremylt                                                (comp_out)*layout_er[1] + e*layout_er[2];
1419d1d35e2fSjeremylt             const CeedInt elem_dof_index_col = (j)*layout_er[0] +
1420d1d35e2fSjeremylt                                                (comp_in)*layout_er[1] + e*layout_er[2];
1421e2f04181SAndrew T. Barker 
1422d1d35e2fSjeremylt             const CeedInt row = elem_dof_a[elem_dof_index_row];
1423d1d35e2fSjeremylt             const CeedInt col = elem_dof_a[elem_dof_index_col];
1424e2f04181SAndrew T. Barker 
1425e2f04181SAndrew T. Barker             rows[offset + count] = row;
1426e2f04181SAndrew T. Barker             cols[offset + count] = col;
1427e2f04181SAndrew T. Barker             count++;
1428e2f04181SAndrew T. Barker           }
1429e2f04181SAndrew T. Barker         }
1430e2f04181SAndrew T. Barker       }
1431e2f04181SAndrew T. Barker     }
1432e2f04181SAndrew T. Barker   }
1433d1d35e2fSjeremylt   if (count != local_num_entries)
1434e2f04181SAndrew T. Barker     // LCOV_EXCL_START
1435e2f04181SAndrew T. Barker     return CeedError(ceed, CEED_ERROR_MAJOR, "Error computing assembled entries");
1436e2f04181SAndrew T. Barker   // LCOV_EXCL_STOP
1437e2f04181SAndrew T. Barker   ierr = CeedVectorRestoreArrayRead(elem_dof, &elem_dof_a); CeedChk(ierr);
1438e2f04181SAndrew T. Barker   ierr = CeedVectorDestroy(&elem_dof); CeedChk(ierr);
1439e2f04181SAndrew T. Barker 
1440e2f04181SAndrew T. Barker   return CEED_ERROR_SUCCESS;
1441e2f04181SAndrew T. Barker }
1442e2f04181SAndrew T. Barker 
1443e2f04181SAndrew T. Barker /**
1444e2f04181SAndrew T. Barker    @brief Assemble nonzero entries for non-composite operator
1445e2f04181SAndrew T. Barker 
1446e2f04181SAndrew T. Barker    Users should generally use CeedOperatorLinearAssemble()
1447e2f04181SAndrew T. Barker 
1448e2f04181SAndrew T. Barker    @ref Developer
1449e2f04181SAndrew T. Barker **/
1450e2f04181SAndrew T. Barker int CeedSingleOperatorAssemble(CeedOperator op, CeedInt offset,
1451e2f04181SAndrew T. Barker                                CeedVector values) {
1452e2f04181SAndrew T. Barker   int ierr;
1453e2f04181SAndrew T. Barker   Ceed ceed = op->ceed;;
1454e2f04181SAndrew T. Barker   if (op->composite)
1455e2f04181SAndrew T. Barker     // LCOV_EXCL_START
1456e2f04181SAndrew T. Barker     return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
1457e2f04181SAndrew T. Barker                      "Composite operator not supported");
1458e2f04181SAndrew T. Barker   // LCOV_EXCL_STOP
1459e2f04181SAndrew T. Barker 
1460e2f04181SAndrew T. Barker   // Assemble QFunction
1461e2f04181SAndrew T. Barker   CeedQFunction qf;
1462e2f04181SAndrew T. Barker   ierr = CeedOperatorGetQFunction(op, &qf); CeedChk(ierr);
1463d1d35e2fSjeremylt   CeedInt num_input_fields, num_output_fields;
1464d1d35e2fSjeremylt   ierr= CeedQFunctionGetNumArgs(qf, &num_input_fields, &num_output_fields);
1465e2f04181SAndrew T. Barker   CeedChk(ierr);
1466d1d35e2fSjeremylt   CeedVector assembled_qf;
1467e2f04181SAndrew T. Barker   CeedElemRestriction rstr_q;
1468e2f04181SAndrew T. Barker   ierr = CeedOperatorLinearAssembleQFunction(
1469d1d35e2fSjeremylt            op, &assembled_qf, &rstr_q, CEED_REQUEST_IMMEDIATE); CeedChk(ierr);
1470e2f04181SAndrew T. Barker 
1471d1d35e2fSjeremylt   CeedInt qf_length;
1472d1d35e2fSjeremylt   ierr = CeedVectorGetLength(assembled_qf, &qf_length); CeedChk(ierr);
1473e2f04181SAndrew T. Barker 
1474e2f04181SAndrew T. Barker   CeedOperatorField *input_fields;
1475e2f04181SAndrew T. Barker   CeedOperatorField *output_fields;
1476e2f04181SAndrew T. Barker   ierr = CeedOperatorGetFields(op, &input_fields, &output_fields); CeedChk(ierr);
1477e2f04181SAndrew T. Barker 
1478e2f04181SAndrew T. Barker   // Determine active input basis
1479d1d35e2fSjeremylt   CeedQFunctionField *qf_fields;
1480d1d35e2fSjeremylt   ierr = CeedQFunctionGetFields(qf, &qf_fields, NULL); CeedChk(ierr);
1481d1d35e2fSjeremylt   CeedInt num_eval_mode_in = 0, dim = 1;
1482d1d35e2fSjeremylt   CeedEvalMode *eval_mode_in = NULL;
1483d1d35e2fSjeremylt   CeedBasis basis_in = NULL;
1484d1d35e2fSjeremylt   CeedElemRestriction rstr_in = NULL;
1485d1d35e2fSjeremylt   for (CeedInt i=0; i<num_input_fields; i++) {
1486e2f04181SAndrew T. Barker     CeedVector vec;
1487e2f04181SAndrew T. Barker     ierr = CeedOperatorFieldGetVector(input_fields[i], &vec); CeedChk(ierr);
1488e2f04181SAndrew T. Barker     if (vec == CEED_VECTOR_ACTIVE) {
1489d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(input_fields[i], &basis_in);
1490e2f04181SAndrew T. Barker       CeedChk(ierr);
1491d1d35e2fSjeremylt       ierr = CeedBasisGetDimension(basis_in, &dim); CeedChk(ierr);
1492d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetElemRestriction(input_fields[i], &rstr_in);
1493e2f04181SAndrew T. Barker       CeedChk(ierr);
1494d1d35e2fSjeremylt       CeedEvalMode eval_mode;
1495d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode);
1496e2f04181SAndrew T. Barker       CeedChk(ierr);
1497d1d35e2fSjeremylt       switch (eval_mode) {
1498e2f04181SAndrew T. Barker       case CEED_EVAL_NONE:
1499e2f04181SAndrew T. Barker       case CEED_EVAL_INTERP:
1500d1d35e2fSjeremylt         ierr = CeedRealloc(num_eval_mode_in + 1, &eval_mode_in); CeedChk(ierr);
1501d1d35e2fSjeremylt         eval_mode_in[num_eval_mode_in] = eval_mode;
1502d1d35e2fSjeremylt         num_eval_mode_in += 1;
1503e2f04181SAndrew T. Barker         break;
1504e2f04181SAndrew T. Barker       case CEED_EVAL_GRAD:
1505d1d35e2fSjeremylt         ierr = CeedRealloc(num_eval_mode_in + dim, &eval_mode_in); CeedChk(ierr);
1506e2f04181SAndrew T. Barker         for (CeedInt d=0; d<dim; d++) {
1507d1d35e2fSjeremylt           eval_mode_in[num_eval_mode_in+d] = eval_mode;
1508e2f04181SAndrew T. Barker         }
1509d1d35e2fSjeremylt         num_eval_mode_in += dim;
1510e2f04181SAndrew T. Barker         break;
1511e2f04181SAndrew T. Barker       case CEED_EVAL_WEIGHT:
1512e2f04181SAndrew T. Barker       case CEED_EVAL_DIV:
1513e2f04181SAndrew T. Barker       case CEED_EVAL_CURL:
1514e2f04181SAndrew T. Barker         break; // Caught by QF Assembly
1515e2f04181SAndrew T. Barker       }
1516e2f04181SAndrew T. Barker     }
1517e2f04181SAndrew T. Barker   }
1518e2f04181SAndrew T. Barker 
1519e2f04181SAndrew T. Barker   // Determine active output basis
1520d1d35e2fSjeremylt   ierr = CeedQFunctionGetFields(qf, NULL, &qf_fields); CeedChk(ierr);
1521d1d35e2fSjeremylt   CeedInt num_eval_mode_out = 0;
1522d1d35e2fSjeremylt   CeedEvalMode *eval_mode_out = NULL;
1523d1d35e2fSjeremylt   CeedBasis basis_out = NULL;
1524d1d35e2fSjeremylt   CeedElemRestriction rstr_out = NULL;
1525d1d35e2fSjeremylt   for (CeedInt i=0; i<num_output_fields; i++) {
1526e2f04181SAndrew T. Barker     CeedVector vec;
1527e2f04181SAndrew T. Barker     ierr = CeedOperatorFieldGetVector(output_fields[i], &vec); CeedChk(ierr);
1528e2f04181SAndrew T. Barker     if (vec == CEED_VECTOR_ACTIVE) {
1529d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(output_fields[i], &basis_out);
1530e2f04181SAndrew T. Barker       CeedChk(ierr);
1531d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetElemRestriction(output_fields[i], &rstr_out);
1532e2f04181SAndrew T. Barker       CeedChk(ierr);
1533d1d35e2fSjeremylt       CeedEvalMode eval_mode;
1534d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode);
1535e2f04181SAndrew T. Barker       CeedChk(ierr);
1536d1d35e2fSjeremylt       switch (eval_mode) {
1537e2f04181SAndrew T. Barker       case CEED_EVAL_NONE:
1538e2f04181SAndrew T. Barker       case CEED_EVAL_INTERP:
1539d1d35e2fSjeremylt         ierr = CeedRealloc(num_eval_mode_out + 1, &eval_mode_out); CeedChk(ierr);
1540d1d35e2fSjeremylt         eval_mode_out[num_eval_mode_out] = eval_mode;
1541d1d35e2fSjeremylt         num_eval_mode_out += 1;
1542e2f04181SAndrew T. Barker         break;
1543e2f04181SAndrew T. Barker       case CEED_EVAL_GRAD:
1544d1d35e2fSjeremylt         ierr = CeedRealloc(num_eval_mode_out + dim, &eval_mode_out); CeedChk(ierr);
1545e2f04181SAndrew T. Barker         for (CeedInt d=0; d<dim; d++) {
1546d1d35e2fSjeremylt           eval_mode_out[num_eval_mode_out+d] = eval_mode;
1547e2f04181SAndrew T. Barker         }
1548d1d35e2fSjeremylt         num_eval_mode_out += dim;
1549e2f04181SAndrew T. Barker         break;
1550e2f04181SAndrew T. Barker       case CEED_EVAL_WEIGHT:
1551e2f04181SAndrew T. Barker       case CEED_EVAL_DIV:
1552e2f04181SAndrew T. Barker       case CEED_EVAL_CURL:
1553e2f04181SAndrew T. Barker         break; // Caught by QF Assembly
1554e2f04181SAndrew T. Barker       }
1555e2f04181SAndrew T. Barker     }
1556e2f04181SAndrew T. Barker   }
1557e2f04181SAndrew T. Barker 
155878464608Sjeremylt   if (num_eval_mode_in == 0 || num_eval_mode_out == 0)
155978464608Sjeremylt     // LCOV_EXCL_START
156078464608Sjeremylt     return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
156178464608Sjeremylt                      "Cannot assemble operator with out inputs/outputs");
156278464608Sjeremylt   // LCOV_EXCL_STOP
156378464608Sjeremylt 
1564d1d35e2fSjeremylt   CeedInt num_elem, elem_size, num_qpts, num_comp;
1565d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumElements(rstr_in, &num_elem); CeedChk(ierr);
1566d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetElementSize(rstr_in, &elem_size); CeedChk(ierr);
1567d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumComponents(rstr_in, &num_comp); CeedChk(ierr);
1568d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis_in, &num_qpts); CeedChk(ierr);
1569e2f04181SAndrew T. Barker 
1570d1d35e2fSjeremylt   CeedInt local_num_entries = elem_size*num_comp * elem_size*num_comp * num_elem;
1571e2f04181SAndrew T. Barker 
1572e2f04181SAndrew T. Barker   // loop over elements and put in data structure
1573d1d35e2fSjeremylt   const CeedScalar *interp_in, *grad_in;
1574d1d35e2fSjeremylt   ierr = CeedBasisGetInterp(basis_in, &interp_in); CeedChk(ierr);
1575d1d35e2fSjeremylt   ierr = CeedBasisGetGrad(basis_in, &grad_in); CeedChk(ierr);
1576e2f04181SAndrew T. Barker 
1577d1d35e2fSjeremylt   const CeedScalar *assembled_qf_array;
1578d1d35e2fSjeremylt   ierr = CeedVectorGetArrayRead(assembled_qf, CEED_MEM_HOST, &assembled_qf_array);
1579e2f04181SAndrew T. Barker   CeedChk(ierr);
1580e2f04181SAndrew T. Barker 
1581e2f04181SAndrew T. Barker   CeedInt layout_qf[3];
1582e2f04181SAndrew T. Barker   ierr = CeedElemRestrictionGetELayout(rstr_q, &layout_qf); CeedChk(ierr);
1583e2f04181SAndrew T. Barker   ierr = CeedElemRestrictionDestroy(&rstr_q); CeedChk(ierr);
1584e2f04181SAndrew T. Barker 
1585d1d35e2fSjeremylt   // we store B_mat_in, B_mat_out, BTD, elem_mat in row-major order
1586d1d35e2fSjeremylt   CeedScalar B_mat_in[(num_qpts * num_eval_mode_in) * elem_size];
1587d1d35e2fSjeremylt   CeedScalar B_mat_out[(num_qpts * num_eval_mode_out) * elem_size];
1588d1d35e2fSjeremylt   CeedScalar D_mat[num_eval_mode_out * num_eval_mode_in *
1589d1d35e2fSjeremylt                                      num_qpts]; // logically 3-tensor
1590d1d35e2fSjeremylt   CeedScalar BTD[elem_size * num_qpts*num_eval_mode_in];
1591d1d35e2fSjeremylt   CeedScalar elem_mat[elem_size * elem_size];
1592e2f04181SAndrew T. Barker   int count = 0;
1593e2f04181SAndrew T. Barker   CeedScalar *vals;
1594e2f04181SAndrew T. Barker   ierr = CeedVectorGetArray(values, CEED_MEM_HOST, &vals); CeedChk(ierr);
1595d1d35e2fSjeremylt   for (int e = 0; e < num_elem; ++e) {
1596d1d35e2fSjeremylt     for (int comp_in = 0; comp_in < num_comp; ++comp_in) {
1597d1d35e2fSjeremylt       for (int comp_out = 0; comp_out < num_comp; ++comp_out) {
1598d1d35e2fSjeremylt         for (int ell = 0; ell < (num_qpts * num_eval_mode_in) * elem_size; ++ell) {
1599d1d35e2fSjeremylt           B_mat_in[ell] = 0.0;
1600e2f04181SAndrew T. Barker         }
1601d1d35e2fSjeremylt         for (int ell = 0; ell < (num_qpts * num_eval_mode_out) * elem_size; ++ell) {
1602d1d35e2fSjeremylt           B_mat_out[ell] = 0.0;
1603e2f04181SAndrew T. Barker         }
1604e2f04181SAndrew T. Barker         // Store block-diagonal D matrix as collection of small dense blocks
1605d1d35e2fSjeremylt         for (int ell = 0; ell < num_eval_mode_in*num_eval_mode_out*num_qpts; ++ell) {
1606d1d35e2fSjeremylt           D_mat[ell] = 0.0;
1607e2f04181SAndrew T. Barker         }
1608e2f04181SAndrew T. Barker         // form element matrix itself (for each block component)
1609d1d35e2fSjeremylt         for (int ell = 0; ell < elem_size*elem_size; ++ell) {
1610e2f04181SAndrew T. Barker           elem_mat[ell] = 0.0;
1611e2f04181SAndrew T. Barker         }
1612d1d35e2fSjeremylt         for (int q = 0; q < num_qpts; ++q) {
1613d1d35e2fSjeremylt           for (int n = 0; n < elem_size; ++n) {
1614d1d35e2fSjeremylt             CeedInt d_in = -1;
1615d1d35e2fSjeremylt             for (int e_in = 0; e_in < num_eval_mode_in; ++e_in) {
1616d1d35e2fSjeremylt               const int qq = num_eval_mode_in*q;
1617d1d35e2fSjeremylt               if (eval_mode_in[e_in] == CEED_EVAL_INTERP) {
1618d1d35e2fSjeremylt                 B_mat_in[(qq+e_in)*elem_size + n] += interp_in[q * elem_size + n];
1619d1d35e2fSjeremylt               } else if (eval_mode_in[e_in] == CEED_EVAL_GRAD) {
1620d1d35e2fSjeremylt                 d_in += 1;
1621d1d35e2fSjeremylt                 B_mat_in[(qq+e_in)*elem_size + n] +=
1622d1d35e2fSjeremylt                   grad_in[(d_in*num_qpts+q) * elem_size + n];
1623e2f04181SAndrew T. Barker               } else {
1624e2f04181SAndrew T. Barker                 // LCOV_EXCL_START
1625e2f04181SAndrew T. Barker                 return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Not implemented!");
1626e2f04181SAndrew T. Barker                 // LCOV_EXCL_STOP
1627e2f04181SAndrew T. Barker               }
1628e2f04181SAndrew T. Barker             }
1629d1d35e2fSjeremylt             CeedInt d_out = -1;
1630d1d35e2fSjeremylt             for (int e_out = 0; e_out < num_eval_mode_out; ++e_out) {
1631d1d35e2fSjeremylt               const int qq = num_eval_mode_out*q;
1632d1d35e2fSjeremylt               if (eval_mode_out[e_out] == CEED_EVAL_INTERP) {
1633d1d35e2fSjeremylt                 B_mat_out[(qq+e_out)*elem_size + n] += interp_in[q * elem_size + n];
1634d1d35e2fSjeremylt               } else if (eval_mode_out[e_out] == CEED_EVAL_GRAD) {
1635d1d35e2fSjeremylt                 d_out += 1;
1636d1d35e2fSjeremylt                 B_mat_out[(qq+e_out)*elem_size + n] +=
1637d1d35e2fSjeremylt                   grad_in[(d_out*num_qpts+q) * elem_size + n];
1638e2f04181SAndrew T. Barker               } else {
1639e2f04181SAndrew T. Barker                 // LCOV_EXCL_START
1640e2f04181SAndrew T. Barker                 return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Not implemented!");
1641e2f04181SAndrew T. Barker                 // LCOV_EXCL_STOP
1642e2f04181SAndrew T. Barker               }
1643e2f04181SAndrew T. Barker             }
1644e2f04181SAndrew T. Barker           }
1645d1d35e2fSjeremylt           for (int ei = 0; ei < num_eval_mode_out; ++ei) {
1646d1d35e2fSjeremylt             for (int ej = 0; ej < num_eval_mode_in; ++ej) {
1647d1d35e2fSjeremylt               const int eval_mode_index = ((ei*num_comp+comp_in)*num_eval_mode_in+ej)*num_comp
1648d1d35e2fSjeremylt                                           +comp_out;
1649d1d35e2fSjeremylt               const int index = q*layout_qf[0] + eval_mode_index*layout_qf[1] +
1650d1d35e2fSjeremylt                                 e*layout_qf[2];
1651d1d35e2fSjeremylt               D_mat[(ei*num_eval_mode_in+ej)*num_qpts + q] += assembled_qf_array[index];
1652e2f04181SAndrew T. Barker             }
1653e2f04181SAndrew T. Barker           }
1654e2f04181SAndrew T. Barker         }
1655e2f04181SAndrew T. Barker         // Compute B^T*D
1656d1d35e2fSjeremylt         for (int ell = 0; ell < elem_size*num_qpts*num_eval_mode_in; ++ell) {
1657e2f04181SAndrew T. Barker           BTD[ell] = 0.0;
1658e2f04181SAndrew T. Barker         }
1659d1d35e2fSjeremylt         for (int j = 0; j<elem_size; ++j) {
1660d1d35e2fSjeremylt           for (int q = 0; q<num_qpts; ++q) {
1661d1d35e2fSjeremylt             int qq = num_eval_mode_out*q;
1662d1d35e2fSjeremylt             for (int ei = 0; ei < num_eval_mode_in; ++ei) {
1663d1d35e2fSjeremylt               for (int ej = 0; ej < num_eval_mode_out; ++ej) {
1664d1d35e2fSjeremylt                 BTD[j*(num_qpts*num_eval_mode_in) + (qq+ei)] +=
1665d1d35e2fSjeremylt                   B_mat_out[(qq+ej)*elem_size + j] * D_mat[(ei*num_eval_mode_in+ej)*num_qpts + q];
1666e2f04181SAndrew T. Barker               }
1667e2f04181SAndrew T. Barker             }
1668e2f04181SAndrew T. Barker           }
1669e2f04181SAndrew T. Barker         }
1670e2f04181SAndrew T. Barker 
1671d1d35e2fSjeremylt         ierr = CeedMatrixMultiply(ceed, BTD, B_mat_in, elem_mat, elem_size,
1672d1d35e2fSjeremylt                                   elem_size, num_qpts*num_eval_mode_in); CeedChk(ierr);
1673e2f04181SAndrew T. Barker 
1674e2f04181SAndrew T. Barker         // put element matrix in coordinate data structure
1675d1d35e2fSjeremylt         for (int i = 0; i < elem_size; ++i) {
1676d1d35e2fSjeremylt           for (int j = 0; j < elem_size; ++j) {
1677d1d35e2fSjeremylt             vals[offset + count] = elem_mat[i*elem_size + j];
1678e2f04181SAndrew T. Barker             count++;
1679e2f04181SAndrew T. Barker           }
1680e2f04181SAndrew T. Barker         }
1681e2f04181SAndrew T. Barker       }
1682e2f04181SAndrew T. Barker     }
1683e2f04181SAndrew T. Barker   }
1684d1d35e2fSjeremylt   if (count != local_num_entries)
1685e2f04181SAndrew T. Barker     // LCOV_EXCL_START
1686e2f04181SAndrew T. Barker     return CeedError(ceed, CEED_ERROR_MAJOR, "Error computing entries");
1687e2f04181SAndrew T. Barker   // LCOV_EXCL_STOP
1688e2f04181SAndrew T. Barker   ierr = CeedVectorRestoreArray(values, &vals); CeedChk(ierr);
1689e2f04181SAndrew T. Barker 
1690d1d35e2fSjeremylt   ierr = CeedVectorRestoreArrayRead(assembled_qf, &assembled_qf_array);
1691e2f04181SAndrew T. Barker   CeedChk(ierr);
1692d1d35e2fSjeremylt   ierr = CeedVectorDestroy(&assembled_qf); CeedChk(ierr);
1693d1d35e2fSjeremylt   ierr = CeedFree(&eval_mode_in); CeedChk(ierr);
1694d1d35e2fSjeremylt   ierr = CeedFree(&eval_mode_out); CeedChk(ierr);
1695e2f04181SAndrew T. Barker 
1696e2f04181SAndrew T. Barker   return CEED_ERROR_SUCCESS;
1697e2f04181SAndrew T. Barker }
1698e2f04181SAndrew T. Barker 
1699e2f04181SAndrew T. Barker /**
1700e2f04181SAndrew T. Barker    @ref Utility
1701e2f04181SAndrew T. Barker **/
1702d1d35e2fSjeremylt int CeedSingleOperatorAssemblyCountEntries(CeedOperator op,
1703d1d35e2fSjeremylt     CeedInt *num_entries) {
1704e2f04181SAndrew T. Barker   int ierr;
1705e2f04181SAndrew T. Barker   CeedElemRestriction rstr;
1706d1d35e2fSjeremylt   CeedInt num_elem, elem_size, num_comp;
1707e2f04181SAndrew T. Barker 
1708e2f04181SAndrew T. Barker   if (op->composite)
1709e2f04181SAndrew T. Barker     // LCOV_EXCL_START
1710e2f04181SAndrew T. Barker     return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED,
1711e2f04181SAndrew T. Barker                      "Composite operator not supported");
1712e2f04181SAndrew T. Barker   // LCOV_EXCL_STOP
1713e2f04181SAndrew T. Barker   ierr = CeedOperatorGetActiveElemRestriction(op, &rstr); CeedChk(ierr);
1714d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumElements(rstr, &num_elem); CeedChk(ierr);
1715d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetElementSize(rstr, &elem_size); CeedChk(ierr);
1716d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumComponents(rstr, &num_comp); CeedChk(ierr);
1717d1d35e2fSjeremylt   *num_entries = elem_size*num_comp * elem_size*num_comp * num_elem;
1718e2f04181SAndrew T. Barker 
1719e2f04181SAndrew T. Barker   return CEED_ERROR_SUCCESS;
1720e2f04181SAndrew T. Barker }
1721e2f04181SAndrew T. Barker 
1722e2f04181SAndrew T. Barker /**
1723e2f04181SAndrew T. Barker    @brief Fully assemble the nonzero pattern of a linear operator.
1724e2f04181SAndrew T. Barker 
1725e2f04181SAndrew T. Barker    Expected to be used in conjunction with CeedOperatorLinearAssemble()
1726e2f04181SAndrew T. Barker 
1727d1d35e2fSjeremylt    The assembly routines use coordinate format, with num_entries tuples of the
1728e2f04181SAndrew T. Barker    form (i, j, value) which indicate that value should be added to the matrix
1729e2f04181SAndrew T. Barker    in entry (i, j). Note that the (i, j) pairs are not unique and may repeat.
1730e2f04181SAndrew T. Barker    This function returns the number of entries and their (i, j) locations,
1731e2f04181SAndrew T. Barker    while CeedOperatorLinearAssemble() provides the values in the same
1732e2f04181SAndrew T. Barker    ordering.
1733e2f04181SAndrew T. Barker 
1734e2f04181SAndrew T. Barker    This will generally be slow unless your operator is low-order.
1735e2f04181SAndrew T. Barker 
1736e2f04181SAndrew T. Barker    @param[in]  op           CeedOperator to assemble
1737d1d35e2fSjeremylt    @param[out] num_entries  Number of entries in coordinate nonzero pattern.
1738e2f04181SAndrew T. Barker    @param[out] rows         Row number for each entry.
1739e2f04181SAndrew T. Barker    @param[out] cols         Column number for each entry.
1740e2f04181SAndrew T. Barker 
1741e2f04181SAndrew T. Barker    @ref User
1742e2f04181SAndrew T. Barker **/
1743e2f04181SAndrew T. Barker int CeedOperatorLinearAssembleSymbolic(CeedOperator op,
1744d1d35e2fSjeremylt                                        CeedInt *num_entries, CeedInt **rows, CeedInt **cols) {
1745e2f04181SAndrew T. Barker   int ierr;
1746d1d35e2fSjeremylt   CeedInt num_suboperators, single_entries;
1747d1d35e2fSjeremylt   CeedOperator *sub_operators;
1748d1d35e2fSjeremylt   bool is_composite;
1749e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1750e2f04181SAndrew T. Barker 
1751e2f04181SAndrew T. Barker   // Use backend version, if available
1752e2f04181SAndrew T. Barker   if (op->LinearAssembleSymbolic) {
1753d1d35e2fSjeremylt     return op->LinearAssembleSymbolic(op, num_entries, rows, cols);
1754e2f04181SAndrew T. Barker   } else {
1755e2f04181SAndrew T. Barker     // Check for valid fallback resource
1756d1d35e2fSjeremylt     const char *resource, *fallback_resource;
1757e2f04181SAndrew T. Barker     ierr = CeedGetResource(op->ceed, &resource); CeedChk(ierr);
1758d1d35e2fSjeremylt     ierr = CeedGetOperatorFallbackResource(op->ceed, &fallback_resource);
175978464608Sjeremylt     CeedChk(ierr);
1760d1d35e2fSjeremylt     if (strcmp(fallback_resource, "") && strcmp(resource, fallback_resource)) {
1761e2f04181SAndrew T. Barker       // Fallback to reference Ceed
1762d1d35e2fSjeremylt       if (!op->op_fallback) {
1763e2f04181SAndrew T. Barker         ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1764e2f04181SAndrew T. Barker       }
1765e2f04181SAndrew T. Barker       // Assemble
1766d1d35e2fSjeremylt       return CeedOperatorLinearAssembleSymbolic(op->op_fallback, num_entries, rows,
1767d1d35e2fSjeremylt              cols);
1768e2f04181SAndrew T. Barker     }
1769e2f04181SAndrew T. Barker   }
1770e2f04181SAndrew T. Barker 
1771e2f04181SAndrew T. Barker   // if neither backend nor fallback resource provides
1772e2f04181SAndrew T. Barker   // LinearAssembleSymbolic, continue with interface-level implementation
1773e2f04181SAndrew T. Barker 
1774e2f04181SAndrew T. Barker   // count entries and allocate rows, cols arrays
1775d1d35e2fSjeremylt   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1776d1d35e2fSjeremylt   *num_entries = 0;
1777d1d35e2fSjeremylt   if (is_composite) {
1778d1d35e2fSjeremylt     ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1779d1d35e2fSjeremylt     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1780d1d35e2fSjeremylt     for (int k = 0; k < num_suboperators; ++k) {
1781d1d35e2fSjeremylt       ierr = CeedSingleOperatorAssemblyCountEntries(sub_operators[k],
1782e2f04181SAndrew T. Barker              &single_entries); CeedChk(ierr);
1783d1d35e2fSjeremylt       *num_entries += single_entries;
1784e2f04181SAndrew T. Barker     }
1785e2f04181SAndrew T. Barker   } else {
1786e2f04181SAndrew T. Barker     ierr = CeedSingleOperatorAssemblyCountEntries(op,
1787e2f04181SAndrew T. Barker            &single_entries); CeedChk(ierr);
1788d1d35e2fSjeremylt     *num_entries += single_entries;
1789e2f04181SAndrew T. Barker   }
1790d1d35e2fSjeremylt   ierr = CeedCalloc(*num_entries, rows); CeedChk(ierr);
1791d1d35e2fSjeremylt   ierr = CeedCalloc(*num_entries, cols); CeedChk(ierr);
1792e2f04181SAndrew T. Barker 
1793e2f04181SAndrew T. Barker   // assemble nonzero locations
1794e2f04181SAndrew T. Barker   CeedInt offset = 0;
1795d1d35e2fSjeremylt   if (is_composite) {
1796d1d35e2fSjeremylt     ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1797d1d35e2fSjeremylt     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1798d1d35e2fSjeremylt     for (int k = 0; k < num_suboperators; ++k) {
1799d1d35e2fSjeremylt       ierr = CeedSingleOperatorAssembleSymbolic(sub_operators[k], offset, *rows,
1800e2f04181SAndrew T. Barker              *cols); CeedChk(ierr);
1801d1d35e2fSjeremylt       ierr = CeedSingleOperatorAssemblyCountEntries(sub_operators[k],
1802d1d35e2fSjeremylt              &single_entries);
1803e2f04181SAndrew T. Barker       CeedChk(ierr);
1804e2f04181SAndrew T. Barker       offset += single_entries;
1805e2f04181SAndrew T. Barker     }
1806e2f04181SAndrew T. Barker   } else {
1807e2f04181SAndrew T. Barker     ierr = CeedSingleOperatorAssembleSymbolic(op, offset, *rows, *cols);
1808e2f04181SAndrew T. Barker     CeedChk(ierr);
1809e2f04181SAndrew T. Barker   }
1810e2f04181SAndrew T. Barker 
1811e2f04181SAndrew T. Barker   return CEED_ERROR_SUCCESS;
1812e2f04181SAndrew T. Barker }
1813e2f04181SAndrew T. Barker 
1814e2f04181SAndrew T. Barker /**
1815e2f04181SAndrew T. Barker    @brief Fully assemble the nonzero entries of a linear operator.
1816e2f04181SAndrew T. Barker 
1817e2f04181SAndrew T. Barker    Expected to be used in conjunction with CeedOperatorLinearAssembleSymbolic()
1818e2f04181SAndrew T. Barker 
1819d1d35e2fSjeremylt    The assembly routines use coordinate format, with num_entries tuples of the
1820e2f04181SAndrew T. Barker    form (i, j, value) which indicate that value should be added to the matrix
1821e2f04181SAndrew T. Barker    in entry (i, j). Note that the (i, j) pairs are not unique and may repeat.
1822e2f04181SAndrew T. Barker    This function returns the values of the nonzero entries to be added, their
1823e2f04181SAndrew T. Barker    (i, j) locations are provided by CeedOperatorLinearAssembleSymbolic()
1824e2f04181SAndrew T. Barker 
1825e2f04181SAndrew T. Barker    This will generally be slow unless your operator is low-order.
1826e2f04181SAndrew T. Barker 
1827e2f04181SAndrew T. Barker    @param[in]  op      CeedOperator to assemble
1828e2f04181SAndrew T. Barker    @param[out] values  Values to assemble into matrix
1829e2f04181SAndrew T. Barker 
1830e2f04181SAndrew T. Barker    @ref User
1831e2f04181SAndrew T. Barker **/
1832e2f04181SAndrew T. Barker int CeedOperatorLinearAssemble(CeedOperator op, CeedVector values) {
1833e2f04181SAndrew T. Barker   int ierr;
183478464608Sjeremylt   CeedInt num_suboperators, single_entries = 0;
1835d1d35e2fSjeremylt   CeedOperator *sub_operators;
1836e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1837e2f04181SAndrew T. Barker 
1838e2f04181SAndrew T. Barker   // Use backend version, if available
1839e2f04181SAndrew T. Barker   if (op->LinearAssemble) {
1840e2f04181SAndrew T. Barker     return op->LinearAssemble(op, values);
1841e2f04181SAndrew T. Barker   } else {
1842e2f04181SAndrew T. Barker     // Check for valid fallback resource
1843d1d35e2fSjeremylt     const char *resource, *fallback_resource;
1844e2f04181SAndrew T. Barker     ierr = CeedGetResource(op->ceed, &resource); CeedChk(ierr);
1845d1d35e2fSjeremylt     ierr = CeedGetOperatorFallbackResource(op->ceed, &fallback_resource);
184678464608Sjeremylt     CeedChk(ierr);
1847d1d35e2fSjeremylt     if (strcmp(fallback_resource, "") && strcmp(resource, fallback_resource)) {
1848e2f04181SAndrew T. Barker       // Fallback to reference Ceed
1849d1d35e2fSjeremylt       if (!op->op_fallback) {
1850e2f04181SAndrew T. Barker         ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1851e2f04181SAndrew T. Barker       }
1852e2f04181SAndrew T. Barker       // Assemble
1853d1d35e2fSjeremylt       return CeedOperatorLinearAssemble(op->op_fallback, values);
1854e2f04181SAndrew T. Barker     }
1855e2f04181SAndrew T. Barker   }
1856e2f04181SAndrew T. Barker 
1857e2f04181SAndrew T. Barker   // if neither backend nor fallback resource provides
1858e2f04181SAndrew T. Barker   // LinearAssemble, continue with interface-level implementation
1859e2f04181SAndrew T. Barker 
1860d1d35e2fSjeremylt   bool is_composite;
1861d1d35e2fSjeremylt   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1862e2f04181SAndrew T. Barker 
1863e2f04181SAndrew T. Barker   CeedInt offset = 0;
1864d1d35e2fSjeremylt   if (is_composite) {
1865d1d35e2fSjeremylt     ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1866d1d35e2fSjeremylt     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1867d1d35e2fSjeremylt     for (int k = 0; k < num_suboperators; ++k) {
1868d1d35e2fSjeremylt       ierr = CeedSingleOperatorAssemble(sub_operators[k], offset, values);
1869e2f04181SAndrew T. Barker       CeedChk(ierr);
1870d1d35e2fSjeremylt       ierr = CeedSingleOperatorAssemblyCountEntries(sub_operators[k],
1871d1d35e2fSjeremylt              &single_entries);
1872e2f04181SAndrew T. Barker       CeedChk(ierr);
1873e2f04181SAndrew T. Barker       offset += single_entries;
1874e2f04181SAndrew T. Barker     }
1875e2f04181SAndrew T. Barker   } else {
1876e2f04181SAndrew T. Barker     ierr = CeedSingleOperatorAssemble(op, offset, values); CeedChk(ierr);
1877e2f04181SAndrew T. Barker   }
1878e2f04181SAndrew T. Barker 
1879e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1880d965c7a7SJeremy L Thompson }
1881d965c7a7SJeremy L Thompson 
1882d965c7a7SJeremy L Thompson /**
1883d99fa3c5SJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators
1884d99fa3c5SJeremy L Thompson            for a CeedOperator, creating the prolongation basis from the
1885d99fa3c5SJeremy L Thompson            fine and coarse grid interpolation
1886d99fa3c5SJeremy L Thompson 
1887d1d35e2fSjeremylt   @param[in] op_fine       Fine grid operator
1888d1d35e2fSjeremylt   @param[in] p_mult_fine   L-vector multiplicity in parallel gather/scatter
1889d1d35e2fSjeremylt   @param[in] rstr_coarse   Coarse grid restriction
1890d1d35e2fSjeremylt   @param[in] basis_coarse  Coarse grid active vector basis
1891d1d35e2fSjeremylt   @param[out] op_coarse    Coarse grid operator
1892d1d35e2fSjeremylt   @param[out] op_prolong   Coarse to fine operator
1893d1d35e2fSjeremylt   @param[out] op_restrict  Fine to coarse operator
1894d99fa3c5SJeremy L Thompson 
1895d99fa3c5SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1896d99fa3c5SJeremy L Thompson 
1897d99fa3c5SJeremy L Thompson   @ref User
1898d99fa3c5SJeremy L Thompson **/
1899d1d35e2fSjeremylt int CeedOperatorMultigridLevelCreate(CeedOperator op_fine,
1900d1d35e2fSjeremylt                                      CeedVector p_mult_fine,
1901d1d35e2fSjeremylt                                      CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
1902d1d35e2fSjeremylt                                      CeedOperator *op_coarse, CeedOperator *op_prolong,
1903d1d35e2fSjeremylt                                      CeedOperator *op_restrict) {
1904d99fa3c5SJeremy L Thompson   int ierr;
1905d99fa3c5SJeremy L Thompson   Ceed ceed;
1906d1d35e2fSjeremylt   ierr = CeedOperatorGetCeed(op_fine, &ceed); CeedChk(ierr);
1907d99fa3c5SJeremy L Thompson 
1908d99fa3c5SJeremy L Thompson   // Check for compatible quadrature spaces
1909d1d35e2fSjeremylt   CeedBasis basis_fine;
1910d1d35e2fSjeremylt   ierr = CeedOperatorGetActiveBasis(op_fine, &basis_fine); CeedChk(ierr);
1911d1d35e2fSjeremylt   CeedInt Q_f, Q_c;
1912d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis_fine, &Q_f); CeedChk(ierr);
1913d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis_coarse, &Q_c); CeedChk(ierr);
1914d1d35e2fSjeremylt   if (Q_f != Q_c)
1915d99fa3c5SJeremy L Thompson     // LCOV_EXCL_START
1916e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_DIMENSION,
1917e15f9bd0SJeremy L Thompson                      "Bases must have compatible quadrature spaces");
1918d99fa3c5SJeremy L Thompson   // LCOV_EXCL_STOP
1919d99fa3c5SJeremy L Thompson 
1920d99fa3c5SJeremy L Thompson   // Coarse to fine basis
1921d1d35e2fSjeremylt   CeedInt P_f, P_c, Q = Q_f;
1922d1d35e2fSjeremylt   bool is_tensor_f, is_tensor_c;
1923d1d35e2fSjeremylt   ierr = CeedBasisIsTensor(basis_fine, &is_tensor_f); CeedChk(ierr);
1924d1d35e2fSjeremylt   ierr = CeedBasisIsTensor(basis_coarse, &is_tensor_c); CeedChk(ierr);
1925d1d35e2fSjeremylt   CeedScalar *interp_c, *interp_f, *interp_c_to_f, *tau;
1926d1d35e2fSjeremylt   if (is_tensor_f && is_tensor_c) {
1927d1d35e2fSjeremylt     ierr = CeedBasisGetNumNodes1D(basis_fine, &P_f); CeedChk(ierr);
1928d1d35e2fSjeremylt     ierr = CeedBasisGetNumNodes1D(basis_coarse, &P_c); CeedChk(ierr);
1929d1d35e2fSjeremylt     ierr = CeedBasisGetNumQuadraturePoints1D(basis_coarse, &Q); CeedChk(ierr);
1930d1d35e2fSjeremylt   } else if (!is_tensor_f && !is_tensor_c) {
1931d1d35e2fSjeremylt     ierr = CeedBasisGetNumNodes(basis_fine, &P_f); CeedChk(ierr);
1932d1d35e2fSjeremylt     ierr = CeedBasisGetNumNodes(basis_coarse, &P_c); CeedChk(ierr);
1933d99fa3c5SJeremy L Thompson   } else {
1934d99fa3c5SJeremy L Thompson     // LCOV_EXCL_START
1935e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_MINOR,
1936e15f9bd0SJeremy L Thompson                      "Bases must both be tensor or non-tensor");
1937d99fa3c5SJeremy L Thompson     // LCOV_EXCL_STOP
1938d99fa3c5SJeremy L Thompson   }
1939d99fa3c5SJeremy L Thompson 
1940d1d35e2fSjeremylt   ierr = CeedMalloc(Q*P_f, &interp_f); CeedChk(ierr);
1941d1d35e2fSjeremylt   ierr = CeedMalloc(Q*P_c, &interp_c); CeedChk(ierr);
1942d1d35e2fSjeremylt   ierr = CeedCalloc(P_c*P_f, &interp_c_to_f); CeedChk(ierr);
1943d99fa3c5SJeremy L Thompson   ierr = CeedMalloc(Q, &tau); CeedChk(ierr);
1944d1d35e2fSjeremylt   if (is_tensor_f) {
1945d1d35e2fSjeremylt     memcpy(interp_f, basis_fine->interp_1d, Q*P_f*sizeof basis_fine->interp_1d[0]);
1946d1d35e2fSjeremylt     memcpy(interp_c, basis_coarse->interp_1d,
1947d1d35e2fSjeremylt            Q*P_c*sizeof basis_coarse->interp_1d[0]);
1948d99fa3c5SJeremy L Thompson   } else {
1949d1d35e2fSjeremylt     memcpy(interp_f, basis_fine->interp, Q*P_f*sizeof basis_fine->interp[0]);
1950d1d35e2fSjeremylt     memcpy(interp_c, basis_coarse->interp, Q*P_c*sizeof basis_coarse->interp[0]);
1951d99fa3c5SJeremy L Thompson   }
1952d99fa3c5SJeremy L Thompson 
1953d1d35e2fSjeremylt   // -- QR Factorization, interp_f = Q R
1954d1d35e2fSjeremylt   ierr = CeedQRFactorization(ceed, interp_f, tau, Q, P_f); CeedChk(ierr);
1955d99fa3c5SJeremy L Thompson 
1956d1d35e2fSjeremylt   // -- Apply Qtranspose, interp_c = Qtranspose interp_c
1957d1d35e2fSjeremylt   ierr = CeedHouseholderApplyQ(interp_c, interp_f, tau, CEED_TRANSPOSE,
1958d1d35e2fSjeremylt                                Q, P_c, P_f, P_c, 1); CeedChk(ierr);
1959d99fa3c5SJeremy L Thompson 
1960d1d35e2fSjeremylt   // -- Apply Rinv, interp_c_to_f = Rinv interp_c
1961d1d35e2fSjeremylt   for (CeedInt j=0; j<P_c; j++) { // Column j
1962d1d35e2fSjeremylt     interp_c_to_f[j+P_c*(P_f-1)] = interp_c[j+P_c*(P_f-1)]/interp_f[P_f*P_f-1];
1963d1d35e2fSjeremylt     for (CeedInt i=P_f-2; i>=0; i--) { // Row i
1964d1d35e2fSjeremylt       interp_c_to_f[j+P_c*i] = interp_c[j+P_c*i];
1965d1d35e2fSjeremylt       for (CeedInt k=i+1; k<P_f; k++)
1966d1d35e2fSjeremylt         interp_c_to_f[j+P_c*i] -= interp_f[k+P_f*i]*interp_c_to_f[j+P_c*k];
1967d1d35e2fSjeremylt       interp_c_to_f[j+P_c*i] /= interp_f[i+P_f*i];
1968d99fa3c5SJeremy L Thompson     }
1969d99fa3c5SJeremy L Thompson   }
1970d99fa3c5SJeremy L Thompson   ierr = CeedFree(&tau); CeedChk(ierr);
1971d1d35e2fSjeremylt   ierr = CeedFree(&interp_c); CeedChk(ierr);
1972d1d35e2fSjeremylt   ierr = CeedFree(&interp_f); CeedChk(ierr);
1973d99fa3c5SJeremy L Thompson 
1974d1d35e2fSjeremylt   // Complete with interp_c_to_f versions of code
1975d1d35e2fSjeremylt   if (is_tensor_f) {
1976d1d35e2fSjeremylt     ierr = CeedOperatorMultigridLevelCreateTensorH1(op_fine, p_mult_fine,
1977d1d35e2fSjeremylt            rstr_coarse, basis_coarse, interp_c_to_f, op_coarse, op_prolong, op_restrict);
1978d99fa3c5SJeremy L Thompson     CeedChk(ierr);
1979d99fa3c5SJeremy L Thompson   } else {
1980d1d35e2fSjeremylt     ierr = CeedOperatorMultigridLevelCreateH1(op_fine, p_mult_fine,
1981d1d35e2fSjeremylt            rstr_coarse, basis_coarse, interp_c_to_f, op_coarse, op_prolong, op_restrict);
1982d99fa3c5SJeremy L Thompson     CeedChk(ierr);
1983d99fa3c5SJeremy L Thompson   }
1984d99fa3c5SJeremy L Thompson 
1985d99fa3c5SJeremy L Thompson   // Cleanup
1986d1d35e2fSjeremylt   ierr = CeedFree(&interp_c_to_f); CeedChk(ierr);
1987e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1988d99fa3c5SJeremy L Thompson }
1989d99fa3c5SJeremy L Thompson 
1990d99fa3c5SJeremy L Thompson /**
1991d99fa3c5SJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators
1992d99fa3c5SJeremy L Thompson            for a CeedOperator with a tensor basis for the active basis
1993d99fa3c5SJeremy L Thompson 
1994d1d35e2fSjeremylt   @param[in] op_fine        Fine grid operator
1995d1d35e2fSjeremylt   @param[in] p_mult_fine    L-vector multiplicity in parallel gather/scatter
1996d1d35e2fSjeremylt   @param[in] rstr_coarse    Coarse grid restriction
1997d1d35e2fSjeremylt   @param[in] basis_coarse   Coarse grid active vector basis
1998d1d35e2fSjeremylt   @param[in] interp_c_to_f  Matrix for coarse to fine interpolation
1999d1d35e2fSjeremylt   @param[out] op_coarse     Coarse grid operator
2000d1d35e2fSjeremylt   @param[out] op_prolong    Coarse to fine operator
2001d1d35e2fSjeremylt   @param[out] op_restrict   Fine to coarse operator
2002d99fa3c5SJeremy L Thompson 
2003d99fa3c5SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2004d99fa3c5SJeremy L Thompson 
2005d99fa3c5SJeremy L Thompson   @ref User
2006d99fa3c5SJeremy L Thompson **/
2007d1d35e2fSjeremylt int CeedOperatorMultigridLevelCreateTensorH1(CeedOperator op_fine,
2008d1d35e2fSjeremylt     CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
2009d1d35e2fSjeremylt     const CeedScalar *interp_c_to_f, CeedOperator *op_coarse,
2010d1d35e2fSjeremylt     CeedOperator *op_prolong, CeedOperator *op_restrict) {
2011d99fa3c5SJeremy L Thompson   int ierr;
2012d99fa3c5SJeremy L Thompson   Ceed ceed;
2013d1d35e2fSjeremylt   ierr = CeedOperatorGetCeed(op_fine, &ceed); CeedChk(ierr);
2014d99fa3c5SJeremy L Thompson 
2015d99fa3c5SJeremy L Thompson   // Check for compatible quadrature spaces
2016d1d35e2fSjeremylt   CeedBasis basis_fine;
2017d1d35e2fSjeremylt   ierr = CeedOperatorGetActiveBasis(op_fine, &basis_fine); CeedChk(ierr);
2018d1d35e2fSjeremylt   CeedInt Q_f, Q_c;
2019d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis_fine, &Q_f); CeedChk(ierr);
2020d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis_coarse, &Q_c); CeedChk(ierr);
2021d1d35e2fSjeremylt   if (Q_f != Q_c)
2022d99fa3c5SJeremy L Thompson     // LCOV_EXCL_START
2023e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_DIMENSION,
2024e15f9bd0SJeremy L Thompson                      "Bases must have compatible quadrature spaces");
2025d99fa3c5SJeremy L Thompson   // LCOV_EXCL_STOP
2026d99fa3c5SJeremy L Thompson 
2027d99fa3c5SJeremy L Thompson   // Coarse to fine basis
2028d1d35e2fSjeremylt   CeedInt dim, num_comp, num_nodes_c, P_1d_f, P_1d_c;
2029d1d35e2fSjeremylt   ierr = CeedBasisGetDimension(basis_fine, &dim); CeedChk(ierr);
2030d1d35e2fSjeremylt   ierr = CeedBasisGetNumComponents(basis_fine, &num_comp); CeedChk(ierr);
2031d1d35e2fSjeremylt   ierr = CeedBasisGetNumNodes1D(basis_fine, &P_1d_f); CeedChk(ierr);
2032d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetElementSize(rstr_coarse, &num_nodes_c);
2033d99fa3c5SJeremy L Thompson   CeedChk(ierr);
2034d1d35e2fSjeremylt   P_1d_c = dim == 1 ? num_nodes_c :
2035d1d35e2fSjeremylt            dim == 2 ? sqrt(num_nodes_c) :
2036d1d35e2fSjeremylt            cbrt(num_nodes_c);
2037d1d35e2fSjeremylt   CeedScalar *q_ref, *q_weight, *grad;
2038d1d35e2fSjeremylt   ierr = CeedCalloc(P_1d_f, &q_ref); CeedChk(ierr);
2039d1d35e2fSjeremylt   ierr = CeedCalloc(P_1d_f, &q_weight); CeedChk(ierr);
2040d1d35e2fSjeremylt   ierr = CeedCalloc(P_1d_f*P_1d_c*dim, &grad); CeedChk(ierr);
2041d1d35e2fSjeremylt   CeedBasis basis_c_to_f;
2042d1d35e2fSjeremylt   ierr = CeedBasisCreateTensorH1(ceed, dim, num_comp, P_1d_c, P_1d_f,
2043d1d35e2fSjeremylt                                  interp_c_to_f, grad, q_ref, q_weight, &basis_c_to_f);
2044d99fa3c5SJeremy L Thompson   CeedChk(ierr);
2045d1d35e2fSjeremylt   ierr = CeedFree(&q_ref); CeedChk(ierr);
2046d1d35e2fSjeremylt   ierr = CeedFree(&q_weight); CeedChk(ierr);
2047d99fa3c5SJeremy L Thompson   ierr = CeedFree(&grad); CeedChk(ierr);
2048d99fa3c5SJeremy L Thompson 
2049d99fa3c5SJeremy L Thompson   // Core code
2050d1d35e2fSjeremylt   ierr = CeedOperatorMultigridLevel_Core(op_fine, p_mult_fine, rstr_coarse,
2051d1d35e2fSjeremylt                                          basis_coarse, basis_c_to_f, op_coarse,
2052d1d35e2fSjeremylt                                          op_prolong, op_restrict);
2053d99fa3c5SJeremy L Thompson   CeedChk(ierr);
2054e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
2055d99fa3c5SJeremy L Thompson }
2056d99fa3c5SJeremy L Thompson 
2057d99fa3c5SJeremy L Thompson /**
2058d99fa3c5SJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators
2059d99fa3c5SJeremy L Thompson            for a CeedOperator with a non-tensor basis for the active vector
2060d99fa3c5SJeremy L Thompson 
2061d1d35e2fSjeremylt   @param[in] op_fine        Fine grid operator
2062d1d35e2fSjeremylt   @param[in] p_mult_fine    L-vector multiplicity in parallel gather/scatter
2063d1d35e2fSjeremylt   @param[in] rstr_coarse    Coarse grid restriction
2064d1d35e2fSjeremylt   @param[in] basis_coarse   Coarse grid active vector basis
2065d1d35e2fSjeremylt   @param[in] interp_c_to_f  Matrix for coarse to fine interpolation
2066d1d35e2fSjeremylt   @param[out] op_coarse     Coarse grid operator
2067d1d35e2fSjeremylt   @param[out] op_prolong    Coarse to fine operator
2068d1d35e2fSjeremylt   @param[out] op_restrict   Fine to coarse operator
2069d99fa3c5SJeremy L Thompson 
2070d99fa3c5SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2071d99fa3c5SJeremy L Thompson 
2072d99fa3c5SJeremy L Thompson   @ref User
2073d99fa3c5SJeremy L Thompson **/
2074d1d35e2fSjeremylt int CeedOperatorMultigridLevelCreateH1(CeedOperator op_fine,
2075d1d35e2fSjeremylt                                        CeedVector p_mult_fine,
2076d1d35e2fSjeremylt                                        CeedElemRestriction rstr_coarse,
2077d1d35e2fSjeremylt                                        CeedBasis basis_coarse,
2078d1d35e2fSjeremylt                                        const CeedScalar *interp_c_to_f,
2079d1d35e2fSjeremylt                                        CeedOperator *op_coarse,
2080d1d35e2fSjeremylt                                        CeedOperator *op_prolong,
2081d1d35e2fSjeremylt                                        CeedOperator *op_restrict) {
2082d99fa3c5SJeremy L Thompson   int ierr;
2083d99fa3c5SJeremy L Thompson   Ceed ceed;
2084d1d35e2fSjeremylt   ierr = CeedOperatorGetCeed(op_fine, &ceed); CeedChk(ierr);
2085d99fa3c5SJeremy L Thompson 
2086d99fa3c5SJeremy L Thompson   // Check for compatible quadrature spaces
2087d1d35e2fSjeremylt   CeedBasis basis_fine;
2088d1d35e2fSjeremylt   ierr = CeedOperatorGetActiveBasis(op_fine, &basis_fine); CeedChk(ierr);
2089d1d35e2fSjeremylt   CeedInt Q_f, Q_c;
2090d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis_fine, &Q_f); CeedChk(ierr);
2091d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis_coarse, &Q_c); CeedChk(ierr);
2092d1d35e2fSjeremylt   if (Q_f != Q_c)
2093d99fa3c5SJeremy L Thompson     // LCOV_EXCL_START
2094e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_DIMENSION,
2095e15f9bd0SJeremy L Thompson                      "Bases must have compatible quadrature spaces");
2096d99fa3c5SJeremy L Thompson   // LCOV_EXCL_STOP
2097d99fa3c5SJeremy L Thompson 
2098d99fa3c5SJeremy L Thompson   // Coarse to fine basis
2099d99fa3c5SJeremy L Thompson   CeedElemTopology topo;
2100d1d35e2fSjeremylt   ierr = CeedBasisGetTopology(basis_fine, &topo); CeedChk(ierr);
2101d1d35e2fSjeremylt   CeedInt dim, num_comp, num_nodes_c, num_nodes_f;
2102d1d35e2fSjeremylt   ierr = CeedBasisGetDimension(basis_fine, &dim); CeedChk(ierr);
2103d1d35e2fSjeremylt   ierr = CeedBasisGetNumComponents(basis_fine, &num_comp); CeedChk(ierr);
2104d1d35e2fSjeremylt   ierr = CeedBasisGetNumNodes(basis_fine, &num_nodes_f); CeedChk(ierr);
2105d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetElementSize(rstr_coarse, &num_nodes_c);
2106d99fa3c5SJeremy L Thompson   CeedChk(ierr);
2107d1d35e2fSjeremylt   CeedScalar *q_ref, *q_weight, *grad;
2108d1d35e2fSjeremylt   ierr = CeedCalloc(num_nodes_f, &q_ref); CeedChk(ierr);
2109d1d35e2fSjeremylt   ierr = CeedCalloc(num_nodes_f, &q_weight); CeedChk(ierr);
2110d1d35e2fSjeremylt   ierr = CeedCalloc(num_nodes_f*num_nodes_c*dim, &grad); CeedChk(ierr);
2111d1d35e2fSjeremylt   CeedBasis basis_c_to_f;
2112d1d35e2fSjeremylt   ierr = CeedBasisCreateH1(ceed, topo, num_comp, num_nodes_c, num_nodes_f,
2113d1d35e2fSjeremylt                            interp_c_to_f, grad, q_ref, q_weight, &basis_c_to_f);
2114d99fa3c5SJeremy L Thompson   CeedChk(ierr);
2115d1d35e2fSjeremylt   ierr = CeedFree(&q_ref); CeedChk(ierr);
2116d1d35e2fSjeremylt   ierr = CeedFree(&q_weight); CeedChk(ierr);
2117d99fa3c5SJeremy L Thompson   ierr = CeedFree(&grad); CeedChk(ierr);
2118d99fa3c5SJeremy L Thompson 
2119d99fa3c5SJeremy L Thompson   // Core code
2120d1d35e2fSjeremylt   ierr = CeedOperatorMultigridLevel_Core(op_fine, p_mult_fine, rstr_coarse,
2121d1d35e2fSjeremylt                                          basis_coarse, basis_c_to_f, op_coarse,
2122d1d35e2fSjeremylt                                          op_prolong, op_restrict);
2123d99fa3c5SJeremy L Thompson   CeedChk(ierr);
2124e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
2125d99fa3c5SJeremy L Thompson }
2126d99fa3c5SJeremy L Thompson 
2127d99fa3c5SJeremy L Thompson /**
21283bd813ffSjeremylt   @brief Build a FDM based approximate inverse for each element for a
21293bd813ffSjeremylt            CeedOperator
21303bd813ffSjeremylt 
21313bd813ffSjeremylt   This returns a CeedOperator and CeedVector to apply a Fast Diagonalization
21323bd813ffSjeremylt     Method based approximate inverse. This function obtains the simultaneous
21333bd813ffSjeremylt     diagonalization for the 1D mass and Laplacian operators,
21343bd813ffSjeremylt       M = V^T V, K = V^T S V.
21353bd813ffSjeremylt     The assembled QFunction is used to modify the eigenvalues from simultaneous
21363bd813ffSjeremylt     diagonalization and obtain an approximate inverse of the form
21373bd813ffSjeremylt       V^T S^hat V. The CeedOperator must be linear and non-composite. The
21383bd813ffSjeremylt     associated CeedQFunction must therefore also be linear.
21393bd813ffSjeremylt 
21403bd813ffSjeremylt   @param op            CeedOperator to create element inverses
2141d1d35e2fSjeremylt   @param[out] fdm_inv  CeedOperator to apply the action of a FDM based inverse
21423bd813ffSjeremylt                          for each element
21433bd813ffSjeremylt   @param request       Address of CeedRequest for non-blocking completion, else
21444cc79fe7SJed Brown                          @ref CEED_REQUEST_IMMEDIATE
21453bd813ffSjeremylt 
21463bd813ffSjeremylt   @return An error code: 0 - success, otherwise - failure
21473bd813ffSjeremylt 
21487a982d89SJeremy L. Thompson   @ref Backend
21493bd813ffSjeremylt **/
2150d1d35e2fSjeremylt int CeedOperatorCreateFDMElementInverse(CeedOperator op, CeedOperator *fdm_inv,
21513bd813ffSjeremylt                                         CeedRequest *request) {
21523bd813ffSjeremylt   int ierr;
2153e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
21543bd813ffSjeremylt 
2155713f43c3Sjeremylt   // Use backend version, if available
2156713f43c3Sjeremylt   if (op->CreateFDMElementInverse) {
2157d1d35e2fSjeremylt     ierr = op->CreateFDMElementInverse(op, fdm_inv, request); CeedChk(ierr);
2158713f43c3Sjeremylt   } else {
2159713f43c3Sjeremylt     // Fallback to reference Ceed
2160d1d35e2fSjeremylt     if (!op->op_fallback) {
2161713f43c3Sjeremylt       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
21623bd813ffSjeremylt     }
2163713f43c3Sjeremylt     // Assemble
2164d1d35e2fSjeremylt     ierr = op->op_fallback->CreateFDMElementInverse(op->op_fallback, fdm_inv,
21653bd813ffSjeremylt            request); CeedChk(ierr);
21663bd813ffSjeremylt   }
2167e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
21683bd813ffSjeremylt }
21693bd813ffSjeremylt 
21707a982d89SJeremy L. Thompson /**
2171*cd4dfc48Sjeremylt   @brief Set the number of quadrature points associated with a CeedOperator.
2172*cd4dfc48Sjeremylt            This should be used when creating a CeedOperator where every
2173*cd4dfc48Sjeremylt            field has a collocated basis. This function cannot be used for
2174*cd4dfc48Sjeremylt            composite CeedOperators.
2175*cd4dfc48Sjeremylt 
2176*cd4dfc48Sjeremylt   @param op        CeedOperator
2177*cd4dfc48Sjeremylt   @param num_qpts  Number of quadrature points to set
2178*cd4dfc48Sjeremylt 
2179*cd4dfc48Sjeremylt   @return An error code: 0 - success, otherwise - failure
2180*cd4dfc48Sjeremylt 
2181*cd4dfc48Sjeremylt   @ref Backend
2182*cd4dfc48Sjeremylt **/
2183*cd4dfc48Sjeremylt 
2184*cd4dfc48Sjeremylt int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
2185*cd4dfc48Sjeremylt   if (op->composite)
2186*cd4dfc48Sjeremylt     // LCOV_EXCL_START
2187*cd4dfc48Sjeremylt     return CeedError(op->ceed, CEED_ERROR_MINOR,
2188*cd4dfc48Sjeremylt                      "Not defined for composite operator");
2189*cd4dfc48Sjeremylt   // LCOV_EXCL_STOP
2190*cd4dfc48Sjeremylt   if (op->num_qpts)
2191*cd4dfc48Sjeremylt     // LCOV_EXCL_START
2192*cd4dfc48Sjeremylt     return CeedError(op->ceed, CEED_ERROR_MINOR,
2193*cd4dfc48Sjeremylt                      "Number of quadrature points already defined");
2194*cd4dfc48Sjeremylt   // LCOV_EXCL_STOP
2195*cd4dfc48Sjeremylt 
2196*cd4dfc48Sjeremylt   op->num_qpts = num_qpts;
2197*cd4dfc48Sjeremylt   return CEED_ERROR_SUCCESS;
2198*cd4dfc48Sjeremylt }
2199*cd4dfc48Sjeremylt 
2200*cd4dfc48Sjeremylt /**
22017a982d89SJeremy L. Thompson   @brief View a CeedOperator
22027a982d89SJeremy L. Thompson 
22037a982d89SJeremy L. Thompson   @param[in] op      CeedOperator to view
22047a982d89SJeremy L. Thompson   @param[in] stream  Stream to write; typically stdout/stderr or a file
22057a982d89SJeremy L. Thompson 
22067a982d89SJeremy L. Thompson   @return Error code: 0 - success, otherwise - failure
22077a982d89SJeremy L. Thompson 
22087a982d89SJeremy L. Thompson   @ref User
22097a982d89SJeremy L. Thompson **/
22107a982d89SJeremy L. Thompson int CeedOperatorView(CeedOperator op, FILE *stream) {
22117a982d89SJeremy L. Thompson   int ierr;
22127a982d89SJeremy L. Thompson 
22137a982d89SJeremy L. Thompson   if (op->composite) {
22147a982d89SJeremy L. Thompson     fprintf(stream, "Composite CeedOperator\n");
22157a982d89SJeremy L. Thompson 
2216d1d35e2fSjeremylt     for (CeedInt i=0; i<op->num_suboperators; i++) {
22177a982d89SJeremy L. Thompson       fprintf(stream, "  SubOperator [%d]:\n", i);
2218d1d35e2fSjeremylt       ierr = CeedOperatorSingleView(op->sub_operators[i], 1, stream);
22197a982d89SJeremy L. Thompson       CeedChk(ierr);
22207a982d89SJeremy L. Thompson     }
22217a982d89SJeremy L. Thompson   } else {
22227a982d89SJeremy L. Thompson     fprintf(stream, "CeedOperator\n");
22237a982d89SJeremy L. Thompson     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
22247a982d89SJeremy L. Thompson   }
2225e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
22267a982d89SJeremy L. Thompson }
22273bd813ffSjeremylt 
22283bd813ffSjeremylt /**
22293bd813ffSjeremylt   @brief Apply CeedOperator to a vector
2230d7b241e6Sjeremylt 
2231d7b241e6Sjeremylt   This computes the action of the operator on the specified (active) input,
2232d7b241e6Sjeremylt   yielding its (active) output.  All inputs and outputs must be specified using
2233d7b241e6Sjeremylt   CeedOperatorSetField().
2234d7b241e6Sjeremylt 
2235d7b241e6Sjeremylt   @param op        CeedOperator to apply
22364cc79fe7SJed Brown   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
223734138859Sjeremylt                      there are no active inputs
2238b11c1e72Sjeremylt   @param[out] out  CeedVector to store result of applying operator (must be
22394cc79fe7SJed Brown                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
224034138859Sjeremylt                      active outputs
2241d7b241e6Sjeremylt   @param request   Address of CeedRequest for non-blocking completion, else
22424cc79fe7SJed Brown                      @ref CEED_REQUEST_IMMEDIATE
2243b11c1e72Sjeremylt 
2244b11c1e72Sjeremylt   @return An error code: 0 - success, otherwise - failure
2245dfdf5a53Sjeremylt 
22467a982d89SJeremy L. Thompson   @ref User
2247b11c1e72Sjeremylt **/
2248692c2638Sjeremylt int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
2249692c2638Sjeremylt                       CeedRequest *request) {
2250d7b241e6Sjeremylt   int ierr;
2251e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
2252d7b241e6Sjeremylt 
2253d1d35e2fSjeremylt   if (op->num_elem)  {
2254250756a7Sjeremylt     // Standard Operator
2255cae8b89aSjeremylt     if (op->Apply) {
2256250756a7Sjeremylt       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
2257cae8b89aSjeremylt     } else {
2258cae8b89aSjeremylt       // Zero all output vectors
2259250756a7Sjeremylt       CeedQFunction qf = op->qf;
2260d1d35e2fSjeremylt       for (CeedInt i=0; i<qf->num_output_fields; i++) {
2261d1d35e2fSjeremylt         CeedVector vec = op->output_fields[i]->vec;
2262cae8b89aSjeremylt         if (vec == CEED_VECTOR_ACTIVE)
2263cae8b89aSjeremylt           vec = out;
2264cae8b89aSjeremylt         if (vec != CEED_VECTOR_NONE) {
2265cae8b89aSjeremylt           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
2266cae8b89aSjeremylt         }
2267cae8b89aSjeremylt       }
2268250756a7Sjeremylt       // Apply
2269250756a7Sjeremylt       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
2270250756a7Sjeremylt     }
2271250756a7Sjeremylt   } else if (op->composite) {
2272250756a7Sjeremylt     // Composite Operator
2273250756a7Sjeremylt     if (op->ApplyComposite) {
2274250756a7Sjeremylt       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
2275250756a7Sjeremylt     } else {
2276d1d35e2fSjeremylt       CeedInt num_suboperators;
2277d1d35e2fSjeremylt       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
2278d1d35e2fSjeremylt       CeedOperator *sub_operators;
2279d1d35e2fSjeremylt       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
2280250756a7Sjeremylt 
2281250756a7Sjeremylt       // Zero all output vectors
2282250756a7Sjeremylt       if (out != CEED_VECTOR_NONE) {
2283cae8b89aSjeremylt         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
2284cae8b89aSjeremylt       }
2285d1d35e2fSjeremylt       for (CeedInt i=0; i<num_suboperators; i++) {
2286d1d35e2fSjeremylt         for (CeedInt j=0; j<sub_operators[i]->qf->num_output_fields; j++) {
2287d1d35e2fSjeremylt           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
2288250756a7Sjeremylt           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
2289250756a7Sjeremylt             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
2290250756a7Sjeremylt           }
2291250756a7Sjeremylt         }
2292250756a7Sjeremylt       }
2293250756a7Sjeremylt       // Apply
2294d1d35e2fSjeremylt       for (CeedInt i=0; i<op->num_suboperators; i++) {
2295d1d35e2fSjeremylt         ierr = CeedOperatorApplyAdd(op->sub_operators[i], in, out, request);
2296cae8b89aSjeremylt         CeedChk(ierr);
2297cae8b89aSjeremylt       }
2298cae8b89aSjeremylt     }
2299250756a7Sjeremylt   }
2300e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
2301cae8b89aSjeremylt }
2302cae8b89aSjeremylt 
2303cae8b89aSjeremylt /**
2304cae8b89aSjeremylt   @brief Apply CeedOperator to a vector and add result to output vector
2305cae8b89aSjeremylt 
2306cae8b89aSjeremylt   This computes the action of the operator on the specified (active) input,
2307cae8b89aSjeremylt   yielding its (active) output.  All inputs and outputs must be specified using
2308cae8b89aSjeremylt   CeedOperatorSetField().
2309cae8b89aSjeremylt 
2310cae8b89aSjeremylt   @param op        CeedOperator to apply
2311cae8b89aSjeremylt   @param[in] in    CeedVector containing input state or NULL if there are no
2312cae8b89aSjeremylt                      active inputs
2313cae8b89aSjeremylt   @param[out] out  CeedVector to sum in result of applying operator (must be
2314cae8b89aSjeremylt                      distinct from @a in) or NULL if there are no active outputs
2315cae8b89aSjeremylt   @param request   Address of CeedRequest for non-blocking completion, else
23164cc79fe7SJed Brown                      @ref CEED_REQUEST_IMMEDIATE
2317cae8b89aSjeremylt 
2318cae8b89aSjeremylt   @return An error code: 0 - success, otherwise - failure
2319cae8b89aSjeremylt 
23207a982d89SJeremy L. Thompson   @ref User
2321cae8b89aSjeremylt **/
2322cae8b89aSjeremylt int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
2323cae8b89aSjeremylt                          CeedRequest *request) {
2324cae8b89aSjeremylt   int ierr;
2325e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
2326cae8b89aSjeremylt 
2327d1d35e2fSjeremylt   if (op->num_elem)  {
2328250756a7Sjeremylt     // Standard Operator
2329250756a7Sjeremylt     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
2330250756a7Sjeremylt   } else if (op->composite) {
2331250756a7Sjeremylt     // Composite Operator
2332250756a7Sjeremylt     if (op->ApplyAddComposite) {
2333250756a7Sjeremylt       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
2334cae8b89aSjeremylt     } else {
2335d1d35e2fSjeremylt       CeedInt num_suboperators;
2336d1d35e2fSjeremylt       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
2337d1d35e2fSjeremylt       CeedOperator *sub_operators;
2338d1d35e2fSjeremylt       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
2339250756a7Sjeremylt 
2340d1d35e2fSjeremylt       for (CeedInt i=0; i<num_suboperators; i++) {
2341d1d35e2fSjeremylt         ierr = CeedOperatorApplyAdd(sub_operators[i], in, out, request);
2342cae8b89aSjeremylt         CeedChk(ierr);
23431d7d2407SJeremy L Thompson       }
2344250756a7Sjeremylt     }
2345250756a7Sjeremylt   }
2346e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
2347d7b241e6Sjeremylt }
2348d7b241e6Sjeremylt 
2349d7b241e6Sjeremylt /**
2350b11c1e72Sjeremylt   @brief Destroy a CeedOperator
2351d7b241e6Sjeremylt 
2352d7b241e6Sjeremylt   @param op  CeedOperator to destroy
2353b11c1e72Sjeremylt 
2354b11c1e72Sjeremylt   @return An error code: 0 - success, otherwise - failure
2355dfdf5a53Sjeremylt 
23567a982d89SJeremy L. Thompson   @ref User
2357b11c1e72Sjeremylt **/
2358d7b241e6Sjeremylt int CeedOperatorDestroy(CeedOperator *op) {
2359d7b241e6Sjeremylt   int ierr;
2360d7b241e6Sjeremylt 
2361d1d35e2fSjeremylt   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
2362d7b241e6Sjeremylt   if ((*op)->Destroy) {
2363d7b241e6Sjeremylt     ierr = (*op)->Destroy(*op); CeedChk(ierr);
2364d7b241e6Sjeremylt   }
2365fe2413ffSjeremylt   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
2366fe2413ffSjeremylt   // Free fields
2367d1d35e2fSjeremylt   for (int i=0; i<(*op)->num_fields; i++)
2368d1d35e2fSjeremylt     if ((*op)->input_fields[i]) {
2369d1d35e2fSjeremylt       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
2370d1d35e2fSjeremylt         ierr = CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr);
237171352a93Sjeremylt         CeedChk(ierr);
237215910d16Sjeremylt       }
2373d1d35e2fSjeremylt       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
2374d1d35e2fSjeremylt         ierr = CeedBasisDestroy(&(*op)->input_fields[i]->basis); CeedChk(ierr);
237571352a93Sjeremylt       }
2376d1d35e2fSjeremylt       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE &&
2377d1d35e2fSjeremylt           (*op)->input_fields[i]->vec != CEED_VECTOR_NONE ) {
2378d1d35e2fSjeremylt         ierr = CeedVectorDestroy(&(*op)->input_fields[i]->vec); CeedChk(ierr);
237971352a93Sjeremylt       }
2380d1d35e2fSjeremylt       ierr = CeedFree(&(*op)->input_fields[i]->field_name); CeedChk(ierr);
2381d1d35e2fSjeremylt       ierr = CeedFree(&(*op)->input_fields[i]); CeedChk(ierr);
2382fe2413ffSjeremylt     }
2383d1d35e2fSjeremylt   for (int i=0; i<(*op)->num_fields; i++)
2384d1d35e2fSjeremylt     if ((*op)->output_fields[i]) {
2385d1d35e2fSjeremylt       ierr = CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr);
238671352a93Sjeremylt       CeedChk(ierr);
2387d1d35e2fSjeremylt       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
2388d1d35e2fSjeremylt         ierr = CeedBasisDestroy(&(*op)->output_fields[i]->basis); CeedChk(ierr);
238971352a93Sjeremylt       }
2390d1d35e2fSjeremylt       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE &&
2391d1d35e2fSjeremylt           (*op)->output_fields[i]->vec != CEED_VECTOR_NONE ) {
2392d1d35e2fSjeremylt         ierr = CeedVectorDestroy(&(*op)->output_fields[i]->vec); CeedChk(ierr);
239371352a93Sjeremylt       }
2394d1d35e2fSjeremylt       ierr = CeedFree(&(*op)->output_fields[i]->field_name); CeedChk(ierr);
2395d1d35e2fSjeremylt       ierr = CeedFree(&(*op)->output_fields[i]); CeedChk(ierr);
2396fe2413ffSjeremylt     }
2397d1d35e2fSjeremylt   // Destroy sub_operators
2398d1d35e2fSjeremylt   for (int i=0; i<(*op)->num_suboperators; i++)
2399d1d35e2fSjeremylt     if ((*op)->sub_operators[i]) {
2400d1d35e2fSjeremylt       ierr = CeedOperatorDestroy(&(*op)->sub_operators[i]); CeedChk(ierr);
240152d6035fSJeremy L Thompson     }
2402e15f9bd0SJeremy L Thompson   if ((*op)->qf)
2403e15f9bd0SJeremy L Thompson     // LCOV_EXCL_START
2404d1d35e2fSjeremylt     (*op)->qf->operators_set--;
2405e15f9bd0SJeremy L Thompson   // LCOV_EXCL_STOP
2406d7b241e6Sjeremylt   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
2407e15f9bd0SJeremy L Thompson   if ((*op)->dqf && (*op)->dqf != CEED_QFUNCTION_NONE)
2408e15f9bd0SJeremy L Thompson     // LCOV_EXCL_START
2409d1d35e2fSjeremylt     (*op)->dqf->operators_set--;
2410e15f9bd0SJeremy L Thompson   // LCOV_EXCL_STOP
2411d7b241e6Sjeremylt   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
2412e15f9bd0SJeremy L Thompson   if ((*op)->dqfT && (*op)->dqfT != CEED_QFUNCTION_NONE)
2413e15f9bd0SJeremy L Thompson     // LCOV_EXCL_START
2414d1d35e2fSjeremylt     (*op)->dqfT->operators_set--;
2415e15f9bd0SJeremy L Thompson   // LCOV_EXCL_STOP
2416d7b241e6Sjeremylt   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
2417fe2413ffSjeremylt 
24185107b09fSJeremy L Thompson   // Destroy fallback
2419d1d35e2fSjeremylt   if ((*op)->op_fallback) {
2420d1d35e2fSjeremylt     ierr = (*op)->qf_fallback->Destroy((*op)->qf_fallback); CeedChk(ierr);
2421d1d35e2fSjeremylt     ierr = CeedFree(&(*op)->qf_fallback); CeedChk(ierr);
2422d1d35e2fSjeremylt     ierr = (*op)->op_fallback->Destroy((*op)->op_fallback); CeedChk(ierr);
2423d1d35e2fSjeremylt     ierr = CeedFree(&(*op)->op_fallback); CeedChk(ierr);
24245107b09fSJeremy L Thompson   }
24255107b09fSJeremy L Thompson 
2426d1d35e2fSjeremylt   ierr = CeedFree(&(*op)->input_fields); CeedChk(ierr);
2427d1d35e2fSjeremylt   ierr = CeedFree(&(*op)->output_fields); CeedChk(ierr);
2428d1d35e2fSjeremylt   ierr = CeedFree(&(*op)->sub_operators); CeedChk(ierr);
2429d7b241e6Sjeremylt   ierr = CeedFree(op); CeedChk(ierr);
2430e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
2431d7b241e6Sjeremylt }
2432d7b241e6Sjeremylt 
2433d7b241e6Sjeremylt /// @}
2434