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