xref: /libCEED/interface/ceed-operator.c (revision 70a7ffb32f2ac8f6de27898459ba6016395fdb67)
1 // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at
2 // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights
3 // reserved. See files LICENSE and NOTICE for details.
4 //
5 // This file is part of CEED, a collection of benchmarks, miniapps, software
6 // libraries and APIs for efficient high-order finite element and spectral
7 // element discretizations for exascale applications. For more information and
8 // source code availability see http://github.com/ceed.
9 //
10 // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
11 // a collaborative effort of two U.S. Department of Energy organizations (Office
12 // of Science and the National Nuclear Security Administration) responsible for
13 // the planning and preparation of a capable exascale ecosystem, including
14 // software, applications, hardware, advanced system engineering and early
15 // testbed platforms, in support of the nation's exascale computing imperative.
16 
17 #include <ceed/ceed.h>
18 #include <ceed/backend.h>
19 #include <ceed-impl.h>
20 #include <math.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <string.h>
24 
25 /// @file
26 /// Implementation of CeedOperator interfaces
27 
28 /// ----------------------------------------------------------------------------
29 /// CeedOperator Library Internal Functions
30 /// ----------------------------------------------------------------------------
31 /// @addtogroup CeedOperatorDeveloper
32 /// @{
33 
34 /**
35   @brief Check if a CeedOperator Field matches the QFunction Field
36 
37   @param[in] ceed      Ceed object for error handling
38   @param[in] qf_field  QFunction Field matching Operator Field
39   @param[in] r         Operator Field ElemRestriction
40   @param[in] b         Operator Field Basis
41 
42   @return An error code: 0 - success, otherwise - failure
43 
44   @ref Developer
45 **/
46 static int CeedOperatorCheckField(Ceed ceed, CeedQFunctionField qf_field,
47                                   CeedElemRestriction r, CeedBasis b) {
48   int ierr;
49   CeedEvalMode eval_mode = qf_field->eval_mode;
50   CeedInt dim = 1, num_comp = 1, restr_num_comp = 1, size = qf_field->size;
51   // Restriction
52   if (r != CEED_ELEMRESTRICTION_NONE) {
53     if (eval_mode == CEED_EVAL_WEIGHT) {
54       // LCOV_EXCL_START
55       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
56                        "CEED_ELEMRESTRICTION_NONE should be used "
57                        "for a field with eval mode CEED_EVAL_WEIGHT");
58       // LCOV_EXCL_STOP
59     }
60     ierr = CeedElemRestrictionGetNumComponents(r, &restr_num_comp);
61     CeedChk(ierr);
62   }
63   if ((r == CEED_ELEMRESTRICTION_NONE) != (eval_mode == CEED_EVAL_WEIGHT)) {
64     // LCOV_EXCL_START
65     return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
66                      "CEED_ELEMRESTRICTION_NONE and CEED_EVAL_WEIGHT "
67                      "must be used together.");
68     // LCOV_EXCL_STOP
69   }
70   // Basis
71   if (b != CEED_BASIS_COLLOCATED) {
72     if (eval_mode == CEED_EVAL_NONE)
73       // LCOV_EXCL_START
74       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
75                        "Field '%s' configured with CEED_EVAL_NONE must "
76                        "be used with CEED_BASIS_COLLOCATED",
77                        // LCOV_EXCL_STOP
78                        qf_field->field_name);
79     ierr = CeedBasisGetDimension(b, &dim); CeedChk(ierr);
80     ierr = CeedBasisGetNumComponents(b, &num_comp); CeedChk(ierr);
81     if (r != CEED_ELEMRESTRICTION_NONE && restr_num_comp != num_comp) {
82       // LCOV_EXCL_START
83       return CeedError(ceed, CEED_ERROR_DIMENSION,
84                        "Field '%s' of size %d and EvalMode %s: ElemRestriction "
85                        "has %d components, but Basis has %d components",
86                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
87                        restr_num_comp,
88                        num_comp);
89       // LCOV_EXCL_STOP
90     }
91   }
92   // Field size
93   switch(eval_mode) {
94   case CEED_EVAL_NONE:
95     if (size != restr_num_comp)
96       // LCOV_EXCL_START
97       return CeedError(ceed, CEED_ERROR_DIMENSION,
98                        "Field '%s' of size %d and EvalMode %s: ElemRestriction has %d components",
99                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
100                        restr_num_comp);
101     // LCOV_EXCL_STOP
102     break;
103   case CEED_EVAL_INTERP:
104     if (size != num_comp)
105       // LCOV_EXCL_START
106       return CeedError(ceed, CEED_ERROR_DIMENSION,
107                        "Field '%s' of size %d and EvalMode %s: ElemRestriction/Basis has %d components",
108                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
109                        num_comp);
110     // LCOV_EXCL_STOP
111     break;
112   case CEED_EVAL_GRAD:
113     if (size != num_comp * dim)
114       // LCOV_EXCL_START
115       return CeedError(ceed, CEED_ERROR_DIMENSION,
116                        "Field '%s' of size %d and EvalMode %s in %d dimensions: "
117                        "ElemRestriction/Basis has %d components",
118                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode], dim,
119                        num_comp);
120     // LCOV_EXCL_STOP
121     break;
122   case CEED_EVAL_WEIGHT:
123     // No additional checks required
124     break;
125   case CEED_EVAL_DIV:
126     // Not implemented
127     break;
128   case CEED_EVAL_CURL:
129     // Not implemented
130     break;
131   }
132   return CEED_ERROR_SUCCESS;
133 }
134 
135 /**
136   @brief Check if a CeedOperator is ready to be used.
137 
138   @param[in] op  CeedOperator to check
139 
140   @return An error code: 0 - success, otherwise - failure
141 
142   @ref Developer
143 **/
144 int CeedOperatorCheckReady(CeedOperator op) {
145   int ierr;
146   Ceed ceed;
147   ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
148 
149   if (op->is_interface_setup)
150     return CEED_ERROR_SUCCESS;
151 
152   CeedQFunction qf = op->qf;
153   if (op->is_composite) {
154     if (!op->num_suboperators)
155       // LCOV_EXCL_START
156       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No sub_operators set");
157     // LCOV_EXCL_STOP
158   } else {
159     if (op->num_fields == 0)
160       // LCOV_EXCL_START
161       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No operator fields set");
162     // LCOV_EXCL_STOP
163     if (op->num_fields < qf->num_input_fields + qf->num_output_fields)
164       // LCOV_EXCL_START
165       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set");
166     // LCOV_EXCL_STOP
167     if (!op->has_restriction)
168       // LCOV_EXCL_START
169       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
170                        "At least one restriction required");
171     // LCOV_EXCL_STOP
172     if (op->num_qpts == 0)
173       // LCOV_EXCL_START
174       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
175                        "At least one non-collocated basis is required "
176                        "or the number of quadrature points must be set");
177     // LCOV_EXCL_STOP
178   }
179 
180   // Flag as immutable and ready
181   op->is_interface_setup = true;
182   if (op->qf && op->qf != CEED_QFUNCTION_NONE)
183     // LCOV_EXCL_START
184     op->qf->is_immutable = true;
185   // LCOV_EXCL_STOP
186   if (op->dqf && op->dqf != CEED_QFUNCTION_NONE)
187     // LCOV_EXCL_START
188     op->dqf->is_immutable = true;
189   // LCOV_EXCL_STOP
190   if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE)
191     // LCOV_EXCL_START
192     op->dqfT->is_immutable = true;
193   // LCOV_EXCL_STOP
194   return CEED_ERROR_SUCCESS;
195 }
196 
197 /**
198   @brief View a field of a CeedOperator
199 
200   @param[in] field         Operator field to view
201   @param[in] qf_field      QFunction field (carries field name)
202   @param[in] field_number  Number of field being viewed
203   @param[in] sub           true indicates sub-operator, which increases indentation; false for top-level operator
204   @param[in] input         true for an input field; false for output field
205   @param[in] stream        Stream to view to, e.g., stdout
206 
207   @return An error code: 0 - success, otherwise - failure
208 
209   @ref Utility
210 **/
211 static int CeedOperatorFieldView(CeedOperatorField field,
212                                  CeedQFunctionField qf_field,
213                                  CeedInt field_number, bool sub, bool input,
214                                  FILE *stream) {
215   const char *pre = sub ? "  " : "";
216   const char *in_out = input ? "Input" : "Output";
217 
218   fprintf(stream, "%s    %s Field [%d]:\n"
219           "%s      Name: \"%s\"\n",
220           pre, in_out, field_number, pre, qf_field->field_name);
221 
222   if (field->basis == CEED_BASIS_COLLOCATED)
223     fprintf(stream, "%s      Collocated basis\n", pre);
224 
225   if (field->vec == CEED_VECTOR_ACTIVE)
226     fprintf(stream, "%s      Active vector\n", pre);
227   else if (field->vec == CEED_VECTOR_NONE)
228     fprintf(stream, "%s      No vector\n", pre);
229   return CEED_ERROR_SUCCESS;
230 }
231 
232 /**
233   @brief View a single CeedOperator
234 
235   @param[in] op      CeedOperator to view
236   @param[in] sub     Boolean flag for sub-operator
237   @param[in] stream  Stream to write; typically stdout/stderr or a file
238 
239   @return Error code: 0 - success, otherwise - failure
240 
241   @ref Utility
242 **/
243 int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) {
244   int ierr;
245   const char *pre = sub ? "  " : "";
246 
247   CeedInt total_fields = 0;
248   ierr = CeedOperatorGetNumArgs(op, &total_fields); CeedChk(ierr);
249 
250   fprintf(stream, "%s  %d Field%s\n", pre, total_fields,
251           total_fields>1 ? "s" : "");
252 
253   fprintf(stream, "%s  %d Input Field%s:\n", pre, op->qf->num_input_fields,
254           op->qf->num_input_fields>1 ? "s" : "");
255   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
256     ierr = CeedOperatorFieldView(op->input_fields[i], op->qf->input_fields[i],
257                                  i, sub, 1, stream); CeedChk(ierr);
258   }
259 
260   fprintf(stream, "%s  %d Output Field%s:\n", pre, op->qf->num_output_fields,
261           op->qf->num_output_fields>1 ? "s" : "");
262   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
263     ierr = CeedOperatorFieldView(op->output_fields[i], op->qf->output_fields[i],
264                                  i, sub, 0, stream); CeedChk(ierr);
265   }
266   return CEED_ERROR_SUCCESS;
267 }
268 
269 /**
270   @brief Find the active vector basis for a CeedOperator
271 
272   @param[in] op             CeedOperator to find active basis for
273   @param[out] active_basis  Basis for active input vector
274 
275   @return An error code: 0 - success, otherwise - failure
276 
277   @ ref Developer
278 **/
279 int CeedOperatorGetActiveBasis(CeedOperator op, CeedBasis *active_basis) {
280   *active_basis = NULL;
281   for (int i = 0; i < op->qf->num_input_fields; i++)
282     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
283       *active_basis = op->input_fields[i]->basis;
284       break;
285     }
286 
287   if (!*active_basis) {
288     // LCOV_EXCL_START
289     int ierr;
290     Ceed ceed;
291     ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
292     return CeedError(ceed, CEED_ERROR_MINOR,
293                      "No active CeedBasis found");
294     // LCOV_EXCL_STOP
295   }
296   return CEED_ERROR_SUCCESS;
297 }
298 
299 /**
300   @brief Find the active vector ElemRestriction for a CeedOperator
301 
302   @param[in] op            CeedOperator to find active basis for
303   @param[out] active_rstr  ElemRestriction for active input vector
304 
305   @return An error code: 0 - success, otherwise - failure
306 
307   @ref Utility
308 **/
309 int CeedOperatorGetActiveElemRestriction(CeedOperator op,
310     CeedElemRestriction *active_rstr) {
311   *active_rstr = NULL;
312   for (int i = 0; i < op->qf->num_input_fields; i++)
313     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
314       *active_rstr = op->input_fields[i]->elem_restr;
315       break;
316     }
317 
318   if (!*active_rstr) {
319     // LCOV_EXCL_START
320     int ierr;
321     Ceed ceed;
322     ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
323     return CeedError(ceed, CEED_ERROR_INCOMPLETE,
324                      "No active CeedElemRestriction found");
325     // LCOV_EXCL_STOP
326   }
327   return CEED_ERROR_SUCCESS;
328 }
329 
330 /// @}
331 
332 /// ----------------------------------------------------------------------------
333 /// CeedOperator Backend API
334 /// ----------------------------------------------------------------------------
335 /// @addtogroup CeedOperatorBackend
336 /// @{
337 
338 /**
339   @brief Get the number of arguments associated with a CeedOperator
340 
341   @param op             CeedOperator
342   @param[out] num_args  Variable to store vector number of arguments
343 
344   @return An error code: 0 - success, otherwise - failure
345 
346   @ref Backend
347 **/
348 
349 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *num_args) {
350   if (op->is_composite)
351     // LCOV_EXCL_START
352     return CeedError(op->ceed, CEED_ERROR_MINOR,
353                      "Not defined for composite operators");
354   // LCOV_EXCL_STOP
355 
356   *num_args = op->num_fields;
357   return CEED_ERROR_SUCCESS;
358 }
359 
360 /**
361   @brief Get the setup status of a CeedOperator
362 
363   @param op                  CeedOperator
364   @param[out] is_setup_done  Variable to store setup status
365 
366   @return An error code: 0 - success, otherwise - failure
367 
368   @ref Backend
369 **/
370 
371 int CeedOperatorIsSetupDone(CeedOperator op, bool *is_setup_done) {
372   *is_setup_done = op->is_backend_setup;
373   return CEED_ERROR_SUCCESS;
374 }
375 
376 /**
377   @brief Get the QFunction associated with a CeedOperator
378 
379   @param op       CeedOperator
380   @param[out] qf  Variable to store QFunction
381 
382   @return An error code: 0 - success, otherwise - failure
383 
384   @ref Backend
385 **/
386 
387 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
388   if (op->is_composite)
389     // LCOV_EXCL_START
390     return CeedError(op->ceed, CEED_ERROR_MINOR,
391                      "Not defined for composite operator");
392   // LCOV_EXCL_STOP
393 
394   *qf = op->qf;
395   return CEED_ERROR_SUCCESS;
396 }
397 
398 /**
399   @brief Get a boolean value indicating if the CeedOperator is composite
400 
401   @param op                 CeedOperator
402   @param[out] is_composite  Variable to store composite status
403 
404   @return An error code: 0 - success, otherwise - failure
405 
406   @ref Backend
407 **/
408 
409 int CeedOperatorIsComposite(CeedOperator op, bool *is_composite) {
410   *is_composite = op->is_composite;
411   return CEED_ERROR_SUCCESS;
412 }
413 
414 /**
415   @brief Get the number of sub_operators associated with a CeedOperator
416 
417   @param op                     CeedOperator
418   @param[out] num_suboperators  Variable to store number of sub_operators
419 
420   @return An error code: 0 - success, otherwise - failure
421 
422   @ref Backend
423 **/
424 
425 int CeedOperatorGetNumSub(CeedOperator op, CeedInt *num_suboperators) {
426   if (!op->is_composite)
427     // LCOV_EXCL_START
428     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
429   // LCOV_EXCL_STOP
430 
431   *num_suboperators = op->num_suboperators;
432   return CEED_ERROR_SUCCESS;
433 }
434 
435 /**
436   @brief Get the list of sub_operators associated with a CeedOperator
437 
438   @param op                  CeedOperator
439   @param[out] sub_operators  Variable to store list of sub_operators
440 
441   @return An error code: 0 - success, otherwise - failure
442 
443   @ref Backend
444 **/
445 
446 int CeedOperatorGetSubList(CeedOperator op, CeedOperator **sub_operators) {
447   if (!op->is_composite)
448     // LCOV_EXCL_START
449     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
450   // LCOV_EXCL_STOP
451 
452   *sub_operators = op->sub_operators;
453   return CEED_ERROR_SUCCESS;
454 }
455 
456 /**
457   @brief Get the backend data of a CeedOperator
458 
459   @param op         CeedOperator
460   @param[out] data  Variable to store data
461 
462   @return An error code: 0 - success, otherwise - failure
463 
464   @ref Backend
465 **/
466 
467 int CeedOperatorGetData(CeedOperator op, void *data) {
468   *(void **)data = op->data;
469   return CEED_ERROR_SUCCESS;
470 }
471 
472 /**
473   @brief Set the backend data of a CeedOperator
474 
475   @param[out] op  CeedOperator
476   @param data     Data to set
477 
478   @return An error code: 0 - success, otherwise - failure
479 
480   @ref Backend
481 **/
482 
483 int CeedOperatorSetData(CeedOperator op, void *data) {
484   op->data = data;
485   return CEED_ERROR_SUCCESS;
486 }
487 
488 /**
489   @brief Increment the reference counter for a CeedOperator
490 
491   @param op  CeedOperator to increment the reference counter
492 
493   @return An error code: 0 - success, otherwise - failure
494 
495   @ref Backend
496 **/
497 int CeedOperatorReference(CeedOperator op) {
498   op->ref_count++;
499   return CEED_ERROR_SUCCESS;
500 }
501 
502 /**
503   @brief Set the setup flag of a CeedOperator to True
504 
505   @param op  CeedOperator
506 
507   @return An error code: 0 - success, otherwise - failure
508 
509   @ref Backend
510 **/
511 
512 int CeedOperatorSetSetupDone(CeedOperator op) {
513   op->is_backend_setup = true;
514   return CEED_ERROR_SUCCESS;
515 }
516 
517 /// @}
518 
519 /// ----------------------------------------------------------------------------
520 /// CeedOperator Public API
521 /// ----------------------------------------------------------------------------
522 /// @addtogroup CeedOperatorUser
523 /// @{
524 
525 /**
526   @brief Create a CeedOperator and associate a CeedQFunction. A CeedBasis and
527            CeedElemRestriction can be associated with CeedQFunction fields with
528            \ref CeedOperatorSetField.
529 
530   @param ceed     A Ceed object where the CeedOperator will be created
531   @param qf       QFunction defining the action of the operator at quadrature points
532   @param dqf      QFunction defining the action of the Jacobian of @a qf (or
533                     @ref CEED_QFUNCTION_NONE)
534   @param dqfT     QFunction defining the action of the transpose of the Jacobian
535                     of @a qf (or @ref CEED_QFUNCTION_NONE)
536   @param[out] op  Address of the variable where the newly created
537                     CeedOperator will be stored
538 
539   @return An error code: 0 - success, otherwise - failure
540 
541   @ref User
542  */
543 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf,
544                        CeedQFunction dqfT, CeedOperator *op) {
545   int ierr;
546 
547   if (!ceed->OperatorCreate) {
548     Ceed delegate;
549     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
550 
551     if (!delegate)
552       // LCOV_EXCL_START
553       return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
554                        "Backend does not support OperatorCreate");
555     // LCOV_EXCL_STOP
556 
557     ierr = CeedOperatorCreate(delegate, qf, dqf, dqfT, op); CeedChk(ierr);
558     return CEED_ERROR_SUCCESS;
559   }
560 
561   if (!qf || qf == CEED_QFUNCTION_NONE)
562     // LCOV_EXCL_START
563     return CeedError(ceed, CEED_ERROR_MINOR,
564                      "Operator must have a valid QFunction.");
565   // LCOV_EXCL_STOP
566   ierr = CeedCalloc(1, op); CeedChk(ierr);
567   (*op)->ceed = ceed;
568   ierr = CeedReference(ceed); CeedChk(ierr);
569   (*op)->ref_count = 1;
570   (*op)->qf = qf;
571   ierr = CeedQFunctionReference(qf); CeedChk(ierr);
572   if (dqf && dqf != CEED_QFUNCTION_NONE) {
573     (*op)->dqf = dqf;
574     ierr = CeedQFunctionReference(dqf); CeedChk(ierr);
575   }
576   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
577     (*op)->dqfT = dqfT;
578     ierr = CeedQFunctionReference(dqfT); CeedChk(ierr);
579   }
580   ierr = CeedCalloc(16, &(*op)->input_fields); CeedChk(ierr);
581   ierr = CeedCalloc(16, &(*op)->output_fields); CeedChk(ierr);
582   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
583   return CEED_ERROR_SUCCESS;
584 }
585 
586 /**
587   @brief Create an operator that composes the action of several operators
588 
589   @param ceed     A Ceed object where the CeedOperator will be created
590   @param[out] op  Address of the variable where the newly created
591                     Composite CeedOperator will be stored
592 
593   @return An error code: 0 - success, otherwise - failure
594 
595   @ref User
596  */
597 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
598   int ierr;
599 
600   if (!ceed->CompositeOperatorCreate) {
601     Ceed delegate;
602     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
603 
604     if (delegate) {
605       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
606       return CEED_ERROR_SUCCESS;
607     }
608   }
609 
610   ierr = CeedCalloc(1, op); CeedChk(ierr);
611   (*op)->ceed = ceed;
612   ierr = CeedReference(ceed); CeedChk(ierr);
613   (*op)->is_composite = true;
614   ierr = CeedCalloc(16, &(*op)->sub_operators); CeedChk(ierr);
615 
616   if (ceed->CompositeOperatorCreate) {
617     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
618   }
619   return CEED_ERROR_SUCCESS;
620 }
621 
622 /**
623   @brief Copy the pointer to a CeedOperator. Both pointers should
624            be destroyed with `CeedOperatorDestroy()`;
625            Note: If `*op_copy` is non-NULL, then it is assumed that
626            `*op_copy` is a pointer to a CeedOperator. This
627            CeedOperator will be destroyed if `*op_copy` is the only
628            reference to this CeedOperator.
629 
630   @param op            CeedOperator to copy reference to
631   @param[out] op_copy  Variable to store copied reference
632 
633   @return An error code: 0 - success, otherwise - failure
634 
635   @ref User
636 **/
637 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) {
638   int ierr;
639 
640   ierr = CeedOperatorReference(op); CeedChk(ierr);
641   ierr = CeedOperatorDestroy(op_copy); CeedChk(ierr);
642   *op_copy = op;
643   return CEED_ERROR_SUCCESS;
644 }
645 
646 /**
647   @brief Provide a field to a CeedOperator for use by its CeedQFunction
648 
649   This function is used to specify both active and passive fields to a
650   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
651   fields can inputs or outputs (updated in-place when operator is applied).
652 
653   Active fields must be specified using this function, but their data (in a
654   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
655   input and at most one active output.
656 
657   @param op          CeedOperator on which to provide the field
658   @param field_name  Name of the field (to be matched with the name used by
659                        CeedQFunction)
660   @param r           CeedElemRestriction
661   @param b           CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED
662                        if collocated with quadrature points
663   @param v           CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE
664                        if field is active or @ref CEED_VECTOR_NONE if using
665                        @ref CEED_EVAL_WEIGHT in the QFunction
666 
667   @return An error code: 0 - success, otherwise - failure
668 
669   @ref User
670 **/
671 int CeedOperatorSetField(CeedOperator op, const char *field_name,
672                          CeedElemRestriction r, CeedBasis b, CeedVector v) {
673   int ierr;
674   if (op->is_composite)
675     // LCOV_EXCL_START
676     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
677                      "Cannot add field to composite operator.");
678   // LCOV_EXCL_STOP
679   if (op->is_immutable)
680     // LCOV_EXCL_START
681     return CeedError(op->ceed, CEED_ERROR_MAJOR,
682                      "Operator cannot be changed after set as immutable");
683   // LCOV_EXCL_STOP
684   if (!r)
685     // LCOV_EXCL_START
686     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
687                      "ElemRestriction r for field \"%s\" must be non-NULL.",
688                      field_name);
689   // LCOV_EXCL_STOP
690   if (!b)
691     // LCOV_EXCL_START
692     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
693                      "Basis b for field \"%s\" must be non-NULL.",
694                      field_name);
695   // LCOV_EXCL_STOP
696   if (!v)
697     // LCOV_EXCL_START
698     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
699                      "Vector v for field \"%s\" must be non-NULL.",
700                      field_name);
701   // LCOV_EXCL_STOP
702 
703   CeedInt num_elem;
704   ierr = CeedElemRestrictionGetNumElements(r, &num_elem); CeedChk(ierr);
705   if (r != CEED_ELEMRESTRICTION_NONE && op->has_restriction &&
706       op->num_elem != num_elem)
707     // LCOV_EXCL_START
708     return CeedError(op->ceed, CEED_ERROR_DIMENSION,
709                      "ElemRestriction with %d elements incompatible with prior "
710                      "%d elements", num_elem, op->num_elem);
711   // LCOV_EXCL_STOP
712 
713   CeedInt num_qpts = 0;
714   if (b != CEED_BASIS_COLLOCATED) {
715     ierr = CeedBasisGetNumQuadraturePoints(b, &num_qpts); CeedChk(ierr);
716     if (op->num_qpts && op->num_qpts != num_qpts)
717       // LCOV_EXCL_START
718       return CeedError(op->ceed, CEED_ERROR_DIMENSION,
719                        "Basis with %d quadrature points "
720                        "incompatible with prior %d points", num_qpts,
721                        op->num_qpts);
722     // LCOV_EXCL_STOP
723   }
724   CeedQFunctionField qf_field;
725   CeedOperatorField *op_field;
726   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
727     if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) {
728       qf_field = op->qf->input_fields[i];
729       op_field = &op->input_fields[i];
730       goto found;
731     }
732   }
733   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
734     if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) {
735       qf_field = op->qf->output_fields[i];
736       op_field = &op->output_fields[i];
737       goto found;
738     }
739   }
740   // LCOV_EXCL_START
741   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE,
742                    "QFunction has no knowledge of field '%s'",
743                    field_name);
744   // LCOV_EXCL_STOP
745 found:
746   ierr = CeedOperatorCheckField(op->ceed, qf_field, r, b); CeedChk(ierr);
747   ierr = CeedCalloc(1, op_field); CeedChk(ierr);
748 
749   (*op_field)->vec = v;
750   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE) {
751     ierr = CeedVectorReference(v); CeedChk(ierr);
752   }
753 
754   (*op_field)->elem_restr = r;
755   ierr = CeedElemRestrictionReference(r); CeedChk(ierr);
756   if (r != CEED_ELEMRESTRICTION_NONE) {
757     op->num_elem = num_elem;
758     op->has_restriction = true; // Restriction set, but num_elem may be 0
759   }
760 
761   (*op_field)->basis = b;
762   if (b != CEED_BASIS_COLLOCATED) {
763     if (!op->num_qpts) {
764       ierr = CeedOperatorSetNumQuadraturePoints(op, num_qpts); CeedChk(ierr);
765     }
766     ierr = CeedBasisReference(b); CeedChk(ierr);
767   }
768 
769   op->num_fields += 1;
770   size_t len = strlen(field_name);
771   char *tmp;
772   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
773   memcpy(tmp, field_name, len+1);
774   (*op_field)->field_name = tmp;
775   return CEED_ERROR_SUCCESS;
776 }
777 
778 /**
779   @brief Get the CeedOperatorFields of a CeedOperator
780 
781   Note: Calling this function asserts that setup is complete
782           and sets the CeedOperator as immutable.
783 
784   @param op                  CeedOperator
785   @param[out] input_fields   Variable to store input_fields
786   @param[out] output_fields  Variable to store output_fields
787 
788   @return An error code: 0 - success, otherwise - failure
789 
790   @ref Advanced
791 **/
792 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields,
793                           CeedOperatorField **input_fields,
794                           CeedInt *num_output_fields,
795                           CeedOperatorField **output_fields) {
796   int ierr;
797 
798   if (op->is_composite)
799     // LCOV_EXCL_START
800     return CeedError(op->ceed, CEED_ERROR_MINOR,
801                      "Not defined for composite operator");
802   // LCOV_EXCL_STOP
803   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
804 
805   if (num_input_fields) *num_input_fields = op->qf->num_input_fields;
806   if (input_fields) *input_fields = op->input_fields;
807   if (num_output_fields) *num_output_fields = op->qf->num_output_fields;
808   if (output_fields) *output_fields = op->output_fields;
809   return CEED_ERROR_SUCCESS;
810 }
811 
812 /**
813   @brief Get the name of a CeedOperatorField
814 
815   @param op_field         CeedOperatorField
816   @param[out] field_name  Variable to store the field name
817 
818   @return An error code: 0 - success, otherwise - failure
819 
820   @ref Advanced
821 **/
822 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) {
823   *field_name = (char *)op_field->field_name;
824   return CEED_ERROR_SUCCESS;
825 }
826 
827 /**
828   @brief Get the CeedElemRestriction of a CeedOperatorField
829 
830   @param op_field   CeedOperatorField
831   @param[out] rstr  Variable to store CeedElemRestriction
832 
833   @return An error code: 0 - success, otherwise - failure
834 
835   @ref Advanced
836 **/
837 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field,
838                                         CeedElemRestriction *rstr) {
839   *rstr = op_field->elem_restr;
840   return CEED_ERROR_SUCCESS;
841 }
842 
843 /**
844   @brief Get the CeedBasis of a CeedOperatorField
845 
846   @param op_field    CeedOperatorField
847   @param[out] basis  Variable to store CeedBasis
848 
849   @return An error code: 0 - success, otherwise - failure
850 
851   @ref Advanced
852 **/
853 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) {
854   *basis = op_field->basis;
855   return CEED_ERROR_SUCCESS;
856 }
857 
858 /**
859   @brief Get the CeedVector of a CeedOperatorField
860 
861   @param op_field  CeedOperatorField
862   @param[out] vec  Variable to store CeedVector
863 
864   @return An error code: 0 - success, otherwise - failure
865 
866   @ref Advanced
867 **/
868 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) {
869   *vec = op_field->vec;
870   return CEED_ERROR_SUCCESS;
871 }
872 
873 /**
874   @brief Add a sub-operator to a composite CeedOperator
875 
876   @param[out] composite_op  Composite CeedOperator
877   @param      sub_op        Sub-operator CeedOperator
878 
879   @return An error code: 0 - success, otherwise - failure
880 
881   @ref User
882  */
883 int CeedCompositeOperatorAddSub(CeedOperator composite_op,
884                                 CeedOperator sub_op) {
885   int ierr;
886 
887   if (!composite_op->is_composite)
888     // LCOV_EXCL_START
889     return CeedError(composite_op->ceed, CEED_ERROR_MINOR,
890                      "CeedOperator is not a composite operator");
891   // LCOV_EXCL_STOP
892 
893   if (composite_op->num_suboperators == CEED_COMPOSITE_MAX)
894     // LCOV_EXCL_START
895     return CeedError(composite_op->ceed, CEED_ERROR_UNSUPPORTED,
896                      "Cannot add additional sub_operators");
897   // LCOV_EXCL_STOP
898   if (composite_op->is_immutable)
899     // LCOV_EXCL_START
900     return CeedError(composite_op->ceed, CEED_ERROR_MAJOR,
901                      "Operator cannot be changed after set as immutable");
902   // LCOV_EXCL_STOP
903 
904   composite_op->sub_operators[composite_op->num_suboperators] = sub_op;
905   ierr = CeedOperatorReference(sub_op); CeedChk(ierr);
906   composite_op->num_suboperators++;
907   return CEED_ERROR_SUCCESS;
908 }
909 
910 /**
911   @brief Set the number of quadrature points associated with a CeedOperator.
912            This should be used when creating a CeedOperator where every
913            field has a collocated basis. This function cannot be used for
914            composite CeedOperators.
915 
916   @param op        CeedOperator
917   @param num_qpts  Number of quadrature points to set
918 
919   @return An error code: 0 - success, otherwise - failure
920 
921   @ref Advanced
922 **/
923 
924 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
925   if (op->is_composite)
926     // LCOV_EXCL_START
927     return CeedError(op->ceed, CEED_ERROR_MINOR,
928                      "Not defined for composite operator");
929   // LCOV_EXCL_STOP
930   if (op->num_qpts)
931     // LCOV_EXCL_START
932     return CeedError(op->ceed, CEED_ERROR_MINOR,
933                      "Number of quadrature points already defined");
934   // LCOV_EXCL_STOP
935   if (op->is_immutable)
936     // LCOV_EXCL_START
937     return CeedError(op->ceed, CEED_ERROR_MAJOR,
938                      "Operator cannot be changed after set as immutable");
939   // LCOV_EXCL_STOP
940 
941   op->num_qpts = num_qpts;
942   return CEED_ERROR_SUCCESS;
943 }
944 
945 /**
946   @brief View a CeedOperator
947 
948   @param[in] op      CeedOperator to view
949   @param[in] stream  Stream to write; typically stdout/stderr or a file
950 
951   @return Error code: 0 - success, otherwise - failure
952 
953   @ref User
954 **/
955 int CeedOperatorView(CeedOperator op, FILE *stream) {
956   int ierr;
957 
958   if (op->is_composite) {
959     fprintf(stream, "Composite CeedOperator\n");
960 
961     for (CeedInt i=0; i<op->num_suboperators; i++) {
962       fprintf(stream, "  SubOperator [%d]:\n", i);
963       ierr = CeedOperatorSingleView(op->sub_operators[i], 1, stream);
964       CeedChk(ierr);
965     }
966   } else {
967     fprintf(stream, "CeedOperator\n");
968     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
969   }
970   return CEED_ERROR_SUCCESS;
971 }
972 
973 /**
974   @brief Get the Ceed associated with a CeedOperator
975 
976   @param op         CeedOperator
977   @param[out] ceed  Variable to store Ceed
978 
979   @return An error code: 0 - success, otherwise - failure
980 
981   @ref Advanced
982 **/
983 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
984   *ceed = op->ceed;
985   return CEED_ERROR_SUCCESS;
986 }
987 
988 /**
989   @brief Get the number of elements associated with a CeedOperator
990 
991   @param op             CeedOperator
992   @param[out] num_elem  Variable to store number of elements
993 
994   @return An error code: 0 - success, otherwise - failure
995 
996   @ref Advanced
997 **/
998 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) {
999   if (op->is_composite)
1000     // LCOV_EXCL_START
1001     return CeedError(op->ceed, CEED_ERROR_MINOR,
1002                      "Not defined for composite operator");
1003   // LCOV_EXCL_STOP
1004 
1005   *num_elem = op->num_elem;
1006   return CEED_ERROR_SUCCESS;
1007 }
1008 
1009 /**
1010   @brief Get the number of quadrature points associated with a CeedOperator
1011 
1012   @param op             CeedOperator
1013   @param[out] num_qpts  Variable to store vector number of quadrature points
1014 
1015   @return An error code: 0 - success, otherwise - failure
1016 
1017   @ref Advanced
1018 **/
1019 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) {
1020   if (op->is_composite)
1021     // LCOV_EXCL_START
1022     return CeedError(op->ceed, CEED_ERROR_MINOR,
1023                      "Not defined for composite operator");
1024   // LCOV_EXCL_STOP
1025 
1026   *num_qpts = op->num_qpts;
1027   return CEED_ERROR_SUCCESS;
1028 }
1029 
1030 /**
1031   @brief Apply CeedOperator to a vector
1032 
1033   This computes the action of the operator on the specified (active) input,
1034   yielding its (active) output.  All inputs and outputs must be specified using
1035   CeedOperatorSetField().
1036 
1037   Note: Calling this function asserts that setup is complete
1038           and sets the CeedOperator as immutable.
1039 
1040   @param op        CeedOperator to apply
1041   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1042                      there are no active inputs
1043   @param[out] out  CeedVector to store result of applying operator (must be
1044                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1045                      active outputs
1046   @param request   Address of CeedRequest for non-blocking completion, else
1047                      @ref CEED_REQUEST_IMMEDIATE
1048 
1049   @return An error code: 0 - success, otherwise - failure
1050 
1051   @ref User
1052 **/
1053 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1054                       CeedRequest *request) {
1055   int ierr;
1056   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1057 
1058   if (op->num_elem)  {
1059     // Standard Operator
1060     if (op->Apply) {
1061       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1062     } else {
1063       // Zero all output vectors
1064       CeedQFunction qf = op->qf;
1065       for (CeedInt i=0; i<qf->num_output_fields; i++) {
1066         CeedVector vec = op->output_fields[i]->vec;
1067         if (vec == CEED_VECTOR_ACTIVE)
1068           vec = out;
1069         if (vec != CEED_VECTOR_NONE) {
1070           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1071         }
1072       }
1073       // Apply
1074       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1075     }
1076   } else if (op->is_composite) {
1077     // Composite Operator
1078     if (op->ApplyComposite) {
1079       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1080     } else {
1081       CeedInt num_suboperators;
1082       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1083       CeedOperator *sub_operators;
1084       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1085 
1086       // Zero all output vectors
1087       if (out != CEED_VECTOR_NONE) {
1088         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1089       }
1090       for (CeedInt i=0; i<num_suboperators; i++) {
1091         for (CeedInt j=0; j<sub_operators[i]->qf->num_output_fields; j++) {
1092           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1093           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1094             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1095           }
1096         }
1097       }
1098       // Apply
1099       for (CeedInt i=0; i<op->num_suboperators; i++) {
1100         ierr = CeedOperatorApplyAdd(op->sub_operators[i], in, out, request);
1101         CeedChk(ierr);
1102       }
1103     }
1104   }
1105   return CEED_ERROR_SUCCESS;
1106 }
1107 
1108 /**
1109   @brief Apply CeedOperator to a vector and add result to output vector
1110 
1111   This computes the action of the operator on the specified (active) input,
1112   yielding its (active) output.  All inputs and outputs must be specified using
1113   CeedOperatorSetField().
1114 
1115   @param op        CeedOperator to apply
1116   @param[in] in    CeedVector containing input state or NULL if there are no
1117                      active inputs
1118   @param[out] out  CeedVector to sum in result of applying operator (must be
1119                      distinct from @a in) or NULL if there are no active outputs
1120   @param request   Address of CeedRequest for non-blocking completion, else
1121                      @ref CEED_REQUEST_IMMEDIATE
1122 
1123   @return An error code: 0 - success, otherwise - failure
1124 
1125   @ref User
1126 **/
1127 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1128                          CeedRequest *request) {
1129   int ierr;
1130   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1131 
1132   if (op->num_elem)  {
1133     // Standard Operator
1134     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1135   } else if (op->is_composite) {
1136     // Composite Operator
1137     if (op->ApplyAddComposite) {
1138       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1139     } else {
1140       CeedInt num_suboperators;
1141       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1142       CeedOperator *sub_operators;
1143       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1144 
1145       for (CeedInt i=0; i<num_suboperators; i++) {
1146         ierr = CeedOperatorApplyAdd(sub_operators[i], in, out, request);
1147         CeedChk(ierr);
1148       }
1149     }
1150   }
1151   return CEED_ERROR_SUCCESS;
1152 }
1153 
1154 /**
1155   @brief Destroy a CeedOperator
1156 
1157   @param op  CeedOperator to destroy
1158 
1159   @return An error code: 0 - success, otherwise - failure
1160 
1161   @ref User
1162 **/
1163 int CeedOperatorDestroy(CeedOperator *op) {
1164   int ierr;
1165 
1166   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
1167   if ((*op)->Destroy) {
1168     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1169   }
1170   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1171   // Free fields
1172   for (int i=0; i<(*op)->num_fields; i++)
1173     if ((*op)->input_fields[i]) {
1174       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
1175         ierr = CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr);
1176         CeedChk(ierr);
1177       }
1178       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1179         ierr = CeedBasisDestroy(&(*op)->input_fields[i]->basis); CeedChk(ierr);
1180       }
1181       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1182           (*op)->input_fields[i]->vec != CEED_VECTOR_NONE ) {
1183         ierr = CeedVectorDestroy(&(*op)->input_fields[i]->vec); CeedChk(ierr);
1184       }
1185       ierr = CeedFree(&(*op)->input_fields[i]->field_name); CeedChk(ierr);
1186       ierr = CeedFree(&(*op)->input_fields[i]); CeedChk(ierr);
1187     }
1188   for (int i=0; i<(*op)->num_fields; i++)
1189     if ((*op)->output_fields[i]) {
1190       ierr = CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr);
1191       CeedChk(ierr);
1192       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1193         ierr = CeedBasisDestroy(&(*op)->output_fields[i]->basis); CeedChk(ierr);
1194       }
1195       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1196           (*op)->output_fields[i]->vec != CEED_VECTOR_NONE ) {
1197         ierr = CeedVectorDestroy(&(*op)->output_fields[i]->vec); CeedChk(ierr);
1198       }
1199       ierr = CeedFree(&(*op)->output_fields[i]->field_name); CeedChk(ierr);
1200       ierr = CeedFree(&(*op)->output_fields[i]); CeedChk(ierr);
1201     }
1202   // Destroy sub_operators
1203   for (int i=0; i<(*op)->num_suboperators; i++)
1204     if ((*op)->sub_operators[i]) {
1205       ierr = CeedOperatorDestroy(&(*op)->sub_operators[i]); CeedChk(ierr);
1206     }
1207   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1208   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1209   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1210 
1211   // Destroy fallback
1212   if ((*op)->op_fallback) {
1213     ierr = (*op)->qf_fallback->Destroy((*op)->qf_fallback); CeedChk(ierr);
1214     ierr = CeedFree(&(*op)->qf_fallback); CeedChk(ierr);
1215     ierr = CeedVectorDestroy(&(*op)->op_fallback->qf_assembled); CeedChk(ierr);
1216     ierr = CeedElemRestrictionDestroy(&(*op)->op_fallback->qf_assembled_rstr);
1217     CeedChk(ierr);
1218     ierr = (*op)->op_fallback->Destroy((*op)->op_fallback); CeedChk(ierr);
1219     ierr = CeedFree(&(*op)->op_fallback); CeedChk(ierr);
1220   }
1221 
1222   // Destroy QF assembly cache
1223   ierr = CeedVectorDestroy(&(*op)->qf_assembled); CeedChk(ierr);
1224   ierr = CeedElemRestrictionDestroy(&(*op)->qf_assembled_rstr); CeedChk(ierr);
1225 
1226   ierr = CeedFree(&(*op)->input_fields); CeedChk(ierr);
1227   ierr = CeedFree(&(*op)->output_fields); CeedChk(ierr);
1228   ierr = CeedFree(&(*op)->sub_operators); CeedChk(ierr);
1229   ierr = CeedFree(op); CeedChk(ierr);
1230   return CEED_ERROR_SUCCESS;
1231 }
1232 
1233 /// @}
1234