xref: /libCEED/interface/ceed.c (revision 3630284b1409acb80bfb7cd63f63caf76ed5aceb)
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, const char *format,
139                    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     // LCOV_EXCL_START
202     return CeedError(NULL, 1, "Too many backends");
203   // LCOV_EXCL_STOP
204 
205   strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN);
206   backends[num_backends].prefix[CEED_MAX_RESOURCE_LEN-1] = 0;
207   backends[num_backends].init = init;
208   backends[num_backends].priority = priority;
209   num_backends++;
210   return 0;
211 }
212 
213 /**
214   @brief Allocate an array on the host; use CeedMalloc()
215 
216   Memory usage can be tracked by the library.  This ensures sufficient
217     alignment for vectorization and should be used for large allocations.
218 
219   @param n Number of units to allocate
220   @param unit Size of each unit
221   @param p Address of pointer to hold the result.
222 
223   @return An error code: 0 - success, otherwise - failure
224 
225   @sa CeedFree()
226 
227   @ref Advanced
228 **/
229 int CeedMallocArray(size_t n, size_t unit, void *p) {
230   int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit);
231   if (ierr)
232     // LCOV_EXCL_START
233     return CeedError(NULL, ierr, "posix_memalign failed to allocate %zd "
234                      "members of size %zd\n", n, unit);
235   // LCOV_EXCL_STOP
236 
237   return 0;
238 }
239 
240 /**
241   @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc()
242 
243   Memory usage can be tracked by the library.
244 
245   @param n Number of units to allocate
246   @param unit Size of each unit
247   @param p Address of pointer to hold the result.
248 
249   @return An error code: 0 - success, otherwise - failure
250 
251   @sa CeedFree()
252 
253   @ref Advanced
254 **/
255 int CeedCallocArray(size_t n, size_t unit, void *p) {
256   *(void **)p = calloc(n, unit);
257   if (n && unit && !*(void **)p)
258     // LCOV_EXCL_START
259     return CeedError(NULL, 1, "calloc failed to allocate %zd members of size "
260                      "%zd\n", n, unit);
261   // LCOV_EXCL_STOP
262 
263   return 0;
264 }
265 
266 /**
267   @brief Reallocate an array on the host; use CeedRealloc()
268 
269   Memory usage can be tracked by the library.
270 
271   @param n Number of units to allocate
272   @param unit Size of each unit
273   @param p Address of pointer to hold the result.
274 
275   @return An error code: 0 - success, otherwise - failure
276 
277   @sa CeedFree()
278 
279   @ref Advanced
280 **/
281 int CeedReallocArray(size_t n, size_t unit, void *p) {
282   *(void **)p = realloc(*(void **)p, n*unit);
283   if (n && unit && !*(void **)p)
284     // LCOV_EXCL_START
285     return CeedError(NULL, 1, "realloc failed to allocate %zd members of size "
286                      "%zd\n", n, unit);
287   // LCOV_EXCL_STOP
288 
289   return 0;
290 }
291 
292 /// Free memory allocated using CeedMalloc() or CeedCalloc()
293 ///
294 /// @param p address of pointer to memory.  This argument is of type void* to
295 /// avoid needing a cast, but is the address of the pointer (which is zeroed)
296 /// rather than the pointer.
297 int CeedFree(void *p) {
298   free(*(void **)p);
299   *(void **)p = NULL;
300   return 0;
301 }
302 
303 /**
304   @brief Wait for a CeedRequest to complete.
305 
306   Calling CeedRequestWait on a NULL request is a no-op.
307 
308   @param req Address of CeedRequest to wait for; zeroed on completion.
309 
310   @return An error code: 0 - success, otherwise - failure
311 
312   @ref Advanced
313 **/
314 int CeedRequestWait(CeedRequest *req) {
315   if (!*req)
316     return 0;
317   return CeedError(NULL, 2, "CeedRequestWait not implemented");
318 }
319 
320 /**
321   @brief Initialize a \ref Ceed to use the specified resource.
322 
323   @param resource  Resource to use, e.g., "/cpu/self"
324   @param ceed The library context
325   @sa CeedRegister() CeedDestroy()
326 
327   @return An error code: 0 - success, otherwise - failure
328 
329   @ref Basic
330 **/
331 int CeedInit(const char *resource, Ceed *ceed) {
332   int ierr;
333   size_t matchlen = 0, matchidx = UINT_MAX, matchpriority = UINT_MAX, priority;
334 
335   // Find matching backend
336   if (!resource)
337     return CeedError(NULL, 1, "No resource provided");
338   for (size_t i=0; i<num_backends; i++) {
339     size_t n;
340     const char *prefix = backends[i].prefix;
341     for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {}
342     priority = backends[i].priority;
343     if (n > matchlen || (n == matchlen && matchpriority > priority)) {
344       matchlen = n;
345       matchpriority = priority;
346       matchidx = i;
347     }
348   }
349   if (!matchlen)
350     return CeedError(NULL, 1, "No suitable backend");
351 
352   // Setup Ceed
353   ierr = CeedCalloc(1,ceed); CeedChk(ierr);
354   const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
355   if (!ceed_error_handler)
356     ceed_error_handler = "abort";
357   if (!strcmp(ceed_error_handler, "exit"))
358     (*ceed)->Error = CeedErrorExit;
359   else
360     (*ceed)->Error = CeedErrorAbort;
361   (*ceed)->refcount = 1;
362   (*ceed)->data = NULL;
363 
364   // Set lookup table
365   foffset foffsets[] = {
366     CEED_FTABLE_ENTRY(Ceed, Error),
367     CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType),
368     CEED_FTABLE_ENTRY(Ceed, Destroy),
369     CEED_FTABLE_ENTRY(Ceed, VectorCreate),
370     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate),
371     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
372     CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
373     CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
374     CEED_FTABLE_ENTRY(Ceed, TensorContractCreate),
375     CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
376     CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
377     CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
378     CEED_FTABLE_ENTRY(CeedVector, SetArray),
379     CEED_FTABLE_ENTRY(CeedVector, SetValue),
380     CEED_FTABLE_ENTRY(CeedVector, GetArray),
381     CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
382     CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
383     CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
384     CEED_FTABLE_ENTRY(CeedVector, Destroy),
385     CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
386     CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
387     CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
388     CEED_FTABLE_ENTRY(CeedBasis, Apply),
389     CEED_FTABLE_ENTRY(CeedBasis, Destroy),
390     CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
391     CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
392     CEED_FTABLE_ENTRY(CeedQFunction, Apply),
393     CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
394     CEED_FTABLE_ENTRY(CeedOperator, AssembleLinearQFunction),
395     CEED_FTABLE_ENTRY(CeedOperator, AssembleLinearDiagonal),
396     CEED_FTABLE_ENTRY(CeedOperator, Apply),
397     CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
398     CEED_FTABLE_ENTRY(CeedOperator, Destroy),
399     {NULL, 0} // End of lookup table - used in SetBackendFunction loop
400   };
401 
402   ierr = CeedCalloc(sizeof(foffsets), &(*ceed)->foffsets); CeedChk(ierr);
403   memcpy((*ceed)->foffsets, foffsets, sizeof(foffsets));
404 
405   // Backend specific setup
406   ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr);
407 
408   return 0;
409 }
410 
411 /**
412   @brief Retrieve a parent CEED
413 
414   @param ceed           Ceed to retrieve parent of
415   @param[out] parent    Address to save the parent to
416 
417   @return An error code: 0 - success, otherwise - failure
418 
419   @ref Developer
420 **/
421 int CeedGetParent(Ceed ceed, Ceed *parent) {
422   int ierr;
423   if (ceed->parent) {
424     ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr);
425     return 0;
426   }
427   *parent = ceed;
428   return 0;
429 }
430 
431 /**
432   @brief Retrieve a delegate CEED
433 
434   @param ceed           Ceed to retrieve delegate of
435   @param[out] delegate  Address to save the delegate to
436 
437   @return An error code: 0 - success, otherwise - failure
438 
439   @ref Developer
440 **/
441 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
442   *delegate = ceed->delegate;
443   return 0;
444 }
445 
446 /**
447   @brief Set a delegate CEED
448 
449   This function allows a CEED to set a delegate CEED. All backend
450   implementations default to the delegate CEED, unless overridden.
451 
452   @param ceed           Ceed to set delegate of
453   @param[out] delegate  Address to set the delegate to
454 
455   @return An error code: 0 - success, otherwise - failure
456 
457   @ref Advanced
458 **/
459 int CeedSetDelegate(Ceed ceed, Ceed delegate) {
460   ceed->delegate = delegate;
461   delegate->parent = ceed;
462   return 0;
463 }
464 
465 /**
466   @brief Retrieve a delegate CEED for a specific object type
467 
468   @param ceed           Ceed to retrieve delegate of
469   @param[out] delegate  Address to save the delegate to
470   @param[in] objname    Name of the object type to retrieve delegate for
471 
472   @return An error code: 0 - success, otherwise - failure
473 
474   @ref Developer
475 **/
476 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *objname) {
477   CeedInt ierr;
478 
479   // Check for object delegate
480   for (CeedInt i=0; i<ceed->objdelegatecount; i++)
481     if (!strcmp(objname, ceed->objdelegates->objname)) {
482       *delegate = ceed->objdelegates->delegate;
483       return 0;
484     }
485 
486   // Use default delegate if no object delegate
487   ierr = CeedGetDelegate(ceed, delegate); CeedChk(ierr);
488 
489   return 0;
490 }
491 
492 /**
493   @brief Set a delegate CEED for a specific object type
494 
495   This function allows a CEED to set a delegate CEED for a given type of
496   CEED object. All backend implementations default to the delegate CEED for
497   this object. For example,
498     CeedSetObjectDelegate(ceed, refceed, "Basis")
499   uses refceed implementations for all CeedBasis backend functions.
500 
501   @param ceed           Ceed to set delegate of
502   @param[out] delegate  Address to set the delegate to
503   @param[in] objname    Name of the object type to set delegate for
504 
505   @return An error code: 0 - success, otherwise - failure
506 
507   @ref Advanced
508 **/
509 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *objname) {
510   CeedInt ierr;
511   CeedInt count = ceed->objdelegatecount;
512 
513   // Malloc or Realloc
514   if (count) {
515     ierr = CeedRealloc(count+1, &ceed->objdelegates); CeedChk(ierr);
516   } else {
517     ierr = CeedCalloc(1, &ceed->objdelegates); CeedChk(ierr);
518   }
519   ceed->objdelegatecount++;
520 
521   // Set object delegate
522   ceed->objdelegates[count].delegate = delegate;
523   size_t slen = strlen(objname) + 1;
524   ierr = CeedMalloc(slen, &ceed->objdelegates[count].objname); CeedChk(ierr);
525   memcpy(ceed->objdelegates[count].objname, objname, slen);
526 
527   // Set delegate parent
528   delegate->parent = ceed;
529 
530   return 0;
531 }
532 
533 /**
534   @brief Return Ceed preferred memory type
535 
536   @param ceed           Ceed to get preferred memory type of
537   @param[out] type      Address to save preferred memory type to
538 
539   @return An error code: 0 - success, otherwise - failure
540 
541   @ref Basic
542 **/
543 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *type) {
544   int ierr;
545 
546   if (ceed->GetPreferredMemType) {
547     ierr = ceed->GetPreferredMemType(type); CeedChk(ierr);
548   } else {
549     Ceed delegate;
550     ierr = CeedGetDelegate(ceed, &delegate); CeedChk(ierr);
551 
552     if (delegate) {
553       ierr = CeedGetPreferredMemType(delegate, type); CeedChk(ierr);
554     } else {
555       *type = CEED_MEM_HOST;
556     }
557   }
558 
559   return 0;
560 }
561 
562 /**
563   @brief Set a backend function
564 
565   This function is used for a backend to set the function associated with
566   the CEED objects. For example,
567     CeedSetBackendFunction(ceed, "Ceed", ceed, "VectorCreate", BackendVectorCreate)
568   sets the backend implementation of 'CeedVectorCreate' and
569     CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply)
570   sets the backend implementation of 'CeedBasisApply'. Note, the prefix 'Ceed'
571   is not required for the object type ("Basis" vs "CeedBasis").
572 
573   @param ceed           Ceed for error handling
574   @param type           Type of Ceed object to set function for
575   @param[out] object    Ceed object to set function for
576   @param fname          Name of function to set
577   @param f              Function to set
578 
579   @return An error code: 0 - success, otherwise - failure
580 
581   @ref Advanced
582 **/
583 int CeedSetBackendFunction(Ceed ceed,
584                            const char *type, void *object,
585                            const char *fname, int (*f)()) {
586   char lookupname[CEED_MAX_RESOURCE_LEN+1] = "";
587 
588   // Build lookup name
589   if (strcmp(type, "Ceed"))
590     strncat (lookupname, "Ceed", CEED_MAX_RESOURCE_LEN);
591   strncat(lookupname, type, CEED_MAX_RESOURCE_LEN);
592   strncat(lookupname, fname, CEED_MAX_RESOURCE_LEN);
593 
594   // Find and use offset
595   for (CeedInt i = 0; ceed->foffsets[i].fname; i++)
596     if (!strcmp(ceed->foffsets[i].fname, lookupname)) {
597       size_t offset = ceed->foffsets[i].offset;
598       int (**fpointer)(void) = (int (* *)(void))((char *)object + offset);
599       *fpointer = f;
600       return 0;
601     }
602 
603   // LCOV_EXCL_START
604   return CeedError(ceed, 1, "Requested function '%s' was not found for CEED "
605                    "object '%s'", fname, type);
606   // LCOV_EXCL_STOP
607 }
608 
609 /**
610   @brief Retrieve backend data for a CEED
611 
612   @param ceed           Ceed to retrieve data of
613   @param[out] data      Address to save data to
614 
615   @return An error code: 0 - success, otherwise - failure
616 
617   @ref Advanced
618 **/
619 int CeedGetData(Ceed ceed, void* *data) {
620   *data = ceed->data;
621   return 0;
622 }
623 
624 /**
625   @brief Set backend data for a CEED
626 
627   @param ceed           Ceed to set data of
628   @param data           Address of data to set
629 
630   @return An error code: 0 - success, otherwise - failure
631 
632   @ref Advanced
633 **/
634 int CeedSetData(Ceed ceed, void* *data) {
635   ceed->data = *data;
636   return 0;
637 }
638 
639 /**
640   @brief Destroy a Ceed context
641 
642   @param ceed Address of Ceed context to destroy
643 
644   @return An error code: 0 - success, otherwise - failure
645 
646   @ref Basic
647 **/
648 int CeedDestroy(Ceed *ceed) {
649   int ierr;
650 
651   if (!*ceed || --(*ceed)->refcount > 0)
652     return 0;
653   if ((*ceed)->delegate) {
654     ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr);
655   }
656   if ((*ceed)->objdelegatecount > 0) {
657     for (int i=0; i<(*ceed)->objdelegatecount; i++) {
658       ierr = CeedDestroy(&((*ceed)->objdelegates[i].delegate)); CeedChk(ierr);
659       ierr = CeedFree(&(*ceed)->objdelegates[i].objname); CeedChk(ierr);
660     }
661     ierr = CeedFree(&(*ceed)->objdelegates); CeedChk(ierr);
662   }
663   if ((*ceed)->Destroy) {
664     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
665   }
666   ierr = CeedFree(&(*ceed)->foffsets); CeedChk(ierr);
667   ierr = CeedFree(ceed); CeedChk(ierr);
668   return 0;
669 }
670 
671 /// @}
672