xref: /libCEED/interface/ceed-vector.c (revision eccc2849f69a9f016cade2e1c45046d05a5ce45c)
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 #include <ceed.h>
18 #include <ceed-backend.h>
19 #include <ceed-impl.h>
20 #include <math.h>
21 #include <stdint.h>
22 #include <stdio.h>
23 
24 /// @file
25 /// Implementation of public CeedVector interfaces
26 
27 /// @cond DOXYGEN_SKIP
28 static struct CeedVector_private ceed_vector_active;
29 static struct CeedVector_private ceed_vector_none;
30 /// @endcond
31 
32 /// @addtogroup CeedVectorUser
33 /// @{
34 
35 /// Indicate that vector will be provided as an explicit argument to
36 ///   CeedOperatorApply().
37 const CeedVector CEED_VECTOR_ACTIVE = &ceed_vector_active;
38 
39 /// Indicate that no vector is applicable (i.e., for @ref CEED_EVAL_WEIGHTS).
40 const CeedVector CEED_VECTOR_NONE = &ceed_vector_none;
41 
42 /// @}
43 
44 /// ----------------------------------------------------------------------------
45 /// CeedVector Backend API
46 /// ----------------------------------------------------------------------------
47 /// @addtogroup CeedVectorBackend
48 /// @{
49 
50 /**
51   @brief Get the Ceed associated with a CeedVector
52 
53   @param vec           CeedVector to retrieve state
54   @param[out] ceed     Variable to store ceed
55 
56   @return An error code: 0 - success, otherwise - failure
57 
58   @ref Backend
59 **/
60 int CeedVectorGetCeed(CeedVector vec, Ceed *ceed) {
61   *ceed = vec->ceed;
62   return CEED_ERROR_SUCCESS;
63 }
64 
65 /**
66   @brief Get the state of a CeedVector
67 
68   @param vec           CeedVector to retrieve state
69   @param[out] state    Variable to store state
70 
71   @return An error code: 0 - success, otherwise - failure
72 
73   @ref Backend
74 **/
75 int CeedVectorGetState(CeedVector vec, uint64_t *state) {
76   *state = vec->state;
77   return CEED_ERROR_SUCCESS;
78 }
79 
80 /**
81   @brief Add a reference to a CeedVector
82 
83   @param[out] vec     CeedVector to increment reference counter
84 
85   @return An error code: 0 - success, otherwise - failure
86 
87   @ref Backend
88 **/
89 int CeedVectorAddReference(CeedVector vec) {
90   vec->refcount++;
91   return CEED_ERROR_SUCCESS;
92 }
93 
94 /**
95   @brief Get the backend data of a CeedVector
96 
97   @param vec           CeedVector to retrieve state
98   @param[out] data     Variable to store data
99 
100   @return An error code: 0 - success, otherwise - failure
101 
102   @ref Backend
103 **/
104 int CeedVectorGetData(CeedVector vec, void *data) {
105   *(void **)data = vec->data;
106   return CEED_ERROR_SUCCESS;
107 }
108 
109 /**
110   @brief Set the backend data of a CeedVector
111 
112   @param[out] vec     CeedVector to retrieve state
113   @param data         Data to set
114 
115   @return An error code: 0 - success, otherwise - failure
116 
117   @ref Backend
118 **/
119 int CeedVectorSetData(CeedVector vec, void *data) {
120   vec->data = data;
121   return CEED_ERROR_SUCCESS;
122 }
123 
124 /// @}
125 
126 /// ----------------------------------------------------------------------------
127 /// CeedVector Public API
128 /// ----------------------------------------------------------------------------
129 /// @addtogroup CeedVectorUser
130 /// @{
131 
132 /**
133   @brief Create a CeedVector of the specified length (does not allocate memory)
134 
135   @param ceed      Ceed object where the CeedVector will be created
136   @param length    Length of vector
137   @param[out] vec  Address of the variable where the newly created
138                      CeedVector will be stored
139 
140   @return An error code: 0 - success, otherwise - failure
141 
142   @ref User
143 **/
144 int CeedVectorCreate(Ceed ceed, CeedInt length, CeedVector *vec) {
145   int ierr;
146 
147   if (!ceed->VectorCreate) {
148     Ceed delegate;
149     ierr = CeedGetObjectDelegate(ceed, &delegate, "Vector"); CeedChk(ierr);
150 
151     if (!delegate)
152       // LCOV_EXCL_START
153       return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
154                        "Backend does not support VectorCreate");
155     // LCOV_EXCL_STOP
156 
157     ierr = CeedVectorCreate(delegate, length, vec); CeedChk(ierr);
158     return CEED_ERROR_SUCCESS;
159   }
160 
161   ierr = CeedCalloc(1, vec); CeedChk(ierr);
162   (*vec)->ceed = ceed;
163   ceed->refcount++;
164   (*vec)->refcount = 1;
165   (*vec)->length = length;
166   (*vec)->state = 0;
167   ierr = ceed->VectorCreate(length, *vec); CeedChk(ierr);
168   return CEED_ERROR_SUCCESS;
169 }
170 
171 /**
172   @brief Set the array used by a CeedVector, freeing any previously allocated
173            array if applicable. The backend may copy values to a different
174            memtype, such as during @ref CeedOperatorApply().
175            See also @ref CeedVectorSyncArray() and @ref CeedVectorTakeArray().
176 
177   @param vec   CeedVector
178   @param mtype Memory type of the array being passed
179   @param cmode Copy mode for the array
180   @param array Array to be used, or NULL with @ref CEED_COPY_VALUES to have the
181                  library allocate
182 
183   @return An error code: 0 - success, otherwise - failure
184 
185   @ref User
186 **/
187 int CeedVectorSetArray(CeedVector vec, CeedMemType mtype, CeedCopyMode cmode,
188                        CeedScalar *array) {
189   int ierr;
190 
191   if (!vec->SetArray)
192     // LCOV_EXCL_START
193     return CeedError(vec->ceed, CEED_ERROR_UNSUPPORTED,
194                      "Backend does not support VectorSetArray");
195   // LCOV_EXCL_STOP
196 
197   if (vec->state % 2 == 1)
198     return CeedError(vec->ceed, CEED_ERROR_ACCESS,
199                      "Cannot grant CeedVector array access, the "
200                      "access lock is already in use");
201 
202   if (vec->numreaders > 0)
203     return CeedError(vec->ceed, CEED_ERROR_ACCESS,
204                      "Cannot grant CeedVector array access, a "
205                      "process has read access");
206 
207   ierr = vec->SetArray(vec, mtype, cmode, array); CeedChk(ierr);
208   vec->state += 2;
209   return CEED_ERROR_SUCCESS;
210 }
211 
212 /**
213   @brief Set the CeedVector to a constant value
214 
215   @param vec        CeedVector
216   @param[in] value  Value to be used
217 
218   @return An error code: 0 - success, otherwise - failure
219 
220   @ref User
221 **/
222 int CeedVectorSetValue(CeedVector vec, CeedScalar value) {
223   int ierr;
224 
225   if (vec->state % 2 == 1)
226     return CeedError(vec->ceed, CEED_ERROR_ACCESS,
227                      "Cannot grant CeedVector array access, the "
228                      "access lock is already in use");
229 
230   if (vec->SetValue) {
231     ierr = vec->SetValue(vec, value); CeedChk(ierr);
232   } else {
233     CeedScalar *array;
234     ierr = CeedVectorGetArray(vec, CEED_MEM_HOST, &array); CeedChk(ierr);
235     for (int i=0; i<vec->length; i++) array[i] = value;
236     ierr = CeedVectorRestoreArray(vec, &array); CeedChk(ierr);
237   }
238   vec->state += 2;
239   return CEED_ERROR_SUCCESS;
240 }
241 
242 /**
243   @brief Sync the CeedVector to a specified memtype. This function is used to
244            force synchronization of arrays set with @ref CeedVectorSetArray().
245            If the requested memtype is already synchronized, this function
246            results in a no-op.
247 
248   @param vec        CeedVector
249   @param mtype      Memtype to be synced
250 
251   @return An error code: 0 - success, otherwise - failure
252 
253   @ref User
254 **/
255 int CeedVectorSyncArray(CeedVector vec, CeedMemType mtype) {
256   int ierr;
257 
258   if (vec->state % 2 == 1)
259     return CeedError(vec->ceed, CEED_ERROR_ACCESS,
260                      "Cannot sync CeedVector, the access lock is "
261                      "already in use");
262 
263   if (vec->SyncArray) {
264     ierr = vec->SyncArray(vec, mtype); CeedChk(ierr);
265   } else {
266     const CeedScalar *array;
267     ierr = CeedVectorGetArrayRead(vec, mtype, &array); CeedChk(ierr);
268     ierr = CeedVectorRestoreArrayRead(vec, &array); CeedChk(ierr);
269   }
270   return CEED_ERROR_SUCCESS;
271 }
272 
273 /**
274   @brief Take ownership of the CeedVector array and remove the array from the
275            CeedVector. The caller is responsible for managing and freeing
276            the array.
277 
278   @param vec        CeedVector
279   @param mtype      Memory type on which to take the array. If the backend
280                     uses a different memory type, this will perform a copy.
281   @param[out] array Array on memory type mtype, or NULL if array pointer is
282                       not required
283 
284   @return An error code: 0 - success, otherwise - failure
285 
286   @ref User
287 **/
288 int CeedVectorTakeArray(CeedVector vec, CeedMemType mtype, CeedScalar **array) {
289   int ierr;
290 
291   if (vec->state % 2 == 1)
292     // LCOV_EXCL_START
293     return CeedError(vec->ceed, CEED_ERROR_ACCESS,
294                      "Cannot take CeedVector array, the access "
295                      "lock is already in use");
296   // LCOV_EXCL_STOP
297   if (vec->numreaders > 0)
298     // LCOV_EXCL_START
299     return CeedError(vec->ceed, CEED_ERROR_ACCESS,
300                      "Cannot take CeedVector array, a process "
301                      "has read access");
302   // LCOV_EXCL_STOP
303 
304   CeedScalar *tempArray = NULL;
305   ierr = vec->TakeArray(vec, mtype, &tempArray); CeedChk(ierr);
306   if (array) (*array) = tempArray;
307   return CEED_ERROR_SUCCESS;
308 }
309 
310 /**
311   @brief Get read/write access to a CeedVector via the specified memory type.
312            Restore access with @ref CeedVectorRestoreArray().
313 
314   @param vec        CeedVector to access
315   @param mtype      Memory type on which to access the array. If the backend
316                     uses a different memory type, this will perform a copy.
317   @param[out] array Array on memory type mtype
318 
319   @note The CeedVectorGetArray* and CeedVectorRestoreArray* functions provide
320     access to array pointers in the desired memory space. Pairing get/restore
321     allows the Vector to track access, thus knowing if norms or other
322     operations may need to be recomputed.
323 
324   @return An error code: 0 - success, otherwise - failure
325 
326   @ref User
327 **/
328 int CeedVectorGetArray(CeedVector vec, CeedMemType mtype, CeedScalar **array) {
329   int ierr;
330 
331   if (!vec->GetArray)
332     // LCOV_EXCL_START
333     return CeedError(vec->ceed, CEED_ERROR_UNSUPPORTED,
334                      "Backend does not support GetArray");
335   // LCOV_EXCL_STOP
336 
337   if (vec->state % 2 == 1)
338     return CeedError(vec->ceed, CEED_ERROR_ACCESS,
339                      "Cannot grant CeedVector array access, the "
340                      "access lock is already in use");
341 
342   if (vec->numreaders > 0)
343     return CeedError(vec->ceed, CEED_ERROR_ACCESS,
344                      "Cannot grant CeedVector array access, a "
345                      "process has read access");
346 
347   ierr = vec->GetArray(vec, mtype, array); CeedChk(ierr);
348   vec->state += 1;
349   return CEED_ERROR_SUCCESS;
350 }
351 
352 /**
353   @brief Get read-only access to a CeedVector via the specified memory type.
354            Restore access with @ref CeedVectorRestoreArrayRead().
355 
356   @param vec        CeedVector to access
357   @param mtype      Memory type on which to access the array.  If the backend
358                     uses a different memory type, this will perform a copy
359                     (possibly cached).
360   @param[out] array Array on memory type mtype
361 
362   @return An error code: 0 - success, otherwise - failure
363 
364   @ref User
365 **/
366 int CeedVectorGetArrayRead(CeedVector vec, CeedMemType mtype,
367                            const CeedScalar **array) {
368   int ierr;
369 
370   if (!vec->GetArrayRead)
371     // LCOV_EXCL_START
372     return CeedError(vec->ceed, CEED_ERROR_UNSUPPORTED,
373                      "Backend does not support GetArrayRead");
374   // LCOV_EXCL_STOP
375 
376   if (vec->state % 2 == 1)
377     return CeedError(vec->ceed, CEED_ERROR_ACCESS,
378                      "Cannot grant CeedVector read-only array "
379                      "access, the access lock is already in use");
380 
381   ierr = vec->GetArrayRead(vec, mtype, array); CeedChk(ierr);
382   vec->numreaders++;
383   return CEED_ERROR_SUCCESS;
384 }
385 
386 /**
387   @brief Restore an array obtained using @ref CeedVectorGetArray()
388 
389   @param vec     CeedVector to restore
390   @param array   Array of vector data
391 
392   @return An error code: 0 - success, otherwise - failure
393 
394   @ref User
395 **/
396 int CeedVectorRestoreArray(CeedVector vec, CeedScalar **array) {
397   int ierr;
398 
399   if (!vec->RestoreArray)
400     // LCOV_EXCL_START
401     return CeedError(vec->ceed, CEED_ERROR_UNSUPPORTED,
402                      "Backend does not support RestoreArray");
403   // LCOV_EXCL_STOP
404 
405   if (vec->state % 2 != 1)
406     return CeedError(vec->ceed, CEED_ERROR_ACCESS,
407                      "Cannot restore CeedVector array access, "
408                      "access was not granted");
409 
410   ierr = vec->RestoreArray(vec); CeedChk(ierr);
411   *array = NULL;
412   vec->state += 1;
413   return CEED_ERROR_SUCCESS;
414 }
415 
416 /**
417   @brief Restore an array obtained using @ref CeedVectorGetArrayRead()
418 
419   @param vec     CeedVector to restore
420   @param array   Array of vector data
421 
422   @return An error code: 0 - success, otherwise - failure
423 
424   @ref User
425 **/
426 int CeedVectorRestoreArrayRead(CeedVector vec, const CeedScalar **array) {
427   int ierr;
428 
429   if (!vec->RestoreArrayRead)
430     // LCOV_EXCL_START
431     return CeedError(vec->ceed, CEED_ERROR_UNSUPPORTED,
432                      "Backend does not support RestoreArrayRead");
433   // LCOV_EXCL_STOP
434 
435   ierr = vec->RestoreArrayRead(vec); CeedChk(ierr);
436   *array = NULL;
437   vec->numreaders--;
438   return CEED_ERROR_SUCCESS;
439 }
440 
441 /**
442   @brief Get the norm of a CeedVector.
443 
444   Note: This operation is local to the CeedVector. This function will likely
445           not provide the desired results for the norm of the libCEED portion
446           of a parallel vector or a CeedVector with duplicated or hanging nodes.
447 
448   @param vec           CeedVector to retrieve maximum value
449   @param type          Norm type @ref CEED_NORM_1, @ref CEED_NORM_2, or @ref CEED_NORM_MAX
450   @param[out] norm     Variable to store norm value
451 
452   @return An error code: 0 - success, otherwise - failure
453 
454   @ref User
455 **/
456 int CeedVectorNorm(CeedVector vec, CeedNormType type, CeedScalar *norm) {
457   int ierr;
458 
459   // Backend impl for GPU, if added
460   if (vec->Norm) {
461     ierr = vec->Norm(vec, type, norm); CeedChk(ierr);
462     return CEED_ERROR_SUCCESS;
463   }
464 
465   const CeedScalar *array;
466   ierr = CeedVectorGetArrayRead(vec, CEED_MEM_HOST, &array); CeedChk(ierr);
467 
468   *norm = 0.;
469   switch (type) {
470   case CEED_NORM_1:
471     for (int i=0; i<vec->length; i++) {
472       *norm += fabs(array[i]);
473     }
474     break;
475   case CEED_NORM_2:
476     for (int i=0; i<vec->length; i++) {
477       *norm += fabs(array[i])*fabs(array[i]);
478     }
479     break;
480   case CEED_NORM_MAX:
481     for (int i=0; i<vec->length; i++) {
482       const CeedScalar absi = fabs(array[i]);
483       *norm = *norm > absi ? *norm : absi;
484     }
485   }
486   if (type == CEED_NORM_2)
487     *norm = sqrt(*norm);
488 
489   ierr = CeedVectorRestoreArrayRead(vec, &array); CeedChk(ierr);
490   return CEED_ERROR_SUCCESS;
491 }
492 
493 /**
494   @brief Take the reciprocal of a CeedVector.
495 
496   @param vec           CeedVector to take reciprocal
497 
498   @return An error code: 0 - success, otherwise - failure
499 
500   @ref User
501 **/
502 int CeedVectorReciprocal(CeedVector vec) {
503   int ierr;
504 
505   // Check if vector data set
506   if (!vec->state)
507     // LCOV_EXCL_START
508     return CeedError(vec->ceed, CEED_ERROR_INCOMPLETE,
509                      "CeedVector must have data set to take reciprocal");
510   // LCOV_EXCL_STOP
511 
512   // Backend impl for GPU, if added
513   if (vec->Reciprocal) {
514     ierr = vec->Reciprocal(vec); CeedChk(ierr);
515     return CEED_ERROR_SUCCESS;
516   }
517 
518   CeedInt len;
519   ierr = CeedVectorGetLength(vec, &len); CeedChk(ierr);
520   CeedScalar *array;
521   ierr = CeedVectorGetArray(vec, CEED_MEM_HOST, &array); CeedChk(ierr);
522   for (CeedInt i=0; i<len; i++)
523     if (fabs(array[i]) > CEED_EPSILON)
524       array[i] = 1./array[i];
525 
526   ierr = CeedVectorRestoreArray(vec, &array); CeedChk(ierr);
527   return CEED_ERROR_SUCCESS;
528 }
529 
530 /**
531   @brief View a CeedVector
532 
533   @param[in] vec           CeedVector to view
534   @param[in] fpfmt         Printing format
535   @param[in] stream        Filestream to write to
536 
537   @return An error code: 0 - success, otherwise - failure
538 
539   @ref User
540 **/
541 int CeedVectorView(CeedVector vec, const char *fpfmt, FILE *stream) {
542   const CeedScalar *x;
543 
544   int ierr = CeedVectorGetArrayRead(vec, CEED_MEM_HOST, &x); CeedChk(ierr);
545 
546   char fmt[1024];
547   fprintf(stream, "CeedVector length %ld\n", (long)vec->length);
548   snprintf(fmt, sizeof fmt, "  %s\n", fpfmt ? fpfmt : "%g");
549   for (CeedInt i=0; i<vec->length; i++)
550     fprintf(stream, fmt, x[i]);
551 
552   ierr = CeedVectorRestoreArrayRead(vec, &x); CeedChk(ierr);
553   return CEED_ERROR_SUCCESS;
554 }
555 
556 /**
557   @brief Get the length of a CeedVector
558 
559   @param vec           CeedVector to retrieve length
560   @param[out] length   Variable to store length
561 
562   @return An error code: 0 - success, otherwise - failure
563 
564   @ref User
565 **/
566 int CeedVectorGetLength(CeedVector vec, CeedInt *length) {
567   *length = vec->length;
568   return CEED_ERROR_SUCCESS;
569 }
570 
571 /**
572   @brief Destroy a CeedVector
573 
574   @param vec   CeedVector to destroy
575 
576   @return An error code: 0 - success, otherwise - failure
577 
578   @ref User
579 **/
580 int CeedVectorDestroy(CeedVector *vec) {
581   int ierr;
582 
583   if (!*vec || --(*vec)->refcount > 0) return CEED_ERROR_SUCCESS;
584 
585   if (((*vec)->state % 2) == 1)
586     return CeedError((*vec)->ceed, CEED_ERROR_ACCESS,
587                      "Cannot destroy CeedVector, the writable access "
588                      "lock is in use");
589 
590   if ((*vec)->numreaders > 0)
591     return CeedError((*vec)->ceed, CEED_ERROR_ACCESS,
592                      "Cannot destroy CeedVector, a process has "
593                      "read access");
594 
595   if ((*vec)->Destroy) {
596     ierr = (*vec)->Destroy(*vec); CeedChk(ierr);
597   }
598 
599   ierr = CeedDestroy(&(*vec)->ceed); CeedChk(ierr);
600   ierr = CeedFree(vec); CeedChk(ierr);
601   return CEED_ERROR_SUCCESS;
602 }
603 
604 /// @}
605