xref: /libCEED/rust/libceed-sys/c-src/interface/ceed-operator.c (revision f04ea552c0bc984e9734ef5cac4fa0e4550ba0f1)
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 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->is_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->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   *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->is_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->is_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->is_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->is_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->is_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->is_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->is_backend_setup = true;
574   return CEED_ERROR_SUCCESS;
575 }
576 
577 /// @}
578 
579 /// ----------------------------------------------------------------------------
580 /// CeedOperator Public API
581 /// ----------------------------------------------------------------------------
582 /// @addtogroup CeedOperatorUser
583 /// @{
584 
585 /**
586   @brief Create a CeedOperator and associate a CeedQFunction. A CeedBasis and
587            CeedElemRestriction can be associated with CeedQFunction fields with
588            \ref CeedOperatorSetField.
589 
590   @param ceed     A Ceed object where the CeedOperator will be created
591   @param qf       QFunction defining the action of the operator at quadrature points
592   @param dqf      QFunction defining the action of the Jacobian of @a qf (or
593                     @ref CEED_QFUNCTION_NONE)
594   @param dqfT     QFunction defining the action of the transpose of the Jacobian
595                     of @a qf (or @ref CEED_QFUNCTION_NONE)
596   @param[out] op  Address of the variable where the newly created
597                     CeedOperator will be stored
598 
599   @return An error code: 0 - success, otherwise - failure
600 
601   @ref User
602  */
603 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf,
604                        CeedQFunction dqfT, CeedOperator *op) {
605   int ierr;
606 
607   if (!ceed->OperatorCreate) {
608     Ceed delegate;
609     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
610 
611     if (!delegate)
612       // LCOV_EXCL_START
613       return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
614                        "Backend does not support OperatorCreate");
615     // LCOV_EXCL_STOP
616 
617     ierr = CeedOperatorCreate(delegate, qf, dqf, dqfT, op); CeedChk(ierr);
618     return CEED_ERROR_SUCCESS;
619   }
620 
621   if (!qf || qf == CEED_QFUNCTION_NONE)
622     // LCOV_EXCL_START
623     return CeedError(ceed, CEED_ERROR_MINOR,
624                      "Operator must have a valid QFunction.");
625   // LCOV_EXCL_STOP
626   ierr = CeedCalloc(1, op); CeedChk(ierr);
627   (*op)->ceed = ceed;
628   ierr = CeedReference(ceed); CeedChk(ierr);
629   (*op)->ref_count = 1;
630   (*op)->qf = qf;
631   ierr = CeedQFunctionReference(qf); CeedChk(ierr);
632   if (dqf && dqf != CEED_QFUNCTION_NONE) {
633     (*op)->dqf = dqf;
634     ierr = CeedQFunctionReference(dqf); CeedChk(ierr);
635   }
636   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
637     (*op)->dqfT = dqfT;
638     ierr = CeedQFunctionReference(dqfT); CeedChk(ierr);
639   }
640   ierr = CeedCalloc(16, &(*op)->input_fields); CeedChk(ierr);
641   ierr = CeedCalloc(16, &(*op)->output_fields); CeedChk(ierr);
642   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
643   return CEED_ERROR_SUCCESS;
644 }
645 
646 /**
647   @brief Create an operator that composes the action of several operators
648 
649   @param ceed     A Ceed object where the CeedOperator will be created
650   @param[out] op  Address of the variable where the newly created
651                     Composite CeedOperator will be stored
652 
653   @return An error code: 0 - success, otherwise - failure
654 
655   @ref User
656  */
657 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
658   int ierr;
659 
660   if (!ceed->CompositeOperatorCreate) {
661     Ceed delegate;
662     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
663 
664     if (delegate) {
665       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
666       return CEED_ERROR_SUCCESS;
667     }
668   }
669 
670   ierr = CeedCalloc(1, op); CeedChk(ierr);
671   (*op)->ceed = ceed;
672   ierr = CeedReference(ceed); CeedChk(ierr);
673   (*op)->is_composite = true;
674   ierr = CeedCalloc(16, &(*op)->sub_operators); CeedChk(ierr);
675 
676   if (ceed->CompositeOperatorCreate) {
677     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
678   }
679   return CEED_ERROR_SUCCESS;
680 }
681 
682 /**
683   @brief Copy the pointer to a CeedOperator. Both pointers should
684            be destroyed with `CeedOperatorDestroy()`;
685            Note: If `*op_copy` is non-NULL, then it is assumed that
686            `*op_copy` is a pointer to a CeedOperator. This
687            CeedOperator will be destroyed if `*op_copy` is the only
688            reference to this CeedOperator.
689 
690   @param op            CeedOperator to copy reference to
691   @param[out] op_copy  Variable to store copied reference
692 
693   @return An error code: 0 - success, otherwise - failure
694 
695   @ref User
696 **/
697 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) {
698   int ierr;
699 
700   ierr = CeedOperatorReference(op); CeedChk(ierr);
701   ierr = CeedOperatorDestroy(op_copy); CeedChk(ierr);
702   *op_copy = op;
703   return CEED_ERROR_SUCCESS;
704 }
705 
706 /**
707   @brief Provide a field to a CeedOperator for use by its CeedQFunction
708 
709   This function is used to specify both active and passive fields to a
710   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
711   fields can inputs or outputs (updated in-place when operator is applied).
712 
713   Active fields must be specified using this function, but their data (in a
714   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
715   input and at most one active output.
716 
717   @param op          CeedOperator on which to provide the field
718   @param field_name  Name of the field (to be matched with the name used by
719                        CeedQFunction)
720   @param r           CeedElemRestriction
721   @param b           CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED
722                        if collocated with quadrature points
723   @param v           CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE
724                        if field is active or @ref CEED_VECTOR_NONE if using
725                        @ref CEED_EVAL_WEIGHT in the QFunction
726 
727   @return An error code: 0 - success, otherwise - failure
728 
729   @ref User
730 **/
731 int CeedOperatorSetField(CeedOperator op, const char *field_name,
732                          CeedElemRestriction r, CeedBasis b, CeedVector v) {
733   int ierr;
734   if (op->is_composite)
735     // LCOV_EXCL_START
736     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
737                      "Cannot add field to composite operator.");
738   // LCOV_EXCL_STOP
739   if (op->is_immutable)
740     // LCOV_EXCL_START
741     return CeedError(op->ceed, CEED_ERROR_MAJOR,
742                      "Operator cannot be changed after set as immutable");
743   // LCOV_EXCL_STOP
744   if (!r)
745     // LCOV_EXCL_START
746     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
747                      "ElemRestriction r for field \"%s\" must be non-NULL.",
748                      field_name);
749   // LCOV_EXCL_STOP
750   if (!b)
751     // LCOV_EXCL_START
752     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
753                      "Basis b for field \"%s\" must be non-NULL.",
754                      field_name);
755   // LCOV_EXCL_STOP
756   if (!v)
757     // LCOV_EXCL_START
758     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
759                      "Vector v for field \"%s\" must be non-NULL.",
760                      field_name);
761   // LCOV_EXCL_STOP
762 
763   CeedInt num_elem;
764   ierr = CeedElemRestrictionGetNumElements(r, &num_elem); CeedChk(ierr);
765   if (r != CEED_ELEMRESTRICTION_NONE && op->has_restriction &&
766       op->num_elem != num_elem)
767     // LCOV_EXCL_START
768     return CeedError(op->ceed, CEED_ERROR_DIMENSION,
769                      "ElemRestriction with %d elements incompatible with prior "
770                      "%d elements", num_elem, op->num_elem);
771   // LCOV_EXCL_STOP
772 
773   CeedInt num_qpts = 0;
774   if (b != CEED_BASIS_COLLOCATED) {
775     ierr = CeedBasisGetNumQuadraturePoints(b, &num_qpts); CeedChk(ierr);
776     if (op->num_qpts && op->num_qpts != num_qpts)
777       // LCOV_EXCL_START
778       return CeedError(op->ceed, CEED_ERROR_DIMENSION,
779                        "Basis with %d quadrature points "
780                        "incompatible with prior %d points", num_qpts,
781                        op->num_qpts);
782     // LCOV_EXCL_STOP
783   }
784   CeedQFunctionField qf_field;
785   CeedOperatorField *op_field;
786   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
787     if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) {
788       qf_field = op->qf->input_fields[i];
789       op_field = &op->input_fields[i];
790       goto found;
791     }
792   }
793   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
794     if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) {
795       qf_field = op->qf->output_fields[i];
796       op_field = &op->output_fields[i];
797       goto found;
798     }
799   }
800   // LCOV_EXCL_START
801   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE,
802                    "QFunction has no knowledge of field '%s'",
803                    field_name);
804   // LCOV_EXCL_STOP
805 found:
806   ierr = CeedOperatorCheckField(op->ceed, qf_field, r, b); CeedChk(ierr);
807   ierr = CeedCalloc(1, op_field); CeedChk(ierr);
808 
809   (*op_field)->vec = v;
810   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE) {
811     ierr = CeedVectorReference(v); CeedChk(ierr);
812   }
813 
814   (*op_field)->elem_restr = r;
815   ierr = CeedElemRestrictionReference(r); CeedChk(ierr);
816   if (r != CEED_ELEMRESTRICTION_NONE) {
817     op->num_elem = num_elem;
818     op->has_restriction = true; // Restriction set, but num_elem may be 0
819   }
820 
821   (*op_field)->basis = b;
822   if (b != CEED_BASIS_COLLOCATED) {
823     if (!op->num_qpts) {
824       ierr = CeedOperatorSetNumQuadraturePoints(op, num_qpts); CeedChk(ierr);
825     }
826     ierr = CeedBasisReference(b); CeedChk(ierr);
827   }
828 
829   op->num_fields += 1;
830   size_t len = strlen(field_name);
831   char *tmp;
832   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
833   memcpy(tmp, field_name, len+1);
834   (*op_field)->field_name = tmp;
835   return CEED_ERROR_SUCCESS;
836 }
837 
838 /**
839   @brief Get the CeedOperatorFields of a CeedOperator
840 
841   Note: Calling this function asserts that setup is complete
842           and sets the CeedOperator as immutable.
843 
844   @param op                  CeedOperator
845   @param[out] input_fields   Variable to store input_fields
846   @param[out] output_fields  Variable to store output_fields
847 
848   @return An error code: 0 - success, otherwise - failure
849 
850   @ref Backend
851 **/
852 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields,
853                           CeedOperatorField **input_fields,
854                           CeedInt *num_output_fields,
855                           CeedOperatorField **output_fields) {
856   int ierr;
857 
858   if (op->is_composite)
859     // LCOV_EXCL_START
860     return CeedError(op->ceed, CEED_ERROR_MINOR,
861                      "Not defined for composite operator");
862   // LCOV_EXCL_STOP
863   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
864 
865   if (num_input_fields) *num_input_fields = op->qf->num_input_fields;
866   if (input_fields) *input_fields = op->input_fields;
867   if (num_output_fields) *num_output_fields = op->qf->num_output_fields;
868   if (output_fields) *output_fields = op->output_fields;
869   return CEED_ERROR_SUCCESS;
870 }
871 
872 /**
873   @brief Get the name of a CeedOperatorField
874 
875   @param op_field         CeedOperatorField
876   @param[out] field_name  Variable to store the field name
877 
878   @return An error code: 0 - success, otherwise - failure
879 
880   @ref Backend
881 **/
882 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) {
883   *field_name = (char *)op_field->field_name;
884   return CEED_ERROR_SUCCESS;
885 }
886 
887 /**
888   @brief Get the CeedElemRestriction of a CeedOperatorField
889 
890   @param op_field   CeedOperatorField
891   @param[out] rstr  Variable to store CeedElemRestriction
892 
893   @return An error code: 0 - success, otherwise - failure
894 
895   @ref Backend
896 **/
897 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field,
898                                         CeedElemRestriction *rstr) {
899   *rstr = op_field->elem_restr;
900   return CEED_ERROR_SUCCESS;
901 }
902 
903 /**
904   @brief Get the CeedBasis of a CeedOperatorField
905 
906   @param op_field    CeedOperatorField
907   @param[out] basis  Variable to store CeedBasis
908 
909   @return An error code: 0 - success, otherwise - failure
910 
911   @ref Backend
912 **/
913 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) {
914   *basis = op_field->basis;
915   return CEED_ERROR_SUCCESS;
916 }
917 
918 /**
919   @brief Get the CeedVector of a CeedOperatorField
920 
921   @param op_field  CeedOperatorField
922   @param[out] vec  Variable to store CeedVector
923 
924   @return An error code: 0 - success, otherwise - failure
925 
926   @ref Backend
927 **/
928 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) {
929   *vec = op_field->vec;
930   return CEED_ERROR_SUCCESS;
931 }
932 
933 /**
934   @brief Add a sub-operator to a composite CeedOperator
935 
936   @param[out] composite_op  Composite CeedOperator
937   @param      sub_op        Sub-operator CeedOperator
938 
939   @return An error code: 0 - success, otherwise - failure
940 
941   @ref User
942  */
943 int CeedCompositeOperatorAddSub(CeedOperator composite_op,
944                                 CeedOperator sub_op) {
945   int ierr;
946 
947   if (!composite_op->is_composite)
948     // LCOV_EXCL_START
949     return CeedError(composite_op->ceed, CEED_ERROR_MINOR,
950                      "CeedOperator is not a composite operator");
951   // LCOV_EXCL_STOP
952 
953   if (composite_op->num_suboperators == CEED_COMPOSITE_MAX)
954     // LCOV_EXCL_START
955     return CeedError(composite_op->ceed, CEED_ERROR_UNSUPPORTED,
956                      "Cannot add additional sub_operators");
957   // LCOV_EXCL_STOP
958   if (composite_op->is_immutable)
959     // LCOV_EXCL_START
960     return CeedError(composite_op->ceed, CEED_ERROR_MAJOR,
961                      "Operator cannot be changed after set as immutable");
962   // LCOV_EXCL_STOP
963 
964   composite_op->sub_operators[composite_op->num_suboperators] = sub_op;
965   ierr = CeedOperatorReference(sub_op); CeedChk(ierr);
966   composite_op->num_suboperators++;
967   return CEED_ERROR_SUCCESS;
968 }
969 
970 /**
971   @brief Set the number of quadrature points associated with a CeedOperator.
972            This should be used when creating a CeedOperator where every
973            field has a collocated basis. This function cannot be used for
974            composite CeedOperators.
975 
976   @param op        CeedOperator
977   @param num_qpts  Number of quadrature points to set
978 
979   @return An error code: 0 - success, otherwise - failure
980 
981   @ref Backend
982 **/
983 
984 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
985   if (op->is_composite)
986     // LCOV_EXCL_START
987     return CeedError(op->ceed, CEED_ERROR_MINOR,
988                      "Not defined for composite operator");
989   // LCOV_EXCL_STOP
990   if (op->num_qpts)
991     // LCOV_EXCL_START
992     return CeedError(op->ceed, CEED_ERROR_MINOR,
993                      "Number of quadrature points already defined");
994   // LCOV_EXCL_STOP
995   if (op->is_immutable)
996     // LCOV_EXCL_START
997     return CeedError(op->ceed, CEED_ERROR_MAJOR,
998                      "Operator cannot be changed after set as immutable");
999   // LCOV_EXCL_STOP
1000 
1001   op->num_qpts = num_qpts;
1002   return CEED_ERROR_SUCCESS;
1003 }
1004 
1005 /**
1006   @brief View a CeedOperator
1007 
1008   @param[in] op      CeedOperator to view
1009   @param[in] stream  Stream to write; typically stdout/stderr or a file
1010 
1011   @return Error code: 0 - success, otherwise - failure
1012 
1013   @ref User
1014 **/
1015 int CeedOperatorView(CeedOperator op, FILE *stream) {
1016   int ierr;
1017 
1018   if (op->is_composite) {
1019     fprintf(stream, "Composite CeedOperator\n");
1020 
1021     for (CeedInt i=0; i<op->num_suboperators; i++) {
1022       fprintf(stream, "  SubOperator [%d]:\n", i);
1023       ierr = CeedOperatorSingleView(op->sub_operators[i], 1, stream);
1024       CeedChk(ierr);
1025     }
1026   } else {
1027     fprintf(stream, "CeedOperator\n");
1028     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
1029   }
1030   return CEED_ERROR_SUCCESS;
1031 }
1032 
1033 /**
1034   @brief Apply CeedOperator to a vector
1035 
1036   This computes the action of the operator on the specified (active) input,
1037   yielding its (active) output.  All inputs and outputs must be specified using
1038   CeedOperatorSetField().
1039 
1040   Note: Calling this function asserts that setup is complete
1041           and sets the CeedOperator as immutable.
1042 
1043   @param op        CeedOperator to apply
1044   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1045                      there are no active inputs
1046   @param[out] out  CeedVector to store result of applying operator (must be
1047                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1048                      active outputs
1049   @param request   Address of CeedRequest for non-blocking completion, else
1050                      @ref CEED_REQUEST_IMMEDIATE
1051 
1052   @return An error code: 0 - success, otherwise - failure
1053 
1054   @ref User
1055 **/
1056 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1057                       CeedRequest *request) {
1058   int ierr;
1059   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1060 
1061   if (op->num_elem)  {
1062     // Standard Operator
1063     if (op->Apply) {
1064       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1065     } else {
1066       // Zero all output vectors
1067       CeedQFunction qf = op->qf;
1068       for (CeedInt i=0; i<qf->num_output_fields; i++) {
1069         CeedVector vec = op->output_fields[i]->vec;
1070         if (vec == CEED_VECTOR_ACTIVE)
1071           vec = out;
1072         if (vec != CEED_VECTOR_NONE) {
1073           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1074         }
1075       }
1076       // Apply
1077       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1078     }
1079   } else if (op->is_composite) {
1080     // Composite Operator
1081     if (op->ApplyComposite) {
1082       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1083     } else {
1084       CeedInt num_suboperators;
1085       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1086       CeedOperator *sub_operators;
1087       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1088 
1089       // Zero all output vectors
1090       if (out != CEED_VECTOR_NONE) {
1091         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1092       }
1093       for (CeedInt i=0; i<num_suboperators; i++) {
1094         for (CeedInt j=0; j<sub_operators[i]->qf->num_output_fields; j++) {
1095           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1096           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1097             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1098           }
1099         }
1100       }
1101       // Apply
1102       for (CeedInt i=0; i<op->num_suboperators; i++) {
1103         ierr = CeedOperatorApplyAdd(op->sub_operators[i], in, out, request);
1104         CeedChk(ierr);
1105       }
1106     }
1107   }
1108   return CEED_ERROR_SUCCESS;
1109 }
1110 
1111 /**
1112   @brief Apply CeedOperator to a vector and add result to output vector
1113 
1114   This computes the action of the operator on the specified (active) input,
1115   yielding its (active) output.  All inputs and outputs must be specified using
1116   CeedOperatorSetField().
1117 
1118   @param op        CeedOperator to apply
1119   @param[in] in    CeedVector containing input state or NULL if there are no
1120                      active inputs
1121   @param[out] out  CeedVector to sum in result of applying operator (must be
1122                      distinct from @a in) or NULL if there are no active outputs
1123   @param request   Address of CeedRequest for non-blocking completion, else
1124                      @ref CEED_REQUEST_IMMEDIATE
1125 
1126   @return An error code: 0 - success, otherwise - failure
1127 
1128   @ref User
1129 **/
1130 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1131                          CeedRequest *request) {
1132   int ierr;
1133   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1134 
1135   if (op->num_elem)  {
1136     // Standard Operator
1137     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1138   } else if (op->is_composite) {
1139     // Composite Operator
1140     if (op->ApplyAddComposite) {
1141       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1142     } else {
1143       CeedInt num_suboperators;
1144       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1145       CeedOperator *sub_operators;
1146       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1147 
1148       for (CeedInt i=0; i<num_suboperators; i++) {
1149         ierr = CeedOperatorApplyAdd(sub_operators[i], in, out, request);
1150         CeedChk(ierr);
1151       }
1152     }
1153   }
1154   return CEED_ERROR_SUCCESS;
1155 }
1156 
1157 /**
1158   @brief Destroy a CeedOperator
1159 
1160   @param op  CeedOperator to destroy
1161 
1162   @return An error code: 0 - success, otherwise - failure
1163 
1164   @ref User
1165 **/
1166 int CeedOperatorDestroy(CeedOperator *op) {
1167   int ierr;
1168 
1169   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
1170   if ((*op)->Destroy) {
1171     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1172   }
1173   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1174   // Free fields
1175   for (int i=0; i<(*op)->num_fields; i++)
1176     if ((*op)->input_fields[i]) {
1177       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
1178         ierr = CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr);
1179         CeedChk(ierr);
1180       }
1181       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1182         ierr = CeedBasisDestroy(&(*op)->input_fields[i]->basis); CeedChk(ierr);
1183       }
1184       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1185           (*op)->input_fields[i]->vec != CEED_VECTOR_NONE ) {
1186         ierr = CeedVectorDestroy(&(*op)->input_fields[i]->vec); CeedChk(ierr);
1187       }
1188       ierr = CeedFree(&(*op)->input_fields[i]->field_name); CeedChk(ierr);
1189       ierr = CeedFree(&(*op)->input_fields[i]); CeedChk(ierr);
1190     }
1191   for (int i=0; i<(*op)->num_fields; i++)
1192     if ((*op)->output_fields[i]) {
1193       ierr = CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr);
1194       CeedChk(ierr);
1195       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1196         ierr = CeedBasisDestroy(&(*op)->output_fields[i]->basis); CeedChk(ierr);
1197       }
1198       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1199           (*op)->output_fields[i]->vec != CEED_VECTOR_NONE ) {
1200         ierr = CeedVectorDestroy(&(*op)->output_fields[i]->vec); CeedChk(ierr);
1201       }
1202       ierr = CeedFree(&(*op)->output_fields[i]->field_name); CeedChk(ierr);
1203       ierr = CeedFree(&(*op)->output_fields[i]); CeedChk(ierr);
1204     }
1205   // Destroy sub_operators
1206   for (int i=0; i<(*op)->num_suboperators; i++)
1207     if ((*op)->sub_operators[i]) {
1208       ierr = CeedOperatorDestroy(&(*op)->sub_operators[i]); CeedChk(ierr);
1209     }
1210   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1211   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1212   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1213 
1214   // Destroy fallback
1215   if ((*op)->op_fallback) {
1216     ierr = (*op)->qf_fallback->Destroy((*op)->qf_fallback); CeedChk(ierr);
1217     ierr = CeedFree(&(*op)->qf_fallback); CeedChk(ierr);
1218     ierr = (*op)->op_fallback->Destroy((*op)->op_fallback); CeedChk(ierr);
1219     ierr = CeedFree(&(*op)->op_fallback); CeedChk(ierr);
1220   }
1221 
1222   ierr = CeedFree(&(*op)->input_fields); CeedChk(ierr);
1223   ierr = CeedFree(&(*op)->output_fields); CeedChk(ierr);
1224   ierr = CeedFree(&(*op)->sub_operators); CeedChk(ierr);
1225   ierr = CeedFree(op); CeedChk(ierr);
1226   return CEED_ERROR_SUCCESS;
1227 }
1228 
1229 /// @}
1230