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