xref: /libCEED/interface/ceed-vector.c (revision caee03026e6576cbf7a399c2fc51bb918c77f451)
1 // Copyright (c) 2017-2022, Lawrence Livermore National Security, LLC and other CEED contributors.
2 // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3 //
4 // SPDX-License-Identifier: BSD-2-Clause
5 //
6 // This file is part of CEED:  http://github.com/ceed
7 
8 #include <assert.h>
9 #include <ceed-impl.h>
10 #include <ceed/backend.h>
11 #include <ceed/ceed.h>
12 #include <math.h>
13 #include <stdint.h>
14 #include <stdio.h>
15 
16 /// @file
17 /// Implementation of public CeedVector interfaces
18 
19 /// @cond DOXYGEN_SKIP
20 static struct CeedVector_private ceed_vector_active;
21 static struct CeedVector_private ceed_vector_none;
22 /// @endcond
23 
24 /// @addtogroup CeedVectorUser
25 /// @{
26 
27 /// Indicate that vector will be provided as an explicit argument to
28 ///   CeedOperatorApply().
29 const CeedVector CEED_VECTOR_ACTIVE = &ceed_vector_active;
30 
31 /// Indicate that no vector is applicable (i.e., for @ref CEED_EVAL_WEIGHT).
32 const CeedVector CEED_VECTOR_NONE = &ceed_vector_none;
33 
34 /// @}
35 
36 /// ----------------------------------------------------------------------------
37 /// CeedVector Backend API
38 /// ----------------------------------------------------------------------------
39 /// @addtogroup CeedVectorBackend
40 /// @{
41 
42 /**
43   @brief Check for valid data in a CeedVector
44 
45   @param vec                   CeedVector to check validity
46   @param[out] has_valid_array  Variable to store validity
47 
48   @return An error code: 0 - success, otherwise - failure
49 
50   @ref Backend
51 **/
52 int CeedVectorHasValidArray(CeedVector vec, bool *has_valid_array) {
53   if (!vec->HasValidArray) {
54     // LCOV_EXCL_START
55     return CeedError(vec->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support HasValidArray");
56     // LCOV_EXCL_STOP
57   }
58 
59   CeedCall(vec->HasValidArray(vec, has_valid_array));
60 
61   return CEED_ERROR_SUCCESS;
62 }
63 
64 /**
65   @brief Check for borrowed array of a specific CeedMemType in a CeedVector
66 
67   @param vec                              CeedVector to check
68   @param mem_type                         Memory type to check
69   @param[out] has_borrowed_array_of_type  Variable to store result
70 
71   @return An error code: 0 - success, otherwise - failure
72 
73   @ref Backend
74 **/
75 int CeedVectorHasBorrowedArrayOfType(CeedVector vec, CeedMemType mem_type, bool *has_borrowed_array_of_type) {
76   if (!vec->HasBorrowedArrayOfType) {
77     // LCOV_EXCL_START
78     return CeedError(vec->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support HasBorrowedArrayOfType");
79     // LCOV_EXCL_STOP
80   }
81 
82   CeedCall(vec->HasBorrowedArrayOfType(vec, mem_type, has_borrowed_array_of_type));
83 
84   return CEED_ERROR_SUCCESS;
85 }
86 
87 /**
88   @brief Get the state of a CeedVector
89 
90   @param vec         CeedVector to retrieve state
91   @param[out] state  Variable to store state
92 
93   @return An error code: 0 - success, otherwise - failure
94 
95   @ref Backend
96 **/
97 int CeedVectorGetState(CeedVector vec, uint64_t *state) {
98   *state = vec->state;
99   return CEED_ERROR_SUCCESS;
100 }
101 
102 /**
103   @brief Add a reference to a CeedVector
104 
105   @param[out] vec  CeedVector to increment reference counter
106 
107   @return An error code: 0 - success, otherwise - failure
108 
109   @ref Backend
110 **/
111 int CeedVectorAddReference(CeedVector vec) {
112   vec->ref_count++;
113   return CEED_ERROR_SUCCESS;
114 }
115 
116 /**
117   @brief Get the backend data of a CeedVector
118 
119   @param vec        CeedVector to retrieve state
120   @param[out] data  Variable to store data
121 
122   @return An error code: 0 - success, otherwise - failure
123 
124   @ref Backend
125 **/
126 int CeedVectorGetData(CeedVector vec, void *data) {
127   *(void **)data = vec->data;
128   return CEED_ERROR_SUCCESS;
129 }
130 
131 /**
132   @brief Set the backend data of a CeedVector
133 
134   @param[out] vec  CeedVector to retrieve state
135   @param data      Data to set
136 
137   @return An error code: 0 - success, otherwise - failure
138 
139   @ref Backend
140 **/
141 int CeedVectorSetData(CeedVector vec, void *data) {
142   vec->data = data;
143   return CEED_ERROR_SUCCESS;
144 }
145 
146 /**
147   @brief Increment the reference counter for a CeedVector
148 
149   @param vec  CeedVector to increment the reference counter
150 
151   @return An error code: 0 - success, otherwise - failure
152 
153   @ref Backend
154 **/
155 int CeedVectorReference(CeedVector vec) {
156   vec->ref_count++;
157   return CEED_ERROR_SUCCESS;
158 }
159 
160 /// @}
161 
162 /// ----------------------------------------------------------------------------
163 /// CeedVector Public API
164 /// ----------------------------------------------------------------------------
165 /// @addtogroup CeedVectorUser
166 /// @{
167 
168 /**
169   @brief Create a CeedVector of the specified length (does not allocate memory)
170 
171   @param ceed      Ceed object where the CeedVector will be created
172   @param length    Length of vector
173   @param[out] vec  Address of the variable where the newly created
174                      CeedVector will be stored
175 
176   @return An error code: 0 - success, otherwise - failure
177 
178   @ref User
179 **/
180 int CeedVectorCreate(Ceed ceed, CeedSize length, CeedVector *vec) {
181   if (!ceed->VectorCreate) {
182     Ceed delegate;
183     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Vector"));
184 
185     if (!delegate) {
186       // LCOV_EXCL_START
187       return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support VectorCreate");
188       // LCOV_EXCL_STOP
189     }
190 
191     CeedCall(CeedVectorCreate(delegate, length, vec));
192     return CEED_ERROR_SUCCESS;
193   }
194 
195   CeedCall(CeedCalloc(1, vec));
196   (*vec)->ceed = ceed;
197   CeedCall(CeedReference(ceed));
198   (*vec)->ref_count = 1;
199   (*vec)->length    = length;
200   (*vec)->state     = 0;
201   CeedCall(ceed->VectorCreate(length, *vec));
202   return CEED_ERROR_SUCCESS;
203 }
204 
205 /**
206   @brief Copy the pointer to a CeedVector. Both pointers should
207            be destroyed with `CeedVectorDestroy()`;
208            Note: If `*vec_copy` is non-NULL, then it is assumed that
209            `*vec_copy` is a pointer to a CeedVector. This
210            CeedVector will be destroyed if `*vec_copy` is the only
211            reference to this CeedVector.
212 
213   @param vec            CeedVector to copy reference to
214   @param[out] vec_copy  Variable to store copied reference
215 
216   @return An error code: 0 - success, otherwise - failure
217 
218   @ref User
219 **/
220 int CeedVectorReferenceCopy(CeedVector vec, CeedVector *vec_copy) {
221   CeedCall(CeedVectorReference(vec));
222   CeedCall(CeedVectorDestroy(vec_copy));
223   *vec_copy = vec;
224   return CEED_ERROR_SUCCESS;
225 }
226 
227 /**
228   @brief Set the array used by a CeedVector, freeing any previously allocated
229            array if applicable. The backend may copy values to a different
230            memtype, such as during @ref CeedOperatorApply().
231            See also @ref CeedVectorSyncArray() and @ref CeedVectorTakeArray().
232 
233   @param vec        CeedVector
234   @param mem_type   Memory type of the array being passed
235   @param copy_mode  Copy mode for the array
236   @param array      Array to be used, or NULL with @ref CEED_COPY_VALUES to have the
237                       library allocate
238 
239   @return An error code: 0 - success, otherwise - failure
240 
241   @ref User
242 **/
243 int CeedVectorSetArray(CeedVector vec, CeedMemType mem_type, CeedCopyMode copy_mode, CeedScalar *array) {
244   if (!vec->SetArray) {
245     // LCOV_EXCL_START
246     return CeedError(vec->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support VectorSetArray");
247     // LCOV_EXCL_STOP
248   }
249   if (vec->state % 2 == 1) {
250     return CeedError(vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, the access lock is already in use");
251   }
252   if (vec->num_readers > 0) {
253     return CeedError(vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, a process has read access");
254   }
255 
256   CeedCall(vec->SetArray(vec, mem_type, copy_mode, array));
257   vec->state += 2;
258   return CEED_ERROR_SUCCESS;
259 }
260 
261 /**
262   @brief Set the CeedVector to a constant value
263 
264   @param vec        CeedVector
265   @param[in] value  Value to be used
266 
267   @return An error code: 0 - success, otherwise - failure
268 
269   @ref User
270 **/
271 int CeedVectorSetValue(CeedVector vec, CeedScalar value) {
272   if (vec->state % 2 == 1) {
273     // LCOV_EXCL_START
274     return CeedError(vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, the access lock is already in use");
275     // LCOV_EXCL_STOP
276   }
277   if (vec->num_readers > 0) {
278     // LCOV_EXCL_START
279     return CeedError(vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, a process has read access");
280     // LCOV_EXCL_STOP
281   }
282 
283   if (vec->SetValue) {
284     CeedCall(vec->SetValue(vec, value));
285   } else {
286     CeedScalar *array;
287     CeedCall(CeedVectorGetArrayWrite(vec, CEED_MEM_HOST, &array));
288     for (CeedInt i = 0; i < vec->length; i++) array[i] = value;
289     CeedCall(CeedVectorRestoreArray(vec, &array));
290   }
291   vec->state += 2;
292   return CEED_ERROR_SUCCESS;
293 }
294 
295 /**
296   @brief Sync the CeedVector to a specified memtype. This function is used to
297            force synchronization of arrays set with @ref CeedVectorSetArray().
298            If the requested memtype is already synchronized, this function
299            results in a no-op.
300 
301   @param vec       CeedVector
302   @param mem_type  Memtype to be synced
303 
304   @return An error code: 0 - success, otherwise - failure
305 
306   @ref User
307 **/
308 int CeedVectorSyncArray(CeedVector vec, CeedMemType mem_type) {
309   if (vec->state % 2 == 1) {
310     return CeedError(vec->ceed, CEED_ERROR_ACCESS, "Cannot sync CeedVector, the access lock is already in use");
311   }
312 
313   if (vec->SyncArray) {
314     CeedCall(vec->SyncArray(vec, mem_type));
315   } else {
316     const CeedScalar *array;
317     CeedCall(CeedVectorGetArrayRead(vec, mem_type, &array));
318     CeedCall(CeedVectorRestoreArrayRead(vec, &array));
319   }
320   return CEED_ERROR_SUCCESS;
321 }
322 
323 /**
324   @brief Take ownership of the CeedVector array set by @ref CeedVectorSetArray()
325            with @ref CEED_USE_POINTER and remove the array from the CeedVector.
326            The caller is responsible for managing and freeing the array.
327            This function will error if @ref CeedVectorSetArray() was not previously
328            called with @ref CEED_USE_POINTER for the corresponding mem_type.
329 
330   @param vec         CeedVector
331   @param mem_type    Memory type on which to take 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, or NULL if array pointer is
334                        not required
335 
336   @return An error code: 0 - success, otherwise - failure
337 
338   @ref User
339 **/
340 int CeedVectorTakeArray(CeedVector vec, CeedMemType mem_type, CeedScalar **array) {
341   if (vec->state % 2 == 1) {
342     // LCOV_EXCL_START
343     return CeedError(vec->ceed, CEED_ERROR_ACCESS, "Cannot take CeedVector array, the access lock is already in use");
344     // LCOV_EXCL_STOP
345   }
346   if (vec->num_readers > 0) {
347     // LCOV_EXCL_START
348     return CeedError(vec->ceed, CEED_ERROR_ACCESS, "Cannot take CeedVector array, a process has read access");
349     // LCOV_EXCL_STOP
350   }
351 
352   CeedScalar *temp_array = NULL;
353   if (vec->length > 0) {
354     bool has_borrowed_array_of_type = true;
355     CeedCall(CeedVectorHasBorrowedArrayOfType(vec, mem_type, &has_borrowed_array_of_type));
356     if (!has_borrowed_array_of_type) {
357       // LCOV_EXCL_START
358       return CeedError(vec->ceed, CEED_ERROR_BACKEND, "CeedVector has no borrowed %s array, must set array with CeedVectorSetArray",
359                        CeedMemTypes[mem_type]);
360       // LCOV_EXCL_STOP
361     }
362 
363     bool has_valid_array = true;
364     CeedCall(CeedVectorHasValidArray(vec, &has_valid_array));
365     if (!has_valid_array) {
366       // LCOV_EXCL_START
367       return CeedError(vec->ceed, CEED_ERROR_BACKEND,
368                        "CeedVector has no valid data to take, must set data with CeedVectorSetValue or CeedVectorSetArray");
369       // LCOV_EXCL_STOP
370     }
371 
372     CeedCall(vec->TakeArray(vec, mem_type, &temp_array));
373   }
374   if (array) (*array) = temp_array;
375   return CEED_ERROR_SUCCESS;
376 }
377 
378 /**
379   @brief Get read/write access to a CeedVector via the specified memory type.
380            Restore access with @ref CeedVectorRestoreArray().
381 
382   @param vec         CeedVector to access
383   @param mem_type    Memory type on which to access the array. If the backend
384                        uses a different memory type, this will perform a copy.
385   @param[out] array  Array on memory type mem_type
386 
387   @note The CeedVectorGetArray* and CeedVectorRestoreArray* functions provide
388     access to array pointers in the desired memory space. Pairing get/restore
389     allows the Vector to track access, thus knowing if norms or other
390     operations may need to be recomputed.
391 
392   @return An error code: 0 - success, otherwise - failure
393 
394   @ref User
395 **/
396 int CeedVectorGetArray(CeedVector vec, CeedMemType mem_type, CeedScalar **array) {
397   if (!vec->GetArray) {
398     // LCOV_EXCL_START
399     return CeedError(vec->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support GetArray");
400     // LCOV_EXCL_STOP
401   }
402   if (vec->state % 2 == 1) {
403     return CeedError(vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, the access lock is already in use");
404   }
405   if (vec->num_readers > 0) {
406     return CeedError(vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, a process has read access");
407   }
408 
409   bool has_valid_array = true;
410   CeedCall(CeedVectorHasValidArray(vec, &has_valid_array));
411   if (!has_valid_array) {
412     // LCOV_EXCL_START
413     return CeedError(vec->ceed, CEED_ERROR_BACKEND,
414                      "CeedVector has no valid data to read, must set data with CeedVectorSetValue or CeedVectorSetArray");
415     // LCOV_EXCL_STOP
416   }
417 
418   CeedCall(vec->GetArray(vec, mem_type, array));
419   vec->state++;
420   return CEED_ERROR_SUCCESS;
421 }
422 
423 /**
424   @brief Get read-only access to a CeedVector via the specified memory type.
425            Restore access with @ref CeedVectorRestoreArrayRead().
426 
427   @param vec         CeedVector to access
428   @param mem_type    Memory type on which to access the array.  If the backend
429                        uses a different memory type, this will perform a copy
430                        (possibly cached).
431   @param[out] array  Array on memory type mem_type
432 
433   @return An error code: 0 - success, otherwise - failure
434 
435   @ref User
436 **/
437 int CeedVectorGetArrayRead(CeedVector vec, CeedMemType mem_type, const CeedScalar **array) {
438   if (!vec->GetArrayRead) {
439     // LCOV_EXCL_START
440     return CeedError(vec->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support GetArrayRead");
441     // LCOV_EXCL_STOP
442   }
443   if (vec->state % 2 == 1) {
444     return CeedError(vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector read-only array access, the access lock is already in use");
445   }
446 
447   if (vec->length > 0) {
448     bool has_valid_array = true;
449     CeedCall(CeedVectorHasValidArray(vec, &has_valid_array));
450     if (!has_valid_array) {
451       // LCOV_EXCL_START
452       return CeedError(vec->ceed, CEED_ERROR_BACKEND,
453                        "CeedVector has no valid data to read, must set data with CeedVectorSetValue or CeedVectorSetArray");
454       // LCOV_EXCL_STOP
455     }
456 
457     CeedCall(vec->GetArrayRead(vec, mem_type, array));
458   } else {
459     *array = NULL;
460   }
461   vec->num_readers++;
462   return CEED_ERROR_SUCCESS;
463 }
464 
465 /**
466   @brief Get write access to a CeedVector via the specified memory type.
467            Restore access with @ref CeedVectorRestoreArray(). All old
468            values should be assumed to be invalid.
469 
470   @param vec         CeedVector to access
471   @param mem_type    Memory type on which to access the array.
472   @param[out] array  Array on memory type mem_type
473 
474   @return An error code: 0 - success, otherwise - failure
475 
476   @ref User
477 **/
478 int CeedVectorGetArrayWrite(CeedVector vec, CeedMemType mem_type, CeedScalar **array) {
479   if (!vec->GetArrayWrite) {
480     // LCOV_EXCL_START
481     return CeedError(vec->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support GetArrayWrite");
482     // LCOV_EXCL_STOP
483   }
484   if (vec->state % 2 == 1) {
485     // LCOV_EXCL_START
486     return CeedError(vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, the access lock is already in use");
487     // LCOV_EXCL_STOP
488   }
489   if (vec->num_readers > 0) {
490     // LCOV_EXCL_START
491     return CeedError(vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, a process has read access");
492     // LCOV_EXCL_STOP
493   }
494 
495   CeedCall(vec->GetArrayWrite(vec, mem_type, array));
496   vec->state++;
497   return CEED_ERROR_SUCCESS;
498 }
499 
500 /**
501   @brief Restore an array obtained using @ref CeedVectorGetArray()
502            or @ref CeedVectorGetArrayWrite()
503 
504   @param vec    CeedVector to restore
505   @param array  Array of vector data
506 
507   @return An error code: 0 - success, otherwise - failure
508 
509   @ref User
510 **/
511 int CeedVectorRestoreArray(CeedVector vec, CeedScalar **array) {
512   if (vec->state % 2 != 1) {
513     return CeedError(vec->ceed, CEED_ERROR_ACCESS, "Cannot restore CeedVector array access, access was not granted");
514   }
515   if (vec->RestoreArray) CeedCall(vec->RestoreArray(vec));
516   *array = NULL;
517   vec->state++;
518   return CEED_ERROR_SUCCESS;
519 }
520 
521 /**
522   @brief Restore an array obtained using @ref CeedVectorGetArrayRead()
523 
524   @param vec    CeedVector to restore
525   @param array  Array of vector data
526 
527   @return An error code: 0 - success, otherwise - failure
528 
529   @ref User
530 **/
531 int CeedVectorRestoreArrayRead(CeedVector vec, const CeedScalar **array) {
532   if (vec->num_readers == 0) {
533     // LCOV_EXCL_START
534     return CeedError(vec->ceed, CEED_ERROR_ACCESS, "Cannot restore CeedVector array read access, access was not granted");
535     // LCOV_EXCL_STOP
536   }
537 
538   vec->num_readers--;
539   if (vec->num_readers == 0 && vec->RestoreArrayRead) CeedCall(vec->RestoreArrayRead(vec));
540   *array = NULL;
541 
542   return CEED_ERROR_SUCCESS;
543 }
544 
545 /**
546   @brief Get the norm of a CeedVector.
547 
548   Note: This operation is local to the CeedVector. This function will likely
549           not provide the desired results for the norm of the libCEED portion
550           of a parallel vector or a CeedVector with duplicated or hanging nodes.
551 
552   @param vec        CeedVector to retrieve maximum value
553   @param norm_type  Norm type @ref CEED_NORM_1, @ref CEED_NORM_2, or @ref CEED_NORM_MAX
554   @param[out] norm  Variable to store norm value
555 
556   @return An error code: 0 - success, otherwise - failure
557 
558   @ref User
559 **/
560 int CeedVectorNorm(CeedVector vec, CeedNormType norm_type, CeedScalar *norm) {
561   bool has_valid_array = true;
562   CeedCall(CeedVectorHasValidArray(vec, &has_valid_array));
563   if (!has_valid_array) {
564     // LCOV_EXCL_START
565     return CeedError(vec->ceed, CEED_ERROR_BACKEND,
566                      "CeedVector has no valid data to compute norm, must set data with CeedVectorSetValue or CeedVectorSetArray");
567     // LCOV_EXCL_STOP
568   }
569 
570   // Backend impl for GPU, if added
571   if (vec->Norm) {
572     CeedCall(vec->Norm(vec, norm_type, norm));
573     return CEED_ERROR_SUCCESS;
574   }
575 
576   const CeedScalar *array;
577   CeedCall(CeedVectorGetArrayRead(vec, CEED_MEM_HOST, &array));
578 
579   *norm = 0.;
580   switch (norm_type) {
581     case CEED_NORM_1:
582       for (CeedInt i = 0; i < vec->length; i++) {
583         *norm += fabs(array[i]);
584       }
585       break;
586     case CEED_NORM_2:
587       for (CeedInt i = 0; i < vec->length; i++) {
588         *norm += fabs(array[i]) * fabs(array[i]);
589       }
590       break;
591     case CEED_NORM_MAX:
592       for (CeedInt i = 0; i < vec->length; i++) {
593         const CeedScalar abs_v_i = fabs(array[i]);
594         *norm                    = *norm > abs_v_i ? *norm : abs_v_i;
595       }
596   }
597   if (norm_type == CEED_NORM_2) *norm = sqrt(*norm);
598 
599   CeedCall(CeedVectorRestoreArrayRead(vec, &array));
600   return CEED_ERROR_SUCCESS;
601 }
602 
603 /**
604   @brief Compute x = alpha x
605 
606   @param[in,out] x  vector for scaling
607   @param[in] alpha  scaling factor
608 
609   @return An error code: 0 - success, otherwise - failure
610 
611   @ref User
612 **/
613 int CeedVectorScale(CeedVector x, CeedScalar alpha) {
614   CeedScalar *x_array = NULL;
615   CeedSize    n_x;
616 
617   bool has_valid_array = true;
618   CeedCall(CeedVectorHasValidArray(x, &has_valid_array));
619   if (!has_valid_array) {
620     // LCOV_EXCL_START
621     return CeedError(x->ceed, CEED_ERROR_BACKEND,
622                      "CeedVector has no valid data to scale, must set data with CeedVectorSetValue or CeedVectorSetArray");
623     // LCOV_EXCL_STOP
624   }
625 
626   CeedCall(CeedVectorGetLength(x, &n_x));
627 
628   // Backend implementation
629   if (x->Scale) return x->Scale(x, alpha);
630 
631   // Default implementation
632   CeedCall(CeedVectorGetArrayWrite(x, CEED_MEM_HOST, &x_array));
633   for (CeedInt i = 0; i < n_x; i++) x_array[i] *= alpha;
634   CeedCall(CeedVectorRestoreArray(x, &x_array));
635 
636   return CEED_ERROR_SUCCESS;
637 }
638 
639 /**
640   @brief Compute y = alpha x + y
641 
642   @param[in,out] y  target vector for sum
643   @param[in] alpha  scaling factor
644   @param[in] x      second vector, must be different than y
645 
646   @return An error code: 0 - success, otherwise - failure
647 
648   @ref User
649 **/
650 int CeedVectorAXPY(CeedVector y, CeedScalar alpha, CeedVector x) {
651   CeedScalar       *y_array = NULL;
652   CeedScalar const *x_array = NULL;
653   CeedSize          n_x, n_y;
654 
655   CeedCall(CeedVectorGetLength(y, &n_y));
656   CeedCall(CeedVectorGetLength(x, &n_x));
657   if (n_x != n_y) {
658     // LCOV_EXCL_START
659     return CeedError(y->ceed, CEED_ERROR_UNSUPPORTED, "Cannot add vector of different lengths");
660     // LCOV_EXCL_STOP
661   }
662   if (x == y) {
663     // LCOV_EXCL_START
664     return CeedError(y->ceed, CEED_ERROR_UNSUPPORTED, "Cannot use same vector for x and y in CeedVectorAXPY");
665     // LCOV_EXCL_STOP
666   }
667 
668   bool has_valid_array_x = true, has_valid_array_y = true;
669   CeedCall(CeedVectorHasValidArray(x, &has_valid_array_x));
670   if (!has_valid_array_x) {
671     // LCOV_EXCL_START
672     return CeedError(x->ceed, CEED_ERROR_BACKEND, "CeedVector x has no valid data, must set data with CeedVectorSetValue or CeedVectorSetArray");
673     // LCOV_EXCL_STOP
674   }
675   CeedCall(CeedVectorHasValidArray(y, &has_valid_array_y));
676   if (!has_valid_array_y) {
677     // LCOV_EXCL_START
678     return CeedError(y->ceed, CEED_ERROR_BACKEND, "CeedVector y has no valid data, must set data with CeedVectorSetValue or CeedVectorSetArray");
679     // LCOV_EXCL_STOP
680   }
681 
682   Ceed ceed_parent_x, ceed_parent_y;
683   CeedCall(CeedGetParent(x->ceed, &ceed_parent_x));
684   CeedCall(CeedGetParent(y->ceed, &ceed_parent_y));
685   if (ceed_parent_x != ceed_parent_y) {
686     // LCOV_EXCL_START
687     return CeedError(y->ceed, CEED_ERROR_INCOMPATIBLE, "Vectors x and y must be created by the same Ceed context");
688     // LCOV_EXCL_STOP
689   }
690 
691   // Backend implementation
692   if (y->AXPY) {
693     CeedCall(y->AXPY(y, alpha, x));
694     return CEED_ERROR_SUCCESS;
695   }
696 
697   // Default implementation
698   CeedCall(CeedVectorGetArrayWrite(y, CEED_MEM_HOST, &y_array));
699   CeedCall(CeedVectorGetArrayRead(x, CEED_MEM_HOST, &x_array));
700 
701   assert(x_array);
702   assert(y_array);
703 
704   for (CeedInt i = 0; i < n_y; i++) y_array[i] += alpha * x_array[i];
705 
706   CeedCall(CeedVectorRestoreArray(y, &y_array));
707   CeedCall(CeedVectorRestoreArrayRead(x, &x_array));
708 
709   return CEED_ERROR_SUCCESS;
710 }
711 
712 /**
713   @brief Compute the pointwise multiplication w = x .* y. Any
714            subset of x, y, and w may be the same vector.
715 
716   @param[out] w  target vector for the product
717   @param[in] x   first vector for product
718   @param[in] y   second vector for the product
719 
720   @return An error code: 0 - success, otherwise - failure
721 
722   @ ref User
723 **/
724 int CeedVectorPointwiseMult(CeedVector w, CeedVector x, CeedVector y) {
725   CeedScalar       *w_array = NULL;
726   CeedScalar const *x_array = NULL, *y_array = NULL;
727   CeedSize          n_w, n_x, n_y;
728 
729   CeedCall(CeedVectorGetLength(w, &n_w));
730   CeedCall(CeedVectorGetLength(x, &n_x));
731   CeedCall(CeedVectorGetLength(y, &n_y));
732   if (n_w != n_x || n_w != n_y) {
733     // LCOV_EXCL_START
734     return CeedError(w->ceed, CEED_ERROR_UNSUPPORTED, "Cannot multiply vectors of different lengths");
735     // LCOV_EXCL_STOP
736   }
737 
738   Ceed ceed_parent_w, ceed_parent_x, ceed_parent_y;
739   CeedCall(CeedGetParent(w->ceed, &ceed_parent_w));
740   CeedCall(CeedGetParent(x->ceed, &ceed_parent_x));
741   CeedCall(CeedGetParent(y->ceed, &ceed_parent_y));
742   if ((ceed_parent_w != ceed_parent_x) || (ceed_parent_w != ceed_parent_y)) {
743     // LCOV_EXCL_START
744     return CeedError(w->ceed, CEED_ERROR_INCOMPATIBLE, "Vectors w, x, and y must be created by the same Ceed context");
745     // LCOV_EXCL_STOP
746   }
747 
748   bool has_valid_array_x = true, has_valid_array_y = true;
749   CeedCall(CeedVectorHasValidArray(x, &has_valid_array_x));
750   if (!has_valid_array_x) {
751     // LCOV_EXCL_START
752     return CeedError(x->ceed, CEED_ERROR_BACKEND, "CeedVector x has no valid data, must set data with CeedVectorSetValue or CeedVectorSetArray");
753     // LCOV_EXCL_STOP
754   }
755   CeedCall(CeedVectorHasValidArray(y, &has_valid_array_y));
756   if (!has_valid_array_y) {
757     // LCOV_EXCL_START
758     return CeedError(y->ceed, CEED_ERROR_BACKEND, "CeedVector y has no valid data, must set data with CeedVectorSetValue or CeedVectorSetArray");
759     // LCOV_EXCL_STOP
760   }
761 
762   // Backend implementation
763   if (w->PointwiseMult) {
764     CeedCall(w->PointwiseMult(w, x, y));
765     return CEED_ERROR_SUCCESS;
766   }
767 
768   // Default implementation
769   CeedCall(CeedVectorGetArrayWrite(w, CEED_MEM_HOST, &w_array));
770   if (x != w) {
771     CeedCall(CeedVectorGetArrayRead(x, CEED_MEM_HOST, &x_array));
772   } else {
773     x_array = w_array;
774   }
775 
776   if (y != w && y != x) {
777     CeedCall(CeedVectorGetArrayRead(y, CEED_MEM_HOST, &y_array));
778   } else if (y != x) {
779     y_array = w_array;
780   } else {
781     y_array = x_array;
782   }
783 
784   assert(w_array);
785   assert(x_array);
786   assert(y_array);
787 
788   for (CeedInt i = 0; i < n_w; i++) w_array[i] = x_array[i] * y_array[i];
789 
790   if (y != w && y != x) CeedCall(CeedVectorRestoreArrayRead(y, &y_array));
791   if (x != w) CeedCall(CeedVectorRestoreArrayRead(x, &x_array));
792   CeedCall(CeedVectorRestoreArray(w, &w_array));
793   return CEED_ERROR_SUCCESS;
794 }
795 
796 /**
797   @brief Take the reciprocal of a CeedVector.
798 
799   @param vec  CeedVector to take reciprocal
800 
801   @return An error code: 0 - success, otherwise - failure
802 
803   @ref User
804 **/
805 int CeedVectorReciprocal(CeedVector vec) {
806   bool has_valid_array = true;
807   CeedCall(CeedVectorHasValidArray(vec, &has_valid_array));
808 
809   if (!has_valid_array) {
810     // LCOV_EXCL_START
811     return CeedError(vec->ceed, CEED_ERROR_BACKEND,
812                      "CeedVector has no valid data to compute reciprocal, must set data with CeedVectorSetValue or CeedVectorSetArray");
813     // LCOV_EXCL_STOP
814   }
815 
816   // Check if vector data set
817   if (!vec->state) {
818     // LCOV_EXCL_START
819     return CeedError(vec->ceed, CEED_ERROR_INCOMPLETE, "CeedVector must have data set to take reciprocal");
820     // LCOV_EXCL_STOP
821   }
822 
823   // Backend impl for GPU, if added
824   if (vec->Reciprocal) {
825     CeedCall(vec->Reciprocal(vec));
826     return CEED_ERROR_SUCCESS;
827   }
828 
829   CeedSize len;
830   CeedCall(CeedVectorGetLength(vec, &len));
831   CeedScalar *array;
832   CeedCall(CeedVectorGetArrayWrite(vec, CEED_MEM_HOST, &array));
833   for (CeedInt i = 0; i < len; i++) {
834     if (fabs(array[i]) > CEED_EPSILON) array[i] = 1. / array[i];
835   }
836 
837   CeedCall(CeedVectorRestoreArray(vec, &array));
838   return CEED_ERROR_SUCCESS;
839 }
840 
841 /**
842   @brief View a CeedVector
843 
844   @param[in] vec     CeedVector to view
845   @param[in] fp_fmt  Printing format
846   @param[in] stream  Filestream to write to
847 
848   @return An error code: 0 - success, otherwise - failure
849 
850   @ref User
851 **/
852 int CeedVectorView(CeedVector vec, const char *fp_fmt, FILE *stream) {
853   const CeedScalar *x;
854   CeedCall(CeedVectorGetArrayRead(vec, CEED_MEM_HOST, &x));
855 
856   char fmt[1024];
857   fprintf(stream, "CeedVector length %ld\n", (long)vec->length);
858   snprintf(fmt, sizeof fmt, "  %s\n", fp_fmt ? fp_fmt : "%g");
859   for (CeedInt i = 0; i < vec->length; i++) fprintf(stream, fmt, x[i]);
860 
861   CeedCall(CeedVectorRestoreArrayRead(vec, &x));
862   return CEED_ERROR_SUCCESS;
863 }
864 
865 /**
866   @brief Get the Ceed associated with a CeedVector
867 
868   @param vec        CeedVector to retrieve state
869   @param[out] ceed  Variable to store ceed
870 
871   @return An error code: 0 - success, otherwise - failure
872 
873   @ref Advanced
874 **/
875 int CeedVectorGetCeed(CeedVector vec, Ceed *ceed) {
876   *ceed = vec->ceed;
877   return CEED_ERROR_SUCCESS;
878 }
879 
880 /**
881   @brief Get the length of a CeedVector
882 
883   @param vec          CeedVector to retrieve length
884   @param[out] length  Variable to store length
885 
886   @return An error code: 0 - success, otherwise - failure
887 
888   @ref User
889 **/
890 int CeedVectorGetLength(CeedVector vec, CeedSize *length) {
891   *length = vec->length;
892   return CEED_ERROR_SUCCESS;
893 }
894 
895 /**
896   @brief Destroy a CeedVector
897 
898   @param vec  CeedVector to destroy
899 
900   @return An error code: 0 - success, otherwise - failure
901 
902   @ref User
903 **/
904 int CeedVectorDestroy(CeedVector *vec) {
905   if (!*vec || --(*vec)->ref_count > 0) return CEED_ERROR_SUCCESS;
906 
907   if (((*vec)->state % 2) == 1) {
908     return CeedError((*vec)->ceed, CEED_ERROR_ACCESS, "Cannot destroy CeedVector, the writable access lock is in use");
909   }
910   if ((*vec)->num_readers > 0) {
911     // LCOV_EXCL_START
912     return CeedError((*vec)->ceed, CEED_ERROR_ACCESS, "Cannot destroy CeedVector, a process has read access");
913     // LCOV_EXCL_STOP
914   }
915 
916   if ((*vec)->Destroy) CeedCall((*vec)->Destroy(*vec));
917 
918   CeedCall(CeedDestroy(&(*vec)->ceed));
919   CeedCall(CeedFree(vec));
920   return CEED_ERROR_SUCCESS;
921 }
922 
923 /// @}
924