xref: /libCEED/interface/ceed.c (revision cb37edd8a3b370dbcdb291b3aa85f064d8a8b1a0)
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   va_start(args, format);
106   if (ceed) return ceed->Error(ceed, filename, lineno, func, ecode, format, args);
107   return CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args);
108 }
109 
110 /**
111   @brief Error handler that returns without printing anything.
112 
113   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
114 
115   @ref Developer
116 **/
117 int CeedErrorReturn(Ceed ceed, const char *filename, int lineno,
118                     const char *func, int ecode, const char *format,
119                     va_list args) {
120   return ecode;
121 }
122 
123 /**
124   @brief Error handler that prints to stderr and aborts
125 
126   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
127 
128   @ref Developer
129 **/
130 int CeedErrorAbort(Ceed ceed, const char *filename, int lineno,
131                    const char *func, int ecode,
132                    const char *format, va_list args) {
133   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
134   vfprintf(stderr, format, args);
135   fprintf(stderr, "\n");
136   abort();
137   return ecode;
138 }
139 
140 /**
141   @brief Error handler that prints to stderr and exits
142 
143   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
144 
145   In contrast to CeedErrorAbort(), this exits without a signal, so atexit()
146   handlers (e.g., as used by gcov) are run.
147 
148   @ref Developer
149 **/
150 int CeedErrorExit(Ceed ceed, const char *filename, int lineno,
151                   const char *func, int ecode,
152                   const char *format, va_list args) {
153   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
154   vfprintf(stderr, format, args);
155   fprintf(stderr, "\n");
156   exit(ecode);
157   return ecode;
158 }
159 
160 /**
161   @brief Set error handler
162 
163   A default error handler is set in CeedInit().  Use this function to change
164   the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined
165   error handler.
166 
167   @ref Developer
168 **/
169 int CeedSetErrorHandler(Ceed ceed,
170                         int (eh)(Ceed, const char *, int, const char *,
171                                  int, const char *, va_list)) {
172   ceed->Error = eh;
173   return 0;
174 }
175 
176 /**
177   @brief Register a Ceed backend
178 
179   @param prefix   Prefix of resources for this backend to respond to.  For
180                     example, the reference backend responds to "/cpu/self".
181   @param init     Initialization function called by CeedInit() when the backend
182                     is selected to drive the requested resource.
183   @param priority Integer priority.  Lower values are preferred in case the
184                     resource requested by CeedInit() has non-unique best prefix
185                     match.
186 
187   @return An error code: 0 - success, otherwise - failure
188 
189   @ref Advanced
190 **/
191 int CeedRegister(const char *prefix,
192                  int (*init)(const char *, Ceed), unsigned int priority) {
193   if (num_backends >= sizeof(backends) / sizeof(backends[0])) {
194     return CeedError(NULL, 1, "Too many backends");
195   }
196   strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN);
197   backends[num_backends].init = init;
198   backends[num_backends].priority = priority;
199   num_backends++;
200   return 0;
201 }
202 
203 /**
204   @brief Allocate an array on the host; use CeedMalloc()
205 
206   Memory usage can be tracked by the library.  This ensures sufficient
207     alignment for vectorization and should be used for large allocations.
208 
209   @param n Number of units to allocate
210   @param unit Size of each unit
211   @param p Address of pointer to hold the result.
212 
213   @return An error code: 0 - success, otherwise - failure
214 
215   @sa CeedFree()
216 
217   @ref Advanced
218 **/
219 int CeedMallocArray(size_t n, size_t unit, void *p) {
220   int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit);
221   if (ierr)
222     return CeedError(NULL, ierr,
223                      "posix_memalign failed to allocate %zd members of size %zd\n", n, unit);
224   return 0;
225 }
226 
227 /**
228   @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc()
229 
230   Memory usage can be tracked by the library.
231 
232   @param n Number of units to allocate
233   @param unit Size of each unit
234   @param p Address of pointer to hold the result.
235 
236   @return An error code: 0 - success, otherwise - failure
237 
238   @sa CeedFree()
239 
240   @ref Advanced
241 **/
242 int CeedCallocArray(size_t n, size_t unit, void *p) {
243   *(void **)p = calloc(n, unit);
244   if (n && unit && !*(void **)p)
245     return CeedError(NULL, 1, "calloc failed to allocate %zd members of size %zd\n",
246                      n, unit);
247   return 0;
248 }
249 
250 /**
251   @brief Reallocate an array on the host; use CeedRealloc()
252 
253   Memory usage can be tracked by the library.
254 
255   @param n Number of units to allocate
256   @param unit Size of each unit
257   @param p Address of pointer to hold the result.
258 
259   @return An error code: 0 - success, otherwise - failure
260 
261   @sa CeedFree()
262 
263   @ref Advanced
264 **/
265 int CeedReallocArray(size_t n, size_t unit, void *p) {
266   *(void **)p = realloc(*(void **)p, n*unit);
267   if (n && unit && !*(void **)p)
268     return CeedError(NULL, 1,
269                      "realloc failed to allocate %zd members of size %zd\n",
270                      n, unit);
271   return 0;
272 }
273 
274 /// Free memory allocated using CeedMalloc() or CeedCalloc()
275 ///
276 /// @param p address of pointer to memory.  This argument is of type void* to
277 /// avoid needing a cast, but is the address of the pointer (which is zeroed)
278 /// rather than the pointer.
279 int CeedFree(void *p) {
280   free(*(void **)p);
281   *(void **)p = NULL;
282   return 0;
283 }
284 
285 /**
286   @brief Wait for a CeedRequest to complete.
287 
288   Calling CeedRequestWait on a NULL request is a no-op.
289 
290   @param req Address of CeedRequest to wait for; zeroed on completion.
291 
292   @return An error code: 0 - success, otherwise - failure
293 
294   @ref Advanced
295 **/
296 int CeedRequestWait(CeedRequest *req) {
297   if (!*req) return 0;
298   return CeedError(NULL, 2, "CeedRequestWait not implemented");
299 }
300 
301 /**
302   @brief Initialize a \ref Ceed to use the specified resource.
303 
304   @param resource  Resource to use, e.g., "/cpu/self"
305   @param ceed The library context
306   @sa CeedRegister() CeedDestroy()
307 
308   @return An error code: 0 - success, otherwise - failure
309 
310   @ref Basic
311 **/
312 int CeedInit(const char *resource, Ceed *ceed) {
313   int ierr;
314   size_t matchlen = 0, matchidx = UINT_MAX, matchpriority = UINT_MAX, priority;
315 
316   // Find matching backend
317   if (!resource) return CeedError(NULL, 1, "No resource provided");
318   for (size_t i=0; i<num_backends; i++) {
319     size_t n;
320     const char *prefix = backends[i].prefix;
321     for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {}
322     priority = backends[i].priority;
323     if (n > matchlen || (n == matchlen && matchpriority > priority)) {
324       matchlen = n;
325       matchpriority = priority;
326       matchidx = i;
327     }
328   }
329   if (!matchlen) return CeedError(NULL, 1, "No suitable backend");
330 
331   // Setup Ceed
332   ierr = CeedCalloc(1,ceed); CeedChk(ierr);
333   const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
334   if (!ceed_error_handler) ceed_error_handler = "abort";
335   if (!strcmp(ceed_error_handler, "exit"))
336     (*ceed)->Error = CeedErrorExit;
337   else
338     (*ceed)->Error = CeedErrorAbort;
339   (*ceed)->refcount = 1;
340   (*ceed)->data = NULL;
341 
342   // Set lookup table
343   foffset foffsets[] = {
344     CEED_FTABLE_ENTRY(Ceed, Error),
345     CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType),
346     CEED_FTABLE_ENTRY(Ceed, Destroy),
347     CEED_FTABLE_ENTRY(Ceed, VecCreate),
348     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate),
349     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
350     CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
351     CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
352     CEED_FTABLE_ENTRY(Ceed, TensorContractCreate),
353     CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
354     CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
355     CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
356     CEED_FTABLE_ENTRY(CeedVector, SetArray),
357     CEED_FTABLE_ENTRY(CeedVector, SetValue),
358     CEED_FTABLE_ENTRY(CeedVector, GetArray),
359     CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
360     CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
361     CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
362     CEED_FTABLE_ENTRY(CeedVector, Destroy),
363     CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
364     CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
365     CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
366     CEED_FTABLE_ENTRY(CeedBasis, Apply),
367     CEED_FTABLE_ENTRY(CeedBasis, Destroy),
368     CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
369     CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
370     CEED_FTABLE_ENTRY(CeedQFunction, Apply),
371     CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
372     CEED_FTABLE_ENTRY(CeedOperator, Apply),
373     CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
374     CEED_FTABLE_ENTRY(CeedOperator, Destroy),
375     {NULL, 0} // End of lookup table - used in SetBackendFunction loop
376   };
377 
378   ierr = CeedCalloc(sizeof(foffsets), &(*ceed)->foffsets); CeedChk(ierr);
379   memcpy((*ceed)->foffsets, foffsets, sizeof(foffsets));
380 
381   // Backend specific setup
382   ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr);
383 
384   return 0;
385 }
386 
387 /**
388   @brief Retrieve a parent CEED
389 
390   @param ceed           Ceed to retrieve parent of
391   @param[out] parent    Address to save the parent to
392 
393   @return An error code: 0 - success, otherwise - failure
394 
395   @ref Developer
396 **/
397 int CeedGetParent(Ceed ceed, Ceed *parent) {
398   int ierr;
399   if (ceed->parent) {
400     ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr);
401     return 0;
402   }
403   *parent = ceed;
404   return 0;
405 }
406 
407 /**
408   @brief Retrieve a delegate CEED
409 
410   @param ceed           Ceed to retrieve delegate of
411   @param[out] delegate  Address to save the delegate to
412 
413   @return An error code: 0 - success, otherwise - failure
414 
415   @ref Developer
416 **/
417 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
418   *delegate = ceed->delegate;
419   return 0;
420 }
421 
422 /**
423   @brief Set a delegate CEED
424 
425   @param ceed           Ceed to set delegate of
426   @param[out] delegate  Address to set the delegate to
427 
428   @return An error code: 0 - success, otherwise - failure
429 
430   @ref Advanced
431 **/
432 int CeedSetDelegate(Ceed ceed, Ceed *delegate) {
433   ceed->delegate = *delegate;
434   (*delegate)->parent = ceed;
435   return 0;
436 }
437 
438 /**
439   @brief Return Ceed perferred memory type
440 
441   @param ceed           Ceed to get preferred memory type of
442   @param[out] delegate  Address to save preferred memory type to
443 
444   @return An error code: 0 - success, otherwise - failure
445 
446   @ref Basic
447 **/
448 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *type) {
449   int ierr;
450   if (ceed->GetPreferredMemType) {
451     ierr = ceed->GetPreferredMemType(type); CeedChk(ierr);
452   } else {
453     *type = CEED_MEM_HOST;
454   }
455 
456   return 0;
457 }
458 
459 /**
460   @brief Set a backend function
461 
462   This function is used for a backend to set the function associated with
463   the CEED objects. For example,
464     CeedSetBackendFunction(ceed, "Ceed", ceed, "VecCreate", BackendVecCreate)
465   sets the backend implementation of 'CeedVectorCreate' and
466     CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply)
467   sets the backend implementation of 'CeedBasisApply'. Note, the prefix 'Ceed'
468   is not required for the object type ("Basis" vs "CeedBasis").
469 
470   @param ceed           Ceed for error handling
471   @param type           Type of Ceed object to set function for
472   @param[out] object    Ceed object to set function for
473   @param fname          Name of function to set
474   @param f              Function to set
475 
476   @return An error code: 0 - success, otherwise - failure
477 
478   @ref Advanced
479 **/
480 int CeedSetBackendFunction(Ceed ceed,
481                            const char *type, void *object,
482                            const char *fname, int (*f)()) {
483   char lookupname[CEED_MAX_RESOURCE_LEN+1] = "";
484 
485   // Build lookup name
486   if (strcmp(type, "Ceed"))
487     strncat (lookupname, "Ceed", CEED_MAX_RESOURCE_LEN);
488   strncat(lookupname, type, CEED_MAX_RESOURCE_LEN);
489   strncat(lookupname, fname, CEED_MAX_RESOURCE_LEN);
490 
491   // Find and use offset
492   for (CeedInt i = 0; ceed->foffsets[i].fname; i++) {
493     if (!strcmp(ceed->foffsets[i].fname, lookupname)) {
494       size_t offset = ceed->foffsets[i].offset;
495       int (**fpointer)(void) = (int (**)(void))((char*)object + offset);
496       *fpointer = f;
497       return 0;
498     }
499   }
500 
501   return CeedError(ceed, 1,
502                    "Requested function '%s' was not found for CEED object '%s'", fname, type);
503 }
504 
505 /**
506   @brief Retrieve backend data for a CEED
507 
508   @param ceed           Ceed to retrieve data of
509   @param[out] data      Address to save data to
510 
511   @return An error code: 0 - success, otherwise - failure
512 
513   @ref Advanced
514 **/
515 int CeedGetData(Ceed ceed, void* *data) {
516   *data = ceed->data;
517   return 0;
518 }
519 
520 /**
521   @brief Set backend data for a CEED
522 
523   @param ceed           Ceed to set data of
524   @param data           Address of data to set
525 
526   @return An error code: 0 - success, otherwise - failure
527 
528   @ref Advanced
529 **/
530 int CeedSetData(Ceed ceed, void* *data) {
531   ceed->data = *data;
532   return 0;
533 }
534 
535 /**
536   @brief Destroy a Ceed context
537 
538   @param ceed Address of Ceed context to destroy
539 
540   @return An error code: 0 - success, otherwise - failure
541 
542   @ref Basic
543 **/
544 int CeedDestroy(Ceed *ceed) {
545   int ierr;
546 
547   if (!*ceed || --(*ceed)->refcount > 0) return 0;
548   if ((*ceed)->delegate) {
549     ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr);
550   }
551   if ((*ceed)->Destroy) {
552     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
553   }
554   ierr = CeedFree(&(*ceed)->foffsets); CeedChk(ierr);
555   ierr = CeedFree(ceed); CeedChk(ierr);
556   return 0;
557 }
558 
559 /// @}
560