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