xref: /libCEED/interface/ceed.c (revision be9261b744a4f5557a9c58123cb6ee5694c9a713)
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 <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 /// @cond DOXYGEN_SKIP
27 static CeedRequest ceed_request_immediate;
28 static CeedRequest ceed_request_ordered;
29 
30 static struct {
31   char prefix[CEED_MAX_RESOURCE_LEN];
32   int (*init)(const char *resource, Ceed f);
33   unsigned int priority;
34 } backends[32];
35 static size_t num_backends;
36 
37 #define ceedoffsetof(st, m) \
38     ((size_t) ( (char *)&((st)(0))->m - (char *)0 ))
39 /// @endcond
40 
41 /// @file
42 /// Implementation of core components of Ceed library
43 ///
44 /// @addtogroup Ceed
45 /// @{
46 
47 /**
48   @brief Request immediate completion
49 
50   This predefined constant is passed as the \ref CeedRequest argument to
51   interfaces when the caller wishes for the operation to be performed
52   immediately.  The code
53 
54   @code
55     CeedOperatorApply(op, ..., CEED_REQUEST_IMMEDIATE);
56   @endcode
57 
58   is semantically equivalent to
59 
60   @code
61     CeedRequest request;
62     CeedOperatorApply(op, ..., &request);
63     CeedRequestWait(&request);
64   @endcode
65 
66   @sa CEED_REQUEST_ORDERED
67 **/
68 CeedRequest *const CEED_REQUEST_IMMEDIATE = &ceed_request_immediate;
69 
70 /**
71   @brief Request ordered completion
72 
73   This predefined constant is passed as the \ref CeedRequest argument to
74   interfaces when the caller wishes for the operation to be completed in the
75   order that it is submitted to the device.  It is typically used in a construct
76   such as
77 
78   @code
79     CeedRequest request;
80     CeedOperatorApply(op1, ..., CEED_REQUEST_ORDERED);
81     CeedOperatorApply(op2, ..., &request);
82     // other optional work
83     CeedWait(&request);
84   @endcode
85 
86   which allows the sequence to complete asynchronously but does not start
87   `op2` until `op1` has completed.
88 
89   @fixme The current implementation is overly strict, offering equivalent
90   semantics to CEED_REQUEST_IMMEDIATE.
91 
92   @sa CEED_REQUEST_IMMEDIATE
93  */
94 CeedRequest *const CEED_REQUEST_ORDERED = &ceed_request_ordered;
95 
96 /**
97   @brief Error handling implementation; use \ref CeedError instead.
98 
99   @ref Developer
100 **/
101 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func,
102                   int ecode, const char *format, ...) {
103   va_list args;
104   va_start(args, format);
105   if (ceed) return ceed->Error(ceed, filename, lineno, func, ecode, format, args);
106   return CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args);
107 }
108 
109 /**
110   @brief Error handler that returns without printing anything.
111 
112   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
113 
114   @ref Developer
115 **/
116 int CeedErrorReturn(Ceed ceed, const char *filename, int lineno,
117                     const char *func, int ecode, const char *format,
118                     va_list args) {
119   return ecode;
120 }
121 
122 /**
123   @brief Error handler that prints to stderr and aborts
124 
125   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
126 
127   @ref Developer
128 **/
129 int CeedErrorAbort(Ceed ceed, const char *filename, int lineno,
130                    const char *func, int ecode,
131                    const char *format, va_list args) {
132   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
133   vfprintf(stderr, format, args);
134   fprintf(stderr, "\n");
135   abort();
136   return ecode;
137 }
138 
139 /**
140   @brief Error handler that prints to stderr and exits
141 
142   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
143 
144   In contrast to CeedErrorAbort(), this exits without a signal, so atexit()
145   handlers (e.g., as used by gcov) are run.
146 
147   @ref Developer
148 **/
149 int CeedErrorExit(Ceed ceed, const char *filename, int lineno,
150                   const char *func, int ecode,
151                   const char *format, va_list args) {
152   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
153   vfprintf(stderr, format, args);
154   fprintf(stderr, "\n");
155   exit(ecode);
156   return ecode;
157 }
158 
159 /**
160   @brief Set error handler
161 
162   A default error handler is set in CeedInit().  Use this function to change
163   the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined
164   error handler.
165 
166   @ref Developer
167 **/
168 int CeedSetErrorHandler(Ceed ceed,
169                         int (eh)(Ceed, const char *, int, const char *,
170                                  int, const char *, va_list)) {
171   ceed->Error = eh;
172   return 0;
173 }
174 
175 /**
176   @brief Register a Ceed backend
177 
178   @param prefix   Prefix of resources for this backend to respond to.  For
179                     example, the reference backend responds to "/cpu/self".
180   @param init     Initialization function called by CeedInit() when the backend
181                     is selected to drive the requested resource.
182   @param priority Integer priority.  Lower values are preferred in case the
183                     resource requested by CeedInit() has non-unique best prefix
184                     match.
185 
186   @return An error code: 0 - success, otherwise - failure
187 
188   @ref Advanced
189 **/
190 int CeedRegister(const char *prefix,
191                  int (*init)(const char *, Ceed), unsigned int priority) {
192   if (num_backends >= sizeof(backends) / sizeof(backends[0])) {
193     return CeedError(NULL, 1, "Too many backends");
194   }
195   strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN);
196   backends[num_backends].init = init;
197   backends[num_backends].priority = priority;
198   num_backends++;
199   return 0;
200 }
201 
202 /**
203   @brief Allocate an array on the host; use CeedMalloc()
204 
205   Memory usage can be tracked by the library.  This ensures sufficient
206     alignment for vectorization and should be used for large allocations.
207 
208   @param n Number of units to allocate
209   @param unit Size of each unit
210   @param p Address of pointer to hold the result.
211 
212   @return An error code: 0 - success, otherwise - failure
213 
214   @sa CeedFree()
215 
216   @ref Advanced
217 **/
218 int CeedMallocArray(size_t n, size_t unit, void *p) {
219   int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit);
220   if (ierr)
221     return CeedError(NULL, ierr,
222                      "posix_memalign failed to allocate %zd members of size %zd\n", n, unit);
223   return 0;
224 }
225 
226 /**
227   @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc()
228 
229   Memory usage can be tracked by the library.
230 
231   @param n Number of units to allocate
232   @param unit Size of each unit
233   @param p Address of pointer to hold the result.
234 
235   @return An error code: 0 - success, otherwise - failure
236 
237   @sa CeedFree()
238 
239   @ref Advanced
240 **/
241 int CeedCallocArray(size_t n, size_t unit, void *p) {
242   *(void **)p = calloc(n, unit);
243   if (n && unit && !*(void **)p)
244     return CeedError(NULL, 1, "calloc failed to allocate %zd members of size %zd\n",
245                      n, unit);
246   return 0;
247 }
248 
249 /**
250   @brief Reallocate an array on the host; use CeedRealloc()
251 
252   Memory usage can be tracked by the library.
253 
254   @param n Number of units to allocate
255   @param unit Size of each unit
256   @param p Address of pointer to hold the result.
257 
258   @return An error code: 0 - success, otherwise - failure
259 
260   @sa CeedFree()
261 
262   @ref Advanced
263 **/
264 int CeedReallocArray(size_t n, size_t unit, void *p) {
265   *(void **)p = realloc(*(void **)p, n*unit);
266   if (n && unit && !*(void **)p)
267     return CeedError(NULL, 1,
268                      "realloc failed to allocate %zd members of size %zd\n",
269                      n, unit);
270   return 0;
271 }
272 
273 /// Free memory allocated using CeedMalloc() or CeedCalloc()
274 ///
275 /// @param p address of pointer to memory.  This argument is of type void* to
276 /// avoid needing a cast, but is the address of the pointer (which is zeroed)
277 /// rather than the pointer.
278 int CeedFree(void *p) {
279   free(*(void **)p);
280   *(void **)p = NULL;
281   return 0;
282 }
283 
284 /**
285   @brief Wait for a CeedRequest to complete.
286 
287   Calling CeedRequestWait on a NULL request is a no-op.
288 
289   @param req Address of CeedRequest to wait for; zeroed on completion.
290 
291   @return An error code: 0 - success, otherwise - failure
292 
293   @ref Advanced
294 **/
295 int CeedRequestWait(CeedRequest *req) {
296   if (!*req) return 0;
297   return CeedError(NULL, 2, "CeedRequestWait not implemented");
298 }
299 
300 /**
301   @brief Initialize a \ref Ceed to use the specified resource.
302 
303   @param resource  Resource to use, e.g., "/cpu/self"
304   @param ceed The library context
305   @sa CeedRegister() CeedDestroy()
306 
307   @return An error code: 0 - success, otherwise - failure
308 
309   @ref Basic
310 **/
311 int CeedInit(const char *resource, Ceed *ceed) {
312   int ierr;
313   size_t matchlen = 0, matchidx = UINT_MAX, matchpriority = UINT_MAX, priority;
314 
315   // Find matching backend
316   if (!resource) return CeedError(NULL, 1, "No resource provided");
317   for (size_t i=0; i<num_backends; i++) {
318     size_t n;
319     const char *prefix = backends[i].prefix;
320     for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {}
321     priority = backends[i].priority;
322     if (n > matchlen || (n == matchlen && matchpriority > priority)) {
323       matchlen = n;
324       matchpriority = priority;
325       matchidx = i;
326     }
327   }
328   if (!matchlen) return CeedError(NULL, 1, "No suitable backend");
329 
330   // Setup Ceed
331   ierr = CeedCalloc(1,ceed); CeedChk(ierr);
332   const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
333   if (!ceed_error_handler) ceed_error_handler = "abort";
334   if (!strcmp(ceed_error_handler, "exit"))
335     (*ceed)->Error = CeedErrorExit;
336   else
337     (*ceed)->Error = CeedErrorAbort;
338   (*ceed)->refcount = 1;
339   (*ceed)->data = NULL;
340 
341   // Set lookup table
342   foffset foffsets[CEED_NUM_BACKEND_FUNCTIONS] = {
343     {"CeedError",                  ceedoffsetof(Ceed, Error)},
344     {"CeedGetPreferredMemType",    ceedoffsetof(Ceed, GetPreferredMemType)},
345     {"CeedDestroy",                ceedoffsetof(Ceed, Destroy)},
346     {"CeedVecCreate",              ceedoffsetof(Ceed, VecCreate)},
347     {"CeedElemRestrictionCreate",  ceedoffsetof(Ceed, ElemRestrictionCreate)},
348     {
349       "CeedElemRestrictionCreateBlocked",
350       ceedoffsetof(Ceed, ElemRestrictionCreateBlocked)
351     },
352     {"CeedBasisCreateTensorH1",    ceedoffsetof(Ceed, BasisCreateTensorH1)},
353     {"CeedBasisCreateH1",          ceedoffsetof(Ceed, BasisCreateH1)},
354     {"CeedTensorContractCreate",   ceedoffsetof(Ceed, TensorContractCreate)},
355     {"CeedQFunctionCreate",        ceedoffsetof(Ceed, QFunctionCreate)},
356     {"CeedOperatorCreate",         ceedoffsetof(Ceed, OperatorCreate)},
357     {"CeedCompositeOperatorCreate",ceedoffsetof(Ceed, CompositeOperatorCreate)},
358     {"VectorSetArray",             ceedoffsetof(CeedVector, SetArray)},
359     {"VectorSetValue",             ceedoffsetof(CeedVector, SetValue)},
360     {"VectorGetArray",             ceedoffsetof(CeedVector, GetArray)},
361     {"VectorGetArrayRead",         ceedoffsetof(CeedVector, GetArrayRead)},
362     {"VectorRestoreArray",         ceedoffsetof(CeedVector, RestoreArray)},
363     {"VectorRestoreArrayRead",     ceedoffsetof(CeedVector, RestoreArrayRead)},
364     {"VectorDestroy",              ceedoffsetof(CeedVector, Destroy)},
365     {"ElemRestrictionApply",       ceedoffsetof(CeedElemRestriction, Apply)},
366     {"ElemRestrictionApplyBlock",  ceedoffsetof(CeedElemRestriction, ApplyBlock)},
367     {"ElemRestrictionDestroy",     ceedoffsetof(CeedElemRestriction, Destroy)},
368     {"BasisApply",                 ceedoffsetof(CeedBasis, Apply)},
369     {"BasisDestroy",               ceedoffsetof(CeedBasis, Destroy)},
370     {"TensorContractApply",        ceedoffsetof(CeedTensorContract, Apply)},
371     {"TensorContractDestroy",      ceedoffsetof(CeedTensorContract, Destroy)},
372     {"QFunctionApply",             ceedoffsetof(CeedQFunction, Apply)},
373     {"QFunctionDestroy",           ceedoffsetof(CeedQFunction, Destroy)},
374     {"OperatorApply",              ceedoffsetof(CeedOperator, Apply)},
375     {"OperatorApplyJacobian",      ceedoffsetof(CeedOperator, ApplyJacobian)},
376     {"OperatorDestroy",            ceedoffsetof(CeedOperator, Destroy)}
377   };
378 
379   memcpy((*ceed)->foffsets, foffsets,
380          CEED_NUM_BACKEND_FUNCTIONS*sizeof(foffset));
381 
382   // Backend specific setup
383   ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr);
384 
385   return 0;
386 }
387 
388 /**
389   @brief Retrieve a parent CEED
390 
391   @param ceed           Ceed to retrieve parent of
392   @param[out] parent    Address to save the parent to
393 
394   @return An error code: 0 - success, otherwise - failure
395 
396   @ref Developer
397 **/
398 int CeedGetParent(Ceed ceed, Ceed *parent) {
399   int ierr;
400   if (ceed->parent) {
401     ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr);
402     return 0;
403   }
404   *parent = ceed;
405   return 0;
406 }
407 
408 /**
409   @brief Retrieve a delegate CEED
410 
411   @param ceed           Ceed to retrieve delegate of
412   @param[out] delegate  Address to save the delegate to
413 
414   @return An error code: 0 - success, otherwise - failure
415 
416   @ref Developer
417 **/
418 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
419   *delegate = ceed->delegate;
420   return 0;
421 }
422 
423 /**
424   @brief Set a delegate CEED
425 
426   @param ceed           Ceed to set delegate of
427   @param[out] delegate  Address to set the delegate to
428 
429   @return An error code: 0 - success, otherwise - failure
430 
431   @ref Advanced
432 **/
433 int CeedSetDelegate(Ceed ceed, Ceed *delegate) {
434   ceed->delegate = *delegate;
435   (*delegate)->parent = ceed;
436   return 0;
437 }
438 
439 /**
440   @brief Return Ceed perferred memory type
441 
442   @param ceed           Ceed to get preferred memory type of
443   @param[out] delegate  Address to save preferred memory type to
444 
445   @return An error code: 0 - success, otherwise - failure
446 
447   @ref Basic
448 **/
449 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *type) {
450   int ierr;
451   if (ceed->GetPreferredMemType) {
452     ierr = ceed->GetPreferredMemType(type); CeedChk(ierr);
453   } else {
454     *type = CEED_MEM_HOST;
455   }
456 
457   return 0;
458 }
459 
460 /**
461   @brief Set a backend function
462 
463   @param ceed           Ceed for error handling
464   @param type           Type of Ceed object to set function for
465   @param[out] object    Ceed object to set function for
466   @param fname          Name of function to set
467   @param f              Function to set
468 
469   @return An error code: 0 - success, otherwise - failure
470 
471   @ref Advanced
472 **/
473 int CeedSetBackendFunction(Ceed ceed,
474                            const char *type, void *object,
475                            const char *fname, int (*f)()) {
476   char lookupname[CEED_MAX_RESOURCE_LEN+1] = "";
477 
478   // Build lookup name
479   strncat(lookupname, type, CEED_MAX_RESOURCE_LEN);
480   strncat(lookupname, fname, CEED_MAX_RESOURCE_LEN);
481 
482   // Find and use offset
483   for (CeedInt i = 0; i < CEED_NUM_BACKEND_FUNCTIONS; i++) {
484     if (!strcmp(ceed->foffsets[i].fname, lookupname)) {
485       size_t offset = ceed->foffsets[i].offset;
486       size_t *fpointer;
487       fpointer = (size_t *)(object + offset);
488       *fpointer = (size_t) f;
489       return 0;
490     }
491   }
492 
493   return CeedError(ceed, 1,
494                    "Requested function '%s' was not found for CEED object '%s'", fname, type);
495 }
496 
497 /**
498   @brief Retrieve backend data for a CEED
499 
500   @param ceed           Ceed to retrieve data of
501   @param[out] data      Address to save data to
502 
503   @return An error code: 0 - success, otherwise - failure
504 
505   @ref Advanced
506 **/
507 int CeedGetData(Ceed ceed, void* *data) {
508   *data = ceed->data;
509   return 0;
510 }
511 
512 /**
513   @brief Set backend data for a CEED
514 
515   @param ceed           Ceed to set data of
516   @param data           Address of data to set
517 
518   @return An error code: 0 - success, otherwise - failure
519 
520   @ref Advanced
521 **/
522 int CeedSetData(Ceed ceed, void* *data) {
523   ceed->data = *data;
524   return 0;
525 }
526 
527 /**
528   @brief Destroy a Ceed context
529 
530   @param ceed Address of Ceed context to destroy
531 
532   @return An error code: 0 - success, otherwise - failure
533 
534   @ref Basic
535 **/
536 int CeedDestroy(Ceed *ceed) {
537   int ierr;
538 
539   if (!*ceed || --(*ceed)->refcount > 0) return 0;
540   if ((*ceed)->delegate) {
541     ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr);
542   }
543   if ((*ceed)->Destroy) {
544     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
545   }
546   ierr = CeedFree(ceed); CeedChk(ierr);
547   return 0;
548 }
549 
550 /// @}
551