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