xref: /libCEED/interface/ceed-operator.c (revision 8229195e79c6b6b0e33df6e05b918f262f144d58)
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)
131*8229195eSjeremylt       // LCOV_EXCL_START
132e1e9e29dSJed Brown       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
133d1d35e2fSjeremylt                        "Field '%s' configured with CEED_EVAL_NONE must "
134d1d35e2fSjeremylt                        "be used with CEED_BASIS_COLLOCATED",
135*8229195eSjeremylt                        // LCOV_EXCL_STOP
136d1d35e2fSjeremylt                        qf_field->field_name);
137e15f9bd0SJeremy L Thompson     ierr = CeedBasisGetDimension(b, &dim); CeedChk(ierr);
138d1d35e2fSjeremylt     ierr = CeedBasisGetNumComponents(b, &num_comp); CeedChk(ierr);
139d1d35e2fSjeremylt     if (r != CEED_ELEMRESTRICTION_NONE && restr_num_comp != num_comp) {
140e15f9bd0SJeremy L Thompson       // LCOV_EXCL_START
141e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_DIMENSION,
142d1d35e2fSjeremylt                        "Field '%s' of size %d and EvalMode %s: ElemRestriction "
143d1d35e2fSjeremylt                        "has %d components, but Basis has %d components",
144d1d35e2fSjeremylt                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
145d1d35e2fSjeremylt                        restr_num_comp,
146d1d35e2fSjeremylt                        num_comp);
147e15f9bd0SJeremy L Thompson       // LCOV_EXCL_STOP
148e15f9bd0SJeremy L Thompson     }
149e15f9bd0SJeremy L Thompson   }
150e15f9bd0SJeremy L Thompson   // Field size
151d1d35e2fSjeremylt   switch(eval_mode) {
152e15f9bd0SJeremy L Thompson   case CEED_EVAL_NONE:
153d1d35e2fSjeremylt     if (size != restr_num_comp)
154e15f9bd0SJeremy L Thompson       // LCOV_EXCL_START
155e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_DIMENSION,
156e1e9e29dSJed Brown                        "Field '%s' of size %d and EvalMode %s: ElemRestriction has %d components",
157d1d35e2fSjeremylt                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
158d1d35e2fSjeremylt                        restr_num_comp);
159e15f9bd0SJeremy L Thompson     // LCOV_EXCL_STOP
160e15f9bd0SJeremy L Thompson     break;
161e15f9bd0SJeremy L Thompson   case CEED_EVAL_INTERP:
162d1d35e2fSjeremylt     if (size != num_comp)
163e15f9bd0SJeremy L Thompson       // LCOV_EXCL_START
164e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_DIMENSION,
165e1e9e29dSJed Brown                        "Field '%s' of size %d and EvalMode %s: ElemRestriction/Basis has %d components",
166d1d35e2fSjeremylt                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
167d1d35e2fSjeremylt                        num_comp);
168e15f9bd0SJeremy L Thompson     // LCOV_EXCL_STOP
169e15f9bd0SJeremy L Thompson     break;
170e15f9bd0SJeremy L Thompson   case CEED_EVAL_GRAD:
171d1d35e2fSjeremylt     if (size != num_comp * dim)
172e15f9bd0SJeremy L Thompson       // LCOV_EXCL_START
173e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_DIMENSION,
174d1d35e2fSjeremylt                        "Field '%s' of size %d and EvalMode %s in %d dimensions: "
175d1d35e2fSjeremylt                        "ElemRestriction/Basis has %d components",
176d1d35e2fSjeremylt                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode], dim,
177d1d35e2fSjeremylt                        num_comp);
178e15f9bd0SJeremy L Thompson     // LCOV_EXCL_STOP
179e15f9bd0SJeremy L Thompson     break;
180e15f9bd0SJeremy L Thompson   case CEED_EVAL_WEIGHT:
181d1d35e2fSjeremylt     // No additional checks required
182e15f9bd0SJeremy L Thompson     break;
183e15f9bd0SJeremy L Thompson   case CEED_EVAL_DIV:
184e15f9bd0SJeremy L Thompson     // Not implemented
185e15f9bd0SJeremy L Thompson     break;
186e15f9bd0SJeremy L Thompson   case CEED_EVAL_CURL:
187e15f9bd0SJeremy L Thompson     // Not implemented
188e15f9bd0SJeremy L Thompson     break;
189e15f9bd0SJeremy L Thompson   }
190e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1917a982d89SJeremy L. Thompson }
1927a982d89SJeremy L. Thompson 
1937a982d89SJeremy L. Thompson /**
1947a982d89SJeremy L. Thompson   @brief Check if a CeedOperator is ready to be used.
1957a982d89SJeremy L. Thompson 
1967a982d89SJeremy L. Thompson   @param[in] op  CeedOperator to check
1977a982d89SJeremy L. Thompson 
1987a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
1997a982d89SJeremy L. Thompson 
2007a982d89SJeremy L. Thompson   @ref Developer
2017a982d89SJeremy L. Thompson **/
202e2f04181SAndrew T. Barker static int CeedOperatorCheckReady(CeedOperator op) {
203e2f04181SAndrew T. Barker   int ierr;
204e2f04181SAndrew T. Barker   Ceed ceed;
205e2f04181SAndrew T. Barker   ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
206e2f04181SAndrew T. Barker 
207d1d35e2fSjeremylt   if (op->interface_setup)
208e15f9bd0SJeremy L Thompson     return CEED_ERROR_SUCCESS;
2097a982d89SJeremy L. Thompson 
210e15f9bd0SJeremy L Thompson   CeedQFunction qf = op->qf;
2117a982d89SJeremy L. Thompson   if (op->composite) {
212d1d35e2fSjeremylt     if (!op->num_suboperators)
2137a982d89SJeremy L. Thompson       // LCOV_EXCL_START
214d1d35e2fSjeremylt       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No sub_operators set");
2157a982d89SJeremy L. Thompson     // LCOV_EXCL_STOP
2167a982d89SJeremy L. Thompson   } else {
217d1d35e2fSjeremylt     if (op->num_fields == 0)
2187a982d89SJeremy L. Thompson       // LCOV_EXCL_START
219e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No operator fields set");
2207a982d89SJeremy L. Thompson     // LCOV_EXCL_STOP
221d1d35e2fSjeremylt     if (op->num_fields < qf->num_input_fields + qf->num_output_fields)
2227a982d89SJeremy L. Thompson       // LCOV_EXCL_START
223e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set");
2247a982d89SJeremy L. Thompson     // LCOV_EXCL_STOP
225d1d35e2fSjeremylt     if (!op->has_restriction)
2267a982d89SJeremy L. Thompson       // LCOV_EXCL_START
227e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
228e15f9bd0SJeremy L Thompson                        "At least one restriction required");
2297a982d89SJeremy L. Thompson     // LCOV_EXCL_STOP
230d1d35e2fSjeremylt     if (op->num_qpts == 0)
2317a982d89SJeremy L. Thompson       // LCOV_EXCL_START
232e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
233cd4dfc48Sjeremylt                        "At least one non-collocated basis is required "
234cd4dfc48Sjeremylt                        "or the number of quadrature points must be set");
2357a982d89SJeremy L. Thompson     // LCOV_EXCL_STOP
2367a982d89SJeremy L. Thompson   }
2377a982d89SJeremy L. Thompson 
238e15f9bd0SJeremy L Thompson   // Flag as immutable and ready
239d1d35e2fSjeremylt   op->interface_setup = true;
240e15f9bd0SJeremy L Thompson   if (op->qf && op->qf != CEED_QFUNCTION_NONE)
241e15f9bd0SJeremy L Thompson     // LCOV_EXCL_START
242d1d35e2fSjeremylt     op->qf->operators_set++;
243e15f9bd0SJeremy L Thompson   // LCOV_EXCL_STOP
244e15f9bd0SJeremy L Thompson   if (op->dqf && op->dqf != CEED_QFUNCTION_NONE)
245e15f9bd0SJeremy L Thompson     // LCOV_EXCL_START
246d1d35e2fSjeremylt     op->dqf->operators_set++;
247e15f9bd0SJeremy L Thompson   // LCOV_EXCL_STOP
248e15f9bd0SJeremy L Thompson   if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE)
249e15f9bd0SJeremy L Thompson     // LCOV_EXCL_START
250d1d35e2fSjeremylt     op->dqfT->operators_set++;
251e15f9bd0SJeremy L Thompson   // LCOV_EXCL_STOP
252e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
2537a982d89SJeremy L. Thompson }
2547a982d89SJeremy L. Thompson 
2557a982d89SJeremy L. Thompson /**
2567a982d89SJeremy L. Thompson   @brief View a field of a CeedOperator
2577a982d89SJeremy L. Thompson 
2587a982d89SJeremy L. Thompson   @param[in] field         Operator field to view
259d1d35e2fSjeremylt   @param[in] qf_field      QFunction field (carries field name)
260d1d35e2fSjeremylt   @param[in] field_number  Number of field being viewed
2614c4400c7SValeria Barra   @param[in] sub           true indicates sub-operator, which increases indentation; false for top-level operator
262d1d35e2fSjeremylt   @param[in] input         true for an input field; false for output field
2637a982d89SJeremy L. Thompson   @param[in] stream        Stream to view to, e.g., stdout
2647a982d89SJeremy L. Thompson 
2657a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
2667a982d89SJeremy L. Thompson 
2677a982d89SJeremy L. Thompson   @ref Utility
2687a982d89SJeremy L. Thompson **/
2697a982d89SJeremy L. Thompson static int CeedOperatorFieldView(CeedOperatorField field,
270d1d35e2fSjeremylt                                  CeedQFunctionField qf_field,
271d1d35e2fSjeremylt                                  CeedInt field_number, bool sub, bool input,
2727a982d89SJeremy L. Thompson                                  FILE *stream) {
2737a982d89SJeremy L. Thompson   const char *pre = sub ? "  " : "";
274d1d35e2fSjeremylt   const char *in_out = input ? "Input" : "Output";
2757a982d89SJeremy L. Thompson 
2767a982d89SJeremy L. Thompson   fprintf(stream, "%s    %s Field [%d]:\n"
2777a982d89SJeremy L. Thompson           "%s      Name: \"%s\"\n",
278d1d35e2fSjeremylt           pre, in_out, field_number, pre, qf_field->field_name);
2797a982d89SJeremy L. Thompson 
2807a982d89SJeremy L. Thompson   if (field->basis == CEED_BASIS_COLLOCATED)
2817a982d89SJeremy L. Thompson     fprintf(stream, "%s      Collocated basis\n", pre);
2827a982d89SJeremy L. Thompson 
2837a982d89SJeremy L. Thompson   if (field->vec == CEED_VECTOR_ACTIVE)
2847a982d89SJeremy L. Thompson     fprintf(stream, "%s      Active vector\n", pre);
2857a982d89SJeremy L. Thompson   else if (field->vec == CEED_VECTOR_NONE)
2867a982d89SJeremy L. Thompson     fprintf(stream, "%s      No vector\n", pre);
287e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
2887a982d89SJeremy L. Thompson }
2897a982d89SJeremy L. Thompson 
2907a982d89SJeremy L. Thompson /**
2917a982d89SJeremy L. Thompson   @brief View a single CeedOperator
2927a982d89SJeremy L. Thompson 
2937a982d89SJeremy L. Thompson   @param[in] op      CeedOperator to view
2947a982d89SJeremy L. Thompson   @param[in] sub     Boolean flag for sub-operator
2957a982d89SJeremy L. Thompson   @param[in] stream  Stream to write; typically stdout/stderr or a file
2967a982d89SJeremy L. Thompson 
2977a982d89SJeremy L. Thompson   @return Error code: 0 - success, otherwise - failure
2987a982d89SJeremy L. Thompson 
2997a982d89SJeremy L. Thompson   @ref Utility
3007a982d89SJeremy L. Thompson **/
3017a982d89SJeremy L. Thompson int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) {
3027a982d89SJeremy L. Thompson   int ierr;
3037a982d89SJeremy L. Thompson   const char *pre = sub ? "  " : "";
3047a982d89SJeremy L. Thompson 
30578464608Sjeremylt   CeedInt total_fields = 0;
30678464608Sjeremylt   ierr = CeedOperatorGetNumArgs(op, &total_fields); CeedChk(ierr);
3077a982d89SJeremy L. Thompson 
30878464608Sjeremylt   fprintf(stream, "%s  %d Field%s\n", pre, total_fields,
30978464608Sjeremylt           total_fields>1 ? "s" : "");
3107a982d89SJeremy L. Thompson 
311d1d35e2fSjeremylt   fprintf(stream, "%s  %d Input Field%s:\n", pre, op->qf->num_input_fields,
312d1d35e2fSjeremylt           op->qf->num_input_fields>1 ? "s" : "");
313d1d35e2fSjeremylt   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
314d1d35e2fSjeremylt     ierr = CeedOperatorFieldView(op->input_fields[i], op->qf->input_fields[i],
3157a982d89SJeremy L. Thompson                                  i, sub, 1, stream); CeedChk(ierr);
3167a982d89SJeremy L. Thompson   }
3177a982d89SJeremy L. Thompson 
318d1d35e2fSjeremylt   fprintf(stream, "%s  %d Output Field%s:\n", pre, op->qf->num_output_fields,
319d1d35e2fSjeremylt           op->qf->num_output_fields>1 ? "s" : "");
320d1d35e2fSjeremylt   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
321d1d35e2fSjeremylt     ierr = CeedOperatorFieldView(op->output_fields[i], op->qf->output_fields[i],
3227a982d89SJeremy L. Thompson                                  i, sub, 0, stream); CeedChk(ierr);
3237a982d89SJeremy L. Thompson   }
324e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
3257a982d89SJeremy L. Thompson }
3267a982d89SJeremy L. Thompson 
327d99fa3c5SJeremy L Thompson /**
328e2f04181SAndrew T. Barker   @brief Find the active vector ElemRestriction for a CeedOperator
329e2f04181SAndrew T. Barker 
330e2f04181SAndrew T. Barker   @param[in] op            CeedOperator to find active basis for
331d1d35e2fSjeremylt   @param[out] active_rstr  ElemRestriction for active input vector
332e2f04181SAndrew T. Barker 
333e2f04181SAndrew T. Barker   @return An error code: 0 - success, otherwise - failure
334e2f04181SAndrew T. Barker 
335e2f04181SAndrew T. Barker   @ref Utility
336e2f04181SAndrew T. Barker **/
337e2f04181SAndrew T. Barker static int CeedOperatorGetActiveElemRestriction(CeedOperator op,
338d1d35e2fSjeremylt     CeedElemRestriction *active_rstr) {
339d1d35e2fSjeremylt   *active_rstr = NULL;
340d1d35e2fSjeremylt   for (int i = 0; i < op->qf->num_input_fields; i++)
341d1d35e2fSjeremylt     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
342d1d35e2fSjeremylt       *active_rstr = op->input_fields[i]->elem_restr;
343e2f04181SAndrew T. Barker       break;
344e2f04181SAndrew T. Barker     }
345e2f04181SAndrew T. Barker 
346d1d35e2fSjeremylt   if (!*active_rstr) {
347e2f04181SAndrew T. Barker     // LCOV_EXCL_START
348e2f04181SAndrew T. Barker     int ierr;
349e2f04181SAndrew T. Barker     Ceed ceed;
350e2f04181SAndrew T. Barker     ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
351e2f04181SAndrew T. Barker     return CeedError(ceed, CEED_ERROR_INCOMPLETE,
352e2f04181SAndrew T. Barker                      "No active ElemRestriction found!");
353e2f04181SAndrew T. Barker     // LCOV_EXCL_STOP
354e2f04181SAndrew T. Barker   }
355e2f04181SAndrew T. Barker   return CEED_ERROR_SUCCESS;
356e2f04181SAndrew T. Barker }
357e2f04181SAndrew T. Barker 
358e2f04181SAndrew T. Barker /**
359d99fa3c5SJeremy L Thompson   @brief Find the active vector basis for a CeedOperator
360d99fa3c5SJeremy L Thompson 
361d99fa3c5SJeremy L Thompson   @param[in] op             CeedOperator to find active basis for
362d1d35e2fSjeremylt   @param[out] active_basis  Basis for active input vector
363d99fa3c5SJeremy L Thompson 
364d99fa3c5SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
365d99fa3c5SJeremy L Thompson 
366d99fa3c5SJeremy L Thompson   @ ref Developer
367d99fa3c5SJeremy L Thompson **/
368d99fa3c5SJeremy L Thompson static int CeedOperatorGetActiveBasis(CeedOperator op,
369d1d35e2fSjeremylt                                       CeedBasis *active_basis) {
370d1d35e2fSjeremylt   *active_basis = NULL;
371d1d35e2fSjeremylt   for (int i = 0; i < op->qf->num_input_fields; i++)
372d1d35e2fSjeremylt     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
373d1d35e2fSjeremylt       *active_basis = op->input_fields[i]->basis;
374d99fa3c5SJeremy L Thompson       break;
375d99fa3c5SJeremy L Thompson     }
376d99fa3c5SJeremy L Thompson 
377d1d35e2fSjeremylt   if (!*active_basis) {
378d99fa3c5SJeremy L Thompson     // LCOV_EXCL_START
379d99fa3c5SJeremy L Thompson     int ierr;
380d99fa3c5SJeremy L Thompson     Ceed ceed;
381d99fa3c5SJeremy L Thompson     ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
382e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_MINOR,
383d99fa3c5SJeremy L Thompson                      "No active basis found for automatic multigrid setup");
384d99fa3c5SJeremy L Thompson     // LCOV_EXCL_STOP
385d99fa3c5SJeremy L Thompson   }
386e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
387d99fa3c5SJeremy L Thompson }
388d99fa3c5SJeremy L Thompson 
389d99fa3c5SJeremy L Thompson /**
390d99fa3c5SJeremy L Thompson   @brief Common code for creating a multigrid coarse operator and level
391d99fa3c5SJeremy L Thompson            transfer operators for a CeedOperator
392d99fa3c5SJeremy L Thompson 
393d1d35e2fSjeremylt   @param[in] op_fine       Fine grid operator
394d1d35e2fSjeremylt   @param[in] p_mult_fine   L-vector multiplicity in parallel gather/scatter
395d1d35e2fSjeremylt   @param[in] rstr_coarse   Coarse grid restriction
396d1d35e2fSjeremylt   @param[in] basis_coarse  Coarse grid active vector basis
397d1d35e2fSjeremylt   @param[in] basis_c_to_f  Basis for coarse to fine interpolation
398d1d35e2fSjeremylt   @param[out] op_coarse    Coarse grid operator
399d1d35e2fSjeremylt   @param[out] op_prolong   Coarse to fine operator
400d1d35e2fSjeremylt   @param[out] op_restrict  Fine to coarse operator
401d99fa3c5SJeremy L Thompson 
402d99fa3c5SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
403d99fa3c5SJeremy L Thompson 
404d99fa3c5SJeremy L Thompson   @ref Developer
405d99fa3c5SJeremy L Thompson **/
406d1d35e2fSjeremylt static int CeedOperatorMultigridLevel_Core(CeedOperator op_fine,
407d1d35e2fSjeremylt     CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
408d1d35e2fSjeremylt     CeedBasis basis_c_to_f, CeedOperator *op_coarse, CeedOperator *op_prolong,
409d1d35e2fSjeremylt     CeedOperator *op_restrict) {
410d99fa3c5SJeremy L Thompson   int ierr;
411d99fa3c5SJeremy L Thompson   Ceed ceed;
412d1d35e2fSjeremylt   ierr = CeedOperatorGetCeed(op_fine, &ceed); CeedChk(ierr);
413d99fa3c5SJeremy L Thompson 
414d99fa3c5SJeremy L Thompson   // Check for composite operator
415d1d35e2fSjeremylt   bool is_composite;
416d1d35e2fSjeremylt   ierr = CeedOperatorIsComposite(op_fine, &is_composite); CeedChk(ierr);
417d1d35e2fSjeremylt   if (is_composite)
418d99fa3c5SJeremy L Thompson     // LCOV_EXCL_START
419e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
420d99fa3c5SJeremy L Thompson                      "Automatic multigrid setup for composite operators not supported");
421d99fa3c5SJeremy L Thompson   // LCOV_EXCL_STOP
422d99fa3c5SJeremy L Thompson 
423d99fa3c5SJeremy L Thompson   // Coarse Grid
424d1d35e2fSjeremylt   ierr = CeedOperatorCreate(ceed, op_fine->qf, op_fine->dqf, op_fine->dqfT,
425d1d35e2fSjeremylt                             op_coarse); CeedChk(ierr);
426d1d35e2fSjeremylt   CeedElemRestriction rstr_fine = NULL;
427d99fa3c5SJeremy L Thompson   // -- Clone input fields
428d1d35e2fSjeremylt   for (int i = 0; i < op_fine->qf->num_input_fields; i++) {
429d1d35e2fSjeremylt     if (op_fine->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
430d1d35e2fSjeremylt       rstr_fine = op_fine->input_fields[i]->elem_restr;
431d1d35e2fSjeremylt       ierr = CeedOperatorSetField(*op_coarse, op_fine->input_fields[i]->field_name,
432d1d35e2fSjeremylt                                   rstr_coarse, basis_coarse, CEED_VECTOR_ACTIVE);
433d99fa3c5SJeremy L Thompson       CeedChk(ierr);
434d99fa3c5SJeremy L Thompson     } else {
435d1d35e2fSjeremylt       ierr = CeedOperatorSetField(*op_coarse, op_fine->input_fields[i]->field_name,
436d1d35e2fSjeremylt                                   op_fine->input_fields[i]->elem_restr,
437d1d35e2fSjeremylt                                   op_fine->input_fields[i]->basis,
438d1d35e2fSjeremylt                                   op_fine->input_fields[i]->vec); CeedChk(ierr);
439d99fa3c5SJeremy L Thompson     }
440d99fa3c5SJeremy L Thompson   }
441d99fa3c5SJeremy L Thompson   // -- Clone output fields
442d1d35e2fSjeremylt   for (int i = 0; i < op_fine->qf->num_output_fields; i++) {
443d1d35e2fSjeremylt     if (op_fine->output_fields[i]->vec == CEED_VECTOR_ACTIVE) {
444d1d35e2fSjeremylt       ierr = CeedOperatorSetField(*op_coarse, op_fine->output_fields[i]->field_name,
445d1d35e2fSjeremylt                                   rstr_coarse, basis_coarse, CEED_VECTOR_ACTIVE);
446d99fa3c5SJeremy L Thompson       CeedChk(ierr);
447d99fa3c5SJeremy L Thompson     } else {
448d1d35e2fSjeremylt       ierr = CeedOperatorSetField(*op_coarse, op_fine->output_fields[i]->field_name,
449d1d35e2fSjeremylt                                   op_fine->output_fields[i]->elem_restr,
450d1d35e2fSjeremylt                                   op_fine->output_fields[i]->basis,
451d1d35e2fSjeremylt                                   op_fine->output_fields[i]->vec); CeedChk(ierr);
452d99fa3c5SJeremy L Thompson     }
453d99fa3c5SJeremy L Thompson   }
454d99fa3c5SJeremy L Thompson 
455d99fa3c5SJeremy L Thompson   // Multiplicity vector
456d1d35e2fSjeremylt   CeedVector mult_vec, mult_e_vec;
457d1d35e2fSjeremylt   ierr = CeedElemRestrictionCreateVector(rstr_fine, &mult_vec, &mult_e_vec);
458d99fa3c5SJeremy L Thompson   CeedChk(ierr);
459d1d35e2fSjeremylt   ierr = CeedVectorSetValue(mult_e_vec, 0.0); CeedChk(ierr);
460d1d35e2fSjeremylt   ierr = CeedElemRestrictionApply(rstr_fine, CEED_NOTRANSPOSE, p_mult_fine,
461d1d35e2fSjeremylt                                   mult_e_vec, CEED_REQUEST_IMMEDIATE); CeedChk(ierr);
462d1d35e2fSjeremylt   ierr = CeedVectorSetValue(mult_vec, 0.0); CeedChk(ierr);
463d1d35e2fSjeremylt   ierr = CeedElemRestrictionApply(rstr_fine, CEED_TRANSPOSE, mult_e_vec, mult_vec,
464d99fa3c5SJeremy L Thompson                                   CEED_REQUEST_IMMEDIATE); CeedChk(ierr);
465d1d35e2fSjeremylt   ierr = CeedVectorDestroy(&mult_e_vec); CeedChk(ierr);
466d1d35e2fSjeremylt   ierr = CeedVectorReciprocal(mult_vec); CeedChk(ierr);
467d99fa3c5SJeremy L Thompson 
468d99fa3c5SJeremy L Thompson   // Restriction
469d1d35e2fSjeremylt   CeedInt num_comp;
470d1d35e2fSjeremylt   ierr = CeedBasisGetNumComponents(basis_coarse, &num_comp); CeedChk(ierr);
471d1d35e2fSjeremylt   CeedQFunction qf_restrict;
472d1d35e2fSjeremylt   ierr = CeedQFunctionCreateInteriorByName(ceed, "Scale", &qf_restrict);
473d99fa3c5SJeremy L Thompson   CeedChk(ierr);
474d1d35e2fSjeremylt   CeedInt *num_comp_r_data;
475d1d35e2fSjeremylt   ierr = CeedCalloc(1, &num_comp_r_data); CeedChk(ierr);
476d1d35e2fSjeremylt   num_comp_r_data[0] = num_comp;
477d1d35e2fSjeremylt   CeedQFunctionContext ctx_r;
478d1d35e2fSjeremylt   ierr = CeedQFunctionContextCreate(ceed, &ctx_r); CeedChk(ierr);
479d1d35e2fSjeremylt   ierr = CeedQFunctionContextSetData(ctx_r, CEED_MEM_HOST, CEED_OWN_POINTER,
480d1d35e2fSjeremylt                                      sizeof(*num_comp_r_data), num_comp_r_data);
481777ff853SJeremy L Thompson   CeedChk(ierr);
482d1d35e2fSjeremylt   ierr = CeedQFunctionSetContext(qf_restrict, ctx_r); CeedChk(ierr);
483d1d35e2fSjeremylt   ierr = CeedQFunctionContextDestroy(&ctx_r); CeedChk(ierr);
484d1d35e2fSjeremylt   ierr = CeedQFunctionAddInput(qf_restrict, "input", num_comp, CEED_EVAL_NONE);
485d99fa3c5SJeremy L Thompson   CeedChk(ierr);
486d1d35e2fSjeremylt   ierr = CeedQFunctionAddInput(qf_restrict, "scale", num_comp, CEED_EVAL_NONE);
487d99fa3c5SJeremy L Thompson   CeedChk(ierr);
488d1d35e2fSjeremylt   ierr = CeedQFunctionAddOutput(qf_restrict, "output", num_comp,
489d1d35e2fSjeremylt                                 CEED_EVAL_INTERP); CeedChk(ierr);
490d99fa3c5SJeremy L Thompson 
491d1d35e2fSjeremylt   ierr = CeedOperatorCreate(ceed, qf_restrict, CEED_QFUNCTION_NONE,
492d1d35e2fSjeremylt                             CEED_QFUNCTION_NONE, op_restrict);
493d99fa3c5SJeremy L Thompson   CeedChk(ierr);
494d1d35e2fSjeremylt   ierr = CeedOperatorSetField(*op_restrict, "input", rstr_fine,
495d99fa3c5SJeremy L Thompson                               CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
496d99fa3c5SJeremy L Thompson   CeedChk(ierr);
497d1d35e2fSjeremylt   ierr = CeedOperatorSetField(*op_restrict, "scale", rstr_fine,
498d1d35e2fSjeremylt                               CEED_BASIS_COLLOCATED, mult_vec);
499d99fa3c5SJeremy L Thompson   CeedChk(ierr);
500d1d35e2fSjeremylt   ierr = CeedOperatorSetField(*op_restrict, "output", rstr_coarse, basis_c_to_f,
501d99fa3c5SJeremy L Thompson                               CEED_VECTOR_ACTIVE); CeedChk(ierr);
502d99fa3c5SJeremy L Thompson 
503d99fa3c5SJeremy L Thompson   // Prolongation
504d1d35e2fSjeremylt   CeedQFunction qf_prolong;
505d1d35e2fSjeremylt   ierr = CeedQFunctionCreateInteriorByName(ceed, "Scale", &qf_prolong);
506d99fa3c5SJeremy L Thompson   CeedChk(ierr);
507d1d35e2fSjeremylt   CeedInt *num_comp_p_data;
508d1d35e2fSjeremylt   ierr = CeedCalloc(1, &num_comp_p_data); CeedChk(ierr);
509d1d35e2fSjeremylt   num_comp_p_data[0] = num_comp;
510d1d35e2fSjeremylt   CeedQFunctionContext ctx_p;
511d1d35e2fSjeremylt   ierr = CeedQFunctionContextCreate(ceed, &ctx_p); CeedChk(ierr);
512d1d35e2fSjeremylt   ierr = CeedQFunctionContextSetData(ctx_p, CEED_MEM_HOST, CEED_OWN_POINTER,
513d1d35e2fSjeremylt                                      sizeof(*num_comp_p_data), num_comp_p_data);
514777ff853SJeremy L Thompson   CeedChk(ierr);
515d1d35e2fSjeremylt   ierr = CeedQFunctionSetContext(qf_prolong, ctx_p); CeedChk(ierr);
516d1d35e2fSjeremylt   ierr = CeedQFunctionContextDestroy(&ctx_p); CeedChk(ierr);
517d1d35e2fSjeremylt   ierr = CeedQFunctionAddInput(qf_prolong, "input", num_comp, CEED_EVAL_INTERP);
518d99fa3c5SJeremy L Thompson   CeedChk(ierr);
519d1d35e2fSjeremylt   ierr = CeedQFunctionAddInput(qf_prolong, "scale", num_comp, CEED_EVAL_NONE);
520d99fa3c5SJeremy L Thompson   CeedChk(ierr);
521d1d35e2fSjeremylt   ierr = CeedQFunctionAddOutput(qf_prolong, "output", num_comp, CEED_EVAL_NONE);
522d99fa3c5SJeremy L Thompson   CeedChk(ierr);
523d99fa3c5SJeremy L Thompson 
524d1d35e2fSjeremylt   ierr = CeedOperatorCreate(ceed, qf_prolong, CEED_QFUNCTION_NONE,
525d1d35e2fSjeremylt                             CEED_QFUNCTION_NONE, op_prolong);
526d99fa3c5SJeremy L Thompson   CeedChk(ierr);
527d1d35e2fSjeremylt   ierr = CeedOperatorSetField(*op_prolong, "input", rstr_coarse, basis_c_to_f,
528d99fa3c5SJeremy L Thompson                               CEED_VECTOR_ACTIVE); CeedChk(ierr);
529d1d35e2fSjeremylt   ierr = CeedOperatorSetField(*op_prolong, "scale", rstr_fine,
530d1d35e2fSjeremylt                               CEED_BASIS_COLLOCATED, mult_vec);
531d99fa3c5SJeremy L Thompson   CeedChk(ierr);
532d1d35e2fSjeremylt   ierr = CeedOperatorSetField(*op_prolong, "output", rstr_fine,
533d99fa3c5SJeremy L Thompson                               CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
534d99fa3c5SJeremy L Thompson   CeedChk(ierr);
535d99fa3c5SJeremy L Thompson 
536d99fa3c5SJeremy L Thompson   // Cleanup
537d1d35e2fSjeremylt   ierr = CeedVectorDestroy(&mult_vec); CeedChk(ierr);
538d1d35e2fSjeremylt   ierr = CeedBasisDestroy(&basis_c_to_f); CeedChk(ierr);
539d1d35e2fSjeremylt   ierr = CeedQFunctionDestroy(&qf_restrict); CeedChk(ierr);
540d1d35e2fSjeremylt   ierr = CeedQFunctionDestroy(&qf_prolong); CeedChk(ierr);
541e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
542d99fa3c5SJeremy L Thompson }
543d99fa3c5SJeremy L Thompson 
5447a982d89SJeremy L. Thompson /// @}
5457a982d89SJeremy L. Thompson 
5467a982d89SJeremy L. Thompson /// ----------------------------------------------------------------------------
5477a982d89SJeremy L. Thompson /// CeedOperator Backend API
5487a982d89SJeremy L. Thompson /// ----------------------------------------------------------------------------
5497a982d89SJeremy L. Thompson /// @addtogroup CeedOperatorBackend
5507a982d89SJeremy L. Thompson /// @{
5517a982d89SJeremy L. Thompson 
5527a982d89SJeremy L. Thompson /**
5537a982d89SJeremy L. Thompson   @brief Get the Ceed associated with a CeedOperator
5547a982d89SJeremy L. Thompson 
5557a982d89SJeremy L. Thompson   @param op         CeedOperator
5567a982d89SJeremy L. Thompson   @param[out] ceed  Variable to store Ceed
5577a982d89SJeremy L. Thompson 
5587a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
5597a982d89SJeremy L. Thompson 
5607a982d89SJeremy L. Thompson   @ref Backend
5617a982d89SJeremy L. Thompson **/
5627a982d89SJeremy L. Thompson 
5637a982d89SJeremy L. Thompson int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
5647a982d89SJeremy L. Thompson   *ceed = op->ceed;
565e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
5667a982d89SJeremy L. Thompson }
5677a982d89SJeremy L. Thompson 
5687a982d89SJeremy L. Thompson /**
5697a982d89SJeremy L. Thompson   @brief Get the number of elements associated with a CeedOperator
5707a982d89SJeremy L. Thompson 
5717a982d89SJeremy L. Thompson   @param op             CeedOperator
572d1d35e2fSjeremylt   @param[out] num_elem  Variable to store number of elements
5737a982d89SJeremy L. Thompson 
5747a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
5757a982d89SJeremy L. Thompson 
5767a982d89SJeremy L. Thompson   @ref Backend
5777a982d89SJeremy L. Thompson **/
5787a982d89SJeremy L. Thompson 
579d1d35e2fSjeremylt int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) {
5807a982d89SJeremy L. Thompson   if (op->composite)
5817a982d89SJeremy L. Thompson     // LCOV_EXCL_START
582e15f9bd0SJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_MINOR,
583e15f9bd0SJeremy L Thompson                      "Not defined for composite operator");
5847a982d89SJeremy L. Thompson   // LCOV_EXCL_STOP
5857a982d89SJeremy L. Thompson 
586d1d35e2fSjeremylt   *num_elem = op->num_elem;
587e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
5887a982d89SJeremy L. Thompson }
5897a982d89SJeremy L. Thompson 
5907a982d89SJeremy L. Thompson /**
5917a982d89SJeremy L. Thompson   @brief Get the number of quadrature points associated with a CeedOperator
5927a982d89SJeremy L. Thompson 
5937a982d89SJeremy L. Thompson   @param op             CeedOperator
594d1d35e2fSjeremylt   @param[out] num_qpts  Variable to store vector number of quadrature points
5957a982d89SJeremy L. Thompson 
5967a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
5977a982d89SJeremy L. Thompson 
5987a982d89SJeremy L. Thompson   @ref Backend
5997a982d89SJeremy L. Thompson **/
6007a982d89SJeremy L. Thompson 
601d1d35e2fSjeremylt int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) {
6027a982d89SJeremy L. Thompson   if (op->composite)
6037a982d89SJeremy L. Thompson     // LCOV_EXCL_START
604e15f9bd0SJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_MINOR,
605e15f9bd0SJeremy L Thompson                      "Not defined for composite operator");
6067a982d89SJeremy L. Thompson   // LCOV_EXCL_STOP
6077a982d89SJeremy L. Thompson 
608d1d35e2fSjeremylt   *num_qpts = op->num_qpts;
609e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
6107a982d89SJeremy L. Thompson }
6117a982d89SJeremy L. Thompson 
6127a982d89SJeremy L. Thompson /**
6137a982d89SJeremy L. Thompson   @brief Get the number of arguments associated with a CeedOperator
6147a982d89SJeremy L. Thompson 
6157a982d89SJeremy L. Thompson   @param op             CeedOperator
616d1d35e2fSjeremylt   @param[out] num_args  Variable to store vector number of arguments
6177a982d89SJeremy L. Thompson 
6187a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
6197a982d89SJeremy L. Thompson 
6207a982d89SJeremy L. Thompson   @ref Backend
6217a982d89SJeremy L. Thompson **/
6227a982d89SJeremy L. Thompson 
623d1d35e2fSjeremylt int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *num_args) {
6247a982d89SJeremy L. Thompson   if (op->composite)
6257a982d89SJeremy L. Thompson     // LCOV_EXCL_START
626e15f9bd0SJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_MINOR,
627e15f9bd0SJeremy L Thompson                      "Not defined for composite operators");
6287a982d89SJeremy L. Thompson   // LCOV_EXCL_STOP
6297a982d89SJeremy L. Thompson 
630d1d35e2fSjeremylt   *num_args = op->num_fields;
631e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
6327a982d89SJeremy L. Thompson }
6337a982d89SJeremy L. Thompson 
6347a982d89SJeremy L. Thompson /**
6357a982d89SJeremy L. Thompson   @brief Get the setup status of a CeedOperator
6367a982d89SJeremy L. Thompson 
6377a982d89SJeremy L. Thompson   @param op                  CeedOperator
638d1d35e2fSjeremylt   @param[out] is_setup_done  Variable to store setup status
6397a982d89SJeremy L. Thompson 
6407a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
6417a982d89SJeremy L. Thompson 
6427a982d89SJeremy L. Thompson   @ref Backend
6437a982d89SJeremy L. Thompson **/
6447a982d89SJeremy L. Thompson 
645d1d35e2fSjeremylt int CeedOperatorIsSetupDone(CeedOperator op, bool *is_setup_done) {
646d1d35e2fSjeremylt   *is_setup_done = op->backend_setup;
647e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
6487a982d89SJeremy L. Thompson }
6497a982d89SJeremy L. Thompson 
6507a982d89SJeremy L. Thompson /**
6517a982d89SJeremy L. Thompson   @brief Get the QFunction associated with a CeedOperator
6527a982d89SJeremy L. Thompson 
6537a982d89SJeremy L. Thompson   @param op       CeedOperator
6547a982d89SJeremy L. Thompson   @param[out] qf  Variable to store QFunction
6557a982d89SJeremy L. Thompson 
6567a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
6577a982d89SJeremy L. Thompson 
6587a982d89SJeremy L. Thompson   @ref Backend
6597a982d89SJeremy L. Thompson **/
6607a982d89SJeremy L. Thompson 
6617a982d89SJeremy L. Thompson int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
6627a982d89SJeremy L. Thompson   if (op->composite)
6637a982d89SJeremy L. Thompson     // LCOV_EXCL_START
664e15f9bd0SJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_MINOR,
665e15f9bd0SJeremy L Thompson                      "Not defined for composite operator");
6667a982d89SJeremy L. Thompson   // LCOV_EXCL_STOP
6677a982d89SJeremy L. Thompson 
6687a982d89SJeremy L. Thompson   *qf = op->qf;
669e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
6707a982d89SJeremy L. Thompson }
6717a982d89SJeremy L. Thompson 
6727a982d89SJeremy L. Thompson /**
673c04a41a7SJeremy L Thompson   @brief Get a boolean value indicating if the CeedOperator is composite
674c04a41a7SJeremy L Thompson 
675c04a41a7SJeremy L Thompson   @param op                 CeedOperator
676d1d35e2fSjeremylt   @param[out] is_composite  Variable to store composite status
677c04a41a7SJeremy L Thompson 
678c04a41a7SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
679c04a41a7SJeremy L Thompson 
680c04a41a7SJeremy L Thompson   @ref Backend
681c04a41a7SJeremy L Thompson **/
682c04a41a7SJeremy L Thompson 
683d1d35e2fSjeremylt int CeedOperatorIsComposite(CeedOperator op, bool *is_composite) {
684d1d35e2fSjeremylt   *is_composite = op->composite;
685e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
686c04a41a7SJeremy L Thompson }
687c04a41a7SJeremy L Thompson 
688c04a41a7SJeremy L Thompson /**
689d1d35e2fSjeremylt   @brief Get the number of sub_operators associated with a CeedOperator
6907a982d89SJeremy L. Thompson 
6917a982d89SJeremy L. Thompson   @param op                     CeedOperator
692d1d35e2fSjeremylt   @param[out] num_suboperators  Variable to store number of sub_operators
6937a982d89SJeremy L. Thompson 
6947a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
6957a982d89SJeremy L. Thompson 
6967a982d89SJeremy L. Thompson   @ref Backend
6977a982d89SJeremy L. Thompson **/
6987a982d89SJeremy L. Thompson 
699d1d35e2fSjeremylt int CeedOperatorGetNumSub(CeedOperator op, CeedInt *num_suboperators) {
7007a982d89SJeremy L. Thompson   if (!op->composite)
7017a982d89SJeremy L. Thompson     // LCOV_EXCL_START
702e15f9bd0SJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
7037a982d89SJeremy L. Thompson   // LCOV_EXCL_STOP
7047a982d89SJeremy L. Thompson 
705d1d35e2fSjeremylt   *num_suboperators = op->num_suboperators;
706e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
7077a982d89SJeremy L. Thompson }
7087a982d89SJeremy L. Thompson 
7097a982d89SJeremy L. Thompson /**
710d1d35e2fSjeremylt   @brief Get the list of sub_operators associated with a CeedOperator
7117a982d89SJeremy L. Thompson 
7127a982d89SJeremy L. Thompson   @param op                  CeedOperator
713d1d35e2fSjeremylt   @param[out] sub_operators  Variable to store list of sub_operators
7147a982d89SJeremy L. Thompson 
7157a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
7167a982d89SJeremy L. Thompson 
7177a982d89SJeremy L. Thompson   @ref Backend
7187a982d89SJeremy L. Thompson **/
7197a982d89SJeremy L. Thompson 
720d1d35e2fSjeremylt int CeedOperatorGetSubList(CeedOperator op, CeedOperator **sub_operators) {
7217a982d89SJeremy L. Thompson   if (!op->composite)
7227a982d89SJeremy L. Thompson     // LCOV_EXCL_START
723e15f9bd0SJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
7247a982d89SJeremy L. Thompson   // LCOV_EXCL_STOP
7257a982d89SJeremy L. Thompson 
726d1d35e2fSjeremylt   *sub_operators = op->sub_operators;
727e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
7287a982d89SJeremy L. Thompson }
7297a982d89SJeremy L. Thompson 
7307a982d89SJeremy L. Thompson /**
7317a982d89SJeremy L. Thompson   @brief Get the backend data of a CeedOperator
7327a982d89SJeremy L. Thompson 
7337a982d89SJeremy L. Thompson   @param op         CeedOperator
7347a982d89SJeremy L. Thompson   @param[out] data  Variable to store data
7357a982d89SJeremy L. Thompson 
7367a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
7377a982d89SJeremy L. Thompson 
7387a982d89SJeremy L. Thompson   @ref Backend
7397a982d89SJeremy L. Thompson **/
7407a982d89SJeremy L. Thompson 
741777ff853SJeremy L Thompson int CeedOperatorGetData(CeedOperator op, void *data) {
742777ff853SJeremy L Thompson   *(void **)data = op->data;
743e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
7447a982d89SJeremy L. Thompson }
7457a982d89SJeremy L. Thompson 
7467a982d89SJeremy L. Thompson /**
7477a982d89SJeremy L. Thompson   @brief Set the backend data of a CeedOperator
7487a982d89SJeremy L. Thompson 
7497a982d89SJeremy L. Thompson   @param[out] op  CeedOperator
7507a982d89SJeremy L. Thompson   @param data     Data to set
7517a982d89SJeremy L. Thompson 
7527a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
7537a982d89SJeremy L. Thompson 
7547a982d89SJeremy L. Thompson   @ref Backend
7557a982d89SJeremy L. Thompson **/
7567a982d89SJeremy L. Thompson 
757777ff853SJeremy L Thompson int CeedOperatorSetData(CeedOperator op, void *data) {
758777ff853SJeremy L Thompson   op->data = data;
759e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
7607a982d89SJeremy L. Thompson }
7617a982d89SJeremy L. Thompson 
7627a982d89SJeremy L. Thompson /**
76334359f16Sjeremylt   @brief Increment the reference counter for a CeedOperator
76434359f16Sjeremylt 
76534359f16Sjeremylt   @param op  CeedOperator to increment the reference counter
76634359f16Sjeremylt 
76734359f16Sjeremylt   @return An error code: 0 - success, otherwise - failure
76834359f16Sjeremylt 
76934359f16Sjeremylt   @ref Backend
77034359f16Sjeremylt **/
7719560d06aSjeremylt int CeedOperatorReference(CeedOperator op) {
77234359f16Sjeremylt   op->ref_count++;
77334359f16Sjeremylt   return CEED_ERROR_SUCCESS;
77434359f16Sjeremylt }
77534359f16Sjeremylt 
77634359f16Sjeremylt /**
7777a982d89SJeremy L. Thompson   @brief Set the setup flag of a CeedOperator to True
7787a982d89SJeremy L. Thompson 
7797a982d89SJeremy L. Thompson   @param op  CeedOperator
7807a982d89SJeremy L. Thompson 
7817a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
7827a982d89SJeremy L. Thompson 
7837a982d89SJeremy L. Thompson   @ref Backend
7847a982d89SJeremy L. Thompson **/
7857a982d89SJeremy L. Thompson 
7867a982d89SJeremy L. Thompson int CeedOperatorSetSetupDone(CeedOperator op) {
787d1d35e2fSjeremylt   op->backend_setup = true;
788e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
7897a982d89SJeremy L. Thompson }
7907a982d89SJeremy L. Thompson 
7917a982d89SJeremy L. Thompson /**
7927a982d89SJeremy L. Thompson   @brief Get the CeedOperatorFields of a CeedOperator
7937a982d89SJeremy L. Thompson 
7947a982d89SJeremy L. Thompson   @param op                  CeedOperator
795d1d35e2fSjeremylt   @param[out] input_fields   Variable to store input_fields
796d1d35e2fSjeremylt   @param[out] output_fields  Variable to store output_fields
7977a982d89SJeremy L. Thompson 
7987a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
7997a982d89SJeremy L. Thompson 
8007a982d89SJeremy L. Thompson   @ref Backend
8017a982d89SJeremy L. Thompson **/
8027a982d89SJeremy L. Thompson 
803d1d35e2fSjeremylt int CeedOperatorGetFields(CeedOperator op, CeedOperatorField **input_fields,
804d1d35e2fSjeremylt                           CeedOperatorField **output_fields) {
8057a982d89SJeremy L. Thompson   if (op->composite)
8067a982d89SJeremy L. Thompson     // LCOV_EXCL_START
807e15f9bd0SJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_MINOR,
808e15f9bd0SJeremy L Thompson                      "Not defined for composite operator");
8097a982d89SJeremy L. Thompson   // LCOV_EXCL_STOP
8107a982d89SJeremy L. Thompson 
811d1d35e2fSjeremylt   if (input_fields) *input_fields = op->input_fields;
812d1d35e2fSjeremylt   if (output_fields) *output_fields = op->output_fields;
813e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
8147a982d89SJeremy L. Thompson }
8157a982d89SJeremy L. Thompson 
8167a982d89SJeremy L. Thompson /**
8177a982d89SJeremy L. Thompson   @brief Get the CeedElemRestriction of a CeedOperatorField
8187a982d89SJeremy L. Thompson 
819d1d35e2fSjeremylt   @param op_field   CeedOperatorField
8207a982d89SJeremy L. Thompson   @param[out] rstr  Variable to store CeedElemRestriction
8217a982d89SJeremy L. Thompson 
8227a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
8237a982d89SJeremy L. Thompson 
8247a982d89SJeremy L. Thompson   @ref Backend
8257a982d89SJeremy L. Thompson **/
8267a982d89SJeremy L. Thompson 
827d1d35e2fSjeremylt int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field,
8287a982d89SJeremy L. Thompson                                         CeedElemRestriction *rstr) {
829d1d35e2fSjeremylt   *rstr = op_field->elem_restr;
830e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
8317a982d89SJeremy L. Thompson }
8327a982d89SJeremy L. Thompson 
8337a982d89SJeremy L. Thompson /**
8347a982d89SJeremy L. Thompson   @brief Get the CeedBasis of a CeedOperatorField
8357a982d89SJeremy L. Thompson 
836d1d35e2fSjeremylt   @param op_field    CeedOperatorField
8377a982d89SJeremy L. Thompson   @param[out] basis  Variable to store CeedBasis
8387a982d89SJeremy L. Thompson 
8397a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
8407a982d89SJeremy L. Thompson 
8417a982d89SJeremy L. Thompson   @ref Backend
8427a982d89SJeremy L. Thompson **/
8437a982d89SJeremy L. Thompson 
844d1d35e2fSjeremylt int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) {
845d1d35e2fSjeremylt   *basis = op_field->basis;
846e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
8477a982d89SJeremy L. Thompson }
8487a982d89SJeremy L. Thompson 
8497a982d89SJeremy L. Thompson /**
8507a982d89SJeremy L. Thompson   @brief Get the CeedVector of a CeedOperatorField
8517a982d89SJeremy L. Thompson 
852d1d35e2fSjeremylt   @param op_field  CeedOperatorField
8537a982d89SJeremy L. Thompson   @param[out] vec  Variable to store CeedVector
8547a982d89SJeremy L. Thompson 
8557a982d89SJeremy L. Thompson   @return An error code: 0 - success, otherwise - failure
8567a982d89SJeremy L. Thompson 
8577a982d89SJeremy L. Thompson   @ref Backend
8587a982d89SJeremy L. Thompson **/
8597a982d89SJeremy L. Thompson 
860d1d35e2fSjeremylt int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) {
861d1d35e2fSjeremylt   *vec = op_field->vec;
862e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
8637a982d89SJeremy L. Thompson }
8647a982d89SJeremy L. Thompson 
8657a982d89SJeremy L. Thompson /// @}
8667a982d89SJeremy L. Thompson 
8677a982d89SJeremy L. Thompson /// ----------------------------------------------------------------------------
8687a982d89SJeremy L. Thompson /// CeedOperator Public API
8697a982d89SJeremy L. Thompson /// ----------------------------------------------------------------------------
8707a982d89SJeremy L. Thompson /// @addtogroup CeedOperatorUser
871dfdf5a53Sjeremylt /// @{
872d7b241e6Sjeremylt 
873d7b241e6Sjeremylt /**
8740219ea01SJeremy L Thompson   @brief Create a CeedOperator and associate a CeedQFunction. A CeedBasis and
8750219ea01SJeremy L Thompson            CeedElemRestriction can be associated with CeedQFunction fields with
8760219ea01SJeremy L Thompson            \ref CeedOperatorSetField.
877d7b241e6Sjeremylt 
878b11c1e72Sjeremylt   @param ceed     A Ceed object where the CeedOperator will be created
879d7b241e6Sjeremylt   @param qf       QFunction defining the action of the operator at quadrature points
88034138859Sjeremylt   @param dqf      QFunction defining the action of the Jacobian of @a qf (or
8814cc79fe7SJed Brown                     @ref CEED_QFUNCTION_NONE)
882d7b241e6Sjeremylt   @param dqfT     QFunction defining the action of the transpose of the Jacobian
8834cc79fe7SJed Brown                     of @a qf (or @ref CEED_QFUNCTION_NONE)
884b11c1e72Sjeremylt   @param[out] op  Address of the variable where the newly created
885b11c1e72Sjeremylt                     CeedOperator will be stored
886b11c1e72Sjeremylt 
887b11c1e72Sjeremylt   @return An error code: 0 - success, otherwise - failure
888dfdf5a53Sjeremylt 
8897a982d89SJeremy L. Thompson   @ref User
890d7b241e6Sjeremylt  */
891d7b241e6Sjeremylt int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf,
892d7b241e6Sjeremylt                        CeedQFunction dqfT, CeedOperator *op) {
893d7b241e6Sjeremylt   int ierr;
894d7b241e6Sjeremylt 
8955fe0d4faSjeremylt   if (!ceed->OperatorCreate) {
8965fe0d4faSjeremylt     Ceed delegate;
897aefd8378Sjeremylt     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
8985fe0d4faSjeremylt 
8995fe0d4faSjeremylt     if (!delegate)
900c042f62fSJeremy L Thompson       // LCOV_EXCL_START
901e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
902e15f9bd0SJeremy L Thompson                        "Backend does not support OperatorCreate");
903c042f62fSJeremy L Thompson     // LCOV_EXCL_STOP
9045fe0d4faSjeremylt 
9055fe0d4faSjeremylt     ierr = CeedOperatorCreate(delegate, qf, dqf, dqfT, op); CeedChk(ierr);
906e15f9bd0SJeremy L Thompson     return CEED_ERROR_SUCCESS;
9075fe0d4faSjeremylt   }
9085fe0d4faSjeremylt 
909b3b7035fSJeremy L Thompson   if (!qf || qf == CEED_QFUNCTION_NONE)
910b3b7035fSJeremy L Thompson     // LCOV_EXCL_START
911e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_MINOR,
912e15f9bd0SJeremy L Thompson                      "Operator must have a valid QFunction.");
913b3b7035fSJeremy L Thompson   // LCOV_EXCL_STOP
914d7b241e6Sjeremylt   ierr = CeedCalloc(1, op); CeedChk(ierr);
915d7b241e6Sjeremylt   (*op)->ceed = ceed;
9169560d06aSjeremylt   ierr = CeedReference(ceed); CeedChk(ierr);
917d1d35e2fSjeremylt   (*op)->ref_count = 1;
918d7b241e6Sjeremylt   (*op)->qf = qf;
9199560d06aSjeremylt   ierr = CeedQFunctionReference(qf); CeedChk(ierr);
920442e7f0bSjeremylt   if (dqf && dqf != CEED_QFUNCTION_NONE) {
921d7b241e6Sjeremylt     (*op)->dqf = dqf;
9229560d06aSjeremylt     ierr = CeedQFunctionReference(dqf); CeedChk(ierr);
923442e7f0bSjeremylt   }
924442e7f0bSjeremylt   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
925d7b241e6Sjeremylt     (*op)->dqfT = dqfT;
9269560d06aSjeremylt     ierr = CeedQFunctionReference(dqfT); CeedChk(ierr);
927442e7f0bSjeremylt   }
928d1d35e2fSjeremylt   ierr = CeedCalloc(16, &(*op)->input_fields); CeedChk(ierr);
929d1d35e2fSjeremylt   ierr = CeedCalloc(16, &(*op)->output_fields); CeedChk(ierr);
930d7b241e6Sjeremylt   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
931e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
932d7b241e6Sjeremylt }
933d7b241e6Sjeremylt 
934d7b241e6Sjeremylt /**
93552d6035fSJeremy L Thompson   @brief Create an operator that composes the action of several operators
93652d6035fSJeremy L Thompson 
93752d6035fSJeremy L Thompson   @param ceed     A Ceed object where the CeedOperator will be created
93852d6035fSJeremy L Thompson   @param[out] op  Address of the variable where the newly created
93952d6035fSJeremy L Thompson                     Composite CeedOperator will be stored
94052d6035fSJeremy L Thompson 
94152d6035fSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
94252d6035fSJeremy L Thompson 
9437a982d89SJeremy L. Thompson   @ref User
94452d6035fSJeremy L Thompson  */
94552d6035fSJeremy L Thompson int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
94652d6035fSJeremy L Thompson   int ierr;
94752d6035fSJeremy L Thompson 
94852d6035fSJeremy L Thompson   if (!ceed->CompositeOperatorCreate) {
94952d6035fSJeremy L Thompson     Ceed delegate;
950aefd8378Sjeremylt     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
95152d6035fSJeremy L Thompson 
952250756a7Sjeremylt     if (delegate) {
95352d6035fSJeremy L Thompson       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
954e15f9bd0SJeremy L Thompson       return CEED_ERROR_SUCCESS;
95552d6035fSJeremy L Thompson     }
956250756a7Sjeremylt   }
95752d6035fSJeremy L Thompson 
95852d6035fSJeremy L Thompson   ierr = CeedCalloc(1, op); CeedChk(ierr);
95952d6035fSJeremy L Thompson   (*op)->ceed = ceed;
9609560d06aSjeremylt   ierr = CeedReference(ceed); CeedChk(ierr);
96152d6035fSJeremy L Thompson   (*op)->composite = true;
962d1d35e2fSjeremylt   ierr = CeedCalloc(16, &(*op)->sub_operators); CeedChk(ierr);
963250756a7Sjeremylt 
964250756a7Sjeremylt   if (ceed->CompositeOperatorCreate) {
96552d6035fSJeremy L Thompson     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
966250756a7Sjeremylt   }
967e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
96852d6035fSJeremy L Thompson }
96952d6035fSJeremy L Thompson 
97052d6035fSJeremy L Thompson /**
9719560d06aSjeremylt   @brief Copy the pointer to a CeedOperator. Both pointers should
9729560d06aSjeremylt            be destroyed with `CeedOperatorDestroy()`;
9739560d06aSjeremylt            Note: If `*op_copy` is non-NULL, then it is assumed that
9749560d06aSjeremylt            `*op_copy` is a pointer to a CeedOperator. This
9759560d06aSjeremylt            CeedOperator will be destroyed if `*op_copy` is the only
9769560d06aSjeremylt            reference to this CeedOperator.
9779560d06aSjeremylt 
9789560d06aSjeremylt   @param op            CeedOperator to copy reference to
9799560d06aSjeremylt   @param[out] op_copy  Variable to store copied reference
9809560d06aSjeremylt 
9819560d06aSjeremylt   @return An error code: 0 - success, otherwise - failure
9829560d06aSjeremylt 
9839560d06aSjeremylt   @ref User
9849560d06aSjeremylt **/
9859560d06aSjeremylt int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) {
9869560d06aSjeremylt   int ierr;
9879560d06aSjeremylt 
9889560d06aSjeremylt   ierr = CeedOperatorReference(op); CeedChk(ierr);
9899560d06aSjeremylt   ierr = CeedOperatorDestroy(op_copy); CeedChk(ierr);
9909560d06aSjeremylt   *op_copy = op;
9919560d06aSjeremylt   return CEED_ERROR_SUCCESS;
9929560d06aSjeremylt }
9939560d06aSjeremylt 
9949560d06aSjeremylt /**
995b11c1e72Sjeremylt   @brief Provide a field to a CeedOperator for use by its CeedQFunction
996d7b241e6Sjeremylt 
997d7b241e6Sjeremylt   This function is used to specify both active and passive fields to a
998d7b241e6Sjeremylt   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
999d7b241e6Sjeremylt   fields can inputs or outputs (updated in-place when operator is applied).
1000d7b241e6Sjeremylt 
1001d7b241e6Sjeremylt   Active fields must be specified using this function, but their data (in a
1002d7b241e6Sjeremylt   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
1003d7b241e6Sjeremylt   input and at most one active output.
1004d7b241e6Sjeremylt 
10058c91a0c9SJeremy L Thompson   @param op          CeedOperator on which to provide the field
1006d1d35e2fSjeremylt   @param field_name  Name of the field (to be matched with the name used by
10078795c945Sjeremylt                        CeedQFunction)
1008b11c1e72Sjeremylt   @param r           CeedElemRestriction
10094cc79fe7SJed Brown   @param b           CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED
1010b11c1e72Sjeremylt                        if collocated with quadrature points
10114cc79fe7SJed Brown   @param v           CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE
10124cc79fe7SJed Brown                        if field is active or @ref CEED_VECTOR_NONE if using
10134cc79fe7SJed Brown                        @ref CEED_EVAL_WEIGHT in the QFunction
1014b11c1e72Sjeremylt 
1015b11c1e72Sjeremylt   @return An error code: 0 - success, otherwise - failure
1016dfdf5a53Sjeremylt 
10177a982d89SJeremy L. Thompson   @ref User
1018b11c1e72Sjeremylt **/
1019d1d35e2fSjeremylt int CeedOperatorSetField(CeedOperator op, const char *field_name,
1020a8d32208Sjeremylt                          CeedElemRestriction r, CeedBasis b, CeedVector v) {
1021d7b241e6Sjeremylt   int ierr;
102252d6035fSJeremy L Thompson   if (op->composite)
1023c042f62fSJeremy L Thompson     // LCOV_EXCL_START
1024e1e9e29dSJed Brown     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1025e15f9bd0SJeremy L Thompson                      "Cannot add field to composite operator.");
1026c042f62fSJeremy L Thompson   // LCOV_EXCL_STOP
10278b067b84SJed Brown   if (!r)
1028c042f62fSJeremy L Thompson     // LCOV_EXCL_START
1029e1e9e29dSJed Brown     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1030c042f62fSJeremy L Thompson                      "ElemRestriction r for field \"%s\" must be non-NULL.",
1031d1d35e2fSjeremylt                      field_name);
1032c042f62fSJeremy L Thompson   // LCOV_EXCL_STOP
10338b067b84SJed Brown   if (!b)
1034c042f62fSJeremy L Thompson     // LCOV_EXCL_START
1035e1e9e29dSJed Brown     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1036e15f9bd0SJeremy L Thompson                      "Basis b for field \"%s\" must be non-NULL.",
1037d1d35e2fSjeremylt                      field_name);
1038c042f62fSJeremy L Thompson   // LCOV_EXCL_STOP
10398b067b84SJed Brown   if (!v)
1040c042f62fSJeremy L Thompson     // LCOV_EXCL_START
1041e1e9e29dSJed Brown     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1042e15f9bd0SJeremy L Thompson                      "Vector v for field \"%s\" must be non-NULL.",
1043d1d35e2fSjeremylt                      field_name);
1044c042f62fSJeremy L Thompson   // LCOV_EXCL_STOP
104552d6035fSJeremy L Thompson 
1046d1d35e2fSjeremylt   CeedInt num_elem;
1047d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumElements(r, &num_elem); CeedChk(ierr);
1048d1d35e2fSjeremylt   if (r != CEED_ELEMRESTRICTION_NONE && op->has_restriction &&
1049d1d35e2fSjeremylt       op->num_elem != num_elem)
1050c042f62fSJeremy L Thompson     // LCOV_EXCL_START
1051e15f9bd0SJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_DIMENSION,
10521d102b48SJeremy L Thompson                      "ElemRestriction with %d elements incompatible with prior "
1053d1d35e2fSjeremylt                      "%d elements", num_elem, op->num_elem);
1054c042f62fSJeremy L Thompson   // LCOV_EXCL_STOP
1055d7b241e6Sjeremylt 
105678464608Sjeremylt   CeedInt num_qpts = 0;
1057e15f9bd0SJeremy L Thompson   if (b != CEED_BASIS_COLLOCATED) {
1058d1d35e2fSjeremylt     ierr = CeedBasisGetNumQuadraturePoints(b, &num_qpts); CeedChk(ierr);
1059d1d35e2fSjeremylt     if (op->num_qpts && op->num_qpts != num_qpts)
1060c042f62fSJeremy L Thompson       // LCOV_EXCL_START
1061e15f9bd0SJeremy L Thompson       return CeedError(op->ceed, CEED_ERROR_DIMENSION,
1062e15f9bd0SJeremy L Thompson                        "Basis with %d quadrature points "
1063d1d35e2fSjeremylt                        "incompatible with prior %d points", num_qpts,
1064d1d35e2fSjeremylt                        op->num_qpts);
1065c042f62fSJeremy L Thompson     // LCOV_EXCL_STOP
1066d7b241e6Sjeremylt   }
1067d1d35e2fSjeremylt   CeedQFunctionField qf_field;
1068d1d35e2fSjeremylt   CeedOperatorField *op_field;
1069d1d35e2fSjeremylt   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
1070d1d35e2fSjeremylt     if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) {
1071d1d35e2fSjeremylt       qf_field = op->qf->input_fields[i];
1072d1d35e2fSjeremylt       op_field = &op->input_fields[i];
1073d7b241e6Sjeremylt       goto found;
1074d7b241e6Sjeremylt     }
1075d7b241e6Sjeremylt   }
1076d1d35e2fSjeremylt   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
1077d1d35e2fSjeremylt     if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) {
1078d1d35e2fSjeremylt       qf_field = op->qf->output_fields[i];
1079d1d35e2fSjeremylt       op_field = &op->output_fields[i];
1080d7b241e6Sjeremylt       goto found;
1081d7b241e6Sjeremylt     }
1082d7b241e6Sjeremylt   }
1083c042f62fSJeremy L Thompson   // LCOV_EXCL_START
1084e15f9bd0SJeremy L Thompson   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE,
1085e15f9bd0SJeremy L Thompson                    "QFunction has no knowledge of field '%s'",
1086d1d35e2fSjeremylt                    field_name);
1087c042f62fSJeremy L Thompson   // LCOV_EXCL_STOP
1088d7b241e6Sjeremylt found:
1089d1d35e2fSjeremylt   ierr = CeedOperatorCheckField(op->ceed, qf_field, r, b); CeedChk(ierr);
1090d1d35e2fSjeremylt   ierr = CeedCalloc(1, op_field); CeedChk(ierr);
1091e15f9bd0SJeremy L Thompson 
1092d1d35e2fSjeremylt   (*op_field)->vec = v;
1093e15f9bd0SJeremy L Thompson   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE) {
10949560d06aSjeremylt     ierr = CeedVectorReference(v); CeedChk(ierr);
1095e15f9bd0SJeremy L Thompson   }
1096e15f9bd0SJeremy L Thompson 
1097d1d35e2fSjeremylt   (*op_field)->elem_restr = r;
10989560d06aSjeremylt   ierr = CeedElemRestrictionReference(r); CeedChk(ierr);
1099e15f9bd0SJeremy L Thompson   if (r != CEED_ELEMRESTRICTION_NONE) {
1100d1d35e2fSjeremylt     op->num_elem = num_elem;
1101d1d35e2fSjeremylt     op->has_restriction = true; // Restriction set, but num_elem may be 0
1102e15f9bd0SJeremy L Thompson   }
1103d99fa3c5SJeremy L Thompson 
1104d1d35e2fSjeremylt   (*op_field)->basis = b;
1105e15f9bd0SJeremy L Thompson   if (b != CEED_BASIS_COLLOCATED) {
1106cd4dfc48Sjeremylt     if (!op->num_qpts) {
1107cd4dfc48Sjeremylt       ierr = CeedOperatorSetNumQuadraturePoints(op, num_qpts); CeedChk(ierr);
1108cd4dfc48Sjeremylt     }
11099560d06aSjeremylt     ierr = CeedBasisReference(b); CeedChk(ierr);
1110e15f9bd0SJeremy L Thompson   }
1111e15f9bd0SJeremy L Thompson 
1112d1d35e2fSjeremylt   op->num_fields += 1;
1113d1d35e2fSjeremylt   size_t len = strlen(field_name);
1114d99fa3c5SJeremy L Thompson   char *tmp;
1115d99fa3c5SJeremy L Thompson   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
1116d1d35e2fSjeremylt   memcpy(tmp, field_name, len+1);
1117d1d35e2fSjeremylt   (*op_field)->field_name = tmp;
1118e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1119d7b241e6Sjeremylt }
1120d7b241e6Sjeremylt 
1121d7b241e6Sjeremylt /**
112252d6035fSJeremy L Thompson   @brief Add a sub-operator to a composite CeedOperator
1123288c0443SJeremy L Thompson 
1124d1d35e2fSjeremylt   @param[out] composite_op  Composite CeedOperator
1125d1d35e2fSjeremylt   @param      sub_op        Sub-operator CeedOperator
112652d6035fSJeremy L Thompson 
112752d6035fSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
112852d6035fSJeremy L Thompson 
11297a982d89SJeremy L. Thompson   @ref User
113052d6035fSJeremy L Thompson  */
1131d1d35e2fSjeremylt int CeedCompositeOperatorAddSub(CeedOperator composite_op,
1132d1d35e2fSjeremylt                                 CeedOperator sub_op) {
113334359f16Sjeremylt   int ierr;
113434359f16Sjeremylt 
1135d1d35e2fSjeremylt   if (!composite_op->composite)
1136c042f62fSJeremy L Thompson     // LCOV_EXCL_START
1137d1d35e2fSjeremylt     return CeedError(composite_op->ceed, CEED_ERROR_MINOR,
1138e2f04181SAndrew T. Barker                      "CeedOperator is not a composite operator");
1139c042f62fSJeremy L Thompson   // LCOV_EXCL_STOP
114052d6035fSJeremy L Thompson 
1141d1d35e2fSjeremylt   if (composite_op->num_suboperators == CEED_COMPOSITE_MAX)
1142c042f62fSJeremy L Thompson     // LCOV_EXCL_START
1143d1d35e2fSjeremylt     return CeedError(composite_op->ceed, CEED_ERROR_UNSUPPORTED,
1144d1d35e2fSjeremylt                      "Cannot add additional sub_operators");
1145c042f62fSJeremy L Thompson   // LCOV_EXCL_STOP
114652d6035fSJeremy L Thompson 
1147d1d35e2fSjeremylt   composite_op->sub_operators[composite_op->num_suboperators] = sub_op;
11489560d06aSjeremylt   ierr = CeedOperatorReference(sub_op); CeedChk(ierr);
1149d1d35e2fSjeremylt   composite_op->num_suboperators++;
1150e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
115152d6035fSJeremy L Thompson }
115252d6035fSJeremy L Thompson 
115352d6035fSJeremy L Thompson /**
11541d102b48SJeremy L Thompson   @brief Assemble a linear CeedQFunction associated with a CeedOperator
11551d102b48SJeremy L Thompson 
11561d102b48SJeremy L Thompson   This returns a CeedVector containing a matrix at each quadrature point
11571d102b48SJeremy L Thompson     providing the action of the CeedQFunction associated with the CeedOperator.
11581d102b48SJeremy L Thompson     The vector 'assembled' is of shape
11591d102b48SJeremy L Thompson       [num_elements, num_input_fields, num_output_fields, num_quad_points]
11601d102b48SJeremy L Thompson     and contains column-major matrices representing the action of the
11611d102b48SJeremy L Thompson     CeedQFunction for a corresponding quadrature point on an element. Inputs and
11621d102b48SJeremy L Thompson     outputs are in the order provided by the user when adding CeedOperator fields.
11634cc79fe7SJed Brown     For example, a CeedQFunction with inputs 'u' and 'gradu' and outputs 'gradv' and
11641d102b48SJeremy L Thompson     'v', provided in that order, would result in an assembled QFunction that
11651d102b48SJeremy L Thompson     consists of (1 + dim) x (dim + 1) matrices at each quadrature point acting
11661d102b48SJeremy L Thompson     on the input [u, du_0, du_1] and producing the output [dv_0, dv_1, v].
11671d102b48SJeremy L Thompson 
11681d102b48SJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
11691d102b48SJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedQFunction at
11701d102b48SJeremy L Thompson                            quadrature points
11711d102b48SJeremy L Thompson   @param[out] rstr       CeedElemRestriction for CeedVector containing assembled
11721d102b48SJeremy L Thompson                            CeedQFunction
11731d102b48SJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
11744cc79fe7SJed Brown                            @ref CEED_REQUEST_IMMEDIATE
11751d102b48SJeremy L Thompson 
11761d102b48SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
11771d102b48SJeremy L Thompson 
11787a982d89SJeremy L. Thompson   @ref User
11791d102b48SJeremy L Thompson **/
118080ac2e43SJeremy L Thompson int CeedOperatorLinearAssembleQFunction(CeedOperator op, CeedVector *assembled,
11817f823360Sjeremylt                                         CeedElemRestriction *rstr,
11827f823360Sjeremylt                                         CeedRequest *request) {
11831d102b48SJeremy L Thompson   int ierr;
1184e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
11851d102b48SJeremy L Thompson 
11867172caadSjeremylt   // Backend version
118780ac2e43SJeremy L Thompson   if (op->LinearAssembleQFunction) {
1188e2f04181SAndrew T. Barker     return op->LinearAssembleQFunction(op, assembled, rstr, request);
11895107b09fSJeremy L Thompson   } else {
11905107b09fSJeremy L Thompson     // Fallback to reference Ceed
1191d1d35e2fSjeremylt     if (!op->op_fallback) {
11925107b09fSJeremy L Thompson       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
11935107b09fSJeremy L Thompson     }
11945107b09fSJeremy L Thompson     // Assemble
1195d1d35e2fSjeremylt     return CeedOperatorLinearAssembleQFunction(op->op_fallback, assembled,
1196e2f04181SAndrew T. Barker            rstr, request);
11975107b09fSJeremy L Thompson   }
11981d102b48SJeremy L Thompson }
11991d102b48SJeremy L Thompson 
12001d102b48SJeremy L Thompson /**
1201d965c7a7SJeremy L Thompson   @brief Assemble the diagonal of a square linear CeedOperator
1202b7ec98d8SJeremy L Thompson 
12039e9210b8SJeremy L Thompson   This overwrites a CeedVector with the diagonal of a linear CeedOperator.
1204b7ec98d8SJeremy L Thompson 
1205c04a41a7SJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and
1206c04a41a7SJeremy L Thompson           composite CeedOperators with single field sub-operators are supported.
1207d965c7a7SJeremy L Thompson 
1208b7ec98d8SJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
1209b7ec98d8SJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedOperator diagonal
1210b7ec98d8SJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
12114cc79fe7SJed Brown                            @ref CEED_REQUEST_IMMEDIATE
1212b7ec98d8SJeremy L Thompson 
1213b7ec98d8SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1214b7ec98d8SJeremy L Thompson 
12157a982d89SJeremy L. Thompson   @ref User
1216b7ec98d8SJeremy L Thompson **/
12172bba3ffaSJeremy L Thompson int CeedOperatorLinearAssembleDiagonal(CeedOperator op, CeedVector assembled,
12187f823360Sjeremylt                                        CeedRequest *request) {
1219b7ec98d8SJeremy L Thompson   int ierr;
1220e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1221b7ec98d8SJeremy L Thompson 
1222b7ec98d8SJeremy L Thompson   // Use backend version, if available
122380ac2e43SJeremy L Thompson   if (op->LinearAssembleDiagonal) {
1224e2f04181SAndrew T. Barker     return op->LinearAssembleDiagonal(op, assembled, request);
12259e9210b8SJeremy L Thompson   } else if (op->LinearAssembleAddDiagonal) {
12269e9210b8SJeremy L Thompson     ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
12279e9210b8SJeremy L Thompson     return CeedOperatorLinearAssembleAddDiagonal(op, assembled, request);
12289e9210b8SJeremy L Thompson   } else {
12299e9210b8SJeremy L Thompson     // Fallback to reference Ceed
1230d1d35e2fSjeremylt     if (!op->op_fallback) {
12319e9210b8SJeremy L Thompson       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
12329e9210b8SJeremy L Thompson     }
12339e9210b8SJeremy L Thompson     // Assemble
1234d1d35e2fSjeremylt     return CeedOperatorLinearAssembleDiagonal(op->op_fallback, assembled,
1235e2f04181SAndrew T. Barker            request);
12369e9210b8SJeremy L Thompson   }
12379e9210b8SJeremy L Thompson }
12389e9210b8SJeremy L Thompson 
12399e9210b8SJeremy L Thompson /**
12409e9210b8SJeremy L Thompson   @brief Assemble the diagonal of a square linear CeedOperator
12419e9210b8SJeremy L Thompson 
12429e9210b8SJeremy L Thompson   This sums into a CeedVector the diagonal of a linear CeedOperator.
12439e9210b8SJeremy L Thompson 
12449e9210b8SJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and
12459e9210b8SJeremy L Thompson           composite CeedOperators with single field sub-operators are supported.
12469e9210b8SJeremy L Thompson 
12479e9210b8SJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
12489e9210b8SJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedOperator diagonal
12499e9210b8SJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
12509e9210b8SJeremy L Thompson                            @ref CEED_REQUEST_IMMEDIATE
12519e9210b8SJeremy L Thompson 
12529e9210b8SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
12539e9210b8SJeremy L Thompson 
12549e9210b8SJeremy L Thompson   @ref User
12559e9210b8SJeremy L Thompson **/
12569e9210b8SJeremy L Thompson int CeedOperatorLinearAssembleAddDiagonal(CeedOperator op, CeedVector assembled,
12579e9210b8SJeremy L Thompson     CeedRequest *request) {
12589e9210b8SJeremy L Thompson   int ierr;
1259e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
12609e9210b8SJeremy L Thompson 
12619e9210b8SJeremy L Thompson   // Use backend version, if available
12629e9210b8SJeremy L Thompson   if (op->LinearAssembleAddDiagonal) {
1263e2f04181SAndrew T. Barker     return op->LinearAssembleAddDiagonal(op, assembled, request);
12647172caadSjeremylt   } else {
12657172caadSjeremylt     // Fallback to reference Ceed
1266d1d35e2fSjeremylt     if (!op->op_fallback) {
12677172caadSjeremylt       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1268b7ec98d8SJeremy L Thompson     }
12697172caadSjeremylt     // Assemble
1270d1d35e2fSjeremylt     return CeedOperatorLinearAssembleAddDiagonal(op->op_fallback, assembled,
1271e2f04181SAndrew T. Barker            request);
1272b7ec98d8SJeremy L Thompson   }
1273b7ec98d8SJeremy L Thompson }
1274b7ec98d8SJeremy L Thompson 
1275b7ec98d8SJeremy L Thompson /**
1276d965c7a7SJeremy L Thompson   @brief Assemble the point block diagonal of a square linear CeedOperator
1277d965c7a7SJeremy L Thompson 
12789e9210b8SJeremy L Thompson   This overwrites a CeedVector with the point block diagonal of a linear
1279d965c7a7SJeremy L Thompson     CeedOperator.
1280d965c7a7SJeremy L Thompson 
1281c04a41a7SJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and
1282c04a41a7SJeremy L Thompson           composite CeedOperators with single field sub-operators are supported.
1283d965c7a7SJeremy L Thompson 
1284d965c7a7SJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
1285d965c7a7SJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedOperator point block
1286d965c7a7SJeremy L Thompson                            diagonal, provided in row-major form with an
1287d1d35e2fSjeremylt                            @a num_comp * @a num_comp block at each node. The dimensions
1288d965c7a7SJeremy L Thompson                            of this vector are derived from the active vector
1289d965c7a7SJeremy L Thompson                            for the CeedOperator. The array has shape
1290d965c7a7SJeremy L Thompson                            [nodes, component out, component in].
1291d965c7a7SJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
1292d965c7a7SJeremy L Thompson                            CEED_REQUEST_IMMEDIATE
1293d965c7a7SJeremy L Thompson 
1294d965c7a7SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1295d965c7a7SJeremy L Thompson 
1296d965c7a7SJeremy L Thompson   @ref User
1297d965c7a7SJeremy L Thompson **/
129880ac2e43SJeremy L Thompson int CeedOperatorLinearAssemblePointBlockDiagonal(CeedOperator op,
12992bba3ffaSJeremy L Thompson     CeedVector assembled, CeedRequest *request) {
1300d965c7a7SJeremy L Thompson   int ierr;
1301e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1302d965c7a7SJeremy L Thompson 
1303d965c7a7SJeremy L Thompson   // Use backend version, if available
130480ac2e43SJeremy L Thompson   if (op->LinearAssemblePointBlockDiagonal) {
1305e2f04181SAndrew T. Barker     return op->LinearAssemblePointBlockDiagonal(op, assembled, request);
13069e9210b8SJeremy L Thompson   } else if (op->LinearAssembleAddPointBlockDiagonal) {
13079e9210b8SJeremy L Thompson     ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
13089e9210b8SJeremy L Thompson     return CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled,
13099e9210b8SJeremy L Thompson            request);
1310d965c7a7SJeremy L Thompson   } else {
1311d965c7a7SJeremy L Thompson     // Fallback to reference Ceed
1312d1d35e2fSjeremylt     if (!op->op_fallback) {
1313d965c7a7SJeremy L Thompson       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1314d965c7a7SJeremy L Thompson     }
1315d965c7a7SJeremy L Thompson     // Assemble
1316d1d35e2fSjeremylt     return CeedOperatorLinearAssemblePointBlockDiagonal(op->op_fallback,
1317e2f04181SAndrew T. Barker            assembled, request);
13189e9210b8SJeremy L Thompson   }
13199e9210b8SJeremy L Thompson }
13209e9210b8SJeremy L Thompson 
13219e9210b8SJeremy L Thompson /**
13229e9210b8SJeremy L Thompson   @brief Assemble the point block diagonal of a square linear CeedOperator
13239e9210b8SJeremy L Thompson 
13249e9210b8SJeremy L Thompson   This sums into a CeedVector with the point block diagonal of a linear
13259e9210b8SJeremy L Thompson     CeedOperator.
13269e9210b8SJeremy L Thompson 
13279e9210b8SJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and
13289e9210b8SJeremy L Thompson           composite CeedOperators with single field sub-operators are supported.
13299e9210b8SJeremy L Thompson 
13309e9210b8SJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
13319e9210b8SJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedOperator point block
13329e9210b8SJeremy L Thompson                            diagonal, provided in row-major form with an
1333d1d35e2fSjeremylt                            @a num_comp * @a num_comp block at each node. The dimensions
13349e9210b8SJeremy L Thompson                            of this vector are derived from the active vector
13359e9210b8SJeremy L Thompson                            for the CeedOperator. The array has shape
13369e9210b8SJeremy L Thompson                            [nodes, component out, component in].
13379e9210b8SJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
13389e9210b8SJeremy L Thompson                            CEED_REQUEST_IMMEDIATE
13399e9210b8SJeremy L Thompson 
13409e9210b8SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
13419e9210b8SJeremy L Thompson 
13429e9210b8SJeremy L Thompson   @ref User
13439e9210b8SJeremy L Thompson **/
13449e9210b8SJeremy L Thompson int CeedOperatorLinearAssembleAddPointBlockDiagonal(CeedOperator op,
13459e9210b8SJeremy L Thompson     CeedVector assembled, CeedRequest *request) {
13469e9210b8SJeremy L Thompson   int ierr;
1347e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
13489e9210b8SJeremy L Thompson 
13499e9210b8SJeremy L Thompson   // Use backend version, if available
13509e9210b8SJeremy L Thompson   if (op->LinearAssembleAddPointBlockDiagonal) {
1351e2f04181SAndrew T. Barker     return op->LinearAssembleAddPointBlockDiagonal(op, assembled, request);
13529e9210b8SJeremy L Thompson   } else {
13539e9210b8SJeremy L Thompson     // Fallback to reference Ceed
1354d1d35e2fSjeremylt     if (!op->op_fallback) {
13559e9210b8SJeremy L Thompson       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
13569e9210b8SJeremy L Thompson     }
13579e9210b8SJeremy L Thompson     // Assemble
1358d1d35e2fSjeremylt     return CeedOperatorLinearAssembleAddPointBlockDiagonal(op->op_fallback,
1359e2f04181SAndrew T. Barker            assembled, request);
1360d965c7a7SJeremy L Thompson   }
1361e2f04181SAndrew T. Barker }
1362e2f04181SAndrew T. Barker 
1363e2f04181SAndrew T. Barker /**
1364e2f04181SAndrew T. Barker    @brief Build nonzero pattern for non-composite operator.
1365e2f04181SAndrew T. Barker 
1366e2f04181SAndrew T. Barker    Users should generally use CeedOperatorLinearAssembleSymbolic()
1367e2f04181SAndrew T. Barker 
1368e2f04181SAndrew T. Barker    @ref Developer
1369e2f04181SAndrew T. Barker **/
1370e2f04181SAndrew T. Barker int CeedSingleOperatorAssembleSymbolic(CeedOperator op, CeedInt offset,
1371e2f04181SAndrew T. Barker                                        CeedInt *rows, CeedInt *cols) {
1372e2f04181SAndrew T. Barker   int ierr;
1373e2f04181SAndrew T. Barker   Ceed ceed = op->ceed;
1374e2f04181SAndrew T. Barker   if (op->composite)
1375e2f04181SAndrew T. Barker     // LCOV_EXCL_START
1376e2f04181SAndrew T. Barker     return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
1377e2f04181SAndrew T. Barker                      "Composite operator not supported");
1378e2f04181SAndrew T. Barker   // LCOV_EXCL_STOP
1379e2f04181SAndrew T. Barker 
1380d1d35e2fSjeremylt   CeedElemRestriction rstr_in;
1381d1d35e2fSjeremylt   ierr = CeedOperatorGetActiveElemRestriction(op, &rstr_in); CeedChk(ierr);
1382d1d35e2fSjeremylt   CeedInt num_elem, elem_size, num_nodes, num_comp;
1383d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumElements(rstr_in, &num_elem); CeedChk(ierr);
1384d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetElementSize(rstr_in, &elem_size); CeedChk(ierr);
1385d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetLVectorSize(rstr_in, &num_nodes); CeedChk(ierr);
1386d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumComponents(rstr_in, &num_comp); CeedChk(ierr);
1387e2f04181SAndrew T. Barker   CeedInt layout_er[3];
1388d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetELayout(rstr_in, &layout_er); CeedChk(ierr);
1389e2f04181SAndrew T. Barker 
1390d1d35e2fSjeremylt   CeedInt local_num_entries = elem_size*num_comp * elem_size*num_comp * num_elem;
1391e2f04181SAndrew T. Barker 
1392e2f04181SAndrew T. Barker   // Determine elem_dof relation
1393e2f04181SAndrew T. Barker   CeedVector index_vec;
1394d1d35e2fSjeremylt   ierr = CeedVectorCreate(ceed, num_nodes, &index_vec); CeedChk(ierr);
1395e2f04181SAndrew T. Barker   CeedScalar *array;
1396e2f04181SAndrew T. Barker   ierr = CeedVectorGetArray(index_vec, CEED_MEM_HOST, &array); CeedChk(ierr);
1397d1d35e2fSjeremylt   for (CeedInt i = 0; i < num_nodes; ++i) {
1398e2f04181SAndrew T. Barker     array[i] = i;
1399e2f04181SAndrew T. Barker   }
1400e2f04181SAndrew T. Barker   ierr = CeedVectorRestoreArray(index_vec, &array); CeedChk(ierr);
1401e2f04181SAndrew T. Barker   CeedVector elem_dof;
1402d1d35e2fSjeremylt   ierr = CeedVectorCreate(ceed, num_elem * elem_size * num_comp, &elem_dof);
1403e2f04181SAndrew T. Barker   CeedChk(ierr);
1404e2f04181SAndrew T. Barker   ierr = CeedVectorSetValue(elem_dof, 0.0); CeedChk(ierr);
1405d1d35e2fSjeremylt   CeedElemRestrictionApply(rstr_in, CEED_NOTRANSPOSE, index_vec,
1406e2f04181SAndrew T. Barker                            elem_dof, CEED_REQUEST_IMMEDIATE); CeedChk(ierr);
1407e2f04181SAndrew T. Barker   const CeedScalar *elem_dof_a;
1408e2f04181SAndrew T. Barker   ierr = CeedVectorGetArrayRead(elem_dof, CEED_MEM_HOST, &elem_dof_a);
1409e2f04181SAndrew T. Barker   CeedChk(ierr);
1410e2f04181SAndrew T. Barker   ierr = CeedVectorDestroy(&index_vec); CeedChk(ierr);
1411e2f04181SAndrew T. Barker 
1412e2f04181SAndrew T. Barker   // Determine i, j locations for element matrices
1413e2f04181SAndrew T. Barker   CeedInt count = 0;
1414d1d35e2fSjeremylt   for (int e = 0; e < num_elem; ++e) {
1415d1d35e2fSjeremylt     for (int comp_in = 0; comp_in < num_comp; ++comp_in) {
1416d1d35e2fSjeremylt       for (int comp_out = 0; comp_out < num_comp; ++comp_out) {
1417d1d35e2fSjeremylt         for (int i = 0; i < elem_size; ++i) {
1418d1d35e2fSjeremylt           for (int j = 0; j < elem_size; ++j) {
1419d1d35e2fSjeremylt             const CeedInt elem_dof_index_row = (i)*layout_er[0] +
1420d1d35e2fSjeremylt                                                (comp_out)*layout_er[1] + e*layout_er[2];
1421d1d35e2fSjeremylt             const CeedInt elem_dof_index_col = (j)*layout_er[0] +
1422d1d35e2fSjeremylt                                                (comp_in)*layout_er[1] + e*layout_er[2];
1423e2f04181SAndrew T. Barker 
1424d1d35e2fSjeremylt             const CeedInt row = elem_dof_a[elem_dof_index_row];
1425d1d35e2fSjeremylt             const CeedInt col = elem_dof_a[elem_dof_index_col];
1426e2f04181SAndrew T. Barker 
1427e2f04181SAndrew T. Barker             rows[offset + count] = row;
1428e2f04181SAndrew T. Barker             cols[offset + count] = col;
1429e2f04181SAndrew T. Barker             count++;
1430e2f04181SAndrew T. Barker           }
1431e2f04181SAndrew T. Barker         }
1432e2f04181SAndrew T. Barker       }
1433e2f04181SAndrew T. Barker     }
1434e2f04181SAndrew T. Barker   }
1435d1d35e2fSjeremylt   if (count != local_num_entries)
1436e2f04181SAndrew T. Barker     // LCOV_EXCL_START
1437e2f04181SAndrew T. Barker     return CeedError(ceed, CEED_ERROR_MAJOR, "Error computing assembled entries");
1438e2f04181SAndrew T. Barker   // LCOV_EXCL_STOP
1439e2f04181SAndrew T. Barker   ierr = CeedVectorRestoreArrayRead(elem_dof, &elem_dof_a); CeedChk(ierr);
1440e2f04181SAndrew T. Barker   ierr = CeedVectorDestroy(&elem_dof); CeedChk(ierr);
1441e2f04181SAndrew T. Barker 
1442e2f04181SAndrew T. Barker   return CEED_ERROR_SUCCESS;
1443e2f04181SAndrew T. Barker }
1444e2f04181SAndrew T. Barker 
1445e2f04181SAndrew T. Barker /**
1446e2f04181SAndrew T. Barker    @brief Assemble nonzero entries for non-composite operator
1447e2f04181SAndrew T. Barker 
1448e2f04181SAndrew T. Barker    Users should generally use CeedOperatorLinearAssemble()
1449e2f04181SAndrew T. Barker 
1450e2f04181SAndrew T. Barker    @ref Developer
1451e2f04181SAndrew T. Barker **/
1452e2f04181SAndrew T. Barker int CeedSingleOperatorAssemble(CeedOperator op, CeedInt offset,
1453e2f04181SAndrew T. Barker                                CeedVector values) {
1454e2f04181SAndrew T. Barker   int ierr;
1455e2f04181SAndrew T. Barker   Ceed ceed = op->ceed;;
1456e2f04181SAndrew T. Barker   if (op->composite)
1457e2f04181SAndrew T. Barker     // LCOV_EXCL_START
1458e2f04181SAndrew T. Barker     return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
1459e2f04181SAndrew T. Barker                      "Composite operator not supported");
1460e2f04181SAndrew T. Barker   // LCOV_EXCL_STOP
1461e2f04181SAndrew T. Barker 
1462e2f04181SAndrew T. Barker   // Assemble QFunction
1463e2f04181SAndrew T. Barker   CeedQFunction qf;
1464e2f04181SAndrew T. Barker   ierr = CeedOperatorGetQFunction(op, &qf); CeedChk(ierr);
1465d1d35e2fSjeremylt   CeedInt num_input_fields, num_output_fields;
1466d1d35e2fSjeremylt   ierr= CeedQFunctionGetNumArgs(qf, &num_input_fields, &num_output_fields);
1467e2f04181SAndrew T. Barker   CeedChk(ierr);
1468d1d35e2fSjeremylt   CeedVector assembled_qf;
1469e2f04181SAndrew T. Barker   CeedElemRestriction rstr_q;
1470e2f04181SAndrew T. Barker   ierr = CeedOperatorLinearAssembleQFunction(
1471d1d35e2fSjeremylt            op, &assembled_qf, &rstr_q, CEED_REQUEST_IMMEDIATE); CeedChk(ierr);
1472e2f04181SAndrew T. Barker 
1473d1d35e2fSjeremylt   CeedInt qf_length;
1474d1d35e2fSjeremylt   ierr = CeedVectorGetLength(assembled_qf, &qf_length); CeedChk(ierr);
1475e2f04181SAndrew T. Barker 
1476e2f04181SAndrew T. Barker   CeedOperatorField *input_fields;
1477e2f04181SAndrew T. Barker   CeedOperatorField *output_fields;
1478e2f04181SAndrew T. Barker   ierr = CeedOperatorGetFields(op, &input_fields, &output_fields); CeedChk(ierr);
1479e2f04181SAndrew T. Barker 
1480e2f04181SAndrew T. Barker   // Determine active input basis
1481d1d35e2fSjeremylt   CeedQFunctionField *qf_fields;
1482d1d35e2fSjeremylt   ierr = CeedQFunctionGetFields(qf, &qf_fields, NULL); CeedChk(ierr);
1483d1d35e2fSjeremylt   CeedInt num_eval_mode_in = 0, dim = 1;
1484d1d35e2fSjeremylt   CeedEvalMode *eval_mode_in = NULL;
1485d1d35e2fSjeremylt   CeedBasis basis_in = NULL;
1486d1d35e2fSjeremylt   CeedElemRestriction rstr_in = NULL;
1487d1d35e2fSjeremylt   for (CeedInt i=0; i<num_input_fields; i++) {
1488e2f04181SAndrew T. Barker     CeedVector vec;
1489e2f04181SAndrew T. Barker     ierr = CeedOperatorFieldGetVector(input_fields[i], &vec); CeedChk(ierr);
1490e2f04181SAndrew T. Barker     if (vec == CEED_VECTOR_ACTIVE) {
1491d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(input_fields[i], &basis_in);
1492e2f04181SAndrew T. Barker       CeedChk(ierr);
1493d1d35e2fSjeremylt       ierr = CeedBasisGetDimension(basis_in, &dim); CeedChk(ierr);
1494d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetElemRestriction(input_fields[i], &rstr_in);
1495e2f04181SAndrew T. Barker       CeedChk(ierr);
1496d1d35e2fSjeremylt       CeedEvalMode eval_mode;
1497d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode);
1498e2f04181SAndrew T. Barker       CeedChk(ierr);
1499d1d35e2fSjeremylt       switch (eval_mode) {
1500e2f04181SAndrew T. Barker       case CEED_EVAL_NONE:
1501e2f04181SAndrew T. Barker       case CEED_EVAL_INTERP:
1502d1d35e2fSjeremylt         ierr = CeedRealloc(num_eval_mode_in + 1, &eval_mode_in); CeedChk(ierr);
1503d1d35e2fSjeremylt         eval_mode_in[num_eval_mode_in] = eval_mode;
1504d1d35e2fSjeremylt         num_eval_mode_in += 1;
1505e2f04181SAndrew T. Barker         break;
1506e2f04181SAndrew T. Barker       case CEED_EVAL_GRAD:
1507d1d35e2fSjeremylt         ierr = CeedRealloc(num_eval_mode_in + dim, &eval_mode_in); CeedChk(ierr);
1508e2f04181SAndrew T. Barker         for (CeedInt d=0; d<dim; d++) {
1509d1d35e2fSjeremylt           eval_mode_in[num_eval_mode_in+d] = eval_mode;
1510e2f04181SAndrew T. Barker         }
1511d1d35e2fSjeremylt         num_eval_mode_in += dim;
1512e2f04181SAndrew T. Barker         break;
1513e2f04181SAndrew T. Barker       case CEED_EVAL_WEIGHT:
1514e2f04181SAndrew T. Barker       case CEED_EVAL_DIV:
1515e2f04181SAndrew T. Barker       case CEED_EVAL_CURL:
1516e2f04181SAndrew T. Barker         break; // Caught by QF Assembly
1517e2f04181SAndrew T. Barker       }
1518e2f04181SAndrew T. Barker     }
1519e2f04181SAndrew T. Barker   }
1520e2f04181SAndrew T. Barker 
1521e2f04181SAndrew T. Barker   // Determine active output basis
1522d1d35e2fSjeremylt   ierr = CeedQFunctionGetFields(qf, NULL, &qf_fields); CeedChk(ierr);
1523d1d35e2fSjeremylt   CeedInt num_eval_mode_out = 0;
1524d1d35e2fSjeremylt   CeedEvalMode *eval_mode_out = NULL;
1525d1d35e2fSjeremylt   CeedBasis basis_out = NULL;
1526d1d35e2fSjeremylt   CeedElemRestriction rstr_out = NULL;
1527d1d35e2fSjeremylt   for (CeedInt i=0; i<num_output_fields; i++) {
1528e2f04181SAndrew T. Barker     CeedVector vec;
1529e2f04181SAndrew T. Barker     ierr = CeedOperatorFieldGetVector(output_fields[i], &vec); CeedChk(ierr);
1530e2f04181SAndrew T. Barker     if (vec == CEED_VECTOR_ACTIVE) {
1531d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(output_fields[i], &basis_out);
1532e2f04181SAndrew T. Barker       CeedChk(ierr);
1533d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetElemRestriction(output_fields[i], &rstr_out);
1534e2f04181SAndrew T. Barker       CeedChk(ierr);
1535d1d35e2fSjeremylt       CeedEvalMode eval_mode;
1536d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode);
1537e2f04181SAndrew T. Barker       CeedChk(ierr);
1538d1d35e2fSjeremylt       switch (eval_mode) {
1539e2f04181SAndrew T. Barker       case CEED_EVAL_NONE:
1540e2f04181SAndrew T. Barker       case CEED_EVAL_INTERP:
1541d1d35e2fSjeremylt         ierr = CeedRealloc(num_eval_mode_out + 1, &eval_mode_out); CeedChk(ierr);
1542d1d35e2fSjeremylt         eval_mode_out[num_eval_mode_out] = eval_mode;
1543d1d35e2fSjeremylt         num_eval_mode_out += 1;
1544e2f04181SAndrew T. Barker         break;
1545e2f04181SAndrew T. Barker       case CEED_EVAL_GRAD:
1546d1d35e2fSjeremylt         ierr = CeedRealloc(num_eval_mode_out + dim, &eval_mode_out); CeedChk(ierr);
1547e2f04181SAndrew T. Barker         for (CeedInt d=0; d<dim; d++) {
1548d1d35e2fSjeremylt           eval_mode_out[num_eval_mode_out+d] = eval_mode;
1549e2f04181SAndrew T. Barker         }
1550d1d35e2fSjeremylt         num_eval_mode_out += dim;
1551e2f04181SAndrew T. Barker         break;
1552e2f04181SAndrew T. Barker       case CEED_EVAL_WEIGHT:
1553e2f04181SAndrew T. Barker       case CEED_EVAL_DIV:
1554e2f04181SAndrew T. Barker       case CEED_EVAL_CURL:
1555e2f04181SAndrew T. Barker         break; // Caught by QF Assembly
1556e2f04181SAndrew T. Barker       }
1557e2f04181SAndrew T. Barker     }
1558e2f04181SAndrew T. Barker   }
1559e2f04181SAndrew T. Barker 
156078464608Sjeremylt   if (num_eval_mode_in == 0 || num_eval_mode_out == 0)
156178464608Sjeremylt     // LCOV_EXCL_START
156278464608Sjeremylt     return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
156378464608Sjeremylt                      "Cannot assemble operator with out inputs/outputs");
156478464608Sjeremylt   // LCOV_EXCL_STOP
156578464608Sjeremylt 
1566d1d35e2fSjeremylt   CeedInt num_elem, elem_size, num_qpts, num_comp;
1567d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumElements(rstr_in, &num_elem); CeedChk(ierr);
1568d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetElementSize(rstr_in, &elem_size); CeedChk(ierr);
1569d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumComponents(rstr_in, &num_comp); CeedChk(ierr);
1570d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis_in, &num_qpts); CeedChk(ierr);
1571e2f04181SAndrew T. Barker 
1572d1d35e2fSjeremylt   CeedInt local_num_entries = elem_size*num_comp * elem_size*num_comp * num_elem;
1573e2f04181SAndrew T. Barker 
1574e2f04181SAndrew T. Barker   // loop over elements and put in data structure
1575d1d35e2fSjeremylt   const CeedScalar *interp_in, *grad_in;
1576d1d35e2fSjeremylt   ierr = CeedBasisGetInterp(basis_in, &interp_in); CeedChk(ierr);
1577d1d35e2fSjeremylt   ierr = CeedBasisGetGrad(basis_in, &grad_in); CeedChk(ierr);
1578e2f04181SAndrew T. Barker 
1579d1d35e2fSjeremylt   const CeedScalar *assembled_qf_array;
1580d1d35e2fSjeremylt   ierr = CeedVectorGetArrayRead(assembled_qf, CEED_MEM_HOST, &assembled_qf_array);
1581e2f04181SAndrew T. Barker   CeedChk(ierr);
1582e2f04181SAndrew T. Barker 
1583e2f04181SAndrew T. Barker   CeedInt layout_qf[3];
1584e2f04181SAndrew T. Barker   ierr = CeedElemRestrictionGetELayout(rstr_q, &layout_qf); CeedChk(ierr);
1585e2f04181SAndrew T. Barker   ierr = CeedElemRestrictionDestroy(&rstr_q); CeedChk(ierr);
1586e2f04181SAndrew T. Barker 
1587d1d35e2fSjeremylt   // we store B_mat_in, B_mat_out, BTD, elem_mat in row-major order
1588d1d35e2fSjeremylt   CeedScalar B_mat_in[(num_qpts * num_eval_mode_in) * elem_size];
1589d1d35e2fSjeremylt   CeedScalar B_mat_out[(num_qpts * num_eval_mode_out) * elem_size];
1590d1d35e2fSjeremylt   CeedScalar D_mat[num_eval_mode_out * num_eval_mode_in *
1591d1d35e2fSjeremylt                                      num_qpts]; // logically 3-tensor
1592d1d35e2fSjeremylt   CeedScalar BTD[elem_size * num_qpts*num_eval_mode_in];
1593d1d35e2fSjeremylt   CeedScalar elem_mat[elem_size * elem_size];
1594e2f04181SAndrew T. Barker   int count = 0;
1595e2f04181SAndrew T. Barker   CeedScalar *vals;
1596e2f04181SAndrew T. Barker   ierr = CeedVectorGetArray(values, CEED_MEM_HOST, &vals); CeedChk(ierr);
1597d1d35e2fSjeremylt   for (int e = 0; e < num_elem; ++e) {
1598d1d35e2fSjeremylt     for (int comp_in = 0; comp_in < num_comp; ++comp_in) {
1599d1d35e2fSjeremylt       for (int comp_out = 0; comp_out < num_comp; ++comp_out) {
1600d1d35e2fSjeremylt         for (int ell = 0; ell < (num_qpts * num_eval_mode_in) * elem_size; ++ell) {
1601d1d35e2fSjeremylt           B_mat_in[ell] = 0.0;
1602e2f04181SAndrew T. Barker         }
1603d1d35e2fSjeremylt         for (int ell = 0; ell < (num_qpts * num_eval_mode_out) * elem_size; ++ell) {
1604d1d35e2fSjeremylt           B_mat_out[ell] = 0.0;
1605e2f04181SAndrew T. Barker         }
1606e2f04181SAndrew T. Barker         // Store block-diagonal D matrix as collection of small dense blocks
1607d1d35e2fSjeremylt         for (int ell = 0; ell < num_eval_mode_in*num_eval_mode_out*num_qpts; ++ell) {
1608d1d35e2fSjeremylt           D_mat[ell] = 0.0;
1609e2f04181SAndrew T. Barker         }
1610e2f04181SAndrew T. Barker         // form element matrix itself (for each block component)
1611d1d35e2fSjeremylt         for (int ell = 0; ell < elem_size*elem_size; ++ell) {
1612e2f04181SAndrew T. Barker           elem_mat[ell] = 0.0;
1613e2f04181SAndrew T. Barker         }
1614d1d35e2fSjeremylt         for (int q = 0; q < num_qpts; ++q) {
1615d1d35e2fSjeremylt           for (int n = 0; n < elem_size; ++n) {
1616d1d35e2fSjeremylt             CeedInt d_in = -1;
1617d1d35e2fSjeremylt             for (int e_in = 0; e_in < num_eval_mode_in; ++e_in) {
1618d1d35e2fSjeremylt               const int qq = num_eval_mode_in*q;
1619d1d35e2fSjeremylt               if (eval_mode_in[e_in] == CEED_EVAL_INTERP) {
1620d1d35e2fSjeremylt                 B_mat_in[(qq+e_in)*elem_size + n] += interp_in[q * elem_size + n];
1621d1d35e2fSjeremylt               } else if (eval_mode_in[e_in] == CEED_EVAL_GRAD) {
1622d1d35e2fSjeremylt                 d_in += 1;
1623d1d35e2fSjeremylt                 B_mat_in[(qq+e_in)*elem_size + n] +=
1624d1d35e2fSjeremylt                   grad_in[(d_in*num_qpts+q) * elem_size + n];
1625e2f04181SAndrew T. Barker               } else {
1626e2f04181SAndrew T. Barker                 // LCOV_EXCL_START
1627e2f04181SAndrew T. Barker                 return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Not implemented!");
1628e2f04181SAndrew T. Barker                 // LCOV_EXCL_STOP
1629e2f04181SAndrew T. Barker               }
1630e2f04181SAndrew T. Barker             }
1631d1d35e2fSjeremylt             CeedInt d_out = -1;
1632d1d35e2fSjeremylt             for (int e_out = 0; e_out < num_eval_mode_out; ++e_out) {
1633d1d35e2fSjeremylt               const int qq = num_eval_mode_out*q;
1634d1d35e2fSjeremylt               if (eval_mode_out[e_out] == CEED_EVAL_INTERP) {
1635d1d35e2fSjeremylt                 B_mat_out[(qq+e_out)*elem_size + n] += interp_in[q * elem_size + n];
1636d1d35e2fSjeremylt               } else if (eval_mode_out[e_out] == CEED_EVAL_GRAD) {
1637d1d35e2fSjeremylt                 d_out += 1;
1638d1d35e2fSjeremylt                 B_mat_out[(qq+e_out)*elem_size + n] +=
1639d1d35e2fSjeremylt                   grad_in[(d_out*num_qpts+q) * elem_size + n];
1640e2f04181SAndrew T. Barker               } else {
1641e2f04181SAndrew T. Barker                 // LCOV_EXCL_START
1642e2f04181SAndrew T. Barker                 return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Not implemented!");
1643e2f04181SAndrew T. Barker                 // LCOV_EXCL_STOP
1644e2f04181SAndrew T. Barker               }
1645e2f04181SAndrew T. Barker             }
1646e2f04181SAndrew T. Barker           }
1647d1d35e2fSjeremylt           for (int ei = 0; ei < num_eval_mode_out; ++ei) {
1648d1d35e2fSjeremylt             for (int ej = 0; ej < num_eval_mode_in; ++ej) {
1649d1d35e2fSjeremylt               const int eval_mode_index = ((ei*num_comp+comp_in)*num_eval_mode_in+ej)*num_comp
1650d1d35e2fSjeremylt                                           +comp_out;
1651d1d35e2fSjeremylt               const int index = q*layout_qf[0] + eval_mode_index*layout_qf[1] +
1652d1d35e2fSjeremylt                                 e*layout_qf[2];
1653d1d35e2fSjeremylt               D_mat[(ei*num_eval_mode_in+ej)*num_qpts + q] += assembled_qf_array[index];
1654e2f04181SAndrew T. Barker             }
1655e2f04181SAndrew T. Barker           }
1656e2f04181SAndrew T. Barker         }
1657e2f04181SAndrew T. Barker         // Compute B^T*D
1658d1d35e2fSjeremylt         for (int ell = 0; ell < elem_size*num_qpts*num_eval_mode_in; ++ell) {
1659e2f04181SAndrew T. Barker           BTD[ell] = 0.0;
1660e2f04181SAndrew T. Barker         }
1661d1d35e2fSjeremylt         for (int j = 0; j<elem_size; ++j) {
1662d1d35e2fSjeremylt           for (int q = 0; q<num_qpts; ++q) {
1663d1d35e2fSjeremylt             int qq = num_eval_mode_out*q;
1664d1d35e2fSjeremylt             for (int ei = 0; ei < num_eval_mode_in; ++ei) {
1665d1d35e2fSjeremylt               for (int ej = 0; ej < num_eval_mode_out; ++ej) {
1666d1d35e2fSjeremylt                 BTD[j*(num_qpts*num_eval_mode_in) + (qq+ei)] +=
1667d1d35e2fSjeremylt                   B_mat_out[(qq+ej)*elem_size + j] * D_mat[(ei*num_eval_mode_in+ej)*num_qpts + q];
1668e2f04181SAndrew T. Barker               }
1669e2f04181SAndrew T. Barker             }
1670e2f04181SAndrew T. Barker           }
1671e2f04181SAndrew T. Barker         }
1672e2f04181SAndrew T. Barker 
1673d1d35e2fSjeremylt         ierr = CeedMatrixMultiply(ceed, BTD, B_mat_in, elem_mat, elem_size,
1674d1d35e2fSjeremylt                                   elem_size, num_qpts*num_eval_mode_in); CeedChk(ierr);
1675e2f04181SAndrew T. Barker 
1676e2f04181SAndrew T. Barker         // put element matrix in coordinate data structure
1677d1d35e2fSjeremylt         for (int i = 0; i < elem_size; ++i) {
1678d1d35e2fSjeremylt           for (int j = 0; j < elem_size; ++j) {
1679d1d35e2fSjeremylt             vals[offset + count] = elem_mat[i*elem_size + j];
1680e2f04181SAndrew T. Barker             count++;
1681e2f04181SAndrew T. Barker           }
1682e2f04181SAndrew T. Barker         }
1683e2f04181SAndrew T. Barker       }
1684e2f04181SAndrew T. Barker     }
1685e2f04181SAndrew T. Barker   }
1686d1d35e2fSjeremylt   if (count != local_num_entries)
1687e2f04181SAndrew T. Barker     // LCOV_EXCL_START
1688e2f04181SAndrew T. Barker     return CeedError(ceed, CEED_ERROR_MAJOR, "Error computing entries");
1689e2f04181SAndrew T. Barker   // LCOV_EXCL_STOP
1690e2f04181SAndrew T. Barker   ierr = CeedVectorRestoreArray(values, &vals); CeedChk(ierr);
1691e2f04181SAndrew T. Barker 
1692d1d35e2fSjeremylt   ierr = CeedVectorRestoreArrayRead(assembled_qf, &assembled_qf_array);
1693e2f04181SAndrew T. Barker   CeedChk(ierr);
1694d1d35e2fSjeremylt   ierr = CeedVectorDestroy(&assembled_qf); CeedChk(ierr);
1695d1d35e2fSjeremylt   ierr = CeedFree(&eval_mode_in); CeedChk(ierr);
1696d1d35e2fSjeremylt   ierr = CeedFree(&eval_mode_out); CeedChk(ierr);
1697e2f04181SAndrew T. Barker 
1698e2f04181SAndrew T. Barker   return CEED_ERROR_SUCCESS;
1699e2f04181SAndrew T. Barker }
1700e2f04181SAndrew T. Barker 
1701e2f04181SAndrew T. Barker /**
1702e2f04181SAndrew T. Barker    @ref Utility
1703e2f04181SAndrew T. Barker **/
1704d1d35e2fSjeremylt int CeedSingleOperatorAssemblyCountEntries(CeedOperator op,
1705d1d35e2fSjeremylt     CeedInt *num_entries) {
1706e2f04181SAndrew T. Barker   int ierr;
1707e2f04181SAndrew T. Barker   CeedElemRestriction rstr;
1708d1d35e2fSjeremylt   CeedInt num_elem, elem_size, num_comp;
1709e2f04181SAndrew T. Barker 
1710e2f04181SAndrew T. Barker   if (op->composite)
1711e2f04181SAndrew T. Barker     // LCOV_EXCL_START
1712e2f04181SAndrew T. Barker     return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED,
1713e2f04181SAndrew T. Barker                      "Composite operator not supported");
1714e2f04181SAndrew T. Barker   // LCOV_EXCL_STOP
1715e2f04181SAndrew T. Barker   ierr = CeedOperatorGetActiveElemRestriction(op, &rstr); CeedChk(ierr);
1716d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumElements(rstr, &num_elem); CeedChk(ierr);
1717d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetElementSize(rstr, &elem_size); CeedChk(ierr);
1718d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumComponents(rstr, &num_comp); CeedChk(ierr);
1719d1d35e2fSjeremylt   *num_entries = elem_size*num_comp * elem_size*num_comp * num_elem;
1720e2f04181SAndrew T. Barker 
1721e2f04181SAndrew T. Barker   return CEED_ERROR_SUCCESS;
1722e2f04181SAndrew T. Barker }
1723e2f04181SAndrew T. Barker 
1724e2f04181SAndrew T. Barker /**
1725e2f04181SAndrew T. Barker    @brief Fully assemble the nonzero pattern of a linear operator.
1726e2f04181SAndrew T. Barker 
1727e2f04181SAndrew T. Barker    Expected to be used in conjunction with CeedOperatorLinearAssemble()
1728e2f04181SAndrew T. Barker 
1729d1d35e2fSjeremylt    The assembly routines use coordinate format, with num_entries tuples of the
1730e2f04181SAndrew T. Barker    form (i, j, value) which indicate that value should be added to the matrix
1731e2f04181SAndrew T. Barker    in entry (i, j). Note that the (i, j) pairs are not unique and may repeat.
1732e2f04181SAndrew T. Barker    This function returns the number of entries and their (i, j) locations,
1733e2f04181SAndrew T. Barker    while CeedOperatorLinearAssemble() provides the values in the same
1734e2f04181SAndrew T. Barker    ordering.
1735e2f04181SAndrew T. Barker 
1736e2f04181SAndrew T. Barker    This will generally be slow unless your operator is low-order.
1737e2f04181SAndrew T. Barker 
1738e2f04181SAndrew T. Barker    @param[in]  op           CeedOperator to assemble
1739d1d35e2fSjeremylt    @param[out] num_entries  Number of entries in coordinate nonzero pattern.
1740e2f04181SAndrew T. Barker    @param[out] rows         Row number for each entry.
1741e2f04181SAndrew T. Barker    @param[out] cols         Column number for each entry.
1742e2f04181SAndrew T. Barker 
1743e2f04181SAndrew T. Barker    @ref User
1744e2f04181SAndrew T. Barker **/
1745e2f04181SAndrew T. Barker int CeedOperatorLinearAssembleSymbolic(CeedOperator op,
1746d1d35e2fSjeremylt                                        CeedInt *num_entries, CeedInt **rows, CeedInt **cols) {
1747e2f04181SAndrew T. Barker   int ierr;
1748d1d35e2fSjeremylt   CeedInt num_suboperators, single_entries;
1749d1d35e2fSjeremylt   CeedOperator *sub_operators;
1750d1d35e2fSjeremylt   bool is_composite;
1751e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1752e2f04181SAndrew T. Barker 
1753e2f04181SAndrew T. Barker   // Use backend version, if available
1754e2f04181SAndrew T. Barker   if (op->LinearAssembleSymbolic) {
1755d1d35e2fSjeremylt     return op->LinearAssembleSymbolic(op, num_entries, rows, cols);
1756e2f04181SAndrew T. Barker   } else {
1757e2f04181SAndrew T. Barker     // Check for valid fallback resource
1758d1d35e2fSjeremylt     const char *resource, *fallback_resource;
1759e2f04181SAndrew T. Barker     ierr = CeedGetResource(op->ceed, &resource); CeedChk(ierr);
1760d1d35e2fSjeremylt     ierr = CeedGetOperatorFallbackResource(op->ceed, &fallback_resource);
176178464608Sjeremylt     CeedChk(ierr);
1762d1d35e2fSjeremylt     if (strcmp(fallback_resource, "") && strcmp(resource, fallback_resource)) {
1763e2f04181SAndrew T. Barker       // Fallback to reference Ceed
1764d1d35e2fSjeremylt       if (!op->op_fallback) {
1765e2f04181SAndrew T. Barker         ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1766e2f04181SAndrew T. Barker       }
1767e2f04181SAndrew T. Barker       // Assemble
1768d1d35e2fSjeremylt       return CeedOperatorLinearAssembleSymbolic(op->op_fallback, num_entries, rows,
1769d1d35e2fSjeremylt              cols);
1770e2f04181SAndrew T. Barker     }
1771e2f04181SAndrew T. Barker   }
1772e2f04181SAndrew T. Barker 
1773e2f04181SAndrew T. Barker   // if neither backend nor fallback resource provides
1774e2f04181SAndrew T. Barker   // LinearAssembleSymbolic, continue with interface-level implementation
1775e2f04181SAndrew T. Barker 
1776e2f04181SAndrew T. Barker   // count entries and allocate rows, cols arrays
1777d1d35e2fSjeremylt   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1778d1d35e2fSjeremylt   *num_entries = 0;
1779d1d35e2fSjeremylt   if (is_composite) {
1780d1d35e2fSjeremylt     ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1781d1d35e2fSjeremylt     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1782d1d35e2fSjeremylt     for (int k = 0; k < num_suboperators; ++k) {
1783d1d35e2fSjeremylt       ierr = CeedSingleOperatorAssemblyCountEntries(sub_operators[k],
1784e2f04181SAndrew T. Barker              &single_entries); CeedChk(ierr);
1785d1d35e2fSjeremylt       *num_entries += single_entries;
1786e2f04181SAndrew T. Barker     }
1787e2f04181SAndrew T. Barker   } else {
1788e2f04181SAndrew T. Barker     ierr = CeedSingleOperatorAssemblyCountEntries(op,
1789e2f04181SAndrew T. Barker            &single_entries); CeedChk(ierr);
1790d1d35e2fSjeremylt     *num_entries += single_entries;
1791e2f04181SAndrew T. Barker   }
1792d1d35e2fSjeremylt   ierr = CeedCalloc(*num_entries, rows); CeedChk(ierr);
1793d1d35e2fSjeremylt   ierr = CeedCalloc(*num_entries, cols); CeedChk(ierr);
1794e2f04181SAndrew T. Barker 
1795e2f04181SAndrew T. Barker   // assemble nonzero locations
1796e2f04181SAndrew T. Barker   CeedInt offset = 0;
1797d1d35e2fSjeremylt   if (is_composite) {
1798d1d35e2fSjeremylt     ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1799d1d35e2fSjeremylt     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1800d1d35e2fSjeremylt     for (int k = 0; k < num_suboperators; ++k) {
1801d1d35e2fSjeremylt       ierr = CeedSingleOperatorAssembleSymbolic(sub_operators[k], offset, *rows,
1802e2f04181SAndrew T. Barker              *cols); CeedChk(ierr);
1803d1d35e2fSjeremylt       ierr = CeedSingleOperatorAssemblyCountEntries(sub_operators[k],
1804d1d35e2fSjeremylt              &single_entries);
1805e2f04181SAndrew T. Barker       CeedChk(ierr);
1806e2f04181SAndrew T. Barker       offset += single_entries;
1807e2f04181SAndrew T. Barker     }
1808e2f04181SAndrew T. Barker   } else {
1809e2f04181SAndrew T. Barker     ierr = CeedSingleOperatorAssembleSymbolic(op, offset, *rows, *cols);
1810e2f04181SAndrew T. Barker     CeedChk(ierr);
1811e2f04181SAndrew T. Barker   }
1812e2f04181SAndrew T. Barker 
1813e2f04181SAndrew T. Barker   return CEED_ERROR_SUCCESS;
1814e2f04181SAndrew T. Barker }
1815e2f04181SAndrew T. Barker 
1816e2f04181SAndrew T. Barker /**
1817e2f04181SAndrew T. Barker    @brief Fully assemble the nonzero entries of a linear operator.
1818e2f04181SAndrew T. Barker 
1819e2f04181SAndrew T. Barker    Expected to be used in conjunction with CeedOperatorLinearAssembleSymbolic()
1820e2f04181SAndrew T. Barker 
1821d1d35e2fSjeremylt    The assembly routines use coordinate format, with num_entries tuples of the
1822e2f04181SAndrew T. Barker    form (i, j, value) which indicate that value should be added to the matrix
1823e2f04181SAndrew T. Barker    in entry (i, j). Note that the (i, j) pairs are not unique and may repeat.
1824e2f04181SAndrew T. Barker    This function returns the values of the nonzero entries to be added, their
1825e2f04181SAndrew T. Barker    (i, j) locations are provided by CeedOperatorLinearAssembleSymbolic()
1826e2f04181SAndrew T. Barker 
1827e2f04181SAndrew T. Barker    This will generally be slow unless your operator is low-order.
1828e2f04181SAndrew T. Barker 
1829e2f04181SAndrew T. Barker    @param[in]  op      CeedOperator to assemble
1830e2f04181SAndrew T. Barker    @param[out] values  Values to assemble into matrix
1831e2f04181SAndrew T. Barker 
1832e2f04181SAndrew T. Barker    @ref User
1833e2f04181SAndrew T. Barker **/
1834e2f04181SAndrew T. Barker int CeedOperatorLinearAssemble(CeedOperator op, CeedVector values) {
1835e2f04181SAndrew T. Barker   int ierr;
183678464608Sjeremylt   CeedInt num_suboperators, single_entries = 0;
1837d1d35e2fSjeremylt   CeedOperator *sub_operators;
1838e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1839e2f04181SAndrew T. Barker 
1840e2f04181SAndrew T. Barker   // Use backend version, if available
1841e2f04181SAndrew T. Barker   if (op->LinearAssemble) {
1842e2f04181SAndrew T. Barker     return op->LinearAssemble(op, values);
1843e2f04181SAndrew T. Barker   } else {
1844e2f04181SAndrew T. Barker     // Check for valid fallback resource
1845d1d35e2fSjeremylt     const char *resource, *fallback_resource;
1846e2f04181SAndrew T. Barker     ierr = CeedGetResource(op->ceed, &resource); CeedChk(ierr);
1847d1d35e2fSjeremylt     ierr = CeedGetOperatorFallbackResource(op->ceed, &fallback_resource);
184878464608Sjeremylt     CeedChk(ierr);
1849d1d35e2fSjeremylt     if (strcmp(fallback_resource, "") && strcmp(resource, fallback_resource)) {
1850e2f04181SAndrew T. Barker       // Fallback to reference Ceed
1851d1d35e2fSjeremylt       if (!op->op_fallback) {
1852e2f04181SAndrew T. Barker         ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1853e2f04181SAndrew T. Barker       }
1854e2f04181SAndrew T. Barker       // Assemble
1855d1d35e2fSjeremylt       return CeedOperatorLinearAssemble(op->op_fallback, values);
1856e2f04181SAndrew T. Barker     }
1857e2f04181SAndrew T. Barker   }
1858e2f04181SAndrew T. Barker 
1859e2f04181SAndrew T. Barker   // if neither backend nor fallback resource provides
1860e2f04181SAndrew T. Barker   // LinearAssemble, continue with interface-level implementation
1861e2f04181SAndrew T. Barker 
1862d1d35e2fSjeremylt   bool is_composite;
1863d1d35e2fSjeremylt   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1864e2f04181SAndrew T. Barker 
1865e2f04181SAndrew T. Barker   CeedInt offset = 0;
1866d1d35e2fSjeremylt   if (is_composite) {
1867d1d35e2fSjeremylt     ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1868d1d35e2fSjeremylt     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1869d1d35e2fSjeremylt     for (int k = 0; k < num_suboperators; ++k) {
1870d1d35e2fSjeremylt       ierr = CeedSingleOperatorAssemble(sub_operators[k], offset, values);
1871e2f04181SAndrew T. Barker       CeedChk(ierr);
1872d1d35e2fSjeremylt       ierr = CeedSingleOperatorAssemblyCountEntries(sub_operators[k],
1873d1d35e2fSjeremylt              &single_entries);
1874e2f04181SAndrew T. Barker       CeedChk(ierr);
1875e2f04181SAndrew T. Barker       offset += single_entries;
1876e2f04181SAndrew T. Barker     }
1877e2f04181SAndrew T. Barker   } else {
1878e2f04181SAndrew T. Barker     ierr = CeedSingleOperatorAssemble(op, offset, values); CeedChk(ierr);
1879e2f04181SAndrew T. Barker   }
1880e2f04181SAndrew T. Barker 
1881e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1882d965c7a7SJeremy L Thompson }
1883d965c7a7SJeremy L Thompson 
1884d965c7a7SJeremy L Thompson /**
1885d99fa3c5SJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators
1886d99fa3c5SJeremy L Thompson            for a CeedOperator, creating the prolongation basis from the
1887d99fa3c5SJeremy L Thompson            fine and coarse grid interpolation
1888d99fa3c5SJeremy L Thompson 
1889d1d35e2fSjeremylt   @param[in] op_fine       Fine grid operator
1890d1d35e2fSjeremylt   @param[in] p_mult_fine   L-vector multiplicity in parallel gather/scatter
1891d1d35e2fSjeremylt   @param[in] rstr_coarse   Coarse grid restriction
1892d1d35e2fSjeremylt   @param[in] basis_coarse  Coarse grid active vector basis
1893d1d35e2fSjeremylt   @param[out] op_coarse    Coarse grid operator
1894d1d35e2fSjeremylt   @param[out] op_prolong   Coarse to fine operator
1895d1d35e2fSjeremylt   @param[out] op_restrict  Fine to coarse operator
1896d99fa3c5SJeremy L Thompson 
1897d99fa3c5SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1898d99fa3c5SJeremy L Thompson 
1899d99fa3c5SJeremy L Thompson   @ref User
1900d99fa3c5SJeremy L Thompson **/
1901d1d35e2fSjeremylt int CeedOperatorMultigridLevelCreate(CeedOperator op_fine,
1902d1d35e2fSjeremylt                                      CeedVector p_mult_fine,
1903d1d35e2fSjeremylt                                      CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
1904d1d35e2fSjeremylt                                      CeedOperator *op_coarse, CeedOperator *op_prolong,
1905d1d35e2fSjeremylt                                      CeedOperator *op_restrict) {
1906d99fa3c5SJeremy L Thompson   int ierr;
1907d99fa3c5SJeremy L Thompson   Ceed ceed;
1908d1d35e2fSjeremylt   ierr = CeedOperatorGetCeed(op_fine, &ceed); CeedChk(ierr);
1909d99fa3c5SJeremy L Thompson 
1910d99fa3c5SJeremy L Thompson   // Check for compatible quadrature spaces
1911d1d35e2fSjeremylt   CeedBasis basis_fine;
1912d1d35e2fSjeremylt   ierr = CeedOperatorGetActiveBasis(op_fine, &basis_fine); CeedChk(ierr);
1913d1d35e2fSjeremylt   CeedInt Q_f, Q_c;
1914d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis_fine, &Q_f); CeedChk(ierr);
1915d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis_coarse, &Q_c); CeedChk(ierr);
1916d1d35e2fSjeremylt   if (Q_f != Q_c)
1917d99fa3c5SJeremy L Thompson     // LCOV_EXCL_START
1918e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_DIMENSION,
1919e15f9bd0SJeremy L Thompson                      "Bases must have compatible quadrature spaces");
1920d99fa3c5SJeremy L Thompson   // LCOV_EXCL_STOP
1921d99fa3c5SJeremy L Thompson 
1922d99fa3c5SJeremy L Thompson   // Coarse to fine basis
1923d1d35e2fSjeremylt   CeedInt P_f, P_c, Q = Q_f;
1924d1d35e2fSjeremylt   bool is_tensor_f, is_tensor_c;
1925d1d35e2fSjeremylt   ierr = CeedBasisIsTensor(basis_fine, &is_tensor_f); CeedChk(ierr);
1926d1d35e2fSjeremylt   ierr = CeedBasisIsTensor(basis_coarse, &is_tensor_c); CeedChk(ierr);
1927d1d35e2fSjeremylt   CeedScalar *interp_c, *interp_f, *interp_c_to_f, *tau;
1928d1d35e2fSjeremylt   if (is_tensor_f && is_tensor_c) {
1929d1d35e2fSjeremylt     ierr = CeedBasisGetNumNodes1D(basis_fine, &P_f); CeedChk(ierr);
1930d1d35e2fSjeremylt     ierr = CeedBasisGetNumNodes1D(basis_coarse, &P_c); CeedChk(ierr);
1931d1d35e2fSjeremylt     ierr = CeedBasisGetNumQuadraturePoints1D(basis_coarse, &Q); CeedChk(ierr);
1932d1d35e2fSjeremylt   } else if (!is_tensor_f && !is_tensor_c) {
1933d1d35e2fSjeremylt     ierr = CeedBasisGetNumNodes(basis_fine, &P_f); CeedChk(ierr);
1934d1d35e2fSjeremylt     ierr = CeedBasisGetNumNodes(basis_coarse, &P_c); CeedChk(ierr);
1935d99fa3c5SJeremy L Thompson   } else {
1936d99fa3c5SJeremy L Thompson     // LCOV_EXCL_START
1937e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_MINOR,
1938e15f9bd0SJeremy L Thompson                      "Bases must both be tensor or non-tensor");
1939d99fa3c5SJeremy L Thompson     // LCOV_EXCL_STOP
1940d99fa3c5SJeremy L Thompson   }
1941d99fa3c5SJeremy L Thompson 
1942d1d35e2fSjeremylt   ierr = CeedMalloc(Q*P_f, &interp_f); CeedChk(ierr);
1943d1d35e2fSjeremylt   ierr = CeedMalloc(Q*P_c, &interp_c); CeedChk(ierr);
1944d1d35e2fSjeremylt   ierr = CeedCalloc(P_c*P_f, &interp_c_to_f); CeedChk(ierr);
1945d99fa3c5SJeremy L Thompson   ierr = CeedMalloc(Q, &tau); CeedChk(ierr);
1946d1d35e2fSjeremylt   if (is_tensor_f) {
1947d1d35e2fSjeremylt     memcpy(interp_f, basis_fine->interp_1d, Q*P_f*sizeof basis_fine->interp_1d[0]);
1948d1d35e2fSjeremylt     memcpy(interp_c, basis_coarse->interp_1d,
1949d1d35e2fSjeremylt            Q*P_c*sizeof basis_coarse->interp_1d[0]);
1950d99fa3c5SJeremy L Thompson   } else {
1951d1d35e2fSjeremylt     memcpy(interp_f, basis_fine->interp, Q*P_f*sizeof basis_fine->interp[0]);
1952d1d35e2fSjeremylt     memcpy(interp_c, basis_coarse->interp, Q*P_c*sizeof basis_coarse->interp[0]);
1953d99fa3c5SJeremy L Thompson   }
1954d99fa3c5SJeremy L Thompson 
1955d1d35e2fSjeremylt   // -- QR Factorization, interp_f = Q R
1956d1d35e2fSjeremylt   ierr = CeedQRFactorization(ceed, interp_f, tau, Q, P_f); CeedChk(ierr);
1957d99fa3c5SJeremy L Thompson 
1958d1d35e2fSjeremylt   // -- Apply Qtranspose, interp_c = Qtranspose interp_c
1959d1d35e2fSjeremylt   ierr = CeedHouseholderApplyQ(interp_c, interp_f, tau, CEED_TRANSPOSE,
1960d1d35e2fSjeremylt                                Q, P_c, P_f, P_c, 1); CeedChk(ierr);
1961d99fa3c5SJeremy L Thompson 
1962d1d35e2fSjeremylt   // -- Apply Rinv, interp_c_to_f = Rinv interp_c
1963d1d35e2fSjeremylt   for (CeedInt j=0; j<P_c; j++) { // Column j
1964d1d35e2fSjeremylt     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];
1965d1d35e2fSjeremylt     for (CeedInt i=P_f-2; i>=0; i--) { // Row i
1966d1d35e2fSjeremylt       interp_c_to_f[j+P_c*i] = interp_c[j+P_c*i];
1967d1d35e2fSjeremylt       for (CeedInt k=i+1; k<P_f; k++)
1968d1d35e2fSjeremylt         interp_c_to_f[j+P_c*i] -= interp_f[k+P_f*i]*interp_c_to_f[j+P_c*k];
1969d1d35e2fSjeremylt       interp_c_to_f[j+P_c*i] /= interp_f[i+P_f*i];
1970d99fa3c5SJeremy L Thompson     }
1971d99fa3c5SJeremy L Thompson   }
1972d99fa3c5SJeremy L Thompson   ierr = CeedFree(&tau); CeedChk(ierr);
1973d1d35e2fSjeremylt   ierr = CeedFree(&interp_c); CeedChk(ierr);
1974d1d35e2fSjeremylt   ierr = CeedFree(&interp_f); CeedChk(ierr);
1975d99fa3c5SJeremy L Thompson 
1976d1d35e2fSjeremylt   // Complete with interp_c_to_f versions of code
1977d1d35e2fSjeremylt   if (is_tensor_f) {
1978d1d35e2fSjeremylt     ierr = CeedOperatorMultigridLevelCreateTensorH1(op_fine, p_mult_fine,
1979d1d35e2fSjeremylt            rstr_coarse, basis_coarse, interp_c_to_f, op_coarse, op_prolong, op_restrict);
1980d99fa3c5SJeremy L Thompson     CeedChk(ierr);
1981d99fa3c5SJeremy L Thompson   } else {
1982d1d35e2fSjeremylt     ierr = CeedOperatorMultigridLevelCreateH1(op_fine, p_mult_fine,
1983d1d35e2fSjeremylt            rstr_coarse, basis_coarse, interp_c_to_f, op_coarse, op_prolong, op_restrict);
1984d99fa3c5SJeremy L Thompson     CeedChk(ierr);
1985d99fa3c5SJeremy L Thompson   }
1986d99fa3c5SJeremy L Thompson 
1987d99fa3c5SJeremy L Thompson   // Cleanup
1988d1d35e2fSjeremylt   ierr = CeedFree(&interp_c_to_f); CeedChk(ierr);
1989e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1990d99fa3c5SJeremy L Thompson }
1991d99fa3c5SJeremy L Thompson 
1992d99fa3c5SJeremy L Thompson /**
1993d99fa3c5SJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators
1994d99fa3c5SJeremy L Thompson            for a CeedOperator with a tensor basis for the active basis
1995d99fa3c5SJeremy L Thompson 
1996d1d35e2fSjeremylt   @param[in] op_fine        Fine grid operator
1997d1d35e2fSjeremylt   @param[in] p_mult_fine    L-vector multiplicity in parallel gather/scatter
1998d1d35e2fSjeremylt   @param[in] rstr_coarse    Coarse grid restriction
1999d1d35e2fSjeremylt   @param[in] basis_coarse   Coarse grid active vector basis
2000d1d35e2fSjeremylt   @param[in] interp_c_to_f  Matrix for coarse to fine interpolation
2001d1d35e2fSjeremylt   @param[out] op_coarse     Coarse grid operator
2002d1d35e2fSjeremylt   @param[out] op_prolong    Coarse to fine operator
2003d1d35e2fSjeremylt   @param[out] op_restrict   Fine to coarse operator
2004d99fa3c5SJeremy L Thompson 
2005d99fa3c5SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2006d99fa3c5SJeremy L Thompson 
2007d99fa3c5SJeremy L Thompson   @ref User
2008d99fa3c5SJeremy L Thompson **/
2009d1d35e2fSjeremylt int CeedOperatorMultigridLevelCreateTensorH1(CeedOperator op_fine,
2010d1d35e2fSjeremylt     CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
2011d1d35e2fSjeremylt     const CeedScalar *interp_c_to_f, CeedOperator *op_coarse,
2012d1d35e2fSjeremylt     CeedOperator *op_prolong, CeedOperator *op_restrict) {
2013d99fa3c5SJeremy L Thompson   int ierr;
2014d99fa3c5SJeremy L Thompson   Ceed ceed;
2015d1d35e2fSjeremylt   ierr = CeedOperatorGetCeed(op_fine, &ceed); CeedChk(ierr);
2016d99fa3c5SJeremy L Thompson 
2017d99fa3c5SJeremy L Thompson   // Check for compatible quadrature spaces
2018d1d35e2fSjeremylt   CeedBasis basis_fine;
2019d1d35e2fSjeremylt   ierr = CeedOperatorGetActiveBasis(op_fine, &basis_fine); CeedChk(ierr);
2020d1d35e2fSjeremylt   CeedInt Q_f, Q_c;
2021d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis_fine, &Q_f); CeedChk(ierr);
2022d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis_coarse, &Q_c); CeedChk(ierr);
2023d1d35e2fSjeremylt   if (Q_f != Q_c)
2024d99fa3c5SJeremy L Thompson     // LCOV_EXCL_START
2025e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_DIMENSION,
2026e15f9bd0SJeremy L Thompson                      "Bases must have compatible quadrature spaces");
2027d99fa3c5SJeremy L Thompson   // LCOV_EXCL_STOP
2028d99fa3c5SJeremy L Thompson 
2029d99fa3c5SJeremy L Thompson   // Coarse to fine basis
2030d1d35e2fSjeremylt   CeedInt dim, num_comp, num_nodes_c, P_1d_f, P_1d_c;
2031d1d35e2fSjeremylt   ierr = CeedBasisGetDimension(basis_fine, &dim); CeedChk(ierr);
2032d1d35e2fSjeremylt   ierr = CeedBasisGetNumComponents(basis_fine, &num_comp); CeedChk(ierr);
2033d1d35e2fSjeremylt   ierr = CeedBasisGetNumNodes1D(basis_fine, &P_1d_f); CeedChk(ierr);
2034d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetElementSize(rstr_coarse, &num_nodes_c);
2035d99fa3c5SJeremy L Thompson   CeedChk(ierr);
2036d1d35e2fSjeremylt   P_1d_c = dim == 1 ? num_nodes_c :
2037d1d35e2fSjeremylt            dim == 2 ? sqrt(num_nodes_c) :
2038d1d35e2fSjeremylt            cbrt(num_nodes_c);
2039d1d35e2fSjeremylt   CeedScalar *q_ref, *q_weight, *grad;
2040d1d35e2fSjeremylt   ierr = CeedCalloc(P_1d_f, &q_ref); CeedChk(ierr);
2041d1d35e2fSjeremylt   ierr = CeedCalloc(P_1d_f, &q_weight); CeedChk(ierr);
2042d1d35e2fSjeremylt   ierr = CeedCalloc(P_1d_f*P_1d_c*dim, &grad); CeedChk(ierr);
2043d1d35e2fSjeremylt   CeedBasis basis_c_to_f;
2044d1d35e2fSjeremylt   ierr = CeedBasisCreateTensorH1(ceed, dim, num_comp, P_1d_c, P_1d_f,
2045d1d35e2fSjeremylt                                  interp_c_to_f, grad, q_ref, q_weight, &basis_c_to_f);
2046d99fa3c5SJeremy L Thompson   CeedChk(ierr);
2047d1d35e2fSjeremylt   ierr = CeedFree(&q_ref); CeedChk(ierr);
2048d1d35e2fSjeremylt   ierr = CeedFree(&q_weight); CeedChk(ierr);
2049d99fa3c5SJeremy L Thompson   ierr = CeedFree(&grad); CeedChk(ierr);
2050d99fa3c5SJeremy L Thompson 
2051d99fa3c5SJeremy L Thompson   // Core code
2052d1d35e2fSjeremylt   ierr = CeedOperatorMultigridLevel_Core(op_fine, p_mult_fine, rstr_coarse,
2053d1d35e2fSjeremylt                                          basis_coarse, basis_c_to_f, op_coarse,
2054d1d35e2fSjeremylt                                          op_prolong, op_restrict);
2055d99fa3c5SJeremy L Thompson   CeedChk(ierr);
2056e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
2057d99fa3c5SJeremy L Thompson }
2058d99fa3c5SJeremy L Thompson 
2059d99fa3c5SJeremy L Thompson /**
2060d99fa3c5SJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators
2061d99fa3c5SJeremy L Thompson            for a CeedOperator with a non-tensor basis for the active vector
2062d99fa3c5SJeremy L Thompson 
2063d1d35e2fSjeremylt   @param[in] op_fine        Fine grid operator
2064d1d35e2fSjeremylt   @param[in] p_mult_fine    L-vector multiplicity in parallel gather/scatter
2065d1d35e2fSjeremylt   @param[in] rstr_coarse    Coarse grid restriction
2066d1d35e2fSjeremylt   @param[in] basis_coarse   Coarse grid active vector basis
2067d1d35e2fSjeremylt   @param[in] interp_c_to_f  Matrix for coarse to fine interpolation
2068d1d35e2fSjeremylt   @param[out] op_coarse     Coarse grid operator
2069d1d35e2fSjeremylt   @param[out] op_prolong    Coarse to fine operator
2070d1d35e2fSjeremylt   @param[out] op_restrict   Fine to coarse operator
2071d99fa3c5SJeremy L Thompson 
2072d99fa3c5SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2073d99fa3c5SJeremy L Thompson 
2074d99fa3c5SJeremy L Thompson   @ref User
2075d99fa3c5SJeremy L Thompson **/
2076d1d35e2fSjeremylt int CeedOperatorMultigridLevelCreateH1(CeedOperator op_fine,
2077d1d35e2fSjeremylt                                        CeedVector p_mult_fine,
2078d1d35e2fSjeremylt                                        CeedElemRestriction rstr_coarse,
2079d1d35e2fSjeremylt                                        CeedBasis basis_coarse,
2080d1d35e2fSjeremylt                                        const CeedScalar *interp_c_to_f,
2081d1d35e2fSjeremylt                                        CeedOperator *op_coarse,
2082d1d35e2fSjeremylt                                        CeedOperator *op_prolong,
2083d1d35e2fSjeremylt                                        CeedOperator *op_restrict) {
2084d99fa3c5SJeremy L Thompson   int ierr;
2085d99fa3c5SJeremy L Thompson   Ceed ceed;
2086d1d35e2fSjeremylt   ierr = CeedOperatorGetCeed(op_fine, &ceed); CeedChk(ierr);
2087d99fa3c5SJeremy L Thompson 
2088d99fa3c5SJeremy L Thompson   // Check for compatible quadrature spaces
2089d1d35e2fSjeremylt   CeedBasis basis_fine;
2090d1d35e2fSjeremylt   ierr = CeedOperatorGetActiveBasis(op_fine, &basis_fine); CeedChk(ierr);
2091d1d35e2fSjeremylt   CeedInt Q_f, Q_c;
2092d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis_fine, &Q_f); CeedChk(ierr);
2093d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis_coarse, &Q_c); CeedChk(ierr);
2094d1d35e2fSjeremylt   if (Q_f != Q_c)
2095d99fa3c5SJeremy L Thompson     // LCOV_EXCL_START
2096e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_DIMENSION,
2097e15f9bd0SJeremy L Thompson                      "Bases must have compatible quadrature spaces");
2098d99fa3c5SJeremy L Thompson   // LCOV_EXCL_STOP
2099d99fa3c5SJeremy L Thompson 
2100d99fa3c5SJeremy L Thompson   // Coarse to fine basis
2101d99fa3c5SJeremy L Thompson   CeedElemTopology topo;
2102d1d35e2fSjeremylt   ierr = CeedBasisGetTopology(basis_fine, &topo); CeedChk(ierr);
2103d1d35e2fSjeremylt   CeedInt dim, num_comp, num_nodes_c, num_nodes_f;
2104d1d35e2fSjeremylt   ierr = CeedBasisGetDimension(basis_fine, &dim); CeedChk(ierr);
2105d1d35e2fSjeremylt   ierr = CeedBasisGetNumComponents(basis_fine, &num_comp); CeedChk(ierr);
2106d1d35e2fSjeremylt   ierr = CeedBasisGetNumNodes(basis_fine, &num_nodes_f); CeedChk(ierr);
2107d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetElementSize(rstr_coarse, &num_nodes_c);
2108d99fa3c5SJeremy L Thompson   CeedChk(ierr);
2109d1d35e2fSjeremylt   CeedScalar *q_ref, *q_weight, *grad;
2110d1d35e2fSjeremylt   ierr = CeedCalloc(num_nodes_f, &q_ref); CeedChk(ierr);
2111d1d35e2fSjeremylt   ierr = CeedCalloc(num_nodes_f, &q_weight); CeedChk(ierr);
2112d1d35e2fSjeremylt   ierr = CeedCalloc(num_nodes_f*num_nodes_c*dim, &grad); CeedChk(ierr);
2113d1d35e2fSjeremylt   CeedBasis basis_c_to_f;
2114d1d35e2fSjeremylt   ierr = CeedBasisCreateH1(ceed, topo, num_comp, num_nodes_c, num_nodes_f,
2115d1d35e2fSjeremylt                            interp_c_to_f, grad, q_ref, q_weight, &basis_c_to_f);
2116d99fa3c5SJeremy L Thompson   CeedChk(ierr);
2117d1d35e2fSjeremylt   ierr = CeedFree(&q_ref); CeedChk(ierr);
2118d1d35e2fSjeremylt   ierr = CeedFree(&q_weight); CeedChk(ierr);
2119d99fa3c5SJeremy L Thompson   ierr = CeedFree(&grad); CeedChk(ierr);
2120d99fa3c5SJeremy L Thompson 
2121d99fa3c5SJeremy L Thompson   // Core code
2122d1d35e2fSjeremylt   ierr = CeedOperatorMultigridLevel_Core(op_fine, p_mult_fine, rstr_coarse,
2123d1d35e2fSjeremylt                                          basis_coarse, basis_c_to_f, op_coarse,
2124d1d35e2fSjeremylt                                          op_prolong, op_restrict);
2125d99fa3c5SJeremy L Thompson   CeedChk(ierr);
2126e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
2127d99fa3c5SJeremy L Thompson }
2128d99fa3c5SJeremy L Thompson 
2129d99fa3c5SJeremy L Thompson /**
21303bd813ffSjeremylt   @brief Build a FDM based approximate inverse for each element for a
21313bd813ffSjeremylt            CeedOperator
21323bd813ffSjeremylt 
21333bd813ffSjeremylt   This returns a CeedOperator and CeedVector to apply a Fast Diagonalization
21343bd813ffSjeremylt     Method based approximate inverse. This function obtains the simultaneous
21353bd813ffSjeremylt     diagonalization for the 1D mass and Laplacian operators,
21363bd813ffSjeremylt       M = V^T V, K = V^T S V.
21373bd813ffSjeremylt     The assembled QFunction is used to modify the eigenvalues from simultaneous
21383bd813ffSjeremylt     diagonalization and obtain an approximate inverse of the form
21393bd813ffSjeremylt       V^T S^hat V. The CeedOperator must be linear and non-composite. The
21403bd813ffSjeremylt     associated CeedQFunction must therefore also be linear.
21413bd813ffSjeremylt 
21423bd813ffSjeremylt   @param op            CeedOperator to create element inverses
2143d1d35e2fSjeremylt   @param[out] fdm_inv  CeedOperator to apply the action of a FDM based inverse
21443bd813ffSjeremylt                          for each element
21453bd813ffSjeremylt   @param request       Address of CeedRequest for non-blocking completion, else
21464cc79fe7SJed Brown                          @ref CEED_REQUEST_IMMEDIATE
21473bd813ffSjeremylt 
21483bd813ffSjeremylt   @return An error code: 0 - success, otherwise - failure
21493bd813ffSjeremylt 
21507a982d89SJeremy L. Thompson   @ref Backend
21513bd813ffSjeremylt **/
2152d1d35e2fSjeremylt int CeedOperatorCreateFDMElementInverse(CeedOperator op, CeedOperator *fdm_inv,
21533bd813ffSjeremylt                                         CeedRequest *request) {
21543bd813ffSjeremylt   int ierr;
2155e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
21563bd813ffSjeremylt 
2157713f43c3Sjeremylt   // Use backend version, if available
2158713f43c3Sjeremylt   if (op->CreateFDMElementInverse) {
2159d1d35e2fSjeremylt     ierr = op->CreateFDMElementInverse(op, fdm_inv, request); CeedChk(ierr);
2160713f43c3Sjeremylt   } else {
2161713f43c3Sjeremylt     // Fallback to reference Ceed
2162d1d35e2fSjeremylt     if (!op->op_fallback) {
2163713f43c3Sjeremylt       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
21643bd813ffSjeremylt     }
2165713f43c3Sjeremylt     // Assemble
2166d1d35e2fSjeremylt     ierr = op->op_fallback->CreateFDMElementInverse(op->op_fallback, fdm_inv,
21673bd813ffSjeremylt            request); CeedChk(ierr);
21683bd813ffSjeremylt   }
2169e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
21703bd813ffSjeremylt }
21713bd813ffSjeremylt 
21727a982d89SJeremy L. Thompson /**
2173cd4dfc48Sjeremylt   @brief Set the number of quadrature points associated with a CeedOperator.
2174cd4dfc48Sjeremylt            This should be used when creating a CeedOperator where every
2175cd4dfc48Sjeremylt            field has a collocated basis. This function cannot be used for
2176cd4dfc48Sjeremylt            composite CeedOperators.
2177cd4dfc48Sjeremylt 
2178cd4dfc48Sjeremylt   @param op        CeedOperator
2179cd4dfc48Sjeremylt   @param num_qpts  Number of quadrature points to set
2180cd4dfc48Sjeremylt 
2181cd4dfc48Sjeremylt   @return An error code: 0 - success, otherwise - failure
2182cd4dfc48Sjeremylt 
2183cd4dfc48Sjeremylt   @ref Backend
2184cd4dfc48Sjeremylt **/
2185cd4dfc48Sjeremylt 
2186cd4dfc48Sjeremylt int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
2187cd4dfc48Sjeremylt   if (op->composite)
2188cd4dfc48Sjeremylt     // LCOV_EXCL_START
2189cd4dfc48Sjeremylt     return CeedError(op->ceed, CEED_ERROR_MINOR,
2190cd4dfc48Sjeremylt                      "Not defined for composite operator");
2191cd4dfc48Sjeremylt   // LCOV_EXCL_STOP
2192cd4dfc48Sjeremylt   if (op->num_qpts)
2193cd4dfc48Sjeremylt     // LCOV_EXCL_START
2194cd4dfc48Sjeremylt     return CeedError(op->ceed, CEED_ERROR_MINOR,
2195cd4dfc48Sjeremylt                      "Number of quadrature points already defined");
2196cd4dfc48Sjeremylt   // LCOV_EXCL_STOP
2197cd4dfc48Sjeremylt 
2198cd4dfc48Sjeremylt   op->num_qpts = num_qpts;
2199cd4dfc48Sjeremylt   return CEED_ERROR_SUCCESS;
2200cd4dfc48Sjeremylt }
2201cd4dfc48Sjeremylt 
2202cd4dfc48Sjeremylt /**
22037a982d89SJeremy L. Thompson   @brief View a CeedOperator
22047a982d89SJeremy L. Thompson 
22057a982d89SJeremy L. Thompson   @param[in] op      CeedOperator to view
22067a982d89SJeremy L. Thompson   @param[in] stream  Stream to write; typically stdout/stderr or a file
22077a982d89SJeremy L. Thompson 
22087a982d89SJeremy L. Thompson   @return Error code: 0 - success, otherwise - failure
22097a982d89SJeremy L. Thompson 
22107a982d89SJeremy L. Thompson   @ref User
22117a982d89SJeremy L. Thompson **/
22127a982d89SJeremy L. Thompson int CeedOperatorView(CeedOperator op, FILE *stream) {
22137a982d89SJeremy L. Thompson   int ierr;
22147a982d89SJeremy L. Thompson 
22157a982d89SJeremy L. Thompson   if (op->composite) {
22167a982d89SJeremy L. Thompson     fprintf(stream, "Composite CeedOperator\n");
22177a982d89SJeremy L. Thompson 
2218d1d35e2fSjeremylt     for (CeedInt i=0; i<op->num_suboperators; i++) {
22197a982d89SJeremy L. Thompson       fprintf(stream, "  SubOperator [%d]:\n", i);
2220d1d35e2fSjeremylt       ierr = CeedOperatorSingleView(op->sub_operators[i], 1, stream);
22217a982d89SJeremy L. Thompson       CeedChk(ierr);
22227a982d89SJeremy L. Thompson     }
22237a982d89SJeremy L. Thompson   } else {
22247a982d89SJeremy L. Thompson     fprintf(stream, "CeedOperator\n");
22257a982d89SJeremy L. Thompson     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
22267a982d89SJeremy L. Thompson   }
2227e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
22287a982d89SJeremy L. Thompson }
22293bd813ffSjeremylt 
22303bd813ffSjeremylt /**
22313bd813ffSjeremylt   @brief Apply CeedOperator to a vector
2232d7b241e6Sjeremylt 
2233d7b241e6Sjeremylt   This computes the action of the operator on the specified (active) input,
2234d7b241e6Sjeremylt   yielding its (active) output.  All inputs and outputs must be specified using
2235d7b241e6Sjeremylt   CeedOperatorSetField().
2236d7b241e6Sjeremylt 
2237d7b241e6Sjeremylt   @param op        CeedOperator to apply
22384cc79fe7SJed Brown   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
223934138859Sjeremylt                      there are no active inputs
2240b11c1e72Sjeremylt   @param[out] out  CeedVector to store result of applying operator (must be
22414cc79fe7SJed Brown                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
224234138859Sjeremylt                      active outputs
2243d7b241e6Sjeremylt   @param request   Address of CeedRequest for non-blocking completion, else
22444cc79fe7SJed Brown                      @ref CEED_REQUEST_IMMEDIATE
2245b11c1e72Sjeremylt 
2246b11c1e72Sjeremylt   @return An error code: 0 - success, otherwise - failure
2247dfdf5a53Sjeremylt 
22487a982d89SJeremy L. Thompson   @ref User
2249b11c1e72Sjeremylt **/
2250692c2638Sjeremylt int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
2251692c2638Sjeremylt                       CeedRequest *request) {
2252d7b241e6Sjeremylt   int ierr;
2253e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
2254d7b241e6Sjeremylt 
2255d1d35e2fSjeremylt   if (op->num_elem)  {
2256250756a7Sjeremylt     // Standard Operator
2257cae8b89aSjeremylt     if (op->Apply) {
2258250756a7Sjeremylt       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
2259cae8b89aSjeremylt     } else {
2260cae8b89aSjeremylt       // Zero all output vectors
2261250756a7Sjeremylt       CeedQFunction qf = op->qf;
2262d1d35e2fSjeremylt       for (CeedInt i=0; i<qf->num_output_fields; i++) {
2263d1d35e2fSjeremylt         CeedVector vec = op->output_fields[i]->vec;
2264cae8b89aSjeremylt         if (vec == CEED_VECTOR_ACTIVE)
2265cae8b89aSjeremylt           vec = out;
2266cae8b89aSjeremylt         if (vec != CEED_VECTOR_NONE) {
2267cae8b89aSjeremylt           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
2268cae8b89aSjeremylt         }
2269cae8b89aSjeremylt       }
2270250756a7Sjeremylt       // Apply
2271250756a7Sjeremylt       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
2272250756a7Sjeremylt     }
2273250756a7Sjeremylt   } else if (op->composite) {
2274250756a7Sjeremylt     // Composite Operator
2275250756a7Sjeremylt     if (op->ApplyComposite) {
2276250756a7Sjeremylt       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
2277250756a7Sjeremylt     } else {
2278d1d35e2fSjeremylt       CeedInt num_suboperators;
2279d1d35e2fSjeremylt       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
2280d1d35e2fSjeremylt       CeedOperator *sub_operators;
2281d1d35e2fSjeremylt       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
2282250756a7Sjeremylt 
2283250756a7Sjeremylt       // Zero all output vectors
2284250756a7Sjeremylt       if (out != CEED_VECTOR_NONE) {
2285cae8b89aSjeremylt         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
2286cae8b89aSjeremylt       }
2287d1d35e2fSjeremylt       for (CeedInt i=0; i<num_suboperators; i++) {
2288d1d35e2fSjeremylt         for (CeedInt j=0; j<sub_operators[i]->qf->num_output_fields; j++) {
2289d1d35e2fSjeremylt           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
2290250756a7Sjeremylt           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
2291250756a7Sjeremylt             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
2292250756a7Sjeremylt           }
2293250756a7Sjeremylt         }
2294250756a7Sjeremylt       }
2295250756a7Sjeremylt       // Apply
2296d1d35e2fSjeremylt       for (CeedInt i=0; i<op->num_suboperators; i++) {
2297d1d35e2fSjeremylt         ierr = CeedOperatorApplyAdd(op->sub_operators[i], in, out, request);
2298cae8b89aSjeremylt         CeedChk(ierr);
2299cae8b89aSjeremylt       }
2300cae8b89aSjeremylt     }
2301250756a7Sjeremylt   }
2302e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
2303cae8b89aSjeremylt }
2304cae8b89aSjeremylt 
2305cae8b89aSjeremylt /**
2306cae8b89aSjeremylt   @brief Apply CeedOperator to a vector and add result to output vector
2307cae8b89aSjeremylt 
2308cae8b89aSjeremylt   This computes the action of the operator on the specified (active) input,
2309cae8b89aSjeremylt   yielding its (active) output.  All inputs and outputs must be specified using
2310cae8b89aSjeremylt   CeedOperatorSetField().
2311cae8b89aSjeremylt 
2312cae8b89aSjeremylt   @param op        CeedOperator to apply
2313cae8b89aSjeremylt   @param[in] in    CeedVector containing input state or NULL if there are no
2314cae8b89aSjeremylt                      active inputs
2315cae8b89aSjeremylt   @param[out] out  CeedVector to sum in result of applying operator (must be
2316cae8b89aSjeremylt                      distinct from @a in) or NULL if there are no active outputs
2317cae8b89aSjeremylt   @param request   Address of CeedRequest for non-blocking completion, else
23184cc79fe7SJed Brown                      @ref CEED_REQUEST_IMMEDIATE
2319cae8b89aSjeremylt 
2320cae8b89aSjeremylt   @return An error code: 0 - success, otherwise - failure
2321cae8b89aSjeremylt 
23227a982d89SJeremy L. Thompson   @ref User
2323cae8b89aSjeremylt **/
2324cae8b89aSjeremylt int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
2325cae8b89aSjeremylt                          CeedRequest *request) {
2326cae8b89aSjeremylt   int ierr;
2327e2f04181SAndrew T. Barker   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
2328cae8b89aSjeremylt 
2329d1d35e2fSjeremylt   if (op->num_elem)  {
2330250756a7Sjeremylt     // Standard Operator
2331250756a7Sjeremylt     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
2332250756a7Sjeremylt   } else if (op->composite) {
2333250756a7Sjeremylt     // Composite Operator
2334250756a7Sjeremylt     if (op->ApplyAddComposite) {
2335250756a7Sjeremylt       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
2336cae8b89aSjeremylt     } else {
2337d1d35e2fSjeremylt       CeedInt num_suboperators;
2338d1d35e2fSjeremylt       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
2339d1d35e2fSjeremylt       CeedOperator *sub_operators;
2340d1d35e2fSjeremylt       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
2341250756a7Sjeremylt 
2342d1d35e2fSjeremylt       for (CeedInt i=0; i<num_suboperators; i++) {
2343d1d35e2fSjeremylt         ierr = CeedOperatorApplyAdd(sub_operators[i], in, out, request);
2344cae8b89aSjeremylt         CeedChk(ierr);
23451d7d2407SJeremy L Thompson       }
2346250756a7Sjeremylt     }
2347250756a7Sjeremylt   }
2348e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
2349d7b241e6Sjeremylt }
2350d7b241e6Sjeremylt 
2351d7b241e6Sjeremylt /**
2352b11c1e72Sjeremylt   @brief Destroy a CeedOperator
2353d7b241e6Sjeremylt 
2354d7b241e6Sjeremylt   @param op  CeedOperator to destroy
2355b11c1e72Sjeremylt 
2356b11c1e72Sjeremylt   @return An error code: 0 - success, otherwise - failure
2357dfdf5a53Sjeremylt 
23587a982d89SJeremy L. Thompson   @ref User
2359b11c1e72Sjeremylt **/
2360d7b241e6Sjeremylt int CeedOperatorDestroy(CeedOperator *op) {
2361d7b241e6Sjeremylt   int ierr;
2362d7b241e6Sjeremylt 
2363d1d35e2fSjeremylt   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
2364d7b241e6Sjeremylt   if ((*op)->Destroy) {
2365d7b241e6Sjeremylt     ierr = (*op)->Destroy(*op); CeedChk(ierr);
2366d7b241e6Sjeremylt   }
2367fe2413ffSjeremylt   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
2368fe2413ffSjeremylt   // Free fields
2369d1d35e2fSjeremylt   for (int i=0; i<(*op)->num_fields; i++)
2370d1d35e2fSjeremylt     if ((*op)->input_fields[i]) {
2371d1d35e2fSjeremylt       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
2372d1d35e2fSjeremylt         ierr = CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr);
237371352a93Sjeremylt         CeedChk(ierr);
237415910d16Sjeremylt       }
2375d1d35e2fSjeremylt       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
2376d1d35e2fSjeremylt         ierr = CeedBasisDestroy(&(*op)->input_fields[i]->basis); CeedChk(ierr);
237771352a93Sjeremylt       }
2378d1d35e2fSjeremylt       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE &&
2379d1d35e2fSjeremylt           (*op)->input_fields[i]->vec != CEED_VECTOR_NONE ) {
2380d1d35e2fSjeremylt         ierr = CeedVectorDestroy(&(*op)->input_fields[i]->vec); CeedChk(ierr);
238171352a93Sjeremylt       }
2382d1d35e2fSjeremylt       ierr = CeedFree(&(*op)->input_fields[i]->field_name); CeedChk(ierr);
2383d1d35e2fSjeremylt       ierr = CeedFree(&(*op)->input_fields[i]); CeedChk(ierr);
2384fe2413ffSjeremylt     }
2385d1d35e2fSjeremylt   for (int i=0; i<(*op)->num_fields; i++)
2386d1d35e2fSjeremylt     if ((*op)->output_fields[i]) {
2387d1d35e2fSjeremylt       ierr = CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr);
238871352a93Sjeremylt       CeedChk(ierr);
2389d1d35e2fSjeremylt       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
2390d1d35e2fSjeremylt         ierr = CeedBasisDestroy(&(*op)->output_fields[i]->basis); CeedChk(ierr);
239171352a93Sjeremylt       }
2392d1d35e2fSjeremylt       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE &&
2393d1d35e2fSjeremylt           (*op)->output_fields[i]->vec != CEED_VECTOR_NONE ) {
2394d1d35e2fSjeremylt         ierr = CeedVectorDestroy(&(*op)->output_fields[i]->vec); CeedChk(ierr);
239571352a93Sjeremylt       }
2396d1d35e2fSjeremylt       ierr = CeedFree(&(*op)->output_fields[i]->field_name); CeedChk(ierr);
2397d1d35e2fSjeremylt       ierr = CeedFree(&(*op)->output_fields[i]); CeedChk(ierr);
2398fe2413ffSjeremylt     }
2399d1d35e2fSjeremylt   // Destroy sub_operators
2400d1d35e2fSjeremylt   for (int i=0; i<(*op)->num_suboperators; i++)
2401d1d35e2fSjeremylt     if ((*op)->sub_operators[i]) {
2402d1d35e2fSjeremylt       ierr = CeedOperatorDestroy(&(*op)->sub_operators[i]); CeedChk(ierr);
240352d6035fSJeremy L Thompson     }
2404e15f9bd0SJeremy L Thompson   if ((*op)->qf)
2405e15f9bd0SJeremy L Thompson     // LCOV_EXCL_START
2406d1d35e2fSjeremylt     (*op)->qf->operators_set--;
2407e15f9bd0SJeremy L Thompson   // LCOV_EXCL_STOP
2408d7b241e6Sjeremylt   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
2409e15f9bd0SJeremy L Thompson   if ((*op)->dqf && (*op)->dqf != CEED_QFUNCTION_NONE)
2410e15f9bd0SJeremy L Thompson     // LCOV_EXCL_START
2411d1d35e2fSjeremylt     (*op)->dqf->operators_set--;
2412e15f9bd0SJeremy L Thompson   // LCOV_EXCL_STOP
2413d7b241e6Sjeremylt   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
2414e15f9bd0SJeremy L Thompson   if ((*op)->dqfT && (*op)->dqfT != CEED_QFUNCTION_NONE)
2415e15f9bd0SJeremy L Thompson     // LCOV_EXCL_START
2416d1d35e2fSjeremylt     (*op)->dqfT->operators_set--;
2417e15f9bd0SJeremy L Thompson   // LCOV_EXCL_STOP
2418d7b241e6Sjeremylt   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
2419fe2413ffSjeremylt 
24205107b09fSJeremy L Thompson   // Destroy fallback
2421d1d35e2fSjeremylt   if ((*op)->op_fallback) {
2422d1d35e2fSjeremylt     ierr = (*op)->qf_fallback->Destroy((*op)->qf_fallback); CeedChk(ierr);
2423d1d35e2fSjeremylt     ierr = CeedFree(&(*op)->qf_fallback); CeedChk(ierr);
2424d1d35e2fSjeremylt     ierr = (*op)->op_fallback->Destroy((*op)->op_fallback); CeedChk(ierr);
2425d1d35e2fSjeremylt     ierr = CeedFree(&(*op)->op_fallback); CeedChk(ierr);
24265107b09fSJeremy L Thompson   }
24275107b09fSJeremy L Thompson 
2428d1d35e2fSjeremylt   ierr = CeedFree(&(*op)->input_fields); CeedChk(ierr);
2429d1d35e2fSjeremylt   ierr = CeedFree(&(*op)->output_fields); CeedChk(ierr);
2430d1d35e2fSjeremylt   ierr = CeedFree(&(*op)->sub_operators); CeedChk(ierr);
2431d7b241e6Sjeremylt   ierr = CeedFree(op); CeedChk(ierr);
2432e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
2433d7b241e6Sjeremylt }
2434d7b241e6Sjeremylt 
2435d7b241e6Sjeremylt /// @}
2436