xref: /libCEED/rust/libceed-sys/c-src/interface/ceed.c (revision 288c044332e33f37503f09b6484fec9d0a55fba1)
1 // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at
2 // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights
3 // reserved. See files LICENSE and NOTICE for details.
4 //
5 // This file is part of CEED, a collection of benchmarks, miniapps, software
6 // libraries and APIs for efficient high-order finite element and spectral
7 // element discretizations for exascale applications. For more information and
8 // source code availability see http://github.com/ceed.
9 //
10 // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
11 // a collaborative effort of two U.S. Department of Energy organizations (Office
12 // of Science and the National Nuclear Security Administration) responsible for
13 // the planning and preparation of a capable exascale ecosystem, including
14 // software, applications, hardware, advanced system engineering and early
15 // testbed platforms, in support of the nation's exascale computing imperative.
16 
17 #define _POSIX_C_SOURCE 200112
18 #include <ceed-impl.h>
19 #include <ceed-backend.h>
20 #include <limits.h>
21 #include <stdarg.h>
22 #include <stddef.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 /// @cond DOXYGEN_SKIP
28 static CeedRequest ceed_request_immediate;
29 static CeedRequest ceed_request_ordered;
30 
31 static struct {
32   char prefix[CEED_MAX_RESOURCE_LEN];
33   int (*init)(const char *resource, Ceed f);
34   unsigned int priority;
35 } backends[32];
36 static size_t num_backends;
37 
38 #define CEED_FTABLE_ENTRY(class, method) \
39   {#class #method, offsetof(struct class ##_private, method)}
40 /// @endcond
41 
42 /// @file
43 /// Implementation of core components of Ceed library
44 ///
45 /// @addtogroup Ceed
46 /// @{
47 
48 /**
49   @brief Request immediate completion
50 
51   This predefined constant is passed as the \ref CeedRequest argument to
52   interfaces when the caller wishes for the operation to be performed
53   immediately.  The code
54 
55   @code
56     CeedOperatorApply(op, ..., CEED_REQUEST_IMMEDIATE);
57   @endcode
58 
59   is semantically equivalent to
60 
61   @code
62     CeedRequest request;
63     CeedOperatorApply(op, ..., &request);
64     CeedRequestWait(&request);
65   @endcode
66 
67   @sa CEED_REQUEST_ORDERED
68 **/
69 CeedRequest *const CEED_REQUEST_IMMEDIATE = &ceed_request_immediate;
70 
71 /**
72   @brief Request ordered completion
73 
74   This predefined constant is passed as the \ref CeedRequest argument to
75   interfaces when the caller wishes for the operation to be completed in the
76   order that it is submitted to the device.  It is typically used in a construct
77   such as
78 
79   @code
80     CeedRequest request;
81     CeedOperatorApply(op1, ..., CEED_REQUEST_ORDERED);
82     CeedOperatorApply(op2, ..., &request);
83     // other optional work
84     CeedWait(&request);
85   @endcode
86 
87   which allows the sequence to complete asynchronously but does not start
88   `op2` until `op1` has completed.
89 
90   @todo The current implementation is overly strict, offering equivalent
91   semantics to CEED_REQUEST_IMMEDIATE.
92 
93   @sa CEED_REQUEST_IMMEDIATE
94  */
95 CeedRequest *const CEED_REQUEST_ORDERED = &ceed_request_ordered;
96 
97 /**
98   @brief Error handling implementation; use \ref CeedError instead.
99 
100   @ref Developer
101 **/
102 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func,
103                   int ecode, const char *format, ...) {
104   va_list args;
105   int retval;
106   va_start(args, format);
107   if (ceed) {
108     retval = ceed->Error(ceed, filename, lineno, func, ecode, format, args);
109   } else {
110     // This function doesn't actually return
111     retval = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args);
112   }
113   va_end(args);
114   return retval;
115 }
116 
117 /**
118   @brief Error handler that returns without printing anything.
119 
120   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
121 
122   @ref Developer
123 **/
124 int CeedErrorReturn(Ceed ceed, const char *filename, int lineno,
125                     const char *func, int ecode, const char *format,
126                     va_list args) {
127   return ecode;
128 }
129 
130 /**
131   @brief Error handler that prints to stderr and aborts
132 
133   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
134 
135   @ref Developer
136 **/
137 int CeedErrorAbort(Ceed ceed, const char *filename, int lineno,
138                    const char *func, int ecode,
139                    const char *format, va_list args) {
140   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
141   vfprintf(stderr, format, args);
142   fprintf(stderr, "\n");
143   abort();
144   return ecode;
145 }
146 
147 /**
148   @brief Error handler that prints to stderr and exits
149 
150   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
151 
152   In contrast to CeedErrorAbort(), this exits without a signal, so atexit()
153   handlers (e.g., as used by gcov) are run.
154 
155   @ref Developer
156 **/
157 int CeedErrorExit(Ceed ceed, const char *filename, int lineno,
158                   const char *func, int ecode,
159                   const char *format, va_list args) {
160   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
161   vfprintf(stderr, format, args);
162   fprintf(stderr, "\n");
163   exit(ecode);
164   return ecode;
165 }
166 
167 /**
168   @brief Set error handler
169 
170   A default error handler is set in CeedInit().  Use this function to change
171   the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined
172   error handler.
173 
174   @ref Developer
175 **/
176 int CeedSetErrorHandler(Ceed ceed,
177                         int (eh)(Ceed, const char *, int, const char *,
178                                  int, const char *, va_list)) {
179   ceed->Error = eh;
180   return 0;
181 }
182 
183 /**
184   @brief Register a Ceed backend
185 
186   @param prefix   Prefix of resources for this backend to respond to.  For
187                     example, the reference backend responds to "/cpu/self".
188   @param init     Initialization function called by CeedInit() when the backend
189                     is selected to drive the requested resource.
190   @param priority Integer priority.  Lower values are preferred in case the
191                     resource requested by CeedInit() has non-unique best prefix
192                     match.
193 
194   @return An error code: 0 - success, otherwise - failure
195 
196   @ref Advanced
197 **/
198 int CeedRegister(const char *prefix,
199                  int (*init)(const char *, Ceed), unsigned int priority) {
200   if (num_backends >= sizeof(backends) / sizeof(backends[0])) {
201     return CeedError(NULL, 1, "Too many backends");
202   }
203   strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN);
204   backends[num_backends].prefix[CEED_MAX_RESOURCE_LEN-1] = 0;
205   backends[num_backends].init = init;
206   backends[num_backends].priority = priority;
207   num_backends++;
208   return 0;
209 }
210 
211 /**
212   @brief Allocate an array on the host; use CeedMalloc()
213 
214   Memory usage can be tracked by the library.  This ensures sufficient
215     alignment for vectorization and should be used for large allocations.
216 
217   @param n Number of units to allocate
218   @param unit Size of each unit
219   @param p Address of pointer to hold the result.
220 
221   @return An error code: 0 - success, otherwise - failure
222 
223   @sa CeedFree()
224 
225   @ref Advanced
226 **/
227 int CeedMallocArray(size_t n, size_t unit, void *p) {
228   int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit);
229   if (ierr)
230     return CeedError(NULL, ierr,
231                      "posix_memalign failed to allocate %zd members of size %zd\n",
232                      n, unit);
233   return 0;
234 }
235 
236 /**
237   @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc()
238 
239   Memory usage can be tracked by the library.
240 
241   @param n Number of units to allocate
242   @param unit Size of each unit
243   @param p Address of pointer to hold the result.
244 
245   @return An error code: 0 - success, otherwise - failure
246 
247   @sa CeedFree()
248 
249   @ref Advanced
250 **/
251 int CeedCallocArray(size_t n, size_t unit, void *p) {
252   *(void **)p = calloc(n, unit);
253   if (n && unit && !*(void **)p)
254     return CeedError(NULL, 1, "calloc failed to allocate %zd members of size %zd\n",
255                      n, unit);
256   return 0;
257 }
258 
259 /**
260   @brief Reallocate an array on the host; use CeedRealloc()
261 
262   Memory usage can be tracked by the library.
263 
264   @param n Number of units to allocate
265   @param unit Size of each unit
266   @param p Address of pointer to hold the result.
267 
268   @return An error code: 0 - success, otherwise - failure
269 
270   @sa CeedFree()
271 
272   @ref Advanced
273 **/
274 int CeedReallocArray(size_t n, size_t unit, void *p) {
275   *(void **)p = realloc(*(void **)p, n*unit);
276   if (n && unit && !*(void **)p)
277     return CeedError(NULL, 1,
278                      "realloc failed to allocate %zd members of size %zd\n",
279                      n, unit);
280   return 0;
281 }
282 
283 /// Free memory allocated using CeedMalloc() or CeedCalloc()
284 ///
285 /// @param p address of pointer to memory.  This argument is of type void* to
286 /// avoid needing a cast, but is the address of the pointer (which is zeroed)
287 /// rather than the pointer.
288 int CeedFree(void *p) {
289   free(*(void **)p);
290   *(void **)p = NULL;
291   return 0;
292 }
293 
294 /**
295   @brief Wait for a CeedRequest to complete.
296 
297   Calling CeedRequestWait on a NULL request is a no-op.
298 
299   @param req Address of CeedRequest to wait for; zeroed on completion.
300 
301   @return An error code: 0 - success, otherwise - failure
302 
303   @ref Advanced
304 **/
305 int CeedRequestWait(CeedRequest *req) {
306   if (!*req) return 0;
307   return CeedError(NULL, 2, "CeedRequestWait not implemented");
308 }
309 
310 /**
311   @brief Initialize a \ref Ceed to use the specified resource.
312 
313   @param resource  Resource to use, e.g., "/cpu/self"
314   @param ceed The library context
315   @sa CeedRegister() CeedDestroy()
316 
317   @return An error code: 0 - success, otherwise - failure
318 
319   @ref Basic
320 **/
321 int CeedInit(const char *resource, Ceed *ceed) {
322   int ierr;
323   size_t matchlen = 0, matchidx = UINT_MAX, matchpriority = UINT_MAX, priority;
324 
325   // Find matching backend
326   if (!resource) return CeedError(NULL, 1, "No resource provided");
327   for (size_t i=0; i<num_backends; i++) {
328     size_t n;
329     const char *prefix = backends[i].prefix;
330     for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {}
331     priority = backends[i].priority;
332     if (n > matchlen || (n == matchlen && matchpriority > priority)) {
333       matchlen = n;
334       matchpriority = priority;
335       matchidx = i;
336     }
337   }
338   if (!matchlen) return CeedError(NULL, 1, "No suitable backend");
339 
340   // Setup Ceed
341   ierr = CeedCalloc(1,ceed); CeedChk(ierr);
342   const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
343   if (!ceed_error_handler) ceed_error_handler = "abort";
344   if (!strcmp(ceed_error_handler, "exit"))
345     (*ceed)->Error = CeedErrorExit;
346   else
347     (*ceed)->Error = CeedErrorAbort;
348   (*ceed)->refcount = 1;
349   (*ceed)->data = NULL;
350 
351   // Set lookup table
352   foffset foffsets[] = {
353     CEED_FTABLE_ENTRY(Ceed, Error),
354     CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType),
355     CEED_FTABLE_ENTRY(Ceed, Destroy),
356     CEED_FTABLE_ENTRY(Ceed, VectorCreate),
357     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate),
358     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
359     CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
360     CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
361     CEED_FTABLE_ENTRY(Ceed, TensorContractCreate),
362     CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
363     CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
364     CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
365     CEED_FTABLE_ENTRY(CeedVector, SetArray),
366     CEED_FTABLE_ENTRY(CeedVector, SetValue),
367     CEED_FTABLE_ENTRY(CeedVector, GetArray),
368     CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
369     CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
370     CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
371     CEED_FTABLE_ENTRY(CeedVector, Destroy),
372     CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
373     CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
374     CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
375     CEED_FTABLE_ENTRY(CeedBasis, Apply),
376     CEED_FTABLE_ENTRY(CeedBasis, Destroy),
377     CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
378     CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
379     CEED_FTABLE_ENTRY(CeedQFunction, Apply),
380     CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
381     CEED_FTABLE_ENTRY(CeedOperator, Apply),
382     CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
383     CEED_FTABLE_ENTRY(CeedOperator, Destroy),
384     {NULL, 0} // End of lookup table - used in SetBackendFunction loop
385   };
386 
387   ierr = CeedCalloc(sizeof(foffsets), &(*ceed)->foffsets); CeedChk(ierr);
388   memcpy((*ceed)->foffsets, foffsets, sizeof(foffsets));
389 
390   // Backend specific setup
391   ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr);
392 
393   return 0;
394 }
395 
396 /**
397   @brief Retrieve a parent CEED
398 
399   @param ceed           Ceed to retrieve parent of
400   @param[out] parent    Address to save the parent to
401 
402   @return An error code: 0 - success, otherwise - failure
403 
404   @ref Developer
405 **/
406 int CeedGetParent(Ceed ceed, Ceed *parent) {
407   int ierr;
408   if (ceed->parent) {
409     ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr);
410     return 0;
411   }
412   *parent = ceed;
413   return 0;
414 }
415 
416 /**
417   @brief Retrieve a delegate CEED
418 
419   @param ceed           Ceed to retrieve delegate of
420   @param[out] delegate  Address to save the delegate to
421 
422   @return An error code: 0 - success, otherwise - failure
423 
424   @ref Developer
425 **/
426 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
427   *delegate = ceed->delegate;
428   return 0;
429 }
430 
431 /**
432   @brief Set a delegate CEED
433 
434   This function allows a CEED to set a delegate CEED. All backend
435   implementations default to the delegate CEED, unless overridden.
436 
437   @param ceed           Ceed to set delegate of
438   @param[out] delegate  Address to set the delegate to
439 
440   @return An error code: 0 - success, otherwise - failure
441 
442   @ref Advanced
443 **/
444 int CeedSetDelegate(Ceed ceed, Ceed delegate) {
445   ceed->delegate = delegate;
446   delegate->parent = ceed;
447   return 0;
448 }
449 
450 /**
451   @brief Retrieve a delegate CEED for a specific object type
452 
453   @param ceed           Ceed to retrieve delegate of
454   @param[out] delegate  Address to save the delegate to
455   @param[in] objname    Name of the object type to retrieve delegate for
456 
457   @return An error code: 0 - success, otherwise - failure
458 
459   @ref Developer
460 **/
461 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *objname) {
462   CeedInt ierr;
463 
464   // Check for object delegate
465   for (CeedInt i=0; i<ceed->objdelegatecount; i++) {
466     if (!strcmp(objname, ceed->objdelegates->objname)) {
467       *delegate = ceed->objdelegates->delegate;
468       return 0;
469     }
470   }
471 
472   // Use default delegate if no object delegate
473   ierr = CeedGetDelegate(ceed, delegate); CeedChk(ierr);
474 
475   return 0;
476 }
477 
478 /**
479   @brief Set a delegate CEED for a specific object type
480 
481   This function allows a CEED to set a delegate CEED for a given type of
482   CEED object. All backend implementations default to the delegate CEED for
483   this object. For example,
484     CeedSetObjectDelegate(ceed, refceed, "Basis")
485   uses refceed implementations for all CeedBasis backend functions.
486 
487   @param ceed           Ceed to set delegate of
488   @param[out] delegate  Address to set the delegate to
489   @param[in] objname    Name of the object type to set delegate for
490 
491   @return An error code: 0 - success, otherwise - failure
492 
493   @ref Advanced
494 **/
495 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *objname) {
496   CeedInt ierr;
497   CeedInt count = ceed->objdelegatecount;
498 
499   // Malloc or Realloc
500   if (count) {
501     ierr = CeedRealloc(count+1, &ceed->objdelegates);
502     CeedChk(ierr);
503   } else {
504     ierr = CeedCalloc(1, &ceed->objdelegates); CeedChk(ierr);
505   }
506   ceed->objdelegatecount++;
507 
508   // Set object delegate
509   ceed->objdelegates[count].delegate = delegate;
510   ierr = CeedCalloc(strlen(objname)+1, &ceed->objdelegates[count].objname);
511   CeedChk(ierr);
512   CeedInt len = strlen(ceed->objdelegates[count].objname);
513   strncpy(ceed->objdelegates[count].objname, objname, len);
514   ceed->objdelegates[count].objname[len-1] = 0;
515 
516   // Set delegate parent
517   delegate->parent = ceed;
518 
519   return 0;
520 }
521 
522 /**
523   @brief Return Ceed preferred memory type
524 
525   @param ceed           Ceed to get preferred memory type of
526   @param[out] type      Address to save preferred memory type to
527 
528   @return An error code: 0 - success, otherwise - failure
529 
530   @ref Basic
531 **/
532 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *type) {
533   int ierr;
534 
535   if (ceed->GetPreferredMemType) {
536     ierr = ceed->GetPreferredMemType(type); CeedChk(ierr);
537   } else {
538     Ceed delegate;
539     ierr = CeedGetDelegate(ceed, &delegate); CeedChk(ierr);
540 
541     if (delegate) {
542       ierr = CeedGetPreferredMemType(delegate, type); CeedChk(ierr);
543     } else {
544       *type = CEED_MEM_HOST;
545     }
546   }
547 
548   return 0;
549 }
550 
551 /**
552   @brief Set a backend function
553 
554   This function is used for a backend to set the function associated with
555   the CEED objects. For example,
556     CeedSetBackendFunction(ceed, "Ceed", ceed, "VectorCreate", BackendVectorCreate)
557   sets the backend implementation of 'CeedVectorCreate' and
558     CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply)
559   sets the backend implementation of 'CeedBasisApply'. Note, the prefix 'Ceed'
560   is not required for the object type ("Basis" vs "CeedBasis").
561 
562   @param ceed           Ceed for error handling
563   @param type           Type of Ceed object to set function for
564   @param[out] object    Ceed object to set function for
565   @param fname          Name of function to set
566   @param f              Function to set
567 
568   @return An error code: 0 - success, otherwise - failure
569 
570   @ref Advanced
571 **/
572 int CeedSetBackendFunction(Ceed ceed,
573                            const char *type, void *object,
574                            const char *fname, int (*f)()) {
575   char lookupname[CEED_MAX_RESOURCE_LEN+1] = "";
576 
577   // Build lookup name
578   if (strcmp(type, "Ceed"))
579     strncat (lookupname, "Ceed", CEED_MAX_RESOURCE_LEN);
580   strncat(lookupname, type, CEED_MAX_RESOURCE_LEN);
581   strncat(lookupname, fname, CEED_MAX_RESOURCE_LEN);
582 
583   // Find and use offset
584   for (CeedInt i = 0; ceed->foffsets[i].fname; i++) {
585     if (!strcmp(ceed->foffsets[i].fname, lookupname)) {
586       size_t offset = ceed->foffsets[i].offset;
587       int (**fpointer)(void) = (int (* *)(void))((char *)object + offset);
588       *fpointer = f;
589       return 0;
590     }
591   }
592 
593   return CeedError(ceed, 1,
594                    "Requested function '%s' was not found for CEED object '%s'",
595                    fname, type);
596 }
597 
598 /**
599   @brief Retrieve backend data for a CEED
600 
601   @param ceed           Ceed to retrieve data of
602   @param[out] data      Address to save data to
603 
604   @return An error code: 0 - success, otherwise - failure
605 
606   @ref Advanced
607 **/
608 int CeedGetData(Ceed ceed, void* *data) {
609   *data = ceed->data;
610   return 0;
611 }
612 
613 /**
614   @brief Set backend data for a CEED
615 
616   @param ceed           Ceed to set data of
617   @param data           Address of data to set
618 
619   @return An error code: 0 - success, otherwise - failure
620 
621   @ref Advanced
622 **/
623 int CeedSetData(Ceed ceed, void* *data) {
624   ceed->data = *data;
625   return 0;
626 }
627 
628 /**
629   @brief Destroy a Ceed context
630 
631   @param ceed Address of Ceed context to destroy
632 
633   @return An error code: 0 - success, otherwise - failure
634 
635   @ref Basic
636 **/
637 int CeedDestroy(Ceed *ceed) {
638   int ierr;
639 
640   if (!*ceed || --(*ceed)->refcount > 0) return 0;
641   if ((*ceed)->delegate) {
642     ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr);
643   }
644   if ((*ceed)->objdelegatecount > 0) {
645     for (int i=0; i<(*ceed)->objdelegatecount; i++) {
646       ierr = CeedDestroy(&((*ceed)->objdelegates[i].delegate)); CeedChk(ierr);
647       ierr = CeedFree(&(*ceed)->objdelegates[i].objname); CeedChk(ierr);
648     }
649     ierr = CeedFree(&(*ceed)->objdelegates); CeedChk(ierr);
650   }
651   if ((*ceed)->Destroy) {
652     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
653   }
654   ierr = CeedFree(&(*ceed)->foffsets); CeedChk(ierr);
655   ierr = CeedFree(ceed); CeedChk(ierr);
656   return 0;
657 }
658 
659 /// @}
660