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