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