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