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