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