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