xref: /libCEED/interface/ceed-operator.c (revision 00d548f6cce3ebb77e7917d79b50a2a368cad12e)
1 // Copyright (c) 2017-2022, Lawrence Livermore National Security, LLC and other CEED contributors.
2 // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3 //
4 // SPDX-License-Identifier: BSD-2-Clause
5 //
6 // This file is part of CEED:  http://github.com/ceed
7 
8 #include <ceed-impl.h>
9 #include <ceed/backend.h>
10 #include <ceed/ceed.h>
11 #include <stdbool.h>
12 #include <stdio.h>
13 #include <string.h>
14 
15 /// @file
16 /// Implementation of CeedOperator interfaces
17 
18 /// ----------------------------------------------------------------------------
19 /// CeedOperator Library Internal Functions
20 /// ----------------------------------------------------------------------------
21 /// @addtogroup CeedOperatorDeveloper
22 /// @{
23 
24 /**
25   @brief Check if a CeedOperator Field matches the QFunction Field
26 
27   @param[in] ceed     Ceed object for error handling
28   @param[in] qf_field QFunction Field matching Operator Field
29   @param[in] r        Operator Field ElemRestriction
30   @param[in] b        Operator Field Basis
31 
32   @return An error code: 0 - success, otherwise - failure
33 
34   @ref Developer
35 **/
36 static int CeedOperatorCheckField(Ceed ceed, CeedQFunctionField qf_field, CeedElemRestriction r, CeedBasis b) {
37   CeedEvalMode eval_mode = qf_field->eval_mode;
38   CeedInt      dim = 1, num_comp = 1, Q_comp = 1, restr_num_comp = 1, size = qf_field->size;
39 
40   // Restriction
41   if (r != CEED_ELEMRESTRICTION_NONE) {
42     if (eval_mode == CEED_EVAL_WEIGHT) {
43       // LCOV_EXCL_START
44       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE, "CEED_ELEMRESTRICTION_NONE should be used for a field with eval mode CEED_EVAL_WEIGHT");
45       // LCOV_EXCL_STOP
46     }
47     CeedCall(CeedElemRestrictionGetNumComponents(r, &restr_num_comp));
48   }
49   if ((r == CEED_ELEMRESTRICTION_NONE) != (eval_mode == CEED_EVAL_WEIGHT)) {
50     // LCOV_EXCL_START
51     return CeedError(ceed, CEED_ERROR_INCOMPATIBLE, "CEED_ELEMRESTRICTION_NONE and CEED_EVAL_WEIGHT must be used together.");
52     // LCOV_EXCL_STOP
53   }
54   // Basis
55   if (b != CEED_BASIS_COLLOCATED) {
56     if (eval_mode == CEED_EVAL_NONE) {
57       // LCOV_EXCL_START
58       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE, "Field '%s' configured with CEED_EVAL_NONE must be used with CEED_BASIS_COLLOCATED",
59                        qf_field->field_name);
60       // LCOV_EXCL_STOP
61     }
62     CeedCall(CeedBasisGetDimension(b, &dim));
63     CeedCall(CeedBasisGetNumComponents(b, &num_comp));
64     CeedCall(CeedBasisGetNumQuadratureComponents(b, &Q_comp));
65     if (r != CEED_ELEMRESTRICTION_NONE && restr_num_comp != num_comp) {
66       // LCOV_EXCL_START
67       return CeedError(ceed, CEED_ERROR_DIMENSION,
68                        "Field '%s' of size %" CeedInt_FMT " and EvalMode %s: ElemRestriction has %" CeedInt_FMT
69                        " components, but Basis has %" CeedInt_FMT " components",
70                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode], restr_num_comp, num_comp);
71       // LCOV_EXCL_STOP
72     }
73   } else if (eval_mode != CEED_EVAL_NONE) {
74     // LCOV_EXCL_START
75     return CeedError(ceed, CEED_ERROR_INCOMPATIBLE, "Field '%s' configured with %s cannot be used with CEED_BASIS_COLLOCATED", qf_field->field_name,
76                      CeedEvalModes[eval_mode]);
77     // LCOV_EXCL_STOP
78   }
79   // Field size
80   switch (eval_mode) {
81     case CEED_EVAL_NONE:
82       if (size != restr_num_comp) {
83         // LCOV_EXCL_START
84         return CeedError(ceed, CEED_ERROR_DIMENSION,
85                          "Field '%s' of size %" CeedInt_FMT " and EvalMode %s: ElemRestriction has " CeedInt_FMT " components", qf_field->field_name,
86                          qf_field->size, CeedEvalModes[qf_field->eval_mode], restr_num_comp);
87         // LCOV_EXCL_STOP
88       }
89       break;
90     case CEED_EVAL_INTERP:
91       if (size != num_comp * Q_comp) {
92         // LCOV_EXCL_START
93         return CeedError(ceed, CEED_ERROR_DIMENSION,
94                          "Field '%s' of size %" CeedInt_FMT " and EvalMode %s: ElemRestriction/Basis has " CeedInt_FMT " components",
95                          qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode], num_comp * Q_comp);
96         // LCOV_EXCL_STOP
97       }
98       break;
99     case CEED_EVAL_GRAD:
100       if (size != num_comp * dim) {
101         // LCOV_EXCL_START
102         return CeedError(ceed, CEED_ERROR_DIMENSION,
103                          "Field '%s' of size %" CeedInt_FMT " and EvalMode %s in %" CeedInt_FMT " dimensions: ElemRestriction/Basis has %" CeedInt_FMT
104                          " components",
105                          qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode], dim, num_comp);
106         // LCOV_EXCL_STOP
107       }
108       break;
109     case CEED_EVAL_WEIGHT:
110       // No additional checks required
111       break;
112     case CEED_EVAL_DIV:
113       if (size != num_comp) {
114         // LCOV_EXCL_START
115         return CeedError(ceed, CEED_ERROR_DIMENSION,
116                          "Field '%s' of size %" CeedInt_FMT " and EvalMode %s: ElemRestriction/Basis has " CeedInt_FMT " components",
117                          qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode], num_comp);
118         // LCOV_EXCL_STOP
119       }
120       break;
121     case CEED_EVAL_CURL:
122       // Not implemented
123       break;
124   }
125   return CEED_ERROR_SUCCESS;
126 }
127 
128 /**
129   @brief View a field of a CeedOperator
130 
131   @param[in] field        Operator field to view
132   @param[in] qf_field     QFunction field (carries field name)
133   @param[in] field_number Number of field being viewed
134   @param[in] sub          true indicates sub-operator, which increases indentation; false for top-level operator
135   @param[in] input        true for an input field; false for output field
136   @param[in] stream       Stream to view to, e.g., stdout
137 
138   @return An error code: 0 - success, otherwise - failure
139 
140   @ref Utility
141 **/
142 static int CeedOperatorFieldView(CeedOperatorField field, CeedQFunctionField qf_field, CeedInt field_number, bool sub, bool input, FILE *stream) {
143   const char *pre    = sub ? "  " : "";
144   const char *in_out = input ? "Input" : "Output";
145 
146   fprintf(stream,
147           "%s    %s field %" CeedInt_FMT
148           ":\n"
149           "%s      Name: \"%s\"\n",
150           pre, in_out, field_number, pre, qf_field->field_name);
151 
152   fprintf(stream, "%s      Size: %" CeedInt_FMT "\n", pre, qf_field->size);
153 
154   fprintf(stream, "%s      EvalMode: %s\n", pre, CeedEvalModes[qf_field->eval_mode]);
155 
156   if (field->basis == CEED_BASIS_COLLOCATED) fprintf(stream, "%s      Collocated basis\n", pre);
157 
158   if (field->vec == CEED_VECTOR_ACTIVE) fprintf(stream, "%s      Active vector\n", pre);
159   else if (field->vec == CEED_VECTOR_NONE) fprintf(stream, "%s      No vector\n", pre);
160 
161   return CEED_ERROR_SUCCESS;
162 }
163 
164 /**
165   @brief View a single CeedOperator
166 
167   @param[in] op     CeedOperator to view
168   @param[in] sub    Boolean flag for sub-operator
169   @param[in] stream Stream to write; typically stdout/stderr or a file
170 
171   @return Error code: 0 - success, otherwise - failure
172 
173   @ref Utility
174 **/
175 int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) {
176   const char *pre = sub ? "  " : "";
177 
178   CeedInt num_elem, num_qpts;
179   CeedCall(CeedOperatorGetNumElements(op, &num_elem));
180   CeedCall(CeedOperatorGetNumQuadraturePoints(op, &num_qpts));
181 
182   CeedInt total_fields = 0;
183   CeedCall(CeedOperatorGetNumArgs(op, &total_fields));
184   fprintf(stream, "%s  %" CeedInt_FMT " elements with %" CeedInt_FMT " quadrature points each\n", pre, num_elem, num_qpts);
185 
186   fprintf(stream, "%s  %" CeedInt_FMT " field%s\n", pre, total_fields, total_fields > 1 ? "s" : "");
187 
188   fprintf(stream, "%s  %" CeedInt_FMT " input field%s:\n", pre, op->qf->num_input_fields, op->qf->num_input_fields > 1 ? "s" : "");
189   for (CeedInt i = 0; i < op->qf->num_input_fields; i++) {
190     CeedCall(CeedOperatorFieldView(op->input_fields[i], op->qf->input_fields[i], i, sub, 1, stream));
191   }
192 
193   fprintf(stream, "%s  %" CeedInt_FMT " output field%s:\n", pre, op->qf->num_output_fields, op->qf->num_output_fields > 1 ? "s" : "");
194   for (CeedInt i = 0; i < op->qf->num_output_fields; i++) {
195     CeedCall(CeedOperatorFieldView(op->output_fields[i], op->qf->output_fields[i], i, sub, 0, stream));
196   }
197   return CEED_ERROR_SUCCESS;
198 }
199 
200 /**
201   @brief Find the active vector basis for a non-composite CeedOperator
202 
203   @param[in] op            CeedOperator to find active basis for
204   @param[out] active_basis Basis for active input vector or NULL for composite operator
205 
206   @return An error code: 0 - success, otherwise - failure
207 
208   @ ref Developer
209 **/
210 int CeedOperatorGetActiveBasis(CeedOperator op, CeedBasis *active_basis) {
211   Ceed ceed;
212   CeedCall(CeedOperatorGetCeed(op, &ceed));
213 
214   *active_basis = NULL;
215   if (op->is_composite) return CEED_ERROR_SUCCESS;
216   for (CeedInt i = 0; i < op->qf->num_input_fields; i++) {
217     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
218       if (*active_basis && *active_basis != op->input_fields[i]->basis) {
219         // LCOV_EXCL_START
220         return CeedError(ceed, CEED_ERROR_MINOR, "Multiple active CeedBases found");
221         // LCOV_EXCL_STOP
222       }
223       *active_basis = op->input_fields[i]->basis;
224       break;
225     }
226   }
227 
228   if (!*active_basis) {
229     // LCOV_EXCL_START
230     return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No active CeedBasis found");
231     // LCOV_EXCL_STOP
232   }
233   return CEED_ERROR_SUCCESS;
234 }
235 
236 /**
237   @brief Find the active vector ElemRestriction for a non-composite CeedOperator
238 
239   @param[in] op           CeedOperator to find active basis for
240   @param[out] active_rstr ElemRestriction for active input vector or NULL for composite operator
241 
242   @return An error code: 0 - success, otherwise - failure
243 
244   @ref Utility
245 **/
246 int CeedOperatorGetActiveElemRestriction(CeedOperator op, CeedElemRestriction *active_rstr) {
247   Ceed ceed;
248   CeedCall(CeedOperatorGetCeed(op, &ceed));
249 
250   *active_rstr = NULL;
251   if (op->is_composite) return CEED_ERROR_SUCCESS;
252   for (CeedInt i = 0; i < op->qf->num_input_fields; i++) {
253     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
254       if (*active_rstr && *active_rstr != op->input_fields[i]->elem_restr) {
255         // LCOV_EXCL_START
256         return CeedError(ceed, CEED_ERROR_MINOR, "Multiple active CeedElemRestrictions found");
257         // LCOV_EXCL_STOP
258       }
259       *active_rstr = op->input_fields[i]->elem_restr;
260       break;
261     }
262   }
263 
264   if (!*active_rstr) {
265     // LCOV_EXCL_START
266     return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No active CeedElemRestriction found");
267     // LCOV_EXCL_STOP
268   }
269   return CEED_ERROR_SUCCESS;
270 }
271 
272 /**
273   @brief Set QFunctionContext field value of the specified type.
274            For composite operators, the value is set in all sub-operator QFunctionContexts that have a matching `field_name`.
275            A non-zero error code is returned for single operators that do not have a matching field of the same type or composite operators that do
276 not have any field of a matching type.
277 
278   @param[in,out] op          CeedOperator
279   @param[in]     field_label Label of field to set
280   @param[in]     field_type  Type of field to set
281   @param[in]     value       Value to set
282 
283   @return An error code: 0 - success, otherwise - failure
284 
285   @ref User
286 **/
287 static int CeedOperatorContextSetGeneric(CeedOperator op, CeedContextFieldLabel field_label, CeedContextFieldType field_type, void *value) {
288   if (!field_label) {
289     // LCOV_EXCL_START
290     return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED, "Invalid field label");
291     // LCOV_EXCL_STOP
292   }
293 
294   bool is_composite = false;
295   CeedCall(CeedOperatorIsComposite(op, &is_composite));
296   if (is_composite) {
297     CeedInt       num_sub;
298     CeedOperator *sub_operators;
299 
300     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub));
301     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
302     if (num_sub != field_label->num_sub_labels) {
303       // LCOV_EXCL_START
304       return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED,
305                        "ContextLabel does not correspond to composite operator. Use CeedOperatorGetContextFieldLabel().");
306       // LCOV_EXCL_STOP
307     }
308 
309     for (CeedInt i = 0; i < num_sub; i++) {
310       // Try every sub-operator, ok if some sub-operators do not have field
311       if (field_label->sub_labels[i] && sub_operators[i]->qf->ctx) {
312         CeedCall(CeedQFunctionContextSetGeneric(sub_operators[i]->qf->ctx, field_label->sub_labels[i], field_type, value));
313       }
314     }
315   } else {
316     if (!op->qf->ctx) {
317       // LCOV_EXCL_START
318       return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED, "QFunction does not have context data");
319       // LCOV_EXCL_STOP
320     }
321 
322     CeedCall(CeedQFunctionContextSetGeneric(op->qf->ctx, field_label, field_type, value));
323   }
324 
325   return CEED_ERROR_SUCCESS;
326 }
327 
328 /// @}
329 
330 /// ----------------------------------------------------------------------------
331 /// CeedOperator Backend API
332 /// ----------------------------------------------------------------------------
333 /// @addtogroup CeedOperatorBackend
334 /// @{
335 
336 /**
337   @brief Get the number of arguments associated with a CeedOperator
338 
339   @param[in]  op        CeedOperator
340   @param[out] num_args  Variable to store vector number of arguments
341 
342   @return An error code: 0 - success, otherwise - failure
343 
344   @ref Backend
345 **/
346 
347 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *num_args) {
348   if (op->is_composite) {
349     // LCOV_EXCL_START
350     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not defined for composite operators");
351     // LCOV_EXCL_STOP
352   }
353   *num_args = op->num_fields;
354   return CEED_ERROR_SUCCESS;
355 }
356 
357 /**
358   @brief Get the setup status of a CeedOperator
359 
360   @param[in]  op            CeedOperator
361   @param[out] is_setup_done Variable to store setup status
362 
363   @return An error code: 0 - success, otherwise - failure
364 
365   @ref Backend
366 **/
367 
368 int CeedOperatorIsSetupDone(CeedOperator op, bool *is_setup_done) {
369   *is_setup_done = op->is_backend_setup;
370   return CEED_ERROR_SUCCESS;
371 }
372 
373 /**
374   @brief Get the QFunction associated with a CeedOperator
375 
376   @param[in]  op CeedOperator
377   @param[out] qf Variable to store QFunction
378 
379   @return An error code: 0 - success, otherwise - failure
380 
381   @ref Backend
382 **/
383 
384 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
385   if (op->is_composite) {
386     // LCOV_EXCL_START
387     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
388     // LCOV_EXCL_STOP
389   }
390   *qf = op->qf;
391   return CEED_ERROR_SUCCESS;
392 }
393 
394 /**
395   @brief Get a boolean value indicating if the CeedOperator is composite
396 
397   @param[in]  op           CeedOperator
398   @param[out] is_composite Variable to store composite status
399 
400   @return An error code: 0 - success, otherwise - failure
401 
402   @ref Backend
403 **/
404 
405 int CeedOperatorIsComposite(CeedOperator op, bool *is_composite) {
406   *is_composite = op->is_composite;
407   return CEED_ERROR_SUCCESS;
408 }
409 
410 /**
411   @brief Get the backend data of a CeedOperator
412 
413   @param[in]  op   CeedOperator
414   @param[out] data Variable to store data
415 
416   @return An error code: 0 - success, otherwise - failure
417 
418   @ref Backend
419 **/
420 
421 int CeedOperatorGetData(CeedOperator op, void *data) {
422   *(void **)data = op->data;
423   return CEED_ERROR_SUCCESS;
424 }
425 
426 /**
427   @brief Set the backend data of a CeedOperator
428 
429   @param[in,out] op   CeedOperator
430   @param[in]     data Data to set
431 
432   @return An error code: 0 - success, otherwise - failure
433 
434   @ref Backend
435 **/
436 
437 int CeedOperatorSetData(CeedOperator op, void *data) {
438   op->data = data;
439   return CEED_ERROR_SUCCESS;
440 }
441 
442 /**
443   @brief Increment the reference counter for a CeedOperator
444 
445   @param[in,out] op CeedOperator to increment the reference counter
446 
447   @return An error code: 0 - success, otherwise - failure
448 
449   @ref Backend
450 **/
451 int CeedOperatorReference(CeedOperator op) {
452   op->ref_count++;
453   return CEED_ERROR_SUCCESS;
454 }
455 
456 /**
457   @brief Set the setup flag of a CeedOperator to True
458 
459   @param[in,out] op CeedOperator
460 
461   @return An error code: 0 - success, otherwise - failure
462 
463   @ref Backend
464 **/
465 
466 int CeedOperatorSetSetupDone(CeedOperator op) {
467   op->is_backend_setup = true;
468   return CEED_ERROR_SUCCESS;
469 }
470 
471 /// @}
472 
473 /// ----------------------------------------------------------------------------
474 /// CeedOperator Public API
475 /// ----------------------------------------------------------------------------
476 /// @addtogroup CeedOperatorUser
477 /// @{
478 
479 /**
480   @brief Create a CeedOperator and associate a CeedQFunction.
481            A CeedBasis and CeedElemRestriction can be associated with CeedQFunction fields with \ref CeedOperatorSetField.
482 
483   @param[in]  ceed Ceed object where the CeedOperator will be created
484   @param[in]  qf   QFunction defining the action of the operator at quadrature points
485   @param[in]  dqf  QFunction defining the action of the Jacobian of @a qf (or @ref CEED_QFUNCTION_NONE)
486   @param[in]  dqfT QFunction defining the action of the transpose of the Jacobian of @a qf (or @ref CEED_QFUNCTION_NONE)
487   @param[out] op   Address of the variable where the newly created CeedOperator will be stored
488 
489   @return An error code: 0 - success, otherwise - failure
490 
491   @ref User
492  */
493 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf, CeedQFunction dqfT, CeedOperator *op) {
494   if (!ceed->OperatorCreate) {
495     Ceed delegate;
496     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Operator"));
497 
498     if (!delegate) {
499       // LCOV_EXCL_START
500       return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support OperatorCreate");
501       // LCOV_EXCL_STOP
502     }
503 
504     CeedCall(CeedOperatorCreate(delegate, qf, dqf, dqfT, op));
505     return CEED_ERROR_SUCCESS;
506   }
507 
508   if (!qf || qf == CEED_QFUNCTION_NONE) {
509     // LCOV_EXCL_START
510     return CeedError(ceed, CEED_ERROR_MINOR, "Operator must have a valid QFunction.");
511     // LCOV_EXCL_STOP
512   }
513   CeedCall(CeedCalloc(1, op));
514   (*op)->ceed = ceed;
515   CeedCall(CeedReference(ceed));
516   (*op)->ref_count   = 1;
517   (*op)->qf          = qf;
518   (*op)->input_size  = -1;
519   (*op)->output_size = -1;
520   CeedCall(CeedQFunctionReference(qf));
521   if (dqf && dqf != CEED_QFUNCTION_NONE) {
522     (*op)->dqf = dqf;
523     CeedCall(CeedQFunctionReference(dqf));
524   }
525   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
526     (*op)->dqfT = dqfT;
527     CeedCall(CeedQFunctionReference(dqfT));
528   }
529   CeedCall(CeedQFunctionAssemblyDataCreate(ceed, &(*op)->qf_assembled));
530   CeedCall(CeedCalloc(CEED_FIELD_MAX, &(*op)->input_fields));
531   CeedCall(CeedCalloc(CEED_FIELD_MAX, &(*op)->output_fields));
532   CeedCall(ceed->OperatorCreate(*op));
533   return CEED_ERROR_SUCCESS;
534 }
535 
536 /**
537   @brief Create an operator that composes the action of several operators
538 
539   @param[in]  ceed Ceed object where the CeedOperator will be created
540   @param[out] op   Address of the variable where the newly created Composite CeedOperator will be stored
541 
542   @return An error code: 0 - success, otherwise - failure
543 
544   @ref User
545  */
546 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
547   if (!ceed->CompositeOperatorCreate) {
548     Ceed delegate;
549     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Operator"));
550 
551     if (delegate) {
552       CeedCall(CeedCompositeOperatorCreate(delegate, op));
553       return CEED_ERROR_SUCCESS;
554     }
555   }
556 
557   CeedCall(CeedCalloc(1, op));
558   (*op)->ceed = ceed;
559   CeedCall(CeedReference(ceed));
560   (*op)->ref_count    = 1;
561   (*op)->is_composite = true;
562   CeedCall(CeedCalloc(CEED_COMPOSITE_MAX, &(*op)->sub_operators));
563   (*op)->input_size  = -1;
564   (*op)->output_size = -1;
565 
566   if (ceed->CompositeOperatorCreate) {
567     CeedCall(ceed->CompositeOperatorCreate(*op));
568   }
569   return CEED_ERROR_SUCCESS;
570 }
571 
572 /**
573   @brief Copy the pointer to a CeedOperator.
574            Both pointers should be destroyed with `CeedOperatorDestroy()`.
575            Note: If `*op_copy` is non-NULL, then it is assumed that `*op_copy` is a pointer to a CeedOperator.
576              This CeedOperator will be destroyed if `*op_copy` is the only reference to this CeedOperator.
577 
578   @param[in]  op         CeedOperator to copy reference to
579   @param[in,out] op_copy Variable to store copied reference
580 
581   @return An error code: 0 - success, otherwise - failure
582 
583   @ref User
584 **/
585 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) {
586   CeedCall(CeedOperatorReference(op));
587   CeedCall(CeedOperatorDestroy(op_copy));
588   *op_copy = op;
589   return CEED_ERROR_SUCCESS;
590 }
591 
592 /**
593   @brief Provide a field to a CeedOperator for use by its CeedQFunction.
594 
595   This function is used to specify both active and passive fields to a CeedOperator.
596   For passive fields, a vector @arg v must be provided.
597   Passive fields can inputs or outputs (updated in-place when operator is applied).
598 
599   Active fields must be specified using this function, but their data (in a CeedVector) is passed in CeedOperatorApply().
600   There can be at most one active input CeedVector and at most one active output CeedVector passed to CeedOperatorApply().
601 
602   @param[in,out] op         CeedOperator on which to provide the field
603   @param[in]     field_name Name of the field (to be matched with the name used by CeedQFunction)
604   @param[in]     r          CeedElemRestriction
605   @param[in]     b          CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED if collocated with quadrature points
606   @param[in]     v          CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE if field is active or @ref CEED_VECTOR_NONE if using @ref
607 CEED_EVAL_WEIGHT in the QFunction
608 
609   @return An error code: 0 - success, otherwise - failure
610 
611   @ref User
612 **/
613 int CeedOperatorSetField(CeedOperator op, const char *field_name, CeedElemRestriction r, CeedBasis b, CeedVector v) {
614   if (op->is_composite) {
615     // LCOV_EXCL_START
616     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Cannot add field to composite operator.");
617     // LCOV_EXCL_STOP
618   }
619   if (op->is_immutable) {
620     // LCOV_EXCL_START
621     return CeedError(op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable");
622     // LCOV_EXCL_STOP
623   }
624   if (!r) {
625     // LCOV_EXCL_START
626     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "ElemRestriction r for field \"%s\" must be non-NULL.", field_name);
627     // LCOV_EXCL_STOP
628   }
629   if (!b) {
630     // LCOV_EXCL_START
631     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Basis b for field \"%s\" must be non-NULL.", field_name);
632     // LCOV_EXCL_STOP
633   }
634   if (!v) {
635     // LCOV_EXCL_START
636     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Vector v for field \"%s\" must be non-NULL.", field_name);
637     // LCOV_EXCL_STOP
638   }
639 
640   CeedInt num_elem;
641   CeedCall(CeedElemRestrictionGetNumElements(r, &num_elem));
642   if (r != CEED_ELEMRESTRICTION_NONE && op->has_restriction && op->num_elem != num_elem) {
643     // LCOV_EXCL_START
644     return CeedError(op->ceed, CEED_ERROR_DIMENSION,
645                      "ElemRestriction with %" CeedInt_FMT " elements incompatible with prior %" CeedInt_FMT " elements", num_elem, op->num_elem);
646     // LCOV_EXCL_STOP
647   }
648 
649   CeedInt num_qpts = 0;
650   if (b != CEED_BASIS_COLLOCATED) {
651     CeedCall(CeedBasisGetNumQuadraturePoints(b, &num_qpts));
652     if (op->num_qpts && op->num_qpts != num_qpts) {
653       // LCOV_EXCL_START
654       return CeedError(op->ceed, CEED_ERROR_DIMENSION,
655                        "Basis with %" CeedInt_FMT " quadrature points incompatible with prior %" CeedInt_FMT " points", num_qpts, op->num_qpts);
656       // LCOV_EXCL_STOP
657     }
658   }
659   CeedQFunctionField qf_field;
660   CeedOperatorField *op_field;
661   bool               is_input = true;
662   for (CeedInt i = 0; i < op->qf->num_input_fields; i++) {
663     if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) {
664       qf_field = op->qf->input_fields[i];
665       op_field = &op->input_fields[i];
666       goto found;
667     }
668   }
669   is_input = false;
670   for (CeedInt i = 0; i < op->qf->num_output_fields; i++) {
671     if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) {
672       qf_field = op->qf->output_fields[i];
673       op_field = &op->output_fields[i];
674       goto found;
675     }
676   }
677   // LCOV_EXCL_START
678   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE, "QFunction has no knowledge of field '%s'", field_name);
679   // LCOV_EXCL_STOP
680 found:
681   CeedCall(CeedOperatorCheckField(op->ceed, qf_field, r, b));
682   CeedCall(CeedCalloc(1, op_field));
683 
684   if (v == CEED_VECTOR_ACTIVE) {
685     CeedSize l_size;
686     CeedCall(CeedElemRestrictionGetLVectorSize(r, &l_size));
687     if (is_input) {
688       if (op->input_size == -1) op->input_size = l_size;
689       if (l_size != op->input_size) {
690         // LCOV_EXCL_START
691         return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size, op->input_size);
692         // LCOV_EXCL_STOP
693       }
694     } else {
695       if (op->output_size == -1) op->output_size = l_size;
696       if (l_size != op->output_size) {
697         // LCOV_EXCL_START
698         return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size, op->output_size);
699         // LCOV_EXCL_STOP
700       }
701     }
702   }
703 
704   (*op_field)->vec = v;
705   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE) {
706     CeedCall(CeedVectorReference(v));
707   }
708 
709   (*op_field)->elem_restr = r;
710   CeedCall(CeedElemRestrictionReference(r));
711   if (r != CEED_ELEMRESTRICTION_NONE) {
712     op->num_elem        = num_elem;
713     op->has_restriction = true;  // Restriction set, but num_elem may be 0
714   }
715 
716   (*op_field)->basis = b;
717   if (b != CEED_BASIS_COLLOCATED) {
718     if (!op->num_qpts) {
719       CeedCall(CeedOperatorSetNumQuadraturePoints(op, num_qpts));
720     }
721     CeedCall(CeedBasisReference(b));
722   }
723 
724   op->num_fields += 1;
725   CeedCall(CeedStringAllocCopy(field_name, (char **)&(*op_field)->field_name));
726   return CEED_ERROR_SUCCESS;
727 }
728 
729 /**
730   @brief Get the CeedOperatorFields of a CeedOperator
731 
732   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
733 
734   @param[in]  op                CeedOperator
735   @param[out] num_input_fields  Variable to store number of input fields
736   @param[out] input_fields      Variable to store input_fields
737   @param[out] num_output_fields Variable to store number of output fields
738   @param[out] output_fields     Variable to store output_fields
739 
740   @return An error code: 0 - success, otherwise - failure
741 
742   @ref Advanced
743 **/
744 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields, CeedOperatorField **input_fields, CeedInt *num_output_fields,
745                           CeedOperatorField **output_fields) {
746   if (op->is_composite) {
747     // LCOV_EXCL_START
748     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
749     // LCOV_EXCL_STOP
750   }
751   CeedCall(CeedOperatorCheckReady(op));
752 
753   if (num_input_fields) *num_input_fields = op->qf->num_input_fields;
754   if (input_fields) *input_fields = op->input_fields;
755   if (num_output_fields) *num_output_fields = op->qf->num_output_fields;
756   if (output_fields) *output_fields = op->output_fields;
757   return CEED_ERROR_SUCCESS;
758 }
759 
760 /**
761   @brief Get the name of a CeedOperatorField
762 
763   @param[in]  op_field    CeedOperatorField
764   @param[out] field_name  Variable to store the field name
765 
766   @return An error code: 0 - success, otherwise - failure
767 
768   @ref Advanced
769 **/
770 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) {
771   *field_name = (char *)op_field->field_name;
772   return CEED_ERROR_SUCCESS;
773 }
774 
775 /**
776   @brief Get the CeedElemRestriction of a CeedOperatorField
777 
778   @param[in]  op_field CeedOperatorField
779   @param[out] rstr     Variable to store CeedElemRestriction
780 
781   @return An error code: 0 - success, otherwise - failure
782 
783   @ref Advanced
784 **/
785 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field, CeedElemRestriction *rstr) {
786   *rstr = op_field->elem_restr;
787   return CEED_ERROR_SUCCESS;
788 }
789 
790 /**
791   @brief Get the CeedBasis of a CeedOperatorField
792 
793   @param[in]  op_field CeedOperatorField
794   @param[out] basis    Variable to store CeedBasis
795 
796   @return An error code: 0 - success, otherwise - failure
797 
798   @ref Advanced
799 **/
800 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) {
801   *basis = op_field->basis;
802   return CEED_ERROR_SUCCESS;
803 }
804 
805 /**
806   @brief Get the CeedVector of a CeedOperatorField
807 
808   @param[in]  op_field CeedOperatorField
809   @param[out] vec      Variable to store CeedVector
810 
811   @return An error code: 0 - success, otherwise - failure
812 
813   @ref Advanced
814 **/
815 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) {
816   *vec = op_field->vec;
817   return CEED_ERROR_SUCCESS;
818 }
819 
820 /**
821   @brief Add a sub-operator to a composite CeedOperator
822 
823   @param[in,out] composite_op Composite CeedOperator
824   @param[in]     sub_op       Sub-operator CeedOperator
825 
826   @return An error code: 0 - success, otherwise - failure
827 
828   @ref User
829  */
830 int CeedCompositeOperatorAddSub(CeedOperator composite_op, CeedOperator sub_op) {
831   if (!composite_op->is_composite) {
832     // LCOV_EXCL_START
833     return CeedError(composite_op->ceed, CEED_ERROR_MINOR, "CeedOperator is not a composite operator");
834     // LCOV_EXCL_STOP
835   }
836 
837   if (composite_op->num_suboperators == CEED_COMPOSITE_MAX) {
838     // LCOV_EXCL_START
839     return CeedError(composite_op->ceed, CEED_ERROR_UNSUPPORTED, "Cannot add additional sub-operators");
840     // LCOV_EXCL_STOP
841   }
842   if (composite_op->is_immutable) {
843     // LCOV_EXCL_START
844     return CeedError(composite_op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable");
845     // LCOV_EXCL_STOP
846   }
847 
848   {
849     CeedSize input_size, output_size;
850     CeedCall(CeedOperatorGetActiveVectorLengths(sub_op, &input_size, &output_size));
851     if (composite_op->input_size == -1) composite_op->input_size = input_size;
852     if (composite_op->output_size == -1) composite_op->output_size = output_size;
853     // Note, a size of -1 means no active vector restriction set, so no incompatibility
854     if ((input_size != -1 && input_size != composite_op->input_size) || (output_size != -1 && output_size != composite_op->output_size)) {
855       // LCOV_EXCL_START
856       return CeedError(composite_op->ceed, CEED_ERROR_MAJOR,
857                        "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of "
858                        "shape (%td, %td)",
859                        composite_op->input_size, composite_op->output_size, input_size, output_size);
860       // LCOV_EXCL_STOP
861     }
862   }
863 
864   composite_op->sub_operators[composite_op->num_suboperators] = sub_op;
865   CeedCall(CeedOperatorReference(sub_op));
866   composite_op->num_suboperators++;
867   return CEED_ERROR_SUCCESS;
868 }
869 
870 /**
871   @brief Get the number of sub_operators associated with a CeedOperator
872 
873   @param[in]  op               CeedOperator
874   @param[out] num_suboperators Variable to store number of sub_operators
875 
876   @return An error code: 0 - success, otherwise - failure
877 
878   @ref Backend
879 **/
880 
881 int CeedCompositeOperatorGetNumSub(CeedOperator op, CeedInt *num_suboperators) {
882   if (!op->is_composite) {
883     // LCOV_EXCL_START
884     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
885     // LCOV_EXCL_STOP
886   }
887   *num_suboperators = op->num_suboperators;
888   return CEED_ERROR_SUCCESS;
889 }
890 
891 /**
892   @brief Get the list of sub_operators associated with a CeedOperator
893 
894   @param op                  CeedOperator
895   @param[out] sub_operators  Variable to store list of sub_operators
896 
897   @return An error code: 0 - success, otherwise - failure
898 
899   @ref Backend
900 **/
901 
902 int CeedCompositeOperatorGetSubList(CeedOperator op, CeedOperator **sub_operators) {
903   if (!op->is_composite) {
904     // LCOV_EXCL_START
905     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
906     // LCOV_EXCL_STOP
907   }
908   *sub_operators = op->sub_operators;
909   return CEED_ERROR_SUCCESS;
910 }
911 
912 /**
913   @brief Check if a CeedOperator is ready to be used.
914 
915   @param[in] op CeedOperator to check
916 
917   @return An error code: 0 - success, otherwise - failure
918 
919   @ref User
920 **/
921 int CeedOperatorCheckReady(CeedOperator op) {
922   Ceed ceed;
923   CeedCall(CeedOperatorGetCeed(op, &ceed));
924 
925   if (op->is_interface_setup) return CEED_ERROR_SUCCESS;
926 
927   CeedQFunction qf = op->qf;
928   if (op->is_composite) {
929     if (!op->num_suboperators) {
930       // Empty operator setup
931       op->input_size  = 0;
932       op->output_size = 0;
933     } else {
934       for (CeedInt i = 0; i < op->num_suboperators; i++) {
935         CeedCall(CeedOperatorCheckReady(op->sub_operators[i]));
936       }
937       // Sub-operators could be modified after adding to composite operator
938       // Need to verify no lvec incompatibility from any changes
939       CeedSize input_size, output_size;
940       CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
941     }
942   } else {
943     if (op->num_fields == 0) {
944       // LCOV_EXCL_START
945       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No operator fields set");
946       // LCOV_EXCL_STOP
947     }
948     if (op->num_fields < qf->num_input_fields + qf->num_output_fields) {
949       // LCOV_EXCL_START
950       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set");
951       // LCOV_EXCL_STOP
952     }
953     if (!op->has_restriction) {
954       // LCOV_EXCL_START
955       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "At least one restriction required");
956       // LCOV_EXCL_STOP
957     }
958     if (op->num_qpts == 0) {
959       // LCOV_EXCL_START
960       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "At least one non-collocated basis is required or the number of quadrature points must be set");
961       // LCOV_EXCL_STOP
962     }
963   }
964 
965   // Flag as immutable and ready
966   op->is_interface_setup = true;
967   if (op->qf && op->qf != CEED_QFUNCTION_NONE) op->qf->is_immutable = true;
968   if (op->dqf && op->dqf != CEED_QFUNCTION_NONE) op->dqf->is_immutable = true;
969   if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE) op->dqfT->is_immutable = true;
970   return CEED_ERROR_SUCCESS;
971 }
972 
973 /**
974   @brief Get vector lengths for the active input and/or output vectors of a CeedOperator.
975            Note: Lengths of -1 indicate that the CeedOperator does not have an active input and/or output.
976 
977   @param[in]  op          CeedOperator
978   @param[out] input_size  Variable to store active input vector length, or NULL
979   @param[out] output_size Variable to store active output vector length, or NULL
980 
981   @return An error code: 0 - success, otherwise - failure
982 
983   @ref User
984 **/
985 int CeedOperatorGetActiveVectorLengths(CeedOperator op, CeedSize *input_size, CeedSize *output_size) {
986   bool is_composite;
987 
988   if (input_size) *input_size = op->input_size;
989   if (output_size) *output_size = op->output_size;
990 
991   CeedCall(CeedOperatorIsComposite(op, &is_composite));
992   if (is_composite && (op->input_size == -1 || op->output_size == -1)) {
993     for (CeedInt i = 0; i < op->num_suboperators; i++) {
994       CeedSize sub_input_size, sub_output_size;
995       CeedCall(CeedOperatorGetActiveVectorLengths(op->sub_operators[i], &sub_input_size, &sub_output_size));
996       if (op->input_size == -1) op->input_size = sub_input_size;
997       if (op->output_size == -1) op->output_size = sub_output_size;
998       // Note, a size of -1 means no active vector restriction set, so no incompatibility
999       if ((sub_input_size != -1 && sub_input_size != op->input_size) || (sub_output_size != -1 && sub_output_size != op->output_size)) {
1000         // LCOV_EXCL_START
1001         return CeedError(op->ceed, CEED_ERROR_MAJOR,
1002                          "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of "
1003                          "shape (%td, %td)",
1004                          op->input_size, op->output_size, input_size, output_size);
1005         // LCOV_EXCL_STOP
1006       }
1007     }
1008   }
1009 
1010   return CEED_ERROR_SUCCESS;
1011 }
1012 
1013 /**
1014   @brief Set reuse of CeedQFunction data in CeedOperatorLinearAssemble* functions.
1015            When `reuse_assembly_data = false` (default), the CeedQFunction associated with this CeedOperator is re-assembled every time a
1016 `CeedOperatorLinearAssemble*` function is called. When `reuse_assembly_data = true`, the CeedQFunction associated with this CeedOperator is reused
1017 between calls to `CeedOperatorSetQFunctionAssemblyDataUpdated`.
1018 
1019   @param[in] op                  CeedOperator
1020   @param[in] reuse_assembly_data Boolean flag setting assembly data reuse
1021 
1022   @return An error code: 0 - success, otherwise - failure
1023 
1024   @ref Advanced
1025 **/
1026 int CeedOperatorSetQFunctionAssemblyReuse(CeedOperator op, bool reuse_assembly_data) {
1027   bool is_composite;
1028 
1029   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1030   if (is_composite) {
1031     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1032       CeedCall(CeedOperatorSetQFunctionAssemblyReuse(op->sub_operators[i], reuse_assembly_data));
1033     }
1034   } else {
1035     CeedCall(CeedQFunctionAssemblyDataSetReuse(op->qf_assembled, reuse_assembly_data));
1036   }
1037 
1038   return CEED_ERROR_SUCCESS;
1039 }
1040 
1041 /**
1042   @brief Mark CeedQFunction data as updated and the CeedQFunction as requiring re-assembly.
1043 
1044   @param[in] op                CeedOperator
1045   @param[in] needs_data_update Boolean flag setting assembly data reuse
1046 
1047   @return An error code: 0 - success, otherwise - failure
1048 
1049   @ref Advanced
1050 **/
1051 int CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(CeedOperator op, bool needs_data_update) {
1052   bool is_composite;
1053 
1054   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1055   if (is_composite) {
1056     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1057       CeedCall(CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(op->sub_operators[i], needs_data_update));
1058     }
1059   } else {
1060     CeedCall(CeedQFunctionAssemblyDataSetUpdateNeeded(op->qf_assembled, needs_data_update));
1061   }
1062 
1063   return CEED_ERROR_SUCCESS;
1064 }
1065 
1066 /**
1067   @brief Set the number of quadrature points associated with a CeedOperator.
1068            This should be used when creating a CeedOperator where every field has a collocated basis.
1069            This function cannot be used for composite CeedOperators.
1070 
1071   @param[in,out] op       CeedOperator
1072   @param[in]     num_qpts Number of quadrature points to set
1073 
1074   @return An error code: 0 - success, otherwise - failure
1075 
1076   @ref Advanced
1077 **/
1078 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
1079   if (op->is_composite) {
1080     // LCOV_EXCL_START
1081     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
1082     // LCOV_EXCL_STOP
1083   }
1084   if (op->num_qpts) {
1085     // LCOV_EXCL_START
1086     return CeedError(op->ceed, CEED_ERROR_MINOR, "Number of quadrature points already defined");
1087     // LCOV_EXCL_STOP
1088   }
1089   if (op->is_immutable) {
1090     // LCOV_EXCL_START
1091     return CeedError(op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable");
1092     // LCOV_EXCL_STOP
1093   }
1094   op->num_qpts = num_qpts;
1095   return CEED_ERROR_SUCCESS;
1096 }
1097 
1098 /**
1099   @brief Set name of CeedOperator for CeedOperatorView output
1100 
1101   @param[in,out] op   CeedOperator
1102   @param[in]     name Name to set, or NULL to remove previously set name
1103 
1104   @return An error code: 0 - success, otherwise - failure
1105 
1106   @ref User
1107 **/
1108 int CeedOperatorSetName(CeedOperator op, const char *name) {
1109   char  *name_copy;
1110   size_t name_len = name ? strlen(name) : 0;
1111 
1112   CeedCall(CeedFree(&op->name));
1113   if (name_len > 0) {
1114     CeedCall(CeedCalloc(name_len + 1, &name_copy));
1115     memcpy(name_copy, name, name_len);
1116     op->name = name_copy;
1117   }
1118 
1119   return CEED_ERROR_SUCCESS;
1120 }
1121 
1122 /**
1123   @brief View a CeedOperator
1124 
1125   @param[in] op     CeedOperator to view
1126   @param[in] stream Stream to write; typically stdout/stderr or a file
1127 
1128   @return Error code: 0 - success, otherwise - failure
1129 
1130   @ref User
1131 **/
1132 int CeedOperatorView(CeedOperator op, FILE *stream) {
1133   bool has_name = op->name;
1134 
1135   if (op->is_composite) {
1136     fprintf(stream, "Composite CeedOperator%s%s\n", has_name ? " - " : "", has_name ? op->name : "");
1137 
1138     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1139       has_name = op->sub_operators[i]->name;
1140       fprintf(stream, "  SubOperator %" CeedInt_FMT "%s%s:\n", i, has_name ? " - " : "", has_name ? op->sub_operators[i]->name : "");
1141       CeedCall(CeedOperatorSingleView(op->sub_operators[i], 1, stream));
1142     }
1143   } else {
1144     fprintf(stream, "CeedOperator%s%s\n", has_name ? " - " : "", has_name ? op->name : "");
1145     CeedCall(CeedOperatorSingleView(op, 0, stream));
1146   }
1147   return CEED_ERROR_SUCCESS;
1148 }
1149 
1150 /**
1151   @brief Get the Ceed associated with a CeedOperator
1152 
1153   @param[in]  op   CeedOperator
1154   @param[out] ceed Variable to store Ceed
1155 
1156   @return An error code: 0 - success, otherwise - failure
1157 
1158   @ref Advanced
1159 **/
1160 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
1161   *ceed = op->ceed;
1162   return CEED_ERROR_SUCCESS;
1163 }
1164 
1165 /**
1166   @brief Get the number of elements associated with a CeedOperator
1167 
1168   @param[in]  op       CeedOperator
1169   @param[out] num_elem Variable to store number of elements
1170 
1171   @return An error code: 0 - success, otherwise - failure
1172 
1173   @ref Advanced
1174 **/
1175 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) {
1176   if (op->is_composite) {
1177     // LCOV_EXCL_START
1178     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
1179     // LCOV_EXCL_STOP
1180   }
1181   *num_elem = op->num_elem;
1182   return CEED_ERROR_SUCCESS;
1183 }
1184 
1185 /**
1186   @brief Get the number of quadrature points associated with a CeedOperator
1187 
1188   @param[in]  op       CeedOperator
1189   @param[out] num_qpts Variable to store vector number of quadrature points
1190 
1191   @return An error code: 0 - success, otherwise - failure
1192 
1193   @ref Advanced
1194 **/
1195 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) {
1196   if (op->is_composite) {
1197     // LCOV_EXCL_START
1198     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
1199     // LCOV_EXCL_STOP
1200   }
1201   *num_qpts = op->num_qpts;
1202   return CEED_ERROR_SUCCESS;
1203 }
1204 
1205 /**
1206   @brief Estimate number of FLOPs required to apply CeedOperator on the active vector
1207 
1208   @param[in]  op    CeedOperator to estimate FLOPs for
1209   @param[out] flops Address of variable to hold FLOPs estimate
1210 
1211   @ref Backend
1212 **/
1213 int CeedOperatorGetFlopsEstimate(CeedOperator op, CeedSize *flops) {
1214   bool is_composite;
1215   CeedCall(CeedOperatorCheckReady(op));
1216 
1217   *flops = 0;
1218   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1219   if (is_composite) {
1220     CeedInt num_suboperators;
1221     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
1222     CeedOperator *sub_operators;
1223     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1224 
1225     // FLOPs for each suboperator
1226     for (CeedInt i = 0; i < num_suboperators; i++) {
1227       CeedSize suboperator_flops;
1228       CeedCall(CeedOperatorGetFlopsEstimate(sub_operators[i], &suboperator_flops));
1229       *flops += suboperator_flops;
1230     }
1231   } else {
1232     CeedInt            num_input_fields, num_output_fields;
1233     CeedOperatorField *input_fields, *output_fields;
1234 
1235     CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields));
1236 
1237     CeedInt num_elem = 0;
1238     CeedCall(CeedOperatorGetNumElements(op, &num_elem));
1239     // Input FLOPs
1240     for (CeedInt i = 0; i < num_input_fields; i++) {
1241       if (input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1242         CeedSize restr_flops, basis_flops;
1243 
1244         CeedCall(CeedElemRestrictionGetFlopsEstimate(input_fields[i]->elem_restr, CEED_NOTRANSPOSE, &restr_flops));
1245         *flops += restr_flops;
1246         CeedCall(CeedBasisGetFlopsEstimate(input_fields[i]->basis, CEED_NOTRANSPOSE, op->qf->input_fields[i]->eval_mode, &basis_flops));
1247         *flops += basis_flops * num_elem;
1248       }
1249     }
1250     // QF FLOPs
1251     CeedInt  num_qpts;
1252     CeedSize qf_flops;
1253     CeedCall(CeedOperatorGetNumQuadraturePoints(op, &num_qpts));
1254     CeedCall(CeedQFunctionGetFlopsEstimate(op->qf, &qf_flops));
1255     *flops += num_elem * num_qpts * qf_flops;
1256     // Output FLOPs
1257     for (CeedInt i = 0; i < num_output_fields; i++) {
1258       if (output_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1259         CeedSize restr_flops, basis_flops;
1260 
1261         CeedCall(CeedElemRestrictionGetFlopsEstimate(output_fields[i]->elem_restr, CEED_TRANSPOSE, &restr_flops));
1262         *flops += restr_flops;
1263         CeedCall(CeedBasisGetFlopsEstimate(output_fields[i]->basis, CEED_TRANSPOSE, op->qf->output_fields[i]->eval_mode, &basis_flops));
1264         *flops += basis_flops * num_elem;
1265       }
1266     }
1267   }
1268 
1269   return CEED_ERROR_SUCCESS;
1270 }
1271 
1272 /**
1273   @brief Get label for a registered QFunctionContext field, or `NULL` if no field has been registered with this `field_name`.
1274 
1275   @param[in]  op          CeedOperator
1276   @param[in]  field_name  Name of field to retrieve label
1277   @param[out] field_label Variable to field label
1278 
1279   @return An error code: 0 - success, otherwise - failure
1280 
1281   @ref User
1282 **/
1283 int CeedOperatorContextGetFieldLabel(CeedOperator op, const char *field_name, CeedContextFieldLabel *field_label) {
1284   bool is_composite;
1285   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1286 
1287   if (is_composite) {
1288     // Check if composite label already created
1289     for (CeedInt i = 0; i < op->num_context_labels; i++) {
1290       if (!strcmp(op->context_labels[i]->name, field_name)) {
1291         *field_label = op->context_labels[i];
1292         return CEED_ERROR_SUCCESS;
1293       }
1294     }
1295 
1296     // Create composite label if needed
1297     CeedInt               num_sub;
1298     CeedOperator         *sub_operators;
1299     CeedContextFieldLabel new_field_label;
1300 
1301     CeedCall(CeedCalloc(1, &new_field_label));
1302     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub));
1303     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1304     CeedCall(CeedCalloc(num_sub, &new_field_label->sub_labels));
1305     new_field_label->num_sub_labels = num_sub;
1306 
1307     bool label_found = false;
1308     for (CeedInt i = 0; i < num_sub; i++) {
1309       if (sub_operators[i]->qf->ctx) {
1310         CeedContextFieldLabel new_field_label_i;
1311         CeedCall(CeedQFunctionContextGetFieldLabel(sub_operators[i]->qf->ctx, field_name, &new_field_label_i));
1312         if (new_field_label_i) {
1313           label_found                    = true;
1314           new_field_label->sub_labels[i] = new_field_label_i;
1315           new_field_label->name          = new_field_label_i->name;
1316           new_field_label->description   = new_field_label_i->description;
1317           if (new_field_label->type && new_field_label->type != new_field_label_i->type) {
1318             // LCOV_EXCL_START
1319             CeedCall(CeedFree(&new_field_label));
1320             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Incompatible field types on sub-operator contexts. %s != %s",
1321                              CeedContextFieldTypes[new_field_label->type], CeedContextFieldTypes[new_field_label_i->type]);
1322             // LCOV_EXCL_STOP
1323           } else {
1324             new_field_label->type = new_field_label_i->type;
1325           }
1326           if (new_field_label->num_values != 0 && new_field_label->num_values != new_field_label_i->num_values) {
1327             // LCOV_EXCL_START
1328             CeedCall(CeedFree(&new_field_label));
1329             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Incompatible field number of values on sub-operator contexts. %ld != %ld",
1330                              new_field_label->num_values, new_field_label_i->num_values);
1331             // LCOV_EXCL_STOP
1332           } else {
1333             new_field_label->num_values = new_field_label_i->num_values;
1334           }
1335         }
1336       }
1337     }
1338     if (!label_found) {
1339       // LCOV_EXCL_START
1340       CeedCall(CeedFree(&new_field_label->sub_labels));
1341       CeedCall(CeedFree(&new_field_label));
1342       *field_label = NULL;
1343       // LCOV_EXCL_STOP
1344     } else {
1345       // Move new composite label to operator
1346       if (op->num_context_labels == 0) {
1347         CeedCall(CeedCalloc(1, &op->context_labels));
1348         op->max_context_labels = 1;
1349       } else if (op->num_context_labels == op->max_context_labels) {
1350         CeedCall(CeedRealloc(2 * op->num_context_labels, &op->context_labels));
1351         op->max_context_labels *= 2;
1352       }
1353       op->context_labels[op->num_context_labels] = new_field_label;
1354       *field_label                               = new_field_label;
1355       op->num_context_labels++;
1356     }
1357 
1358     return CEED_ERROR_SUCCESS;
1359   } else {
1360     return CeedQFunctionContextGetFieldLabel(op->qf->ctx, field_name, field_label);
1361   }
1362 }
1363 
1364 /**
1365   @brief Set QFunctionContext field holding a double precision value.
1366            For composite operators, the value is set in all sub-operator QFunctionContexts that have a matching `field_name`.
1367 
1368   @param[in,out] op          CeedOperator
1369   @param[in]     field_label Label of field to register
1370   @param[in]     values      Values to set
1371 
1372   @return An error code: 0 - success, otherwise - failure
1373 
1374   @ref User
1375 **/
1376 int CeedOperatorContextSetDouble(CeedOperator op, CeedContextFieldLabel field_label, double *values) {
1377   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values);
1378 }
1379 
1380 /**
1381   @brief Set QFunctionContext field holding an int32 value.
1382            For composite operators, the value is set in all sub-operator QFunctionContexts that have a matching `field_name`.
1383 
1384   @param[in,out] op          CeedOperator
1385   @param[in]     field_label Label of field to set
1386   @param[in]     values      Values to set
1387 
1388   @return An error code: 0 - success, otherwise - failure
1389 
1390   @ref User
1391 **/
1392 int CeedOperatorContextSetInt32(CeedOperator op, CeedContextFieldLabel field_label, int *values) {
1393   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32, values);
1394 }
1395 
1396 /**
1397   @brief Apply CeedOperator to a vector
1398 
1399   This computes the action of the operator on the specified (active) input, yielding its (active) output.
1400   All inputs and outputs must be specified using CeedOperatorSetField().
1401 
1402   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
1403 
1404   @param[in]  op      CeedOperator to apply
1405   @param[in]  in      CeedVector containing input state or @ref CEED_VECTOR_NONE if there are no active inputs
1406   @param[out] out     CeedVector to store result of applying operator (must be distinct from @a in) or @ref CEED_VECTOR_NONE if there are no active
1407 outputs
1408   @param[in]  request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1409 
1410   @return An error code: 0 - success, otherwise - failure
1411 
1412   @ref User
1413 **/
1414 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) {
1415   CeedCall(CeedOperatorCheckReady(op));
1416 
1417   if (op->num_elem) {
1418     // Standard Operator
1419     if (op->Apply) {
1420       CeedCall(op->Apply(op, in, out, request));
1421     } else {
1422       // Zero all output vectors
1423       CeedQFunction qf = op->qf;
1424       for (CeedInt i = 0; i < qf->num_output_fields; i++) {
1425         CeedVector vec = op->output_fields[i]->vec;
1426         if (vec == CEED_VECTOR_ACTIVE) vec = out;
1427         if (vec != CEED_VECTOR_NONE) {
1428           CeedCall(CeedVectorSetValue(vec, 0.0));
1429         }
1430       }
1431       // Apply
1432       CeedCall(op->ApplyAdd(op, in, out, request));
1433     }
1434   } else if (op->is_composite) {
1435     // Composite Operator
1436     if (op->ApplyComposite) {
1437       CeedCall(op->ApplyComposite(op, in, out, request));
1438     } else {
1439       CeedInt num_suboperators;
1440       CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
1441       CeedOperator *sub_operators;
1442       CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1443 
1444       // Zero all output vectors
1445       if (out != CEED_VECTOR_NONE) {
1446         CeedCall(CeedVectorSetValue(out, 0.0));
1447       }
1448       for (CeedInt i = 0; i < num_suboperators; i++) {
1449         for (CeedInt j = 0; j < sub_operators[i]->qf->num_output_fields; j++) {
1450           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1451           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1452             CeedCall(CeedVectorSetValue(vec, 0.0));
1453           }
1454         }
1455       }
1456       // Apply
1457       for (CeedInt i = 0; i < op->num_suboperators; i++) {
1458         CeedCall(CeedOperatorApplyAdd(op->sub_operators[i], in, out, request));
1459       }
1460     }
1461   }
1462   return CEED_ERROR_SUCCESS;
1463 }
1464 
1465 /**
1466   @brief Apply CeedOperator to a vector and add result to output vector
1467 
1468   This computes the action of the operator on the specified (active) input, yielding its (active) output.
1469   All inputs and outputs must be specified using CeedOperatorSetField().
1470 
1471   @param[in]  op      CeedOperator to apply
1472   @param[in]  in      CeedVector containing input state or NULL if there are no active inputs
1473   @param[out] out     CeedVector to sum in result of applying operator (must be distinct from @a in) or NULL if there are no active outputs
1474   @param[in]  request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1475 
1476   @return An error code: 0 - success, otherwise - failure
1477 
1478   @ref User
1479 **/
1480 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) {
1481   CeedCall(CeedOperatorCheckReady(op));
1482 
1483   if (op->num_elem) {
1484     // Standard Operator
1485     CeedCall(op->ApplyAdd(op, in, out, request));
1486   } else if (op->is_composite) {
1487     // Composite Operator
1488     if (op->ApplyAddComposite) {
1489       CeedCall(op->ApplyAddComposite(op, in, out, request));
1490     } else {
1491       CeedInt num_suboperators;
1492       CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
1493       CeedOperator *sub_operators;
1494       CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1495 
1496       for (CeedInt i = 0; i < num_suboperators; i++) {
1497         CeedCall(CeedOperatorApplyAdd(sub_operators[i], in, out, request));
1498       }
1499     }
1500   }
1501   return CEED_ERROR_SUCCESS;
1502 }
1503 
1504 /**
1505   @brief Destroy a CeedOperator
1506 
1507   @param[in,out] op CeedOperator to destroy
1508 
1509   @return An error code: 0 - success, otherwise - failure
1510 
1511   @ref User
1512 **/
1513 int CeedOperatorDestroy(CeedOperator *op) {
1514   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
1515   if ((*op)->Destroy) CeedCall((*op)->Destroy(*op));
1516   CeedCall(CeedDestroy(&(*op)->ceed));
1517   // Free fields
1518   for (CeedInt i = 0; i < (*op)->num_fields; i++) {
1519     if ((*op)->input_fields[i]) {
1520       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
1521         CeedCall(CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr));
1522       }
1523       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1524         CeedCall(CeedBasisDestroy(&(*op)->input_fields[i]->basis));
1525       }
1526       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->input_fields[i]->vec != CEED_VECTOR_NONE) {
1527         CeedCall(CeedVectorDestroy(&(*op)->input_fields[i]->vec));
1528       }
1529       CeedCall(CeedFree(&(*op)->input_fields[i]->field_name));
1530       CeedCall(CeedFree(&(*op)->input_fields[i]));
1531     }
1532   }
1533   for (CeedInt i = 0; i < (*op)->num_fields; i++) {
1534     if ((*op)->output_fields[i]) {
1535       CeedCall(CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr));
1536       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1537         CeedCall(CeedBasisDestroy(&(*op)->output_fields[i]->basis));
1538       }
1539       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->output_fields[i]->vec != CEED_VECTOR_NONE) {
1540         CeedCall(CeedVectorDestroy(&(*op)->output_fields[i]->vec));
1541       }
1542       CeedCall(CeedFree(&(*op)->output_fields[i]->field_name));
1543       CeedCall(CeedFree(&(*op)->output_fields[i]));
1544     }
1545   }
1546   // Destroy sub_operators
1547   for (CeedInt i = 0; i < (*op)->num_suboperators; i++) {
1548     if ((*op)->sub_operators[i]) {
1549       CeedCall(CeedOperatorDestroy(&(*op)->sub_operators[i]));
1550     }
1551   }
1552   CeedCall(CeedQFunctionDestroy(&(*op)->qf));
1553   CeedCall(CeedQFunctionDestroy(&(*op)->dqf));
1554   CeedCall(CeedQFunctionDestroy(&(*op)->dqfT));
1555   // Destroy any composite labels
1556   for (CeedInt i = 0; i < (*op)->num_context_labels; i++) {
1557     CeedCall(CeedFree(&(*op)->context_labels[i]->sub_labels));
1558     CeedCall(CeedFree(&(*op)->context_labels[i]));
1559   }
1560   CeedCall(CeedFree(&(*op)->context_labels));
1561 
1562   // Destroy fallback
1563   CeedCall(CeedOperatorDestroy(&(*op)->op_fallback));
1564 
1565   // Destroy assembly data
1566   CeedCall(CeedQFunctionAssemblyDataDestroy(&(*op)->qf_assembled));
1567   CeedCall(CeedOperatorAssemblyDataDestroy(&(*op)->op_assembled));
1568 
1569   CeedCall(CeedFree(&(*op)->input_fields));
1570   CeedCall(CeedFree(&(*op)->output_fields));
1571   CeedCall(CeedFree(&(*op)->sub_operators));
1572   CeedCall(CeedFree(&(*op)->name));
1573   CeedCall(CeedFree(op));
1574   return CEED_ERROR_SUCCESS;
1575 }
1576 
1577 /// @}
1578