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