xref: /libCEED/interface/ceed-qfunctioncontext.c (revision 2e64a2b98b92a606ea5279621c68cc90295ae2a9)
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/ceed.h>
9 #include <ceed/backend.h>
10 #include <ceed-impl.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <string.h>
14 
15 /// @file
16 /// Implementation of public CeedQFunctionContext interfaces
17 
18 /// ----------------------------------------------------------------------------
19 /// CeedQFunctionContext Library Internal Functions
20 /// ----------------------------------------------------------------------------
21 /// @addtogroup CeedQFunctionDeveloper
22 /// @{
23 
24 /**
25   @brief Get index for QFunctionContext field
26 
27   @param ctx         CeedQFunctionContext
28   @param field_name  Name of field
29   @param field_index Index of field, or -1 if field is not registered
30 
31   @return An error code: 0 - success, otherwise - failure
32 
33   @ref Developer
34 **/
35 int CeedQFunctionContextGetFieldIndex(CeedQFunctionContext ctx,
36                                       const char *field_name, CeedInt *field_index) {
37   *field_index = -1;
38   for (CeedInt i=0; i<ctx->num_fields; i++)
39     if (!strcmp(ctx->field_labels[i]->name, field_name))
40       *field_index = i;
41   return CEED_ERROR_SUCCESS;
42 }
43 
44 /**
45   @brief Common function for registering QFunctionContext fields
46 
47   @param ctx               CeedQFunctionContext
48   @param field_name        Name of field to register
49   @param field_offset      Offset of field to register
50   @param field_description Description of field, or NULL for none
51   @param field_type        Field data type, such as double or int32
52   @param field_size        Size of field, in bytes
53   @param num_values        Number of values to register, must be contiguous in memory
54 
55   @return An error code: 0 - success, otherwise - failure
56 
57   @ref Developer
58 **/
59 int CeedQFunctionContextRegisterGeneric(CeedQFunctionContext ctx,
60                                         const char *field_name, size_t field_offset,
61                                         const char *field_description,
62                                         CeedContextFieldType field_type,
63                                         size_t field_size, size_t num_values) {
64   int ierr;
65 
66   // Check for duplicate
67   CeedInt field_index = -1;
68   ierr = CeedQFunctionContextGetFieldIndex(ctx, field_name, &field_index);
69   CeedChk(ierr);
70   if (field_index != -1)
71     // LCOV_EXCL_START
72     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
73                      "QFunctionContext field with name \"%s\" already registered",
74                      field_name);
75   // LCOV_EXCL_STOP
76 
77   // Allocate space for field data
78   if (ctx->num_fields == 0) {
79     ierr = CeedCalloc(1, &ctx->field_labels); CeedChk(ierr);
80     ctx->max_fields = 1;
81   } else if (ctx->num_fields == ctx->max_fields) {
82     ierr = CeedRealloc(2*ctx->max_fields, &ctx->field_labels);
83     CeedChk(ierr);
84     ctx->max_fields *= 2;
85   }
86   ierr = CeedCalloc(1, &ctx->field_labels[ctx->num_fields]); CeedChk(ierr);
87 
88   // Copy field data
89   ierr = CeedStringAllocCopy(field_name,
90                              (char **)&ctx->field_labels[ctx->num_fields]->name);
91   CeedChk(ierr);
92   ierr = CeedStringAllocCopy(field_description,
93                              (char **)&ctx->field_labels[ctx->num_fields]->description);
94   CeedChk(ierr);
95   ctx->field_labels[ctx->num_fields]->type = field_type;
96   ctx->field_labels[ctx->num_fields]->offset = field_offset;
97   ctx->field_labels[ctx->num_fields]->size = field_size * num_values;
98   ctx->field_labels[ctx->num_fields]->num_values = num_values;
99   ctx->num_fields++;
100   return CEED_ERROR_SUCCESS;
101 }
102 
103 /// @}
104 
105 /// ----------------------------------------------------------------------------
106 /// CeedQFunctionContext Backend API
107 /// ----------------------------------------------------------------------------
108 /// @addtogroup CeedQFunctionBackend
109 /// @{
110 
111 /**
112   @brief Get the Ceed associated with a CeedQFunctionContext
113 
114   @param ctx        CeedQFunctionContext
115   @param[out] ceed  Variable to store Ceed
116 
117   @return An error code: 0 - success, otherwise - failure
118 
119   @ref Backend
120 **/
121 int CeedQFunctionContextGetCeed(CeedQFunctionContext ctx, Ceed *ceed) {
122   *ceed = ctx->ceed;
123   return CEED_ERROR_SUCCESS;
124 }
125 
126 /**
127   @brief Check for valid data in a CeedQFunctionContext
128 
129   @param ctx                  CeedQFunctionContext to check validity
130   @param[out] has_valid_data  Variable to store validity
131 
132   @return An error code: 0 - success, otherwise - failure
133 
134   @ref Backend
135 **/
136 int CeedQFunctionContextHasValidData(CeedQFunctionContext ctx,
137                                      bool *has_valid_data) {
138   int ierr;
139 
140   if (!ctx->HasValidData)
141     // LCOV_EXCL_START
142     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
143                      "Backend does not support HasValidData");
144   // LCOV_EXCL_STOP
145 
146   ierr = ctx->HasValidData(ctx, has_valid_data); CeedChk(ierr);
147 
148   return CEED_ERROR_SUCCESS;
149 }
150 
151 /**
152   @brief Check for borrowed data of a specific CeedMemType in a
153            CeedQFunctionContext
154 
155   @param ctx                             CeedQFunctionContext to check
156   @param mem_type                        Memory type to check
157   @param[out] has_borrowed_data_of_type  Variable to store result
158 
159   @return An error code: 0 - success, otherwise - failure
160 
161   @ref Backend
162 **/
163 int CeedQFunctionContextHasBorrowedDataOfType(CeedQFunctionContext ctx,
164     CeedMemType mem_type, bool *has_borrowed_data_of_type) {
165   int ierr;
166 
167   if (!ctx->HasBorrowedDataOfType)
168     // LCOV_EXCL_START
169     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
170                      "Backend does not support HasBorrowedDataOfType");
171   // LCOV_EXCL_STOP
172 
173   ierr = ctx->HasBorrowedDataOfType(ctx, mem_type, has_borrowed_data_of_type);
174   CeedChk(ierr);
175 
176   return CEED_ERROR_SUCCESS;
177 }
178 
179 /**
180   @brief Get the state of a CeedQFunctionContext
181 
182   @param ctx         CeedQFunctionContext to retrieve state
183   @param[out] state  Variable to store state
184 
185   @return An error code: 0 - success, otherwise - failure
186 
187   @ref Backend
188 **/
189 int CeedQFunctionContextGetState(CeedQFunctionContext ctx, uint64_t *state) {
190   *state = ctx->state;
191   return CEED_ERROR_SUCCESS;
192 }
193 
194 /**
195   @brief Get backend data of a CeedQFunctionContext
196 
197   @param ctx        CeedQFunctionContext
198   @param[out] data  Variable to store data
199 
200   @return An error code: 0 - success, otherwise - failure
201 
202   @ref Backend
203 **/
204 int CeedQFunctionContextGetBackendData(CeedQFunctionContext ctx, void *data) {
205   *(void **)data = ctx->data;
206   return CEED_ERROR_SUCCESS;
207 }
208 
209 /**
210   @brief Set backend data of a CeedQFunctionContext
211 
212   @param[out] ctx  CeedQFunctionContext
213   @param data      Data to set
214 
215   @return An error code: 0 - success, otherwise - failure
216 
217   @ref Backend
218 **/
219 int CeedQFunctionContextSetBackendData(CeedQFunctionContext ctx, void *data) {
220   ctx->data = data;
221   return CEED_ERROR_SUCCESS;
222 }
223 
224 /**
225   @brief Get label for a registered QFunctionContext field, or `NULL` if no
226            field has been registered with this `field_name`
227 
228   @param[in] ctx           CeedQFunctionContext
229   @param[in] field_name    Name of field to retrieve label
230   @param[out] field_label  Variable to field label
231 
232   @return An error code: 0 - success, otherwise - failure
233 
234   @ref Backend
235 **/
236 int CeedQFunctionContextGetFieldLabel(CeedQFunctionContext ctx,
237                                       const char *field_name,
238                                       CeedContextFieldLabel *field_label) {
239   int ierr;
240 
241   CeedInt field_index;
242   ierr = CeedQFunctionContextGetFieldIndex(ctx, field_name, &field_index);
243   CeedChk(ierr);
244 
245   if (field_index != -1) {
246     *field_label = ctx->field_labels[field_index];
247   } else {
248     *field_label = NULL;
249   }
250 
251   return CEED_ERROR_SUCCESS;
252 }
253 
254 /**
255   @brief Set QFunctionContext field
256 
257   @param ctx         CeedQFunctionContext
258   @param field_label Label of field to set
259   @param field_type  Type of field to set
260   @param value       Value to set
261 
262   @return An error code: 0 - success, otherwise - failure
263 
264   @ref Backend
265 **/
266 int CeedQFunctionContextSetGeneric(CeedQFunctionContext ctx,
267                                    CeedContextFieldLabel field_label,
268                                    CeedContextFieldType field_type,
269                                    void *value) {
270   int ierr;
271 
272   // Check field type
273   if (field_label->type != field_type)
274     // LCOV_EXCL_START
275     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
276                      "QFunctionContext field with name \"%s\" registered as %s, "
277                      "not registered as %s", field_label->name,
278                      CeedContextFieldTypes[field_label->type],
279                      CeedContextFieldTypes[field_type]);
280   // LCOV_EXCL_STOP
281 
282   char *data;
283   ierr = CeedQFunctionContextGetData(ctx, CEED_MEM_HOST, &data); CeedChk(ierr);
284   memcpy(&data[field_label->offset], value, field_label->size);
285   ierr = CeedQFunctionContextRestoreData(ctx, &data); CeedChk(ierr);
286 
287   return CEED_ERROR_SUCCESS;
288 }
289 
290 /**
291   @brief Set QFunctionContext field holding a double precision value
292 
293   @param ctx         CeedQFunctionContext
294   @param field_label Label for field to register
295   @param values      Values to set
296 
297   @return An error code: 0 - success, otherwise - failure
298 
299   @ref Backend
300 **/
301 int CeedQFunctionContextSetDouble(CeedQFunctionContext ctx,
302                                   CeedContextFieldLabel field_label, double *values) {
303   int ierr;
304 
305   if (!field_label)
306     // LCOV_EXCL_START
307     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
308                      "Invalid field label");
309   // LCOV_EXCL_STOP
310 
311   ierr = CeedQFunctionContextSetGeneric(ctx, field_label,
312                                         CEED_CONTEXT_FIELD_DOUBLE,
313                                         values); CeedChk(ierr);
314 
315   return CEED_ERROR_SUCCESS;
316 }
317 
318 /**
319   @brief Set QFunctionContext field holding an int32 value
320 
321   @param ctx         CeedQFunctionContext
322   @param field_label Label for field to register
323   @param values      Values to set
324 
325   @return An error code: 0 - success, otherwise - failure
326 
327   @ref Backend
328 **/
329 int CeedQFunctionContextSetInt32(CeedQFunctionContext ctx,
330                                  CeedContextFieldLabel field_label, int *values) {
331   int ierr;
332 
333   if (!field_label)
334     // LCOV_EXCL_START
335     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
336                      "Invalid field label");
337   // LCOV_EXCL_STOP
338 
339   ierr = CeedQFunctionContextSetGeneric(ctx, field_label,
340                                         CEED_CONTEXT_FIELD_INT32,
341                                         values); CeedChk(ierr);
342 
343   return CEED_ERROR_SUCCESS;
344 }
345 
346 /**
347   @brief Get additional destroy routine for CeedQFunctionContext user data
348 
349   @param[in] ctx          CeedQFunctionContext to get user destroy function
350   @param[out] f_mem_type  Memory type to use when passing data into `f`
351   @param[out] f           Additional routine to use to destroy user data
352 
353   @return An error code: 0 - success, otherwise - failure
354 
355   @ref Backend
356 **/
357 int CeedQFunctionContextGetDataDestroy(CeedQFunctionContext ctx,
358                                        CeedMemType *f_mem_type, CeedQFunctionContextDataDestroyUser *f) {
359   if (f_mem_type) *f_mem_type = ctx->data_destroy_mem_type;
360   if (f) *f = ctx->data_destroy_function;
361   return CEED_ERROR_SUCCESS;
362 }
363 
364 /**
365   @brief Destroy user data held by CeedQFunctionContext, using function set by
366      CeedQFunctionContextSetDataDestroy, if applicable
367 
368   @param[in,out] ctx  CeedQFunctionContext to destroy user data
369 
370   @return An error code: 0 - success, otherwise - failure
371 
372   @ref Backend
373 **/
374 int CeedQFunctionContextDestroyData(CeedQFunctionContext ctx) {
375   int ierr;
376 
377   if (ctx->DataDestroy) {
378     ierr = ctx->DataDestroy(ctx); CeedChk(ierr);
379   } else {
380     CeedQFunctionContextDataDestroyUser data_destroy_function;
381     CeedMemType data_destroy_mem_type;
382 
383     ierr = CeedQFunctionContextGetDataDestroy(ctx, &data_destroy_mem_type,
384            &data_destroy_function); CeedChk(ierr);
385     if (data_destroy_function) {
386       void *data;
387 
388       ierr = CeedQFunctionContextGetData(ctx, data_destroy_mem_type, &data);
389       CeedChk(ierr);
390       ierr = data_destroy_function(data); CeedChk(ierr);
391       ierr = CeedQFunctionContextRestoreData(ctx, &data); CeedChk(ierr);
392     }
393   }
394 
395   return CEED_ERROR_SUCCESS;
396 }
397 
398 /**
399   @brief Increment the reference counter for a CeedQFunctionContext
400 
401   @param ctx  CeedQFunctionContext to increment the reference counter
402 
403   @return An error code: 0 - success, otherwise - failure
404 
405   @ref Backend
406 **/
407 int CeedQFunctionContextReference(CeedQFunctionContext ctx) {
408   ctx->ref_count++;
409   return CEED_ERROR_SUCCESS;
410 }
411 
412 /// @}
413 
414 /// ----------------------------------------------------------------------------
415 /// CeedQFunctionContext Public API
416 /// ----------------------------------------------------------------------------
417 /// @addtogroup CeedQFunctionUser
418 /// @{
419 
420 /**
421   @brief Create a CeedQFunctionContext for storing CeedQFunction user context data
422 
423   @param ceed      A Ceed object where the CeedQFunctionContext will be created
424   @param[out] ctx  Address of the variable where the newly created
425                      CeedQFunctionContext will be stored
426 
427   @return An error code: 0 - success, otherwise - failure
428 
429   @ref User
430 **/
431 int CeedQFunctionContextCreate(Ceed ceed, CeedQFunctionContext *ctx) {
432   int ierr;
433 
434   if (!ceed->QFunctionContextCreate) {
435     Ceed delegate;
436     ierr = CeedGetObjectDelegate(ceed, &delegate, "Context"); CeedChk(ierr);
437 
438     if (!delegate)
439       // LCOV_EXCL_START
440       return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
441                        "Backend does not support ContextCreate");
442     // LCOV_EXCL_STOP
443 
444     ierr = CeedQFunctionContextCreate(delegate, ctx); CeedChk(ierr);
445     return CEED_ERROR_SUCCESS;
446   }
447 
448   ierr = CeedCalloc(1, ctx); CeedChk(ierr);
449   (*ctx)->ceed = ceed;
450   ierr = CeedReference(ceed); CeedChk(ierr);
451   (*ctx)->ref_count = 1;
452   ierr = ceed->QFunctionContextCreate(*ctx); CeedChk(ierr);
453   return CEED_ERROR_SUCCESS;
454 }
455 
456 /**
457   @brief Copy the pointer to a CeedQFunctionContext. Both pointers should
458            be destroyed with `CeedQFunctionContextDestroy()`;
459            Note: If `*ctx_copy` is non-NULL, then it is assumed that
460            `*ctx_copy` is a pointer to a CeedQFunctionContext. This
461            CeedQFunctionContext will be destroyed if `*ctx_copy` is the
462            only reference to this CeedQFunctionContext.
463 
464   @param ctx            CeedQFunctionContext to copy reference to
465   @param[out] ctx_copy  Variable to store copied reference
466 
467   @return An error code: 0 - success, otherwise - failure
468 
469   @ref User
470 **/
471 int CeedQFunctionContextReferenceCopy(CeedQFunctionContext ctx,
472                                       CeedQFunctionContext *ctx_copy) {
473   int ierr;
474 
475   ierr = CeedQFunctionContextReference(ctx); CeedChk(ierr);
476   ierr = CeedQFunctionContextDestroy(ctx_copy); CeedChk(ierr);
477   *ctx_copy = ctx;
478   return CEED_ERROR_SUCCESS;
479 }
480 
481 /**
482   @brief Set the data used by a CeedQFunctionContext, freeing any previously allocated
483            data if applicable. The backend may copy values to a different
484            memtype, such as during @ref CeedQFunctionApply().
485            See also @ref CeedQFunctionContextTakeData().
486 
487   @param ctx        CeedQFunctionContext
488   @param mem_type   Memory type of the data being passed
489   @param copy_mode  Copy mode for the data
490   @param size       Size of data, in bytes
491   @param data       Data to be used
492 
493   @return An error code: 0 - success, otherwise - failure
494 
495   @ref User
496 **/
497 int CeedQFunctionContextSetData(CeedQFunctionContext ctx, CeedMemType mem_type,
498                                 CeedCopyMode copy_mode,
499                                 size_t size, void *data) {
500   int ierr;
501 
502   if (!ctx->SetData)
503     // LCOV_EXCL_START
504     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
505                      "Backend does not support ContextSetData");
506   // LCOV_EXCL_STOP
507 
508   if (ctx->state % 2 == 1)
509     // LCOV_EXCL_START
510     return CeedError(ctx->ceed, 1,
511                      "Cannot grant CeedQFunctionContext data access, the "
512                      "access lock is already in use");
513   // LCOV_EXCL_STOP
514 
515   ierr = CeedQFunctionContextDestroyData(ctx); CeedChk(ierr);
516   ctx->ctx_size = size;
517   ierr = ctx->SetData(ctx, mem_type, copy_mode, data); CeedChk(ierr);
518   ctx->state += 2;
519   return CEED_ERROR_SUCCESS;
520 }
521 
522 /**
523   @brief Take ownership of the data in a CeedQFunctionContext via the specified memory type.
524            The caller is responsible for managing and freeing the memory.
525 
526   @param ctx        CeedQFunctionContext to access
527   @param mem_type   Memory type on which to access the data. If the backend
528                       uses a different memory type, this will perform a copy.
529   @param[out] data  Data on memory type mem_type
530 
531   @return An error code: 0 - success, otherwise - failure
532 
533   @ref User
534 **/
535 int CeedQFunctionContextTakeData(CeedQFunctionContext ctx, CeedMemType mem_type,
536                                  void *data) {
537   int ierr;
538 
539   bool has_valid_data = true;
540   ierr = CeedQFunctionContextHasValidData(ctx, &has_valid_data); CeedChk(ierr);
541   if (!has_valid_data)
542     // LCOV_EXCL_START
543     return CeedError(ctx->ceed, CEED_ERROR_BACKEND,
544                      "CeedQFunctionContext has no valid data to take, must set data");
545   // LCOV_EXCL_STOP
546 
547   if (!ctx->TakeData)
548     // LCOV_EXCL_START
549     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
550                      "Backend does not support TakeData");
551   // LCOV_EXCL_STOP
552 
553   if (ctx->state % 2 == 1)
554     // LCOV_EXCL_START
555     return CeedError(ctx->ceed, 1,
556                      "Cannot grant CeedQFunctionContext data access, the "
557                      "access lock is already in use");
558   // LCOV_EXCL_STOP
559 
560   bool has_borrowed_data_of_type = true;
561   ierr = CeedQFunctionContextHasBorrowedDataOfType(ctx, mem_type,
562          &has_borrowed_data_of_type); CeedChk(ierr);
563   if (!has_borrowed_data_of_type)
564     // LCOV_EXCL_START
565     return CeedError(ctx->ceed, CEED_ERROR_BACKEND,
566                      "CeedQFunctionContext has no borowed %s data, "
567                      "must set data with CeedQFunctionContextSetData",
568                      CeedMemTypes[mem_type]);
569   // LCOV_EXCL_STOP
570 
571   void *temp_data = NULL;
572   ierr = ctx->TakeData(ctx, mem_type, &temp_data); CeedChk(ierr);
573   if (data) (*(void **)data) = temp_data;
574   return CEED_ERROR_SUCCESS;
575 }
576 
577 /**
578   @brief Get read/write access to a CeedQFunctionContext via the specified memory type.
579            Restore access with @ref CeedQFunctionContextRestoreData().
580 
581   @param ctx        CeedQFunctionContext to access
582   @param mem_type   Memory type on which to access the data. If the backend
583                       uses a different memory type, this will perform a copy.
584   @param[out] data  Data on memory type mem_type
585 
586   @note The CeedQFunctionContextGetData() and @ref CeedQFunctionContextRestoreData() functions
587     provide access to array pointers in the desired memory space. Pairing
588     get/restore allows the Context to track access.
589 
590   @return An error code: 0 - success, otherwise - failure
591 
592   @ref User
593 **/
594 int CeedQFunctionContextGetData(CeedQFunctionContext ctx, CeedMemType mem_type,
595                                 void *data) {
596   int ierr;
597 
598   if (!ctx->GetData)
599     // LCOV_EXCL_START
600     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
601                      "Backend does not support GetData");
602   // LCOV_EXCL_STOP
603 
604   if (ctx->state % 2 == 1)
605     // LCOV_EXCL_START
606     return CeedError(ctx->ceed, 1,
607                      "Cannot grant CeedQFunctionContext data access, the "
608                      "access lock is already in use");
609   // LCOV_EXCL_STOP
610 
611   if (ctx->num_readers > 0)
612     // LCOV_EXCL_START
613     return CeedError(ctx->ceed, 1,
614                      "Cannot grant CeedQFunctionContext data access, a "
615                      "process has read access");
616   // LCOV_EXCL_STOP
617 
618   bool has_valid_data = true;
619   ierr = CeedQFunctionContextHasValidData(ctx, &has_valid_data); CeedChk(ierr);
620   if (!has_valid_data)
621     // LCOV_EXCL_START
622     return CeedError(ctx->ceed, CEED_ERROR_BACKEND,
623                      "CeedQFunctionContext has no valid data to get, must set data");
624   // LCOV_EXCL_STOP
625 
626   ierr = ctx->GetData(ctx, mem_type, data); CeedChk(ierr);
627   ctx->state++;
628   return CEED_ERROR_SUCCESS;
629 }
630 
631 /**
632   @brief Get read only access to a CeedQFunctionContext via the specified memory type.
633            Restore access with @ref CeedQFunctionContextRestoreData().
634 
635   @param ctx        CeedQFunctionContext to access
636   @param mem_type   Memory type on which to access the data. If the backend
637                       uses a different memory type, this will perform a copy.
638   @param[out] data  Data on memory type mem_type
639 
640   @note The CeedQFunctionContextGetDataRead() and @ref CeedQFunctionContextRestoreDataRead()
641     functions provide access to array pointers in the desired memory space. Pairing
642     get/restore allows the Context to track access.
643 
644   @return An error code: 0 - success, otherwise - failure
645 
646   @ref User
647 **/
648 int CeedQFunctionContextGetDataRead(CeedQFunctionContext ctx,
649                                     CeedMemType mem_type,
650                                     void *data) {
651   int ierr;
652 
653   if (!ctx->GetDataRead)
654     // LCOV_EXCL_START
655     return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED,
656                      "Backend does not support GetDataRead");
657   // LCOV_EXCL_STOP
658 
659   if (ctx->state % 2 == 1)
660     // LCOV_EXCL_START
661     return CeedError(ctx->ceed, 1,
662                      "Cannot grant CeedQFunctionContext data access, the "
663                      "access lock is already in use");
664   // LCOV_EXCL_STOP
665 
666   bool has_valid_data = true;
667   ierr = CeedQFunctionContextHasValidData(ctx, &has_valid_data); CeedChk(ierr);
668   if (!has_valid_data)
669     // LCOV_EXCL_START
670     return CeedError(ctx->ceed, CEED_ERROR_BACKEND,
671                      "CeedQFunctionContext has no valid data to get, must set data");
672   // LCOV_EXCL_STOP
673 
674   ierr = ctx->GetDataRead(ctx, mem_type, data); CeedChk(ierr);
675   ctx->num_readers++;
676   return CEED_ERROR_SUCCESS;
677 }
678 
679 /**
680   @brief Restore data obtained using @ref CeedQFunctionContextGetData()
681 
682   @param ctx   CeedQFunctionContext to restore
683   @param data  Data to restore
684 
685   @return An error code: 0 - success, otherwise - failure
686 
687   @ref User
688 **/
689 int CeedQFunctionContextRestoreData(CeedQFunctionContext ctx, void *data) {
690   int ierr;
691 
692   if (ctx->state % 2 != 1)
693     // LCOV_EXCL_START
694     return CeedError(ctx->ceed, 1,
695                      "Cannot restore CeedQFunctionContext array access, "
696                      "access was not granted");
697   // LCOV_EXCL_STOP
698 
699   if (ctx->RestoreData) {
700     ierr = ctx->RestoreData(ctx); CeedChk(ierr);
701   }
702   *(void **)data = NULL;
703   ctx->state++;
704   return CEED_ERROR_SUCCESS;
705 }
706 
707 /**
708   @brief Restore data obtained using @ref CeedQFunctionContextGetDataRead()
709 
710   @param ctx   CeedQFunctionContext to restore
711   @param data  Data to restore
712 
713   @return An error code: 0 - success, otherwise - failure
714 
715   @ref User
716 **/
717 int CeedQFunctionContextRestoreDataRead(CeedQFunctionContext ctx, void *data) {
718   int ierr;
719 
720   if (ctx->num_readers == 0)
721     // LCOV_EXCL_START
722     return CeedError(ctx->ceed, 1,
723                      "Cannot restore CeedQFunctionContext array access, "
724                      "access was not granted");
725   // LCOV_EXCL_STOP
726 
727   if (ctx->RestoreDataRead) {
728     ierr = ctx->RestoreData(ctx); CeedChk(ierr);
729   }
730   *(void **)data = NULL;
731   ctx->num_readers--;
732   return CEED_ERROR_SUCCESS;
733 }
734 
735 /**
736   @brief Register QFunctionContext a field holding a double precision value
737 
738   @param ctx               CeedQFunctionContext
739   @param field_name        Name of field to register
740   @param field_offset      Offset of field to register
741   @param num_values        Number of values to register, must be contiguous in memory
742   @param field_description Description of field, or NULL for none
743 
744   @return An error code: 0 - success, otherwise - failure
745 
746   @ref User
747 **/
748 int CeedQFunctionContextRegisterDouble(CeedQFunctionContext ctx,
749                                        const char *field_name, size_t field_offset,
750                                        size_t num_values,
751                                        const char *field_description) {
752   return CeedQFunctionContextRegisterGeneric(ctx, field_name, field_offset,
753          field_description, CEED_CONTEXT_FIELD_DOUBLE, sizeof(double), num_values);
754 }
755 
756 /**
757   @brief Register QFunctionContext a field holding a int32 value
758 
759   @param ctx               CeedQFunctionContext
760   @param field_name        Name of field to register
761   @param field_offset      Offset of field to register
762   @param num_values        Number of values to register, must be contiguous in memory
763   @param field_description Description of field, or NULL for none
764 
765   @return An error code: 0 - success, otherwise - failure
766 
767   @ref User
768 **/
769 int CeedQFunctionContextRegisterInt32(CeedQFunctionContext ctx,
770                                       const char *field_name, size_t field_offset,
771                                       size_t num_values,
772                                       const char *field_description) {
773   return CeedQFunctionContextRegisterGeneric(ctx, field_name, field_offset,
774          field_description, CEED_CONTEXT_FIELD_INT32, sizeof(int), num_values);
775 }
776 
777 /**
778   @brief Get labels for all registered QFunctionContext fields
779 
780   @param ctx                CeedQFunctionContext
781   @param[out] field_labels  Variable to hold array of field labels
782   @param[out] num_fields    Length of field descriptions array
783 
784   @return An error code: 0 - success, otherwise - failure
785 
786   @ref User
787 **/
788 int CeedQFunctionContextGetAllFieldLabels(CeedQFunctionContext ctx,
789     const CeedContextFieldLabel **field_labels, CeedInt *num_fields) {
790   *field_labels = ctx->field_labels;
791   *num_fields = ctx->num_fields;
792   return CEED_ERROR_SUCCESS;
793 }
794 
795 /**
796   @brief Get the descriptive information about a CeedContextFieldLabel
797 
798   @param[in] label              CeedContextFieldLabel
799   @param[out] field_name        Name of labeled field
800   @param[out] field_description Description of field, or NULL for none
801   @param[out] num_values        Number of values registered
802   @param[out] field_type        CeedContextFieldType
803 
804   @return An error code: 0 - success, otherwise - failure
805 
806   @ref User
807 **/
808 int CeedContextFieldLabelGetDescription(CeedContextFieldLabel label,
809                                         const char **field_name,
810                                         const char **field_description,
811                                         size_t *num_values,
812                                         CeedContextFieldType *field_type) {
813   if (field_name) *field_name = label->name;
814   if (field_description) *field_description = label->description;
815   if (num_values) *num_values = label->num_values;
816   if (field_type) *field_type = label->type;
817   return CEED_ERROR_SUCCESS;
818 }
819 
820 /**
821   @brief Get data size for a Context
822 
823   @param ctx            CeedQFunctionContext
824   @param[out] ctx_size  Variable to store size of context data values
825 
826   @return An error code: 0 - success, otherwise - failure
827 
828   @ref User
829 **/
830 int CeedQFunctionContextGetContextSize(CeedQFunctionContext ctx,
831                                        size_t *ctx_size) {
832   *ctx_size = ctx->ctx_size;
833   return CEED_ERROR_SUCCESS;
834 }
835 
836 
837 /**
838   @brief View a CeedQFunctionContext
839 
840   @param[in] ctx     CeedQFunctionContext to view
841   @param[in] stream  Filestream to write to
842 
843   @return An error code: 0 - success, otherwise - failure
844 
845   @ref User
846 **/
847 int CeedQFunctionContextView(CeedQFunctionContext ctx, FILE *stream) {
848   fprintf(stream, "CeedQFunctionContext\n");
849   fprintf(stream, "  Context Data Size: %ld\n", ctx->ctx_size);
850   for (CeedInt i = 0; i < ctx->num_fields; i++) {
851     // LCOV_EXCL_START
852     fprintf(stream, "  Labeled %s field: %s\n",
853             CeedContextFieldTypes[ctx->field_labels[i]->type],
854             ctx->field_labels[i]->name);
855     // LCOV_EXCL_STOP
856   }
857   return CEED_ERROR_SUCCESS;
858 }
859 
860 /**
861   @brief Set additional destroy routine for CeedQFunctionContext user data
862 
863   @param[in] ctx         CeedQFunctionContext to set user destroy function
864   @param[in] f_mem_type  Memory type to use when passing data into `f`
865   @param[in] f           Additional routine to use to destroy user data
866 
867   @return An error code: 0 - success, otherwise - failure
868 
869   @ref User
870 **/
871 int CeedQFunctionContextSetDataDestroy(CeedQFunctionContext ctx,
872                                        CeedMemType f_mem_type, CeedQFunctionContextDataDestroyUser f) {
873   if (!f)
874     // LCOV_EXCL_START
875     return CeedError(ctx->ceed, 1,
876                      "Must provide valid callback function for destroying user data");
877   // LCOV_EXCL_STOP
878   ctx->data_destroy_mem_type = f_mem_type;
879   ctx->data_destroy_function = f;
880   return CEED_ERROR_SUCCESS;
881 }
882 
883 /**
884   @brief Destroy a CeedQFunctionContext
885 
886   @param ctx  CeedQFunctionContext to destroy
887 
888   @return An error code: 0 - success, otherwise - failure
889 
890   @ref User
891 **/
892 int CeedQFunctionContextDestroy(CeedQFunctionContext *ctx) {
893   int ierr;
894 
895   if (!*ctx || --(*ctx)->ref_count > 0)
896     return CEED_ERROR_SUCCESS;
897 
898   if ((*ctx) && ((*ctx)->state % 2) == 1)
899     // LCOV_EXCL_START
900     return CeedError((*ctx)->ceed, 1,
901                      "Cannot destroy CeedQFunctionContext, the access "
902                      "lock is in use");
903   // LCOV_EXCL_STOP
904 
905   ierr = CeedQFunctionContextDestroyData(*ctx); CeedChk(ierr);
906   if ((*ctx)->Destroy) {
907     ierr = (*ctx)->Destroy(*ctx); CeedChk(ierr);
908   }
909   for (CeedInt i=0; i<(*ctx)->num_fields; i++) {
910     ierr = CeedFree(&(*ctx)->field_labels[i]->name); CeedChk(ierr);
911     ierr = CeedFree(&(*ctx)->field_labels[i]->description); CeedChk(ierr);
912     ierr = CeedFree(&(*ctx)->field_labels[i]); CeedChk(ierr);
913   }
914   ierr = CeedFree(&(*ctx)->field_labels); CeedChk(ierr);
915   ierr = CeedDestroy(&(*ctx)->ceed); CeedChk(ierr);
916   ierr = CeedFree(ctx); CeedChk(ierr);
917 
918   return CEED_ERROR_SUCCESS;
919 }
920 
921 /// @}
922