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