xref: /libCEED/interface/ceed-qfunctioncontext.c (revision d3677ae8c8463ed424a8c5269c03811212785cd9)
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 <stdint.h>
21 #include <stdio.h>
22 #include <string.h>
23 
24 /// @file
25 /// Implementation of public CeedQFunctionContext interfaces
26 
27 /// ----------------------------------------------------------------------------
28 /// CeedQFunctionContext Library Internal Functions
29 /// ----------------------------------------------------------------------------
30 /// @addtogroup CeedQFunctionDeveloper
31 /// @{
32 
33 /**
34   @brief Get index for QFunctionContext field
35 
36   @param ctx         CeedQFunctionContext
37   @param field_name  Name of field
38   @param field_index Index of field, or -1 if field is not registered
39 
40   @return An error code: 0 - success, otherwise - failure
41 
42   @ref Developer
43 **/
44 int CeedQFunctionContextGetFieldIndex(CeedQFunctionContext ctx,
45                                       const char *field_name, CeedInt *field_index) {
46   *field_index = -1;
47   for (CeedInt i=0; i<ctx->num_fields; i++)
48     if (!strcmp(ctx->field_descriptions[i].name, field_name))
49       *field_index = i;
50   return CEED_ERROR_SUCCESS;
51 }
52 
53 /**
54   @brief Common function for registering QFunctionContext fields
55 
56   @param ctx               CeedQFunctionContext
57   @param field_name        Name of field to register
58   @param field_offset      Offset of field to register
59   @param field_description Description of field, or NULL for none
60   @param field_type        Field data type, such as double or int32
61   @param field_size        Size of field, in bytes
62 
63   @return An error code: 0 - success, otherwise - failure
64 
65   @ref Developer
66 **/
67 int CeedQFunctionContextRegisterGeneric(CeedQFunctionContext ctx,
68                                         const char *field_name, size_t field_offset,
69                                         const char *field_description,
70                                         CeedContextFieldType field_type,
71                                         size_t field_size) {
72   int ierr;
73 
74   // Check for duplicate
75   CeedInt field_index = -1;
76   ierr = CeedQFunctionContextGetFieldIndex(ctx, field_name, &field_index);
77   CeedChk(ierr);
78   if (field_index != -1)
79     // LCOV_EXCL_START
80     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
81                      "QFunctionContext field with name \"%s\" already registered",
82                      field_name);
83   // LCOV_EXCL_STOP
84 
85   // Allocate space for field data
86   if (ctx->num_fields == 0) {
87     ierr = CeedCalloc(1, &ctx->field_descriptions); CeedChk(ierr);
88     ctx->max_fields = 1;
89   } else if (ctx->num_fields == ctx->max_fields) {
90     ierr = CeedRealloc(2*ctx->max_fields, &ctx->field_descriptions);
91     CeedChk(ierr);
92     ctx->max_fields *= 2;
93   }
94 
95   // Copy field data
96   ierr = CeedStringAllocCopy(field_name,
97                              (char **)&ctx->field_descriptions[ctx->num_fields].name);
98   CeedChk(ierr);
99   ierr = CeedStringAllocCopy(field_description,
100                              (char **)&ctx->field_descriptions[ctx->num_fields].description);
101   CeedChk(ierr);
102   ctx->field_descriptions[ctx->num_fields].type = field_type;
103   ctx->field_descriptions[ctx->num_fields].offset = field_offset;
104   ctx->field_descriptions[ctx->num_fields].size = field_size;
105   ctx->num_fields++;
106   return CEED_ERROR_SUCCESS;
107 }
108 
109 /**
110   @brief Set QFunctionContext field holding a double precision value
111 
112   @param ctx        CeedQFunctionContext
113   @param field_name Name of field to set
114   @param field_type Type of field to set
115   @param value      Value to set
116 
117   @return An error code: 0 - success, otherwise - failure
118 
119   @ref User
120 **/
121 int CeedQFunctionContextSetGeneric(CeedQFunctionContext ctx,
122                                    const char *field_name,
123                                    CeedContextFieldType field_type, void *value) {
124   int ierr;
125 
126   // Check field index
127   CeedInt field_index = -1;
128   ierr = CeedQFunctionContextGetFieldIndex(ctx, field_name, &field_index);
129   CeedChk(ierr);
130   if (field_index == -1)
131     // LCOV_EXCL_START
132     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
133                      "QFunctionContext field with name \"%s\" not registered",
134                      field_name);
135   // LCOV_EXCL_STOP
136 
137   if (ctx->field_descriptions[field_index].type != field_type)
138     // LCOV_EXCL_START
139     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
140                      "QFunctionContext field with name \"%s\" registered as %s, "
141                      "not registered as %s", field_name,
142                      CeedContextFieldTypes[ctx->field_descriptions[field_index].type],
143                      CeedContextFieldTypes[field_type]);
144   // LCOV_EXCL_STOP
145 
146   char *data;
147   ierr = CeedQFunctionContextGetData(ctx, CEED_MEM_HOST, &data); CeedChk(ierr);
148   memcpy(&data[ctx->field_descriptions[field_index].offset], value,
149          ctx->field_descriptions[field_index].size);
150   ierr = CeedQFunctionContextRestoreData(ctx, &data); CeedChk(ierr);
151   return CEED_ERROR_SUCCESS;
152 }
153 
154 /// @}
155 
156 /// ----------------------------------------------------------------------------
157 /// CeedQFunctionContext Backend API
158 /// ----------------------------------------------------------------------------
159 /// @addtogroup CeedQFunctionBackend
160 /// @{
161 
162 /**
163   @brief Get the Ceed associated with a CeedQFunctionContext
164 
165   @param ctx        CeedQFunctionContext
166   @param[out] ceed  Variable to store Ceed
167 
168   @return An error code: 0 - success, otherwise - failure
169 
170   @ref Backend
171 **/
172 int CeedQFunctionContextGetCeed(CeedQFunctionContext ctx, Ceed *ceed) {
173   *ceed = ctx->ceed;
174   return CEED_ERROR_SUCCESS;
175 }
176 
177 /**
178   @brief Check for valid data in a CeedQFunctionContext
179 
180   @param ctx                  CeedQFunctionContext to check validity
181   @param[out] has_valid_data  Variable to store validity
182 
183   @return An error code: 0 - success, otherwise - failure
184 
185   @ref Backend
186 **/
187 int CeedQFunctionContextHasValidData(CeedQFunctionContext ctx,
188                                      bool *has_valid_data) {
189   int ierr;
190 
191   if (!ctx->HasValidData)
192     // LCOV_EXCL_START
193     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
194                      "Backend does not support HasValidData");
195   // LCOV_EXCL_STOP
196 
197   ierr = ctx->HasValidData(ctx, has_valid_data); CeedChk(ierr);
198 
199   return CEED_ERROR_SUCCESS;
200 }
201 
202 /**
203   @brief Check for borrowed data of a specific CeedMemType in a
204            CeedQFunctionContext
205 
206   @param ctx                             CeedQFunctionContext to check
207   @param mem_type                        Memory type to check
208   @param[out] has_borrowed_data_of_type  Variable to store result
209 
210   @return An error code: 0 - success, otherwise - failure
211 
212   @ref Backend
213 **/
214 int CeedQFunctionContextHasBorrowedDataOfType(CeedQFunctionContext ctx,
215     CeedMemType mem_type, bool *has_borrowed_data_of_type) {
216   int ierr;
217 
218   if (!ctx->HasBorrowedDataOfType)
219     // LCOV_EXCL_START
220     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
221                      "Backend does not support HasBorrowedDataOfType");
222   // LCOV_EXCL_STOP
223 
224   ierr = ctx->HasBorrowedDataOfType(ctx, mem_type, has_borrowed_data_of_type);
225   CeedChk(ierr);
226 
227   return CEED_ERROR_SUCCESS;
228 }
229 
230 /**
231   @brief Get the state of a CeedQFunctionContext
232 
233   @param ctx         CeedQFunctionContext to retrieve state
234   @param[out] state  Variable to store state
235 
236   @return An error code: 0 - success, otherwise - failure
237 
238   @ref Backend
239 **/
240 int CeedQFunctionContextGetState(CeedQFunctionContext ctx, uint64_t *state) {
241   *state = ctx->state;
242   return CEED_ERROR_SUCCESS;
243 }
244 
245 /**
246   @brief Get backend data of a CeedQFunctionContext
247 
248   @param ctx        CeedQFunctionContext
249   @param[out] data  Variable to store data
250 
251   @return An error code: 0 - success, otherwise - failure
252 
253   @ref Backend
254 **/
255 int CeedQFunctionContextGetBackendData(CeedQFunctionContext ctx, void *data) {
256   *(void **)data = ctx->data;
257   return CEED_ERROR_SUCCESS;
258 }
259 
260 /**
261   @brief Set backend data of a CeedQFunctionContext
262 
263   @param[out] ctx  CeedQFunctionContext
264   @param data      Data to set
265 
266   @return An error code: 0 - success, otherwise - failure
267 
268   @ref Backend
269 **/
270 int CeedQFunctionContextSetBackendData(CeedQFunctionContext ctx, void *data) {
271   ctx->data = data;
272   return CEED_ERROR_SUCCESS;
273 }
274 
275 /**
276   @brief Increment the reference counter for a CeedQFunctionContext
277 
278   @param ctx  CeedQFunctionContext to increment the reference counter
279 
280   @return An error code: 0 - success, otherwise - failure
281 
282   @ref Backend
283 **/
284 int CeedQFunctionContextReference(CeedQFunctionContext ctx) {
285   ctx->ref_count++;
286   return CEED_ERROR_SUCCESS;
287 }
288 
289 /// @}
290 
291 /// ----------------------------------------------------------------------------
292 /// CeedQFunctionContext Public API
293 /// ----------------------------------------------------------------------------
294 /// @addtogroup CeedQFunctionUser
295 /// @{
296 
297 /**
298   @brief Create a CeedQFunctionContext for storing CeedQFunction user context data
299 
300   @param ceed      A Ceed object where the CeedQFunctionContext will be created
301   @param[out] ctx  Address of the variable where the newly created
302                      CeedQFunctionContext will be stored
303 
304   @return An error code: 0 - success, otherwise - failure
305 
306   @ref User
307 **/
308 int CeedQFunctionContextCreate(Ceed ceed, CeedQFunctionContext *ctx) {
309   int ierr;
310 
311   if (!ceed->QFunctionContextCreate) {
312     Ceed delegate;
313     ierr = CeedGetObjectDelegate(ceed, &delegate, "Context"); CeedChk(ierr);
314 
315     if (!delegate)
316       // LCOV_EXCL_START
317       return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
318                        "Backend does not support ContextCreate");
319     // LCOV_EXCL_STOP
320 
321     ierr = CeedQFunctionContextCreate(delegate, ctx); CeedChk(ierr);
322     return CEED_ERROR_SUCCESS;
323   }
324 
325   ierr = CeedCalloc(1, ctx); CeedChk(ierr);
326   (*ctx)->ceed = ceed;
327   ierr = CeedReference(ceed); CeedChk(ierr);
328   (*ctx)->ref_count = 1;
329   ierr = ceed->QFunctionContextCreate(*ctx); CeedChk(ierr);
330   return CEED_ERROR_SUCCESS;
331 }
332 
333 /**
334   @brief Copy the pointer to a CeedQFunctionContext. Both pointers should
335            be destroyed with `CeedQFunctionContextDestroy()`;
336            Note: If `*ctx_copy` is non-NULL, then it is assumed that
337            `*ctx_copy` is a pointer to a CeedQFunctionContext. This
338            CeedQFunctionContext will be destroyed if `*ctx_copy` is the
339            only reference to this CeedQFunctionContext.
340 
341   @param ctx            CeedQFunctionContext to copy reference to
342   @param[out] ctx_copy  Variable to store copied reference
343 
344   @return An error code: 0 - success, otherwise - failure
345 
346   @ref User
347 **/
348 int CeedQFunctionContextReferenceCopy(CeedQFunctionContext ctx,
349                                       CeedQFunctionContext *ctx_copy) {
350   int ierr;
351 
352   ierr = CeedQFunctionContextReference(ctx); CeedChk(ierr);
353   ierr = CeedQFunctionContextDestroy(ctx_copy); CeedChk(ierr);
354   *ctx_copy = ctx;
355   return CEED_ERROR_SUCCESS;
356 }
357 
358 /**
359   @brief Set the data used by a CeedQFunctionContext, freeing any previously allocated
360            data if applicable. The backend may copy values to a different
361            memtype, such as during @ref CeedQFunctionApply().
362            See also @ref CeedQFunctionContextTakeData().
363 
364   @param ctx        CeedQFunctionContext
365   @param mem_type   Memory type of the data being passed
366   @param copy_mode  Copy mode for the data
367   @param size       Size of data, in bytes
368   @param data       Data to be used
369 
370   @return An error code: 0 - success, otherwise - failure
371 
372   @ref User
373 **/
374 int CeedQFunctionContextSetData(CeedQFunctionContext ctx, CeedMemType mem_type,
375                                 CeedCopyMode copy_mode,
376                                 size_t size, void *data) {
377   int ierr;
378 
379   if (!ctx->SetData)
380     // LCOV_EXCL_START
381     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
382                      "Backend does not support ContextSetData");
383   // LCOV_EXCL_STOP
384 
385   if (ctx->state % 2 == 1)
386     // LCOV_EXCL_START
387     return CeedError(ctx->ceed, 1,
388                      "Cannot grant CeedQFunctionContext data access, the "
389                      "access lock is already in use");
390   // LCOV_EXCL_STOP
391 
392   ctx->ctx_size = size;
393   ierr = ctx->SetData(ctx, mem_type, copy_mode, data); CeedChk(ierr);
394   ctx->state += 2;
395   return CEED_ERROR_SUCCESS;
396 }
397 
398 /**
399   @brief Take ownership of the data in a CeedQFunctionContext via the specified memory type.
400            The caller is responsible for managing and freeing the memory.
401 
402   @param ctx        CeedQFunctionContext to access
403   @param mem_type   Memory type on which to access the data. If the backend
404                       uses a different memory type, this will perform a copy.
405   @param[out] data  Data on memory type mem_type
406 
407   @return An error code: 0 - success, otherwise - failure
408 
409   @ref User
410 **/
411 int CeedQFunctionContextTakeData(CeedQFunctionContext ctx, CeedMemType mem_type,
412                                  void *data) {
413   int ierr;
414 
415   bool has_valid_data = true;
416   ierr = CeedQFunctionContextHasValidData(ctx, &has_valid_data); CeedChk(ierr);
417   if (!has_valid_data)
418     // LCOV_EXCL_START
419     return CeedError(ctx->ceed, CEED_ERROR_BACKEND,
420                      "CeedQFunctionContext has no valid data to take, must set data");
421   // LCOV_EXCL_STOP
422 
423   if (!ctx->TakeData)
424     // LCOV_EXCL_START
425     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
426                      "Backend does not support TakeData");
427   // LCOV_EXCL_STOP
428 
429   if (ctx->state % 2 == 1)
430     // LCOV_EXCL_START
431     return CeedError(ctx->ceed, 1,
432                      "Cannot grant CeedQFunctionContext data access, the "
433                      "access lock is already in use");
434   // LCOV_EXCL_STOP
435 
436   bool has_borrowed_data_of_type = true;
437   ierr = CeedQFunctionContextHasBorrowedDataOfType(ctx, mem_type,
438          &has_borrowed_data_of_type); CeedChk(ierr);
439   if (!has_borrowed_data_of_type)
440     // LCOV_EXCL_START
441     return CeedError(ctx->ceed, CEED_ERROR_BACKEND,
442                      "CeedQFunctionContext has no borowed %s data, "
443                      "must set data with CeedQFunctionContextSetData",
444                      CeedMemTypes[mem_type]);
445   // LCOV_EXCL_STOP
446 
447   void *temp_data = NULL;
448   ierr = ctx->TakeData(ctx, mem_type, &temp_data); CeedChk(ierr);
449   if (data) (*(void **)data) = temp_data;
450   return CEED_ERROR_SUCCESS;
451 }
452 
453 /**
454   @brief Get read/write access to a CeedQFunctionContext via the specified memory type.
455            Restore access with @ref CeedQFunctionContextRestoreData().
456 
457   @param ctx        CeedQFunctionContext to access
458   @param mem_type   Memory type on which to access the data. If the backend
459                       uses a different memory type, this will perform a copy.
460   @param[out] data  Data on memory type mem_type
461 
462   @note The CeedQFunctionContextGetData() and @ref CeedQFunctionContextRestoreData() functions
463     provide access to array pointers in the desired memory space. Pairing
464     get/restore allows the Context to track access.
465 
466   @return An error code: 0 - success, otherwise - failure
467 
468   @ref User
469 **/
470 int CeedQFunctionContextGetData(CeedQFunctionContext ctx, CeedMemType mem_type,
471                                 void *data) {
472   int ierr;
473 
474   if (!ctx->GetData)
475     // LCOV_EXCL_START
476     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
477                      "Backend does not support GetData");
478   // LCOV_EXCL_STOP
479 
480   if (ctx->state % 2 == 1)
481     // LCOV_EXCL_START
482     return CeedError(ctx->ceed, 1,
483                      "Cannot grant CeedQFunctionContext data access, the "
484                      "access lock is already in use");
485   // LCOV_EXCL_STOP
486 
487   bool has_valid_data = true;
488   ierr = CeedQFunctionContextHasValidData(ctx, &has_valid_data); CeedChk(ierr);
489   if (!has_valid_data)
490     // LCOV_EXCL_START
491     return CeedError(ctx->ceed, CEED_ERROR_BACKEND,
492                      "CeedQFunctionContext has no valid data to get, must set data");
493   // LCOV_EXCL_STOP
494 
495   ierr = ctx->GetData(ctx, mem_type, data); CeedChk(ierr);
496   ctx->state += 1;
497   return CEED_ERROR_SUCCESS;
498 }
499 
500 /**
501   @brief Restore data obtained using @ref CeedQFunctionContextGetData()
502 
503   @param ctx   CeedQFunctionContext to restore
504   @param data  Data to restore
505 
506   @return An error code: 0 - success, otherwise - failure
507 
508   @ref User
509 **/
510 int CeedQFunctionContextRestoreData(CeedQFunctionContext ctx, void *data) {
511   int ierr;
512 
513   if (!ctx->RestoreData)
514     // LCOV_EXCL_START
515     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
516                      "Backend does not support RestoreData");
517   // LCOV_EXCL_STOP
518 
519   if (ctx->state % 2 != 1)
520     // LCOV_EXCL_START
521     return CeedError(ctx->ceed, 1,
522                      "Cannot restore CeedQFunctionContext array access, "
523                      "access was not granted");
524   // LCOV_EXCL_STOP
525 
526   ierr = ctx->RestoreData(ctx); CeedChk(ierr);
527   *(void **)data = NULL;
528   ctx->state += 1;
529   return CEED_ERROR_SUCCESS;
530 }
531 
532 /**
533   @brief Register QFunctionContext a field holding a double precision value
534 
535   @param ctx               CeedQFunctionContext
536   @param field_name        Name of field to register
537   @param field_offset      Offset of field to register
538   @param field_description Description of field, or NULL for none
539 
540   @return An error code: 0 - success, otherwise - failure
541 
542   @ref User
543 **/
544 int CeedQFunctionContextRegisterDouble(CeedQFunctionContext ctx,
545                                        const char *field_name, size_t field_offset,
546                                        const char *field_description) {
547   return CeedQFunctionContextRegisterGeneric(ctx, field_name, field_offset,
548          field_description, CEED_CONTEXT_FIELD_DOUBLE, sizeof(double));
549 }
550 
551 /**
552   @brief Register QFunctionContext a field holding a int32 value
553 
554   @param ctx               CeedQFunctionContext
555   @param field_name        Name of field to register
556   @param field_offset      Offset of field to register
557   @param field_description Description of field, or NULL for none
558 
559   @return An error code: 0 - success, otherwise - failure
560 
561   @ref User
562 **/
563 int CeedQFunctionContextRegisterInt32(CeedQFunctionContext ctx,
564                                       const char *field_name, size_t field_offset,
565                                       const char *field_description) {
566   return CeedQFunctionContextRegisterGeneric(ctx, field_name, field_offset,
567          field_description, CEED_CONTEXT_FIELD_INT32, sizeof(int));
568 }
569 
570 /**
571   @brief Get descriptions for registered QFunctionContext fields
572 
573   @param ctx                     CeedQFunctionContext
574   @param[out] field_descriptions Variable to hold array of field descriptions
575   @param[out] num_fields         Length of field descriptions array
576 
577   @return An error code: 0 - success, otherwise - failure
578 
579   @ref User
580 **/
581 int CeedQFunctionContextGetFieldDescriptions(CeedQFunctionContext ctx,
582     const CeedQFunctionContextFieldDescription **field_descriptions,
583     CeedInt *num_fields) {
584   *field_descriptions = ctx->field_descriptions;
585   *num_fields = ctx->num_fields;
586   return CEED_ERROR_SUCCESS;
587 }
588 
589 /**
590   @brief Set QFunctionContext field holding a double precision value
591 
592   @param ctx        CeedQFunctionContext
593   @param field_name Name of field to register
594   @param value      Value to set
595 
596   @return An error code: 0 - success, otherwise - failure
597 
598   @ref User
599 **/
600 int CeedQFunctionContextSetDouble(CeedQFunctionContext ctx,
601                                   const char *field_name, double value) {
602   return CeedQFunctionContextSetGeneric(ctx, field_name,
603                                         CEED_CONTEXT_FIELD_DOUBLE,
604                                         &value);
605 }
606 
607 /**
608   @brief Set QFunctionContext field holding a int32 value
609 
610   @param ctx        CeedQFunctionContext
611   @param field_name Name of field to set
612   @param value      Value to set
613 
614   @return An error code: 0 - success, otherwise - failure
615 
616   @ref User
617 **/
618 int CeedQFunctionContextSetInt32(CeedQFunctionContext ctx,
619                                  const char *field_name, int value) {
620   return CeedQFunctionContextSetGeneric(ctx, field_name, CEED_CONTEXT_FIELD_INT32,
621                                         &value);
622 }
623 
624 /**
625   @brief Get data size for a Context
626 
627   @param ctx            CeedQFunctionContext
628   @param[out] ctx_size  Variable to store size of context data values
629 
630   @return An error code: 0 - success, otherwise - failure
631 
632   @ref User
633 **/
634 int CeedQFunctionContextGetContextSize(CeedQFunctionContext ctx,
635                                        size_t *ctx_size) {
636   *ctx_size = ctx->ctx_size;
637   return CEED_ERROR_SUCCESS;
638 }
639 
640 
641 /**
642   @brief View a CeedQFunctionContext
643 
644   @param[in] ctx     CeedQFunctionContext to view
645   @param[in] stream  Filestream to write to
646 
647   @return An error code: 0 - success, otherwise - failure
648 
649   @ref User
650 **/
651 int CeedQFunctionContextView(CeedQFunctionContext ctx, FILE *stream) {
652   fprintf(stream, "CeedQFunctionContext\n");
653   fprintf(stream, "  Context Data Size: %ld\n", ctx->ctx_size);
654   return CEED_ERROR_SUCCESS;
655 }
656 
657 /**
658   @brief Destroy a CeedQFunctionContext
659 
660   @param ctx  CeedQFunctionContext to destroy
661 
662   @return An error code: 0 - success, otherwise - failure
663 
664   @ref User
665 **/
666 int CeedQFunctionContextDestroy(CeedQFunctionContext *ctx) {
667   int ierr;
668 
669   if (!*ctx || --(*ctx)->ref_count > 0)
670     return CEED_ERROR_SUCCESS;
671 
672   if ((*ctx) && ((*ctx)->state % 2) == 1)
673     // LCOV_EXCL_START
674     return CeedError((*ctx)->ceed, 1,
675                      "Cannot destroy CeedQFunctionContext, the access "
676                      "lock is in use");
677   // LCOV_EXCL_STOP
678 
679   if ((*ctx)->Destroy) {
680     ierr = (*ctx)->Destroy(*ctx); CeedChk(ierr);
681   }
682   for (CeedInt i=0; i<(*ctx)->num_fields; i++) {
683     ierr = CeedFree(&(*ctx)->field_descriptions[i].name); CeedChk(ierr);
684     ierr = CeedFree(&(*ctx)->field_descriptions[i].description); CeedChk(ierr);
685   }
686   ierr = CeedFree(&(*ctx)->field_descriptions); CeedChk(ierr);
687   ierr = CeedDestroy(&(*ctx)->ceed); CeedChk(ierr);
688   ierr = CeedFree(ctx); CeedChk(ierr);
689 
690   return CEED_ERROR_SUCCESS;
691 }
692 
693 /// @}
694