1 /* -----------------------------------------------------------------
2  * Programmer(s): Daniel R. Reynolds @ SMU
3  * -----------------------------------------------------------------
4  * SUNDIALS Copyright Start
5  * Copyright (c) 2002-2020, Lawrence Livermore National Security
6  * and Southern Methodist University.
7  * All rights reserved.
8  *
9  * See the top-level LICENSE and NOTICE files for details.
10  *
11  * SPDX-License-Identifier: BSD-3-Clause
12  * SUNDIALS Copyright End
13  * -----------------------------------------------------------------
14  * This is the implementation file for the ManyVector implementation
15  * of the NVECTOR package.
16  * -----------------------------------------------------------------*/
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <sundials/sundials_math.h>
21 #if SUNDIALS_MPI_ENABLED
22 #include <nvector/nvector_mpimanyvector.h>
23 #else
24 #include <nvector/nvector_manyvector.h>
25 #endif
26 
27 /* Macro to handle separate MPI-aware/unaware installations */
28 #if SUNDIALS_MPI_ENABLED
29 #define MVAPPEND(fun) fun##_MPIManyVector
30 #else
31 #define MVAPPEND(fun) fun##_ManyVector
32 #endif
33 
34 #define ZERO RCONST(0.0)
35 #define ONE  RCONST(1.0)
36 
37 /* -----------------------------------------------------------------
38    ManyVector content accessor macros
39    -----------------------------------------------------------------*/
40 #if SUNDIALS_MPI_ENABLED
41 #define MANYVECTOR_CONTENT(v)     ( (N_VectorContent_MPIManyVector)(v->content) )
42 #define MANYVECTOR_COMM(v)        ( MANYVECTOR_CONTENT(v)->comm )
43 #else
44 #define MANYVECTOR_CONTENT(v)     ( (N_VectorContent_ManyVector)(v->content) )
45 #endif
46 #define MANYVECTOR_NUM_SUBVECS(v) ( MANYVECTOR_CONTENT(v)->num_subvectors )
47 #define MANYVECTOR_GLOBLENGTH(v)  ( MANYVECTOR_CONTENT(v)->global_length )
48 #define MANYVECTOR_SUBVECS(v)     ( MANYVECTOR_CONTENT(v)->subvec_array )
49 #define MANYVECTOR_SUBVEC(v,i)    ( MANYVECTOR_SUBVECS(v)[i] )
50 #define MANYVECTOR_OWN_DATA(v)    ( MANYVECTOR_CONTENT(v)->own_data )
51 
52 /* -----------------------------------------------------------------
53    Prototypes of utility routines
54    -----------------------------------------------------------------*/
55 static N_Vector ManyVectorClone(N_Vector w, booleantype cloneempty);
56 #if SUNDIALS_MPI_ENABLED
57 static int SubvectorMPIRank(N_Vector w);
58 #endif
59 
60 /* -----------------------------------------------------------------
61    ManyVector API routines
62    -----------------------------------------------------------------*/
63 
64 #if SUNDIALS_MPI_ENABLED
65 
66 /* This function creates an MPIManyVector from a set of existing
67    N_Vector objects, along with a user-created MPI (inter/intra)communicator
68    that couples all subvectors together. */
N_VMake_MPIManyVector(MPI_Comm comm,sunindextype num_subvectors,N_Vector * vec_array)69 N_Vector N_VMake_MPIManyVector(MPI_Comm comm, sunindextype num_subvectors,
70                                N_Vector *vec_array)
71 {
72   N_Vector v;
73   N_VectorContent_MPIManyVector content;
74   sunindextype i, local_length;
75   int rank, retval;
76 
77   /* Check that input N_Vectors are non-NULL */
78   if (vec_array == NULL)  return(NULL);
79   for (i=0; i<num_subvectors; i++)
80     if (vec_array[i] == NULL)  return(NULL);
81 
82   /* Create vector */
83   v = NULL;
84   v = N_VNewEmpty();
85   if (v == NULL) return(NULL);
86 
87   /* Attach operations */
88 
89   /* constructors, destructors, and utility operations */
90   v->ops->nvgetvectorid     = N_VGetVectorID_MPIManyVector;
91   v->ops->nvcloneempty      = N_VCloneEmpty_MPIManyVector;
92   v->ops->nvclone           = N_VClone_MPIManyVector;
93   v->ops->nvdestroy         = N_VDestroy_MPIManyVector;
94   v->ops->nvspace           = N_VSpace_MPIManyVector;
95   v->ops->nvgetcommunicator = N_VGetCommunicator_MPIManyVector;
96   v->ops->nvgetlength       = N_VGetLength_MPIManyVector;
97 
98   /* standard vector operations */
99   v->ops->nvlinearsum       = N_VLinearSum_MPIManyVector;
100   v->ops->nvconst           = N_VConst_MPIManyVector;
101   v->ops->nvprod            = N_VProd_MPIManyVector;
102   v->ops->nvdiv             = N_VDiv_MPIManyVector;
103   v->ops->nvscale           = N_VScale_MPIManyVector;
104   v->ops->nvabs             = N_VAbs_MPIManyVector;
105   v->ops->nvinv             = N_VInv_MPIManyVector;
106   v->ops->nvaddconst        = N_VAddConst_MPIManyVector;
107   v->ops->nvdotprod         = N_VDotProd_MPIManyVector;
108   v->ops->nvmaxnorm         = N_VMaxNorm_MPIManyVector;
109   v->ops->nvwrmsnorm        = N_VWrmsNorm_MPIManyVector;
110   v->ops->nvwrmsnormmask    = N_VWrmsNormMask_MPIManyVector;
111   v->ops->nvmin             = N_VMin_MPIManyVector;
112   v->ops->nvwl2norm         = N_VWL2Norm_MPIManyVector;
113   v->ops->nvl1norm          = N_VL1Norm_MPIManyVector;
114   v->ops->nvcompare         = N_VCompare_MPIManyVector;
115   v->ops->nvinvtest         = N_VInvTest_MPIManyVector;
116   v->ops->nvconstrmask      = N_VConstrMask_MPIManyVector;
117   v->ops->nvminquotient     = N_VMinQuotient_MPIManyVector;
118 
119   /* fused vector operations */
120   v->ops->nvlinearcombination = N_VLinearCombination_MPIManyVector;
121   v->ops->nvscaleaddmulti     = N_VScaleAddMulti_MPIManyVector;
122   v->ops->nvdotprodmulti      = N_VDotProdMulti_MPIManyVector;
123 
124   /* vector array operations */
125   v->ops->nvwrmsnormvectorarray     = N_VWrmsNormVectorArray_MPIManyVector;
126   v->ops->nvwrmsnormmaskvectorarray = N_VWrmsNormMaskVectorArray_MPIManyVector;
127 
128   /* local reduction operations */
129   v->ops->nvdotprodlocal     = N_VDotProdLocal_MPIManyVector;
130   v->ops->nvmaxnormlocal     = N_VMaxNormLocal_MPIManyVector;
131   v->ops->nvminlocal         = N_VMinLocal_MPIManyVector;
132   v->ops->nvl1normlocal      = N_VL1NormLocal_MPIManyVector;
133   v->ops->nvinvtestlocal     = N_VInvTestLocal_MPIManyVector;
134   v->ops->nvconstrmasklocal  = N_VConstrMaskLocal_MPIManyVector;
135   v->ops->nvminquotientlocal = N_VMinQuotientLocal_MPIManyVector;
136   v->ops->nvwsqrsumlocal     = N_VWSqrSumLocal_MPIManyVector;
137   v->ops->nvwsqrsummasklocal = N_VWSqrSumMaskLocal_MPIManyVector;
138 
139   /* Create content */
140   content = NULL;
141   content = (N_VectorContent_MPIManyVector) malloc(sizeof *content);
142   if (content == NULL) { N_VDestroy(v); return(NULL); }
143 
144   /* Attach content */
145   v->content = content;
146 
147   /* Attach content components */
148 
149   /* set scalar content entries, and allocate/set subvector array */
150   content->comm           = MPI_COMM_NULL;
151   content->num_subvectors = num_subvectors;
152   content->own_data       = SUNFALSE;
153   content->subvec_array   = NULL;
154   content->subvec_array   = (N_Vector *) malloc(num_subvectors * sizeof(N_Vector));
155   if (content->subvec_array == NULL) { N_VDestroy(v); return(NULL); }
156   for (i=0; i<num_subvectors; i++)
157     content->subvec_array[i] = vec_array[i];
158 
159   /* duplicate input communicator (if non-NULL) */
160   if (comm != MPI_COMM_NULL) {
161     retval = MPI_Comm_dup(comm, &(content->comm));
162     if (retval != MPI_SUCCESS) { N_VDestroy(v); return(NULL); }
163   }
164 
165   /* Determine overall MPIManyVector length: sum contributions from all
166      subvectors where this rank is the root, then perform reduction */
167   local_length = 0;
168   for (i=0; i<num_subvectors; i++) {
169 
170     /* determine rank of this task in subvector communicator
171        (serial vectors default to rank=0) */
172     rank = SubvectorMPIRank(vec_array[i]);
173     if (rank < 0) { N_VDestroy(v); return(NULL); }
174 
175     /* accumulate contribution from root tasks */
176     if (vec_array[i]->ops->nvgetlength) {
177       if (rank == 0) local_length += N_VGetLength(vec_array[i]);
178     } else {
179       N_VDestroy(v);
180       return(NULL);
181     }
182   }
183   if (content->comm != MPI_COMM_NULL) {
184     retval = MPI_Allreduce(&local_length, &(content->global_length), 1,
185                            MPI_SUNINDEXTYPE, MPI_SUM, content->comm);
186     if (retval != MPI_SUCCESS) { N_VDestroy(v); return(NULL); }
187   } else {
188     content->global_length = local_length;
189   }
190 
191   return(v);
192 }
193 #endif
194 
195 
196 #if SUNDIALS_MPI_ENABLED
197 /* This function creates an MPIManyVector from a set of existing
198    N_Vector objects, under the requirement that all MPI-aware
199    sub-vectors use the same MPI communicator (this is verified
200    internally).  If no sub-vector is MPI-aware, then this may be
201    used to describe data partitioning within a single node, and
202    a NULL communicator will be created. */
N_VNew_MPIManyVector(sunindextype num_subvectors,N_Vector * vec_array)203 N_Vector N_VNew_MPIManyVector(sunindextype num_subvectors,
204                               N_Vector *vec_array)
205 {
206   sunindextype i;
207   booleantype nocommfound;
208   void* tmpcomm;
209   MPI_Comm comm, *vcomm;
210   int retval, comparison;
211   N_Vector v;
212 
213   /* Check that all subvectors have identical MPI communicators (if present) */
214   nocommfound = SUNTRUE;
215   comm = MPI_COMM_NULL;
216   for (i=0; i<num_subvectors; i++) {
217 
218     /* access MPI communicator for subvector i (vcomm);
219        if none is present then continue to next subvector */
220     tmpcomm = N_VGetCommunicator(vec_array[i]);
221     if (tmpcomm == NULL)  continue;
222     vcomm = (MPI_Comm *) tmpcomm;
223 
224     /* if this is the first communicator, create a copy */
225     if (nocommfound) {
226 
227       /* set comm to duplicate this first subvector communicator */
228       retval = MPI_Comm_dup(*vcomm, &comm);
229       if (retval != MPI_SUCCESS)  return(NULL);
230       nocommfound = SUNFALSE;
231 
232     /* otherwise, verify that vcomm matches stored comm */
233     } else {
234 
235       retval = MPI_Comm_compare(*vcomm, comm, &comparison);
236       if ((comparison != MPI_IDENT) && (comparison != MPI_CONGRUENT))
237         return(NULL);
238 
239     }
240   }
241 
242   /* Create vector using "Make" routine and shared communicator (if non-NULL) */
243   v = N_VMake_MPIManyVector(comm, num_subvectors, vec_array);
244   if (comm != MPI_COMM_NULL)  MPI_Comm_free(&comm);
245   return(v);
246 }
247 #else
248 /* This function creates a ManyVector from a set of existing
249    N_Vector objects.  ManyVector objects created with this constructor
250    may be used to describe data partitioning within a single node. */
N_VNew_ManyVector(sunindextype num_subvectors,N_Vector * vec_array)251 N_Vector N_VNew_ManyVector(sunindextype num_subvectors,
252                            N_Vector *vec_array)
253 {
254   N_Vector v;
255   N_VectorContent_ManyVector content;
256   sunindextype i;
257 
258   /* Check that input N_Vectors are non-NULL */
259   if (vec_array == NULL)  return(NULL);
260   for (i=0; i<num_subvectors; i++)
261     if (vec_array[i] == NULL)  return(NULL);
262 
263   /* Create vector */
264   v = NULL;
265   v = N_VNewEmpty();
266   if (v == NULL) return(NULL);
267 
268   /* Attach operations */
269 
270   /* constructors, destructors, and utility operations */
271   v->ops->nvgetvectorid     = N_VGetVectorID_ManyVector;
272   v->ops->nvcloneempty      = N_VCloneEmpty_ManyVector;
273   v->ops->nvclone           = N_VClone_ManyVector;
274   v->ops->nvdestroy         = N_VDestroy_ManyVector;
275   v->ops->nvspace           = N_VSpace_ManyVector;
276   v->ops->nvgetlength       = N_VGetLength_ManyVector;
277 
278   /* standard vector operations */
279   v->ops->nvlinearsum       = N_VLinearSum_ManyVector;
280   v->ops->nvconst           = N_VConst_ManyVector;
281   v->ops->nvprod            = N_VProd_ManyVector;
282   v->ops->nvdiv             = N_VDiv_ManyVector;
283   v->ops->nvscale           = N_VScale_ManyVector;
284   v->ops->nvabs             = N_VAbs_ManyVector;
285   v->ops->nvinv             = N_VInv_ManyVector;
286   v->ops->nvaddconst        = N_VAddConst_ManyVector;
287   v->ops->nvdotprod         = N_VDotProdLocal_ManyVector;
288   v->ops->nvmaxnorm         = N_VMaxNormLocal_ManyVector;
289   v->ops->nvwrmsnorm        = N_VWrmsNorm_ManyVector;
290   v->ops->nvwrmsnormmask    = N_VWrmsNormMask_ManyVector;
291   v->ops->nvmin             = N_VMinLocal_ManyVector;
292   v->ops->nvwl2norm         = N_VWL2Norm_ManyVector;
293   v->ops->nvl1norm          = N_VL1NormLocal_ManyVector;
294   v->ops->nvcompare         = N_VCompare_ManyVector;
295   v->ops->nvinvtest         = N_VInvTestLocal_ManyVector;
296   v->ops->nvconstrmask      = N_VConstrMaskLocal_ManyVector;
297   v->ops->nvminquotient     = N_VMinQuotientLocal_ManyVector;
298 
299   /* fused vector operations */
300   v->ops->nvlinearcombination = N_VLinearCombination_ManyVector;
301   v->ops->nvscaleaddmulti     = N_VScaleAddMulti_ManyVector;
302   v->ops->nvdotprodmulti      = N_VDotProdMulti_ManyVector;
303 
304   /* vector array operations */
305   v->ops->nvwrmsnormvectorarray     = N_VWrmsNormVectorArray_ManyVector;
306   v->ops->nvwrmsnormmaskvectorarray = N_VWrmsNormMaskVectorArray_ManyVector;
307 
308   /* local reduction operations */
309   v->ops->nvdotprodlocal     = N_VDotProdLocal_ManyVector;
310   v->ops->nvmaxnormlocal     = N_VMaxNormLocal_ManyVector;
311   v->ops->nvminlocal         = N_VMinLocal_ManyVector;
312   v->ops->nvl1normlocal      = N_VL1NormLocal_ManyVector;
313   v->ops->nvinvtestlocal     = N_VInvTestLocal_ManyVector;
314   v->ops->nvconstrmasklocal  = N_VConstrMaskLocal_ManyVector;
315   v->ops->nvminquotientlocal = N_VMinQuotientLocal_ManyVector;
316   v->ops->nvwsqrsumlocal     = N_VWSqrSumLocal_ManyVector;
317   v->ops->nvwsqrsummasklocal = N_VWSqrSumMaskLocal_ManyVector;
318 
319   /* Create content */
320   content = NULL;
321   content = (N_VectorContent_ManyVector) malloc(sizeof *content);
322   if (content == NULL) { N_VDestroy(v); return(NULL); }
323 
324   /* Attach content */
325   v->content = content;
326 
327   /* Attach content components */
328 
329   /* allocate and set subvector array */
330   content->num_subvectors = num_subvectors;
331   content->own_data       = SUNFALSE;
332 
333   content->subvec_array = NULL;
334   content->subvec_array = (N_Vector *) malloc(num_subvectors * sizeof(N_Vector));
335   if (content->subvec_array == NULL) { N_VDestroy(v); return(NULL); }
336 
337   for (i=0; i<num_subvectors; i++)
338     content->subvec_array[i] = vec_array[i];
339 
340   /* Determine overall ManyVector length: sum contributions from all subvectors */
341   content->global_length = 0;
342   for (i=0; i<num_subvectors; i++) {
343     if (vec_array[i]->ops->nvgetlength) {
344       content->global_length += N_VGetLength(vec_array[i]);
345     } else {
346       N_VDestroy(v);
347       return(NULL);
348     }
349   }
350 
351   return(v);
352 }
353 #endif
354 
355 
356 /* This function returns the vec_num sub-N_Vector from the N_Vector
357    array.  If vec_num is outside of applicable bounds, NULL is returned. */
MVAPPEND(N_VGetSubvector)358 N_Vector MVAPPEND(N_VGetSubvector)(N_Vector v, sunindextype vec_num)
359 {
360   if ( (vec_num < 0) || (vec_num > MANYVECTOR_NUM_SUBVECS(v)) )
361     return(NULL);
362   return(MANYVECTOR_SUBVEC(v,vec_num));
363 }
364 
365 
366 /* This function returns data pointer for the vec_num sub-N_Vector from
367    the N_Vector array.  If vec_num is outside of applicable bounds, or if
368    the subvector does not support the N_VGetArrayPointer routine, then
369    NULL is returned. */
MVAPPEND(N_VGetSubvectorArrayPointer)370 realtype *MVAPPEND(N_VGetSubvectorArrayPointer)(N_Vector v, sunindextype vec_num)
371 {
372   if ( (vec_num < 0) || (vec_num > MANYVECTOR_NUM_SUBVECS(v)) )
373     return(NULL);
374   if ( MANYVECTOR_SUBVEC(v,vec_num)->ops->nvgetarraypointer == NULL )
375     return(NULL);
376   return(N_VGetArrayPointer(MANYVECTOR_SUBVEC(v,vec_num)));
377 }
378 
379 
380 /* This function sets the data pointer for the vec_num sub-N_Vector from
381    the N_Vector array.  If vec_num is outside of applicable bounds, or if
382    the subvector does not support the N_VSetArrayPointer routine, then
383    -1 is returned; otherwise this routine returns 0. */
MVAPPEND(N_VSetSubvectorArrayPointer)384 int MVAPPEND(N_VSetSubvectorArrayPointer)(realtype *v_data, N_Vector v, sunindextype vec_num)
385 {
386   if ( (vec_num < 0) || (vec_num > MANYVECTOR_NUM_SUBVECS(v)) )
387     return(-1);
388   if ( MANYVECTOR_SUBVEC(v,vec_num)->ops->nvsetarraypointer == NULL )
389     return(-1);
390   N_VSetArrayPointer(v_data, MANYVECTOR_SUBVEC(v,vec_num));
391   return(0);
392 }
393 
394 
395 /* This function returns the overall number of sub-vectors.
396    It returns a locally stored integer, and is therefore a local call. */
MVAPPEND(N_VGetNumSubvectors)397 sunindextype MVAPPEND(N_VGetNumSubvectors)(N_Vector v)
398 {
399   return(MANYVECTOR_NUM_SUBVECS(v));
400 }
401 
402 
403 /* -----------------------------------------------------------------
404    ManyVector implementations of generic NVector routines
405    -----------------------------------------------------------------*/
406 
407 /* Returns vector type ID. Used to identify vector implementation
408    from abstract N_Vector interface. */
MVAPPEND(N_VGetVectorID)409 N_Vector_ID MVAPPEND(N_VGetVectorID)(N_Vector v)
410 {
411 #if SUNDIALS_MPI_ENABLED
412   return(SUNDIALS_NVEC_MPIMANYVECTOR);
413 #else
414   return(SUNDIALS_NVEC_MANYVECTOR);
415 #endif
416 }
417 
418 
419 /* Clones a ManyVector, calling CloneEmpty on subvectors. */
MVAPPEND(N_VCloneEmpty)420 N_Vector MVAPPEND(N_VCloneEmpty)(N_Vector w)
421 {
422   return(ManyVectorClone(w, SUNTRUE));
423 }
424 
425 
426 /* Clones a ManyVector, calling Clone on subvectors. */
MVAPPEND(N_VClone)427 N_Vector MVAPPEND(N_VClone)(N_Vector w)
428 {
429   return(ManyVectorClone(w, SUNFALSE));
430 }
431 
432 
433 /* Destroys a ManyVector */
MVAPPEND(N_VDestroy)434 void MVAPPEND(N_VDestroy)(N_Vector v)
435 {
436   sunindextype i;
437 
438   if (v == NULL) return;
439 
440   /* free content */
441   if (v->content != NULL) {
442     /* if subvectors are owned by v, then Destroy those */
443     if ((MANYVECTOR_OWN_DATA(v) == SUNTRUE) && (MANYVECTOR_SUBVECS(v) != NULL)) {
444       for (i=0; i<MANYVECTOR_NUM_SUBVECS(v); i++) {
445         N_VDestroy(MANYVECTOR_SUBVEC(v,i));
446         MANYVECTOR_SUBVEC(v,i) = NULL;
447       }
448     }
449 
450     /* free subvector array */
451     free(MANYVECTOR_SUBVECS(v));
452     MANYVECTOR_SUBVECS(v) = NULL;
453 
454 #if SUNDIALS_MPI_ENABLED
455     /* free communicator */
456     if (MANYVECTOR_COMM(v) != MPI_COMM_NULL)  MPI_Comm_free(&(MANYVECTOR_COMM(v)));
457     MANYVECTOR_COMM(v) = MPI_COMM_NULL;
458 #endif
459 
460     /* free content structure */
461     free(v->content);
462     v->content = NULL;
463   }
464 
465   /* free ops and vector */
466   if (v->ops != NULL) { free(v->ops); v->ops = NULL; }
467   free(v); v = NULL;
468 
469   return;
470 }
471 
472 
473 /* Returns the space requirements for the ManyVector, by accumulating this
474    information from all subvectors. */
MVAPPEND(N_VSpace)475 void MVAPPEND(N_VSpace)(N_Vector v, sunindextype *lrw, sunindextype *liw)
476 {
477   sunindextype i, lrw1, liw1;
478   *lrw = 0;
479   *liw = 0;
480   for (i=0; i<MANYVECTOR_NUM_SUBVECS(v); i++) {
481     /* update space requirements for this subvector (if 'nvspace' is implemented) */
482     if ((MANYVECTOR_SUBVEC(v,i))->ops->nvspace != NULL) {
483       N_VSpace(MANYVECTOR_SUBVEC(v,i), &lrw1, &liw1);
484       *lrw += lrw1;
485       *liw += liw1;
486     }
487   }
488   return;
489 }
490 
491 
492 #if SUNDIALS_MPI_ENABLED
493 /* This function retrieves the MPI Communicator from an MPIManyVector object. */
N_VGetCommunicator_MPIManyVector(N_Vector v)494 void *N_VGetCommunicator_MPIManyVector(N_Vector v)
495 {
496   if (MANYVECTOR_COMM(v) == MPI_COMM_NULL)
497     return NULL;
498   else
499     return((void *) &MANYVECTOR_COMM(v));
500 }
501 #else
502 /* This function retrieves the MPI Communicator from a ManyVector object. */
N_VGetCommunicator_ManyVector(N_Vector v)503 void *N_VGetCommunicator_ManyVector(N_Vector v)
504 {
505   return NULL;
506 }
507 #endif
508 
509 
510 /* This function retrieves the global length of a ManyVector object. */
MVAPPEND(N_VGetLength)511 sunindextype MVAPPEND(N_VGetLength)(N_Vector v)
512 {
513   return(MANYVECTOR_GLOBLENGTH(v));
514 }
515 
516 
517 /* Performs the linear sum z = a*x + b*y by calling N_VLinearSum on all subvectors;
518    this routine does not check that x, y and z are ManyVectors, if they have the
519    same number of subvectors, or if these subvectors are compatible. */
MVAPPEND(N_VLinearSum)520 void MVAPPEND(N_VLinearSum)(realtype a, N_Vector x, realtype b, N_Vector y, N_Vector z)
521 {
522   sunindextype i;
523   for (i=0; i<MANYVECTOR_NUM_SUBVECS(x); i++)
524     N_VLinearSum(a, MANYVECTOR_SUBVEC(x,i), b, MANYVECTOR_SUBVEC(y,i),
525                  MANYVECTOR_SUBVEC(z,i));
526   return;
527 }
528 
529 
530 /* Performs the operation z = c by calling N_VConst on all subvectors. */
MVAPPEND(N_VConst)531 void MVAPPEND(N_VConst)(realtype c, N_Vector z)
532 {
533   sunindextype i;
534   for (i=0; i<MANYVECTOR_NUM_SUBVECS(z); i++)
535     N_VConst(c, MANYVECTOR_SUBVEC(z,i));
536   return;
537 }
538 
539 
540 /* Performs the operation z_j = x_j*y_j by calling N_VProd on all subvectors;
541    this routine does not check that x, y and z are ManyVectors, if they have the
542    same number of subvectors, or if these subvectors are compatible. */
MVAPPEND(N_VProd)543 void MVAPPEND(N_VProd)(N_Vector x, N_Vector y, N_Vector z)
544 {
545   sunindextype i;
546   for (i=0; i<MANYVECTOR_NUM_SUBVECS(x); i++)
547     N_VProd(MANYVECTOR_SUBVEC(x,i), MANYVECTOR_SUBVEC(y,i),
548             MANYVECTOR_SUBVEC(z,i));
549   return;
550 }
551 
552 
553 /* Performs the operation z_j = x_j/y_j by calling N_VDiv on all subvectors;
554    this routine does not check that x, y and z are ManyVectors, if they have the
555    same number of subvectors, or if these subvectors are compatible. */
MVAPPEND(N_VDiv)556 void MVAPPEND(N_VDiv)(N_Vector x, N_Vector y, N_Vector z)
557 {
558   sunindextype i;
559   for (i=0; i<MANYVECTOR_NUM_SUBVECS(x); i++)
560     N_VDiv(MANYVECTOR_SUBVEC(x,i), MANYVECTOR_SUBVEC(y,i),
561            MANYVECTOR_SUBVEC(z,i));
562   return;
563 }
564 
565 
566 /* Performs the operation z_j = c*x_j by calling N_VScale on all subvectors;
567    this routine does not check that x and z are ManyVectors, if they have the
568    same number of subvectors, or if these subvectors are compatible. */
MVAPPEND(N_VScale)569 void MVAPPEND(N_VScale)(realtype c, N_Vector x, N_Vector z)
570 {
571   sunindextype i;
572   for (i=0; i<MANYVECTOR_NUM_SUBVECS(x); i++)
573     N_VScale(c, MANYVECTOR_SUBVEC(x,i), MANYVECTOR_SUBVEC(z,i));
574   return;
575 }
576 
577 
578 /* Performs the operation z_j = |x_j| by calling N_VAbs on all subvectors;
579    this routine does not check that x and z are ManyVectors, if they have the
580    same number of subvectors, or if these subvectors are compatible. */
MVAPPEND(N_VAbs)581 void MVAPPEND(N_VAbs)(N_Vector x, N_Vector z)
582 {
583   sunindextype i;
584   for (i=0; i<MANYVECTOR_NUM_SUBVECS(x); i++)
585     N_VAbs(MANYVECTOR_SUBVEC(x,i), MANYVECTOR_SUBVEC(z,i));
586   return;
587 }
588 
589 
590 /* Performs the operation z_j = 1/x_j by calling N_VInv on all subvectors;
591    this routine does not check that x and z are ManyVectors, if they have the
592    same number of subvectors, or if these subvectors are compatible. */
MVAPPEND(N_VInv)593 void MVAPPEND(N_VInv)(N_Vector x, N_Vector z)
594 {
595   sunindextype i;
596   for (i=0; i<MANYVECTOR_NUM_SUBVECS(x); i++)
597     N_VInv(MANYVECTOR_SUBVEC(x,i), MANYVECTOR_SUBVEC(z,i));
598   return;
599 }
600 
601 
602 /* Performs the operation z_j = x_j + b by calling N_VAddConst on all subvectors;
603    this routine does not check that x and z are ManyVectors, if they have the
604    same number of subvectors, or if these subvectors are compatible. */
MVAPPEND(N_VAddConst)605 void MVAPPEND(N_VAddConst)(N_Vector x, realtype b, N_Vector z)
606 {
607   sunindextype i;
608   for (i=0; i<MANYVECTOR_NUM_SUBVECS(x); i++)
609     N_VAddConst(MANYVECTOR_SUBVEC(x,i), b, MANYVECTOR_SUBVEC(z,i));
610   return;
611 }
612 
613 
614 /* Performs the MPI task-local dot product of two ManyVectors by calling
615    N_VDotProdLocal on all subvectors; this routine does not check that x and
616    y are ManyVectors, if they have the same number of subvectors, or if these
617    subvectors are compatible.
618 
619    If any subvector does not implement the N_VDotProdLocal routine (NULL
620    function pointer), then this routine will call N_VDotProd, but only
621    accumulate the sum if this is the root task for that subvector's
622    communicator (note: serial vectors are always root task). */
MVAPPEND(N_VDotProdLocal)623 realtype MVAPPEND(N_VDotProdLocal)(N_Vector x, N_Vector y)
624 {
625   sunindextype i;
626   realtype sum;
627 #if SUNDIALS_MPI_ENABLED
628   realtype contrib;
629   int rank;
630 #endif
631 
632   /* initialize output*/
633   sum = ZERO;
634 
635   for (i=0; i<MANYVECTOR_NUM_SUBVECS(x); i++) {
636 
637 #if SUNDIALS_MPI_ENABLED
638 
639     /* check for nvdotprodlocal in subvector */
640     if (MANYVECTOR_SUBVEC(x,i)->ops->nvdotprodlocal) {
641 
642       sum += N_VDotProdLocal(MANYVECTOR_SUBVEC(x,i), MANYVECTOR_SUBVEC(y,i));
643 
644       /* otherwise, call nvdotprod and root tasks accumulate to overall sum */
645     } else {
646 
647       contrib = N_VDotProd(MANYVECTOR_SUBVEC(x,i), MANYVECTOR_SUBVEC(y,i));
648 
649       /* get this task's rank in subvector communicator (note: serial
650          subvectors will result in rank==0) */
651       rank = SubvectorMPIRank(MANYVECTOR_SUBVEC(x,i));
652       if (rank < 0)   return(ZERO);
653       if (rank == 0)  sum += contrib;
654 
655     }
656 
657 #else
658 
659     /* add subvector contribution */
660     sum += N_VDotProd(MANYVECTOR_SUBVEC(x,i), MANYVECTOR_SUBVEC(y,i));
661 
662 #endif
663 
664   }
665 
666   return(sum);
667 }
668 
669 
670 #if SUNDIALS_MPI_ENABLED
671 /* Performs the dot product of two ManyVectors by calling N_VDotProdLocal and
672    combining the results.  This routine does not check that x and y are
673    ManyVectors, if they have the same number of subvectors, or if these
674    subvectors are compatible. */
N_VDotProd_MPIManyVector(N_Vector x,N_Vector y)675 realtype N_VDotProd_MPIManyVector(N_Vector x, N_Vector y)
676 {
677   realtype lsum, gsum;
678   lsum = gsum = N_VDotProdLocal_MPIManyVector(x,y);
679   if (MANYVECTOR_COMM(x) != MPI_COMM_NULL)
680     MPI_Allreduce(&lsum, &gsum, 1, MPI_SUNREALTYPE, MPI_SUM, MANYVECTOR_COMM(x));
681   return(gsum);
682 }
683 #endif
684 
685 
686 /* Performs the MPI task-local maximum norm of a ManyVector by calling
687    N_VMaxNormLocal on all subvectors.
688 
689    If any subvector does not implement the N_VMaxNormLocal routine (NULL
690    function pointer), then this routine will call N_VMaxNorm instead. */
MVAPPEND(N_VMaxNormLocal)691 realtype MVAPPEND(N_VMaxNormLocal)(N_Vector x)
692 {
693   sunindextype i;
694   realtype max, lmax;
695 
696   /* initialize output*/
697   max = ZERO;
698 
699   for (i=0; i<MANYVECTOR_NUM_SUBVECS(x); i++) {
700 
701     /* check for nvmaxnormlocal in subvector */
702     if (MANYVECTOR_SUBVEC(x,i)->ops->nvmaxnormlocal) {
703 
704       lmax = N_VMaxNormLocal(MANYVECTOR_SUBVEC(x,i));
705       max = (max > lmax) ? max : lmax;
706 
707       /* otherwise, call nvmaxnorm and accumulate to overall max */
708     } else {
709 
710       lmax = N_VMaxNorm(MANYVECTOR_SUBVEC(x,i));
711       max = (max > lmax) ? max : lmax;
712 
713     }
714   }
715 
716   return(max);
717 }
718 
719 
720 #if SUNDIALS_MPI_ENABLED
721 /* Performs the maximum norm of a ManyVector by calling N_VMaxNormLocal and
722    combining the results. */
N_VMaxNorm_MPIManyVector(N_Vector x)723 realtype N_VMaxNorm_MPIManyVector(N_Vector x)
724 {
725   realtype lmax, gmax;
726   lmax = gmax = N_VMaxNormLocal_MPIManyVector(x);
727   if (MANYVECTOR_COMM(x) != MPI_COMM_NULL)
728     MPI_Allreduce(&lmax, &gmax, 1, MPI_SUNREALTYPE, MPI_MAX, MANYVECTOR_COMM(x));
729   return(gmax);
730 }
731 #endif
732 
733 
734 /* Performs the MPI task-local weighted squared sum of a ManyVector by calling
735    N_VWSqrSumLocal on all subvectors; this routine does not check that x and
736    w are ManyVectors, if they have the same number of subvectors, or if these
737    subvectors are compatible.
738 
739    If any subvector does not implement the N_VWSqrSumLocal routine (NULL
740    function pointer), then this routine will call N_VWrmsNorm and N_VGetLength
741    to unravel the squared sum of the subvector components.  It will then only
742    accumulate this to the overall sum if this is the root task for that
743    subvector's communicator (note: serial vectors are always root task). */
MVAPPEND(N_VWSqrSumLocal)744 realtype MVAPPEND(N_VWSqrSumLocal)(N_Vector x, N_Vector w)
745 {
746   sunindextype i, N;
747   realtype sum, contrib;
748 #if SUNDIALS_MPI_ENABLED
749   int rank;
750 #endif
751 
752   /* initialize output*/
753   sum = ZERO;
754 
755   for (i=0; i<MANYVECTOR_NUM_SUBVECS(x); i++) {
756 
757 #if SUNDIALS_MPI_ENABLED
758 
759     /* check for nvwsqrsumlocal in subvector */
760     if (MANYVECTOR_SUBVEC(x,i)->ops->nvwsqrsumlocal) {
761 
762       sum += N_VWSqrSumLocal(MANYVECTOR_SUBVEC(x,i), MANYVECTOR_SUBVEC(w,i));
763 
764       /* otherwise, call nvwrmsnorm, and accumulate to overall sum on root task */
765     } else {
766 
767       contrib = N_VWrmsNorm(MANYVECTOR_SUBVEC(x,i), MANYVECTOR_SUBVEC(w,i));
768 
769       /* get this task's rank in subvector communicator (note: serial
770          subvectors will result in rank==0) */
771       rank = SubvectorMPIRank(MANYVECTOR_SUBVEC(x,i));
772       if (rank < 0)  return(ZERO);
773       if (rank == 0) {
774         N = N_VGetLength(MANYVECTOR_SUBVEC(x,i));
775         sum += (contrib*contrib*N);
776       }
777     }
778 
779 #else
780 
781     /* accumulate subvector contribution to overall sum */
782     contrib = N_VWrmsNorm(MANYVECTOR_SUBVEC(x,i), MANYVECTOR_SUBVEC(w,i));
783     N = N_VGetLength(MANYVECTOR_SUBVEC(x,i));
784     sum += (contrib*contrib*N);
785 
786 #endif
787 
788   }
789 
790   return(sum);
791 }
792 
793 
794 /* Performs the WRMS norm of a ManyVector by calling N_VWSqrSumLocal and
795    combining the results; this routine does not check that x and
796    w are ManyVectors, if they have the same number of subvectors, or if these
797    subvectors are compatible. */
MVAPPEND(N_VWrmsNorm)798 realtype MVAPPEND(N_VWrmsNorm)(N_Vector x, N_Vector w)
799 {
800   realtype gsum;
801 #if SUNDIALS_MPI_ENABLED
802   realtype lsum;
803   lsum = gsum = N_VWSqrSumLocal_MPIManyVector(x, w);
804   if (MANYVECTOR_COMM(x) != MPI_COMM_NULL)
805     MPI_Allreduce(&lsum, &gsum, 1, MPI_SUNREALTYPE, MPI_SUM, MANYVECTOR_COMM(x));
806 #else
807   gsum = N_VWSqrSumLocal_ManyVector(x, w);
808 #endif
809   return(SUNRsqrt(gsum/(MANYVECTOR_GLOBLENGTH(x))));
810 }
811 
812 
813 /* Performs the MPI task-local masked weighted squared sum of a ManyVector by
814    calling N_VWSqrSumMaskLocal on all subvectors; this routine does not check
815    that x, w and id are ManyVectors, if they have the same number of
816    subvectors, or if these subvectors are compatible.
817 
818    If any subvector does not implement the N_VWSqrSumLocal routine (NULL
819    function pointer), then this routine will call N_VWrmsNormMask and
820    N_VGetLength to unravel the masked squared sum of the subvector components.
821    It will then only accumulate this to the overall sum if this is the root
822    task for that subvector's communicator (note: serial vectors are always
823    root task). */
MVAPPEND(N_VWSqrSumMaskLocal)824 realtype MVAPPEND(N_VWSqrSumMaskLocal)(N_Vector x, N_Vector w, N_Vector id)
825 {
826   sunindextype i, N;
827   realtype sum, contrib;
828 #if SUNDIALS_MPI_ENABLED
829   int rank;
830 #endif
831 
832   /* initialize output*/
833   sum = ZERO;
834 
835   for (i=0; i<MANYVECTOR_NUM_SUBVECS(x); i++) {
836 
837 #if SUNDIALS_MPI_ENABLED
838 
839     /* check for nvwsqrsummasklocal in subvector */
840     if (MANYVECTOR_SUBVEC(x,i)->ops->nvwsqrsummasklocal) {
841 
842       sum += N_VWSqrSumMaskLocal(MANYVECTOR_SUBVEC(x,i), MANYVECTOR_SUBVEC(w,i),
843                                  MANYVECTOR_SUBVEC(id,i));
844 
845       /* otherwise, call nvwrmsnormmask, and accumulate to overall sum on root task */
846     } else {
847 
848       contrib = N_VWrmsNormMask(MANYVECTOR_SUBVEC(x,i), MANYVECTOR_SUBVEC(w,i),
849                                 MANYVECTOR_SUBVEC(id,i));
850 
851       /* get this task's rank in subvector communicator (note: serial
852          subvectors will result in rank==0) */
853       rank = SubvectorMPIRank(MANYVECTOR_SUBVEC(x,i));
854       if (rank < 0)  return(ZERO);
855       if (rank == 0) {
856         N = N_VGetLength(MANYVECTOR_SUBVEC(x,i));
857         sum += (contrib*contrib*N);
858       }
859     }
860 
861 #else
862 
863     /* accumulate subvector contribution to overall sum */
864     contrib = N_VWrmsNormMask(MANYVECTOR_SUBVEC(x,i), MANYVECTOR_SUBVEC(w,i),
865                               MANYVECTOR_SUBVEC(id,i));
866     N = N_VGetLength(MANYVECTOR_SUBVEC(x,i));
867     sum += (contrib*contrib*N);
868 
869 #endif
870 
871   }
872 
873   return(sum);
874 }
875 
876 
877 /* Performs the masked WRMS norm of a ManyVector by calling N_VWSqrSumMaskLocal
878    and combining the results; this routine does not check that x, w and id are
879    ManyVectors, if they have the same number of subvectors, or if these subvectors
880    are compatible. */
MVAPPEND(N_VWrmsNormMask)881 realtype MVAPPEND(N_VWrmsNormMask)(N_Vector x, N_Vector w, N_Vector id)
882 {
883   realtype gsum;
884 #if SUNDIALS_MPI_ENABLED
885   realtype lsum;
886   lsum = gsum = N_VWSqrSumMaskLocal_MPIManyVector(x, w, id);
887   if (MANYVECTOR_COMM(x) != MPI_COMM_NULL)
888     MPI_Allreduce(&lsum, &gsum, 1, MPI_SUNREALTYPE, MPI_SUM, MANYVECTOR_COMM(x));
889 #else
890   gsum = N_VWSqrSumMaskLocal_ManyVector(x, w, id);
891 #endif
892   return(SUNRsqrt(gsum/(MANYVECTOR_GLOBLENGTH(x))));
893 }
894 
895 
896 /* Computes the MPI task-local minimum entry of a ManyVector by calling
897    N_VMinLocal on all subvectors.
898 
899    If any subvector does not implement the N_VMinLocal routine (NULL
900    function pointer), then this routine will call N_VMin instead. */
MVAPPEND(N_VMinLocal)901 realtype MVAPPEND(N_VMinLocal)(N_Vector x)
902 {
903   sunindextype i;
904   realtype min, lmin;
905 
906   /* initialize output*/
907   min = BIG_REAL;
908 
909   for (i=0; i<MANYVECTOR_NUM_SUBVECS(x); i++) {
910 
911     /* check for nvminlocal in subvector */
912     if (MANYVECTOR_SUBVEC(x,i)->ops->nvminlocal) {
913 
914       lmin = N_VMinLocal(MANYVECTOR_SUBVEC(x,i));
915       min = (min < lmin) ? min : lmin;
916 
917       /* otherwise, call nvmin and accumulate to overall min */
918     } else {
919 
920       lmin = N_VMin(MANYVECTOR_SUBVEC(x,i));
921       min = (min < lmin) ? min : lmin;
922 
923     }
924   }
925 
926   return(min);
927 }
928 
929 
930 #if SUNDIALS_MPI_ENABLED
931 /* Computes the minimum entry of a ManyVector by calling N_VMinLocal and
932    combining the results. */
N_VMin_MPIManyVector(N_Vector x)933 realtype N_VMin_MPIManyVector(N_Vector x)
934 {
935   realtype lmin, gmin;
936   lmin = gmin = N_VMinLocal_MPIManyVector(x);
937   if (MANYVECTOR_COMM(x) != MPI_COMM_NULL)
938     MPI_Allreduce(&lmin, &gmin, 1, MPI_SUNREALTYPE, MPI_MIN, MANYVECTOR_COMM(x));
939   return(gmin);
940 }
941 #endif
942 
943 
944 /* Performs the WL2 norm of a ManyVector by calling N_VSqrSumLocal and
945    'massaging' the result.  This routine does not check that x and w are
946    ManyVectors, if they have the same number of subvectors, or if these
947    subvectors are compatible. */
MVAPPEND(N_VWL2Norm)948 realtype MVAPPEND(N_VWL2Norm)(N_Vector x, N_Vector w)
949 {
950   realtype gsum;
951 #if SUNDIALS_MPI_ENABLED
952   realtype lsum;
953   lsum = gsum = N_VWSqrSumLocal_MPIManyVector(x, w);
954   if (MANYVECTOR_COMM(x) != MPI_COMM_NULL)
955     MPI_Allreduce(&lsum, &gsum, 1, MPI_SUNREALTYPE, MPI_SUM, MANYVECTOR_COMM(x));
956 #else
957   gsum = N_VWSqrSumLocal_ManyVector(x, w);
958 #endif
959   return(SUNRsqrt(gsum));
960 }
961 
962 
963 /* Performs the MPI task-local L1 norm of a ManyVector by calling N_VL1NormLocal on
964    all subvectors.  If any subvector does not implement the N_VL1NormLocal routine
965    (NULL function pointer), then this routine will call N_VL1Norm, but only
966    accumulate the sum if this is the root task for that subvector's
967    communicator (note: serial vectors are always root task). */
MVAPPEND(N_VL1NormLocal)968 realtype MVAPPEND(N_VL1NormLocal)(N_Vector x)
969 {
970   sunindextype i;
971   realtype sum;
972 #if SUNDIALS_MPI_ENABLED
973   realtype contrib;
974   int rank;
975 #endif
976 
977   /* initialize output*/
978   sum = ZERO;
979 
980   for (i=0; i<MANYVECTOR_NUM_SUBVECS(x); i++) {
981 
982 #if SUNDIALS_MPI_ENABLED
983 
984     /* check for nvl1normlocal in subvector */
985     if (MANYVECTOR_SUBVEC(x,i)->ops->nvl1normlocal) {
986 
987       sum += N_VL1NormLocal(MANYVECTOR_SUBVEC(x,i));
988 
989       /* otherwise, call nvl1norm and root tasks accumulate to overall sum */
990     } else {
991 
992       contrib = N_VL1Norm(MANYVECTOR_SUBVEC(x,i));
993 
994       /* get this task's rank in subvector communicator (note: serial
995          subvectors will result in rank==0) */
996       rank = SubvectorMPIRank(MANYVECTOR_SUBVEC(x,i));
997       if (rank < 0)   return(ZERO);
998       if (rank == 0)  sum += contrib;
999 
1000     }
1001 
1002 #else
1003 
1004     /* accumulate subvector contribution to overall sum */
1005     sum += N_VL1Norm(MANYVECTOR_SUBVEC(x,i));
1006 
1007 #endif
1008 
1009   }
1010 
1011   return(sum);
1012 }
1013 
1014 
1015 #if SUNDIALS_MPI_ENABLED
1016 /* Performs the L1 norm of a ManyVector by calling N_VL1NormLocal and
1017    combining the results. */
N_VL1Norm_MPIManyVector(N_Vector x)1018 realtype N_VL1Norm_MPIManyVector(N_Vector x)
1019 {
1020   realtype lsum, gsum;
1021   lsum = gsum = N_VL1NormLocal_MPIManyVector(x);
1022   if (MANYVECTOR_COMM(x) != MPI_COMM_NULL)
1023     MPI_Allreduce(&lsum, &gsum, 1, MPI_SUNREALTYPE, MPI_SUM, MANYVECTOR_COMM(x));
1024   return(gsum);
1025 }
1026 #endif
1027 
1028 
1029 /* Performs N_VCompare on all subvectors; this routine does not check that x and z are
1030    ManyVectors, if they have the same number of subvectors, or if these subvectors are
1031    compatible. */
MVAPPEND(N_VCompare)1032 void MVAPPEND(N_VCompare)(realtype c, N_Vector x, N_Vector z)
1033 {
1034   sunindextype i;
1035   for (i=0; i<MANYVECTOR_NUM_SUBVECS(x); i++)
1036     N_VCompare(c, MANYVECTOR_SUBVEC(x,i), MANYVECTOR_SUBVEC(z,i));
1037   return;
1038 }
1039 
1040 
1041 /* Performs the MPI task-local InvTest for a ManyVector by calling N_VInvTestLocal
1042    on all subvectors and combining the results appropriately.  This routine does
1043    not check that x and z are ManyVectors, if they have the same number of
1044    subvectors, or if these subvectors are compatible.
1045 
1046    If any subvector does not implement the N_VInvTestLocal routine (NULL
1047    function pointer), then this routine will call N_VInvTest instead. */
MVAPPEND(N_VInvTestLocal)1048 booleantype MVAPPEND(N_VInvTestLocal)(N_Vector x, N_Vector z)
1049 {
1050   sunindextype i;
1051   booleantype val, subval;
1052 
1053   /* initialize output*/
1054   val = SUNTRUE;
1055 
1056   for (i=0; i<MANYVECTOR_NUM_SUBVECS(x); i++) {
1057 
1058     /* check for nvinvtestlocal in subvector */
1059     if (MANYVECTOR_SUBVEC(x,i)->ops->nvinvtestlocal) {
1060 
1061       subval = N_VInvTestLocal(MANYVECTOR_SUBVEC(x,i), MANYVECTOR_SUBVEC(z,i));
1062       val = (val && subval);
1063 
1064       /* otherwise, call nvinvtest and accumulate to overall val */
1065     } else {
1066 
1067       subval = N_VInvTest(MANYVECTOR_SUBVEC(x,i), MANYVECTOR_SUBVEC(z,i));
1068       val = (val && subval);
1069 
1070     }
1071   }
1072 
1073   return(val);
1074 }
1075 
1076 
1077 #if SUNDIALS_MPI_ENABLED
1078 /* Performs the InvTest for a ManyVector by calling N_VInvTestLocal and
1079    combining the results. This routine does not check that x and z
1080    are ManyVectors, if they have the same number of subvectors, or if these
1081    subvectors are compatible. */
N_VInvTest_MPIManyVector(N_Vector x,N_Vector z)1082 booleantype N_VInvTest_MPIManyVector(N_Vector x, N_Vector z)
1083 {
1084   realtype val, gval;
1085   val = gval = (N_VInvTestLocal_MPIManyVector(x, z)) ? ONE : ZERO;
1086   if (MANYVECTOR_COMM(x) != MPI_COMM_NULL)
1087     MPI_Allreduce(&val, &gval, 1, MPI_SUNREALTYPE, MPI_MIN, MANYVECTOR_COMM(x));
1088   return (gval != ZERO);
1089 }
1090 #endif
1091 
1092 
1093 /* Performs the MPI task-local ConstrMask for a ManyVector by calling N_VConstrMaskLocal
1094    on all subvectors and combining the results appropriately.  This routine does not
1095    check that c, x and m are ManyVectors, if they have the same number of subvectors,
1096    or if these subvectors are compatible.
1097 
1098    If any subvector does not implement the N_VConstrMaskLocal routine (NULL
1099    function pointer), then this routine will call N_VConstrMask instead. */
MVAPPEND(N_VConstrMaskLocal)1100 booleantype MVAPPEND(N_VConstrMaskLocal)(N_Vector c, N_Vector x, N_Vector m)
1101 {
1102   sunindextype i;
1103   booleantype val, subval;
1104 
1105   /* initialize output*/
1106   val = SUNTRUE;
1107 
1108   for (i=0; i<MANYVECTOR_NUM_SUBVECS(x); i++) {
1109 
1110     /* check for nvconstrmasklocal in subvector */
1111     if (MANYVECTOR_SUBVEC(x,i)->ops->nvconstrmasklocal) {
1112 
1113       subval = N_VConstrMaskLocal(MANYVECTOR_SUBVEC(c,i), MANYVECTOR_SUBVEC(x,i),
1114                                   MANYVECTOR_SUBVEC(m,i));
1115       val = (val && subval);
1116 
1117       /* otherwise, call nvconstrmask and accumulate to overall val */
1118     } else {
1119 
1120       subval = N_VConstrMask(MANYVECTOR_SUBVEC(c,i), MANYVECTOR_SUBVEC(x,i),
1121                              MANYVECTOR_SUBVEC(m,i));
1122       val = (val && subval);
1123 
1124     }
1125   }
1126 
1127   return(val);
1128 }
1129 
1130 
1131 #if SUNDIALS_MPI_ENABLED
1132 /* Performs the ConstrMask for a ManyVector by calling N_VConstrMaskLocal and
1133    combining the results.  This routine does not check that c, x and m
1134    are ManyVectors, if they have the same number of subvectors, or if these
1135    subvectors are compatible. */
N_VConstrMask_MPIManyVector(N_Vector c,N_Vector x,N_Vector m)1136 booleantype N_VConstrMask_MPIManyVector(N_Vector c, N_Vector x, N_Vector m)
1137 {
1138   realtype val, gval;
1139   val = gval = (N_VConstrMaskLocal_MPIManyVector(c, x, m)) ? ONE : ZERO;
1140   if (MANYVECTOR_COMM(x) != MPI_COMM_NULL)
1141     MPI_Allreduce(&val, &gval, 1, MPI_SUNREALTYPE, MPI_MIN, MANYVECTOR_COMM(x));
1142   return (gval != ZERO);
1143 }
1144 #endif
1145 
1146 
1147 /* Performs the MPI task-local MinQuotient for a ManyVector by calling N_VMinQuotientLocal
1148    on all subvectors and combining the results appropriately.  This routine does not check
1149    that num and denom are ManyVectors, if they have the same number of subvectors, or if
1150    these subvectors are compatible.
1151 
1152    If any subvector does not implement the N_VMinQuotientLocal routine (NULL
1153    function pointer), then this routine will call N_VMinQuotient instead. */
MVAPPEND(N_VMinQuotientLocal)1154 realtype MVAPPEND(N_VMinQuotientLocal)(N_Vector num, N_Vector denom)
1155 {
1156   sunindextype i;
1157   realtype min, lmin;
1158 
1159   /* initialize output*/
1160   min = BIG_REAL;
1161 
1162   for (i=0; i<MANYVECTOR_NUM_SUBVECS(num); i++) {
1163 
1164     /* check for nvminquotientlocal in subvector */
1165     if (MANYVECTOR_SUBVEC(num,i)->ops->nvminquotientlocal) {
1166 
1167       lmin = N_VMinQuotientLocal(MANYVECTOR_SUBVEC(num,i),
1168                                  MANYVECTOR_SUBVEC(denom,i));
1169       min = (min < lmin) ? min : lmin;
1170 
1171       /* otherwise, call nvmin and accumulate to overall min */
1172     } else {
1173 
1174       lmin = N_VMinQuotient(MANYVECTOR_SUBVEC(num,i),
1175                             MANYVECTOR_SUBVEC(denom,i));
1176       min = (min < lmin) ? min : lmin;
1177 
1178     }
1179   }
1180 
1181   return(min);
1182 }
1183 
1184 
1185 #if SUNDIALS_MPI_ENABLED
1186 /* Performs the MinQuotient for a ManyVector by calling N_VMinQuotientLocal
1187    and combining the results.  This routine does not check that num and
1188    denom are ManyVectors, if they have the same number of subvectors, or if
1189    these subvectors are compatible. */
N_VMinQuotient_MPIManyVector(N_Vector num,N_Vector denom)1190 realtype N_VMinQuotient_MPIManyVector(N_Vector num, N_Vector denom)
1191 {
1192   realtype lmin, gmin;
1193   lmin = gmin = N_VMinQuotientLocal_MPIManyVector(num, denom);
1194   if (MANYVECTOR_COMM(num) != MPI_COMM_NULL)
1195     MPI_Allreduce(&lmin, &gmin, 1, MPI_SUNREALTYPE, MPI_MIN, MANYVECTOR_COMM(num));
1196   return(gmin);
1197 }
1198 #endif
1199 
1200 
1201 
1202 /* -----------------------------------------------------------------
1203    Fused vector operations
1204    ----------------------------------------------------------------- */
1205 
1206 /* Performs the linear combination z = sum_j c[j]*X[j] by calling
1207    N_VLinearCombination on all subvectors; this routine does not check that z
1208    or the components of X are ManyVectors, if they have the same number of
1209    subvectors, or if these subvectors are compatible.
1210 
1211    NOTE: implementation of this routine is more challenging, due to the
1212    array-of-arrays of N_Vectors that comprise X.  This routine will be
1213    passed an array of ManyVectors, so to call the subvector-specific routines
1214    we must unravel the subvectors while retaining an array of outer vectors. */
MVAPPEND(N_VLinearCombination)1215 int MVAPPEND(N_VLinearCombination)(int nvec, realtype* c, N_Vector* X, N_Vector z)
1216 {
1217   sunindextype i, j;
1218   int retval;
1219   N_Vector *Xsub;
1220 
1221   /* create array of nvec N_Vector pointers for reuse within loop */
1222   Xsub = NULL;
1223   Xsub = (N_Vector *) malloc( nvec * sizeof(N_Vector) );
1224   if (Xsub == NULL)  return(1);
1225 
1226   /* perform operation by calling N_VLinearCombination for each subvector */
1227   for (i=0; i<MANYVECTOR_NUM_SUBVECS(z); i++) {
1228 
1229     /* for each subvector, create the array of subvectors of X */
1230     for (j=0; j<nvec; j++)  Xsub[j] = MANYVECTOR_SUBVEC(X[j],i);
1231 
1232     /* now call N_VLinearCombination for this array of subvectors */
1233     retval = N_VLinearCombination(nvec, c, Xsub, MANYVECTOR_SUBVEC(z,i));
1234 
1235     /* fail gracefully */
1236     if (retval) {
1237       free(Xsub);
1238       return(retval);
1239     }
1240 
1241   }
1242 
1243   /* clean up and return */
1244   free(Xsub);
1245   return(0);
1246 }
1247 
1248 
1249 /* Performs the ScaleAddMulti operation by calling N_VScaleAddMulti on all
1250    subvectors; this routine does not check that x, or the components of X and Z are
1251    ManyVectors, if they have the same number of subvectors, or if these subvectors
1252    are compatible.
1253 
1254    NOTE: this routine is more challenging, due to the array-of-arrays of
1255    N_Vectors that comprise Y and Z.  This routine will be passed an array of
1256    ManyVectors, so to call the subvector-specific routines we must unravel
1257    the subvectors while retaining an array of outer vectors. */
MVAPPEND(N_VScaleAddMulti)1258 int MVAPPEND(N_VScaleAddMulti)(int nvec, realtype* a, N_Vector x, N_Vector* Y, N_Vector* Z)
1259 {
1260   sunindextype i, j;
1261   int retval;
1262   N_Vector *Ysub, *Zsub;
1263 
1264   /* create arrays of nvec N_Vector pointers for reuse within loop */
1265   Ysub = Zsub = NULL;
1266   Ysub = (N_Vector *) malloc( nvec * sizeof(N_Vector) );
1267   Zsub = (N_Vector *) malloc( nvec * sizeof(N_Vector) );
1268   if ( (Ysub == NULL) || (Zsub == NULL) )  return(1);
1269 
1270   /* perform operation by calling N_VScaleAddMulti for each subvector */
1271   for (i=0; i<MANYVECTOR_NUM_SUBVECS(x); i++) {
1272 
1273     /* for each subvector, create the array of subvectors of Y and Z */
1274     for (j=0; j<nvec; j++)  {
1275       Ysub[j] = MANYVECTOR_SUBVEC(Y[j],i);
1276       Zsub[j] = MANYVECTOR_SUBVEC(Z[j],i);
1277     }
1278 
1279     /* now call N_VScaleAddMulti for this array of subvectors */
1280     retval = N_VScaleAddMulti(nvec, a, MANYVECTOR_SUBVEC(x,i), Ysub, Zsub);
1281 
1282     /* fail gracefully */
1283     if (retval) {
1284       free(Ysub);
1285       free(Zsub);
1286       return(retval);
1287     }
1288 
1289   }
1290 
1291   /* clean up and return */
1292   free(Ysub);
1293   free(Zsub);
1294   return(0);
1295 }
1296 
1297 
1298 /* Performs the DotProdMulti operation by calling N_VDotProdLocal and combining results.
1299    This routine does not check that x, or the components of Y, are ManyVectors, if
1300    they have the same number of subvectors, or if these subvectors are compatible.
1301 
1302    NOTE: if all subvectors implement the N_VDotProdLocal routine, then this routine
1303    will require only a single array-valued reduction operation (in contrast to calling
1304    N_VDotProdMulti on all subvectors, where we would require num_subvectors separate
1305    reductions). */
MVAPPEND(N_VDotProdMulti)1306 int MVAPPEND(N_VDotProdMulti)(int nvec, N_Vector x, N_Vector* Y, realtype* dotprods)
1307 {
1308   sunindextype i;
1309 
1310   /* call N_VDotProdLocal for each <x,Y[i]> pair */
1311   for (i=0; i<nvec; i++)  dotprods[i] = N_VDotProdLocal(x,Y[i]);
1312 
1313 #if SUNDIALS_MPI_ENABLED
1314   /* accumulate totals and return */
1315   if (MANYVECTOR_COMM(x) != MPI_COMM_NULL)
1316     return(MPI_Allreduce(MPI_IN_PLACE, dotprods, nvec, MPI_SUNREALTYPE,
1317                          MPI_SUM, MANYVECTOR_COMM(x)));
1318 #endif
1319   /* return with success */
1320   return(0);
1321 }
1322 
1323 
1324 /* -----------------------------------------------------------------
1325    Vector array operations
1326    ----------------------------------------------------------------- */
1327 
1328 /* Performs the LinearSumVectorArray operation by calling N_VLinearSumVectorArray
1329    on all subvectors; this routine does not check that the components of X, Y or Z are
1330    ManyVectors, if they have the same number of subvectors, or if these subvectors
1331    are compatible.
1332 
1333    NOTE: this routine is more challenging, due to the array-of-arrays of
1334    N_Vectors that comprise X, Y and Z.  This routine will be passed arrays of
1335    ManyVectors, so to call the subvector-specific routines we must unravel
1336    the subvectors while retaining arrays of outer vectors. */
MVAPPEND(N_VLinearSumVectorArray)1337 int MVAPPEND(N_VLinearSumVectorArray)(int nvec, realtype a,
1338                                       N_Vector *X, realtype b,
1339                                       N_Vector *Y, N_Vector *Z)
1340 {
1341   sunindextype i, j;
1342   int retval;
1343   N_Vector *Xsub, *Ysub, *Zsub;
1344 
1345   /* immediately return if nvec <= 0 */
1346   if (nvec <= 0)  return(0);
1347 
1348   /* create arrays of nvec N_Vector pointers for reuse within loop */
1349   Xsub = Ysub = Zsub = NULL;
1350   Xsub = (N_Vector *) malloc( nvec * sizeof(N_Vector) );
1351   Ysub = (N_Vector *) malloc( nvec * sizeof(N_Vector) );
1352   Zsub = (N_Vector *) malloc( nvec * sizeof(N_Vector) );
1353   if ( (Xsub == NULL) || (Ysub == NULL) || (Zsub == NULL) )  return(1);
1354 
1355   /* perform operation by calling N_VLinearSumVectorArray for each subvector */
1356   for (i=0; i<MANYVECTOR_NUM_SUBVECS(X[0]); i++) {
1357 
1358     /* for each subvector, create the array of subvectors of X, Y and Z */
1359     for (j=0; j<nvec; j++)  {
1360       Xsub[j] = MANYVECTOR_SUBVEC(X[j],i);
1361       Ysub[j] = MANYVECTOR_SUBVEC(Y[j],i);
1362       Zsub[j] = MANYVECTOR_SUBVEC(Z[j],i);
1363     }
1364 
1365     /* now call N_VLinearSumVectorArray for this array of subvectors */
1366     retval = N_VLinearSumVectorArray(nvec, a, Xsub, b, Ysub, Zsub);
1367 
1368     /* fail gracefully */
1369     if (retval) {
1370       free(Xsub);
1371       free(Ysub);
1372       free(Zsub);
1373       return(retval);
1374     }
1375 
1376   }
1377 
1378   /* clean up and return */
1379   free(Xsub);
1380   free(Ysub);
1381   free(Zsub);
1382   return(0);
1383 }
1384 
1385 
1386 /* Performs the ScaleVectorArray operation by calling N_VScaleVectorArray
1387    on all subvectors; this routine does not check that the components of X or Z are
1388    ManyVectors, if they have the same number of subvectors, or if these subvectors
1389    are compatible.
1390 
1391    NOTE: this routine is more challenging, due to the array-of-arrays of
1392    N_Vectors that comprise X and Z.  This routine will be passed arrays of
1393    ManyVectors, so to call the subvector-specific routines we must unravel
1394    the subvectors while retaining arrays of outer vectors. */
MVAPPEND(N_VScaleVectorArray)1395 int MVAPPEND(N_VScaleVectorArray)(int nvec, realtype* c, N_Vector* X, N_Vector* Z)
1396 {
1397   sunindextype i, j;
1398   int retval;
1399   N_Vector *Xsub, *Zsub;
1400 
1401   /* immediately return if nvec <= 0 */
1402   if (nvec <= 0)  return(0);
1403 
1404   /* create arrays of nvec N_Vector pointers for reuse within loop */
1405   Xsub = Zsub = NULL;
1406   Xsub = (N_Vector *) malloc( nvec * sizeof(N_Vector) );
1407   Zsub = (N_Vector *) malloc( nvec * sizeof(N_Vector) );
1408   if ( (Xsub == NULL) || (Zsub == NULL) )  return(1);
1409 
1410   /* perform operation by calling N_VScaleVectorArray for each subvector */
1411   for (i=0; i<MANYVECTOR_NUM_SUBVECS(X[0]); i++) {
1412 
1413     /* for each subvector, create the array of subvectors of X, Y and Z */
1414     for (j=0; j<nvec; j++)  {
1415       Xsub[j] = MANYVECTOR_SUBVEC(X[j],i);
1416       Zsub[j] = MANYVECTOR_SUBVEC(Z[j],i);
1417     }
1418 
1419     /* now call N_VScaleVectorArray for this array of subvectors */
1420     retval = N_VScaleVectorArray(nvec, c, Xsub, Zsub);
1421 
1422     /* fail gracefully */
1423     if (retval) {
1424       free(Xsub);
1425       free(Zsub);
1426       return(retval);
1427     }
1428 
1429   }
1430 
1431   /* clean up and return */
1432   free(Xsub);
1433   free(Zsub);
1434   return(0);
1435 }
1436 
1437 
1438 /* Performs the ConstVectorArray operation by calling N_VConstVectorArray
1439    on all subvectors.
1440 
1441    NOTE: this routine is more challenging, due to the array-of-arrays of
1442    N_Vectors that comprise Z.  This routine will be passed an array of
1443    ManyVectors, so to call the subvector-specific routines we must unravel
1444    the subvectors while retaining an array of outer vectors. */
MVAPPEND(N_VConstVectorArray)1445 int MVAPPEND(N_VConstVectorArray)(int nvec, realtype c, N_Vector* Z)
1446 {
1447   sunindextype i, j;
1448   int retval;
1449   N_Vector *Zsub;
1450 
1451   /* immediately return if nvec <= 0 */
1452   if (nvec <= 0)  return(0);
1453 
1454   /* create array of N_Vector pointers for reuse within loop */
1455   Zsub = NULL;
1456   Zsub = (N_Vector *) malloc( nvec * sizeof(N_Vector) );
1457   if (Zsub == NULL)  return(1);
1458 
1459   /* perform operation by calling N_VConstVectorArray for each subvector */
1460   for (i=0; i<MANYVECTOR_NUM_SUBVECS(Z[0]); i++) {
1461 
1462     /* for each subvector, create the array of subvectors of X, Y and Z */
1463     for (j=0; j<nvec; j++)
1464       Zsub[j] = MANYVECTOR_SUBVEC(Z[j],i);
1465 
1466     /* now call N_VConstVectorArray for this array of subvectors */
1467     retval = N_VConstVectorArray(nvec, c, Zsub);
1468 
1469     /* fail gracefully */
1470     if (retval) {
1471       free(Zsub);
1472       return(retval);
1473     }
1474 
1475   }
1476 
1477   /* clean up and return */
1478   free(Zsub);
1479   return(0);
1480 }
1481 
1482 
1483 /* Performs the WrmsNormVectorArray operation by calling N_VWSqrSumLocal and combining
1484    results.  This routine does not check that the components of X or W are ManyVectors, if
1485    they have the same number of subvectors, or if these subvectors are compatible.
1486 
1487    NOTE: if all subvectors implement the N_VWSqrSumLocal routine, then this routine
1488    will require only a single array-valued reduction operation (in contrast to calling
1489    N_VWrmsNormVectorArray on all subvectors, where we would require num_subvectors
1490    separate reductions). */
MVAPPEND(N_VWrmsNormVectorArray)1491 int MVAPPEND(N_VWrmsNormVectorArray)(int nvec, N_Vector* X, N_Vector* W, realtype* nrm)
1492 {
1493   sunindextype i;
1494   int retval;
1495 
1496   /* immediately return if nvec <= 0 */
1497   if (nvec <= 0)  return(0);
1498 
1499   /* call N_VWSqrSumLocal for each (X[i],W[i]) pair */
1500   for (i=0; i<nvec; i++)  nrm[i] = N_VWSqrSumLocal(X[i], W[i]);
1501 
1502   /* accumulate totals */
1503   retval = 0;
1504 #if SUNDIALS_MPI_ENABLED
1505   if (MANYVECTOR_COMM(X[0]) != MPI_COMM_NULL)
1506     retval = (MPI_Allreduce(MPI_IN_PLACE, nrm, nvec, MPI_SUNREALTYPE, MPI_SUM,
1507                             MANYVECTOR_COMM(X[0])) == MPI_SUCCESS) ? 0 : -1;
1508 #endif
1509 
1510   /* finish off WRMS norms and return */
1511   for (i=0; i<nvec; i++)
1512     nrm[i] = SUNRsqrt(nrm[i]/(MANYVECTOR_GLOBLENGTH(X[i])));
1513 
1514   return(retval);
1515 }
1516 
1517 
1518 /* Performs the WrmsNormMaskVectorArray operation by calling N_VWSqrSumMaskLocal and
1519    combining results.  This routine does not check that id or the components of X and
1520    W are ManyVectors, if they have the same number of subvectors, or if these
1521    subvectors are compatible.
1522 
1523    NOTE: if all subvectors implement the N_VWSqrSumMaskLocal routine, then this
1524    routine will require only a single array-valued reduction operation (in contrast
1525    to calling N_VWrmsNormMaskVectorArray on all subvectors, where we would require
1526    num_subvectors separate reductions). */
MVAPPEND(N_VWrmsNormMaskVectorArray)1527 int MVAPPEND(N_VWrmsNormMaskVectorArray)(int nvec, N_Vector* X, N_Vector* W,
1528                                          N_Vector id, realtype* nrm)
1529 {
1530   sunindextype i;
1531   int retval;
1532 
1533   /* immediately return if nvec <= 0 */
1534   if (nvec <= 0)  return(0);
1535 
1536   /* call N_VWSqrSumMaskLocal for each (X[i],W[i]) pair */
1537   for (i=0; i<nvec; i++)  nrm[i] = N_VWSqrSumMaskLocal(X[i], W[i], id);
1538 
1539   /* accumulate totals */
1540   retval = 0;
1541 #if SUNDIALS_MPI_ENABLED
1542   if (MANYVECTOR_COMM(X[0]) != MPI_COMM_NULL)
1543     retval = (MPI_Allreduce(MPI_IN_PLACE, nrm, nvec, MPI_SUNREALTYPE, MPI_SUM,
1544                             MANYVECTOR_COMM(X[0])) == MPI_SUCCESS) ? 0 : -1;
1545 #endif
1546 
1547   /* finish off WRMS norms and return */
1548   for (i=0; i<nvec; i++)
1549     nrm[i] = SUNRsqrt(nrm[i]/(MANYVECTOR_GLOBLENGTH(X[i])));
1550 
1551   return(retval);
1552 }
1553 
1554 
1555 /* -----------------------------------------------------------------
1556    Enable / Disable fused and vector array operations
1557    ----------------------------------------------------------------- */
1558 
MVAPPEND(N_VEnableFusedOps)1559 int MVAPPEND(N_VEnableFusedOps)(N_Vector v, booleantype tf)
1560 {
1561   /* check that vector is non-NULL */
1562   if (v == NULL) return(-1);
1563 
1564   /* check that ops structure is non-NULL */
1565   if (v->ops == NULL) return(-1);
1566 
1567   if (tf) {
1568     /* enable all fused vector operations */
1569     v->ops->nvlinearcombination = MVAPPEND(N_VLinearCombination);
1570     v->ops->nvscaleaddmulti     = MVAPPEND(N_VScaleAddMulti);
1571     v->ops->nvdotprodmulti      = MVAPPEND(N_VDotProdMulti);
1572     /* enable all vector array operations */
1573     v->ops->nvlinearsumvectorarray         = MVAPPEND(N_VLinearSumVectorArray);
1574     v->ops->nvscalevectorarray             = MVAPPEND(N_VScaleVectorArray);
1575     v->ops->nvconstvectorarray             = MVAPPEND(N_VConstVectorArray);
1576     v->ops->nvwrmsnormvectorarray          = MVAPPEND(N_VWrmsNormVectorArray);
1577     v->ops->nvwrmsnormmaskvectorarray      = MVAPPEND(N_VWrmsNormMaskVectorArray);
1578     v->ops->nvscaleaddmultivectorarray     = NULL;
1579     v->ops->nvlinearcombinationvectorarray = NULL;
1580   } else {
1581     /* disable all fused vector operations */
1582     v->ops->nvlinearcombination = NULL;
1583     v->ops->nvscaleaddmulti     = NULL;
1584     v->ops->nvdotprodmulti      = NULL;
1585     /* disable all vector array operations */
1586     v->ops->nvlinearsumvectorarray         = NULL;
1587     v->ops->nvscalevectorarray             = NULL;
1588     v->ops->nvconstvectorarray             = NULL;
1589     v->ops->nvwrmsnormvectorarray          = NULL;
1590     v->ops->nvwrmsnormmaskvectorarray      = NULL;
1591     v->ops->nvscaleaddmultivectorarray     = NULL;
1592     v->ops->nvlinearcombinationvectorarray = NULL;
1593   }
1594 
1595   /* return success */
1596   return(0);
1597 }
1598 
1599 
MVAPPEND(N_VEnableLinearCombination)1600 int MVAPPEND(N_VEnableLinearCombination)(N_Vector v, booleantype tf)
1601 {
1602   /* check that vector is non-NULL */
1603   if (v == NULL) return(-1);
1604 
1605   /* check that ops structure is non-NULL */
1606   if (v->ops == NULL) return(-1);
1607 
1608   /* enable/disable operation */
1609   if (tf)
1610     v->ops->nvlinearcombination = MVAPPEND(N_VLinearCombination);
1611   else
1612     v->ops->nvlinearcombination = NULL;
1613 
1614   /* return success */
1615   return(0);
1616 }
1617 
MVAPPEND(N_VEnableScaleAddMulti)1618 int MVAPPEND(N_VEnableScaleAddMulti)(N_Vector v, booleantype tf)
1619 {
1620   /* check that vector is non-NULL */
1621   if (v == NULL) return(-1);
1622 
1623   /* check that ops structure is non-NULL */
1624   if (v->ops == NULL) return(-1);
1625 
1626   /* enable/disable operation */
1627   if (tf)
1628     v->ops->nvscaleaddmulti = MVAPPEND(N_VScaleAddMulti);
1629   else
1630     v->ops->nvscaleaddmulti = NULL;
1631 
1632   /* return success */
1633   return(0);
1634 }
1635 
MVAPPEND(N_VEnableDotProdMulti)1636 int MVAPPEND(N_VEnableDotProdMulti)(N_Vector v, booleantype tf)
1637 {
1638   /* check that vector is non-NULL */
1639   if (v == NULL) return(-1);
1640 
1641   /* check that ops structure is non-NULL */
1642   if (v->ops == NULL) return(-1);
1643 
1644   /* enable/disable operation */
1645   if (tf)
1646     v->ops->nvdotprodmulti = MVAPPEND(N_VDotProdMulti);
1647   else
1648     v->ops->nvdotprodmulti = NULL;
1649 
1650   /* return success */
1651   return(0);
1652 }
1653 
MVAPPEND(N_VEnableLinearSumVectorArray)1654 int MVAPPEND(N_VEnableLinearSumVectorArray)(N_Vector v, booleantype tf)
1655 {
1656   /* check that vector is non-NULL */
1657   if (v == NULL) return(-1);
1658 
1659   /* check that ops structure is non-NULL */
1660   if (v->ops == NULL) return(-1);
1661 
1662   /* enable/disable operation */
1663   if (tf)
1664     v->ops->nvlinearsumvectorarray = MVAPPEND(N_VLinearSumVectorArray);
1665   else
1666     v->ops->nvlinearsumvectorarray = NULL;
1667 
1668   /* return success */
1669   return(0);
1670 }
1671 
MVAPPEND(N_VEnableScaleVectorArray)1672 int MVAPPEND(N_VEnableScaleVectorArray)(N_Vector v, booleantype tf)
1673 {
1674   /* check that vector is non-NULL */
1675   if (v == NULL) return(-1);
1676 
1677   /* check that ops structure is non-NULL */
1678   if (v->ops == NULL) return(-1);
1679 
1680   /* enable/disable operation */
1681   if (tf)
1682     v->ops->nvscalevectorarray = MVAPPEND(N_VScaleVectorArray);
1683   else
1684     v->ops->nvscalevectorarray = NULL;
1685 
1686   /* return success */
1687   return(0);
1688 }
1689 
MVAPPEND(N_VEnableConstVectorArray)1690 int MVAPPEND(N_VEnableConstVectorArray)(N_Vector v, booleantype tf)
1691 {
1692   /* check that vector is non-NULL */
1693   if (v == NULL) return(-1);
1694 
1695   /* check that ops structure is non-NULL */
1696   if (v->ops == NULL) return(-1);
1697 
1698   /* enable/disable operation */
1699   if (tf)
1700     v->ops->nvconstvectorarray = MVAPPEND(N_VConstVectorArray);
1701   else
1702     v->ops->nvconstvectorarray = NULL;
1703 
1704   /* return success */
1705   return(0);
1706 }
1707 
MVAPPEND(N_VEnableWrmsNormVectorArray)1708 int MVAPPEND(N_VEnableWrmsNormVectorArray)(N_Vector v, booleantype tf)
1709 {
1710   /* check that vector is non-NULL */
1711   if (v == NULL) return(-1);
1712 
1713   /* check that ops structure is non-NULL */
1714   if (v->ops == NULL) return(-1);
1715 
1716   /* enable/disable operation */
1717   if (tf)
1718     v->ops->nvwrmsnormvectorarray = MVAPPEND(N_VWrmsNormVectorArray);
1719   else
1720     v->ops->nvwrmsnormvectorarray = NULL;
1721 
1722   /* return success */
1723   return(0);
1724 }
1725 
MVAPPEND(N_VEnableWrmsNormMaskVectorArray)1726 int MVAPPEND(N_VEnableWrmsNormMaskVectorArray)(N_Vector v, booleantype tf)
1727 {
1728   /* check that vector is non-NULL */
1729   if (v == NULL) return(-1);
1730 
1731   /* check that ops structure is non-NULL */
1732   if (v->ops == NULL) return(-1);
1733 
1734   /* enable/disable operation */
1735   if (tf)
1736     v->ops->nvwrmsnormmaskvectorarray = MVAPPEND(N_VWrmsNormMaskVectorArray);
1737   else
1738     v->ops->nvwrmsnormmaskvectorarray = NULL;
1739 
1740   /* return success */
1741   return(0);
1742 }
1743 
1744 
1745 /* -----------------------------------------------------------------
1746    Implementation of utility routines
1747    -----------------------------------------------------------------*/
1748 
1749 /* This function performs a generic clone operation on an input N_Vector.
1750    Based on the 'cloneempty' flag it will either call "nvclone" or
1751    "nvcloneempty" when creating subvectors in the cloned vector. */
ManyVectorClone(N_Vector w,booleantype cloneempty)1752 static N_Vector ManyVectorClone(N_Vector w, booleantype cloneempty)
1753 {
1754   N_Vector v;
1755   MVAPPEND(N_VectorContent) content;
1756   sunindextype i;
1757 
1758   if (w == NULL) return(NULL);
1759 
1760   /* Create vector */
1761   v = NULL;
1762   v = N_VNewEmpty();
1763   if (v == NULL) return(NULL);
1764 
1765   /* Attach operations */
1766   if (N_VCopyOps(w, v)) { N_VDestroy(v); return(NULL); }
1767 
1768   /* Create content */
1769   content = NULL;
1770   content = (MVAPPEND(N_VectorContent)) malloc(sizeof *content);
1771   if (content == NULL) { N_VDestroy(v); return(NULL); }
1772 
1773   /* Attach content and ops to new vector, and return */
1774   v->content = content;
1775 
1776   /* Attach content components */
1777 
1778   /* Set scalar components */
1779 #if SUNDIALS_MPI_ENABLED
1780   content->comm           = MPI_COMM_NULL;
1781 #endif
1782   content->num_subvectors = MANYVECTOR_NUM_SUBVECS(w);
1783   content->global_length  = MANYVECTOR_GLOBLENGTH(w);
1784   content->own_data       = SUNTRUE;
1785 
1786   /* Allocate the subvector array */
1787   content->subvec_array = NULL;
1788   content->subvec_array = (N_Vector *) malloc(content->num_subvectors * sizeof(N_Vector));
1789   if (content->subvec_array == NULL) { N_VDestroy(v); return(NULL); }
1790 
1791   /* Initialize the subvector array to NULL */
1792   for (i=0; i<content->num_subvectors; i++)
1793     content->subvec_array[i] = NULL;
1794 
1795   /* Duplicate the input communicator (if applicable) */
1796 #if SUNDIALS_MPI_ENABLED
1797   if (MANYVECTOR_COMM(w) != MPI_COMM_NULL) {
1798     if (MPI_Comm_dup(MANYVECTOR_COMM(w), &(content->comm)) != MPI_SUCCESS)
1799       { N_VDestroy(v); return(NULL); }
1800   }
1801 #endif
1802 
1803   /* Clone vectors into the subvector array */
1804   for (i=0; i<content->num_subvectors; i++) {
1805     if (cloneempty) {
1806       content->subvec_array[i] = N_VCloneEmpty(MANYVECTOR_SUBVEC(w,i));
1807     } else {
1808       content->subvec_array[i] = N_VClone(MANYVECTOR_SUBVEC(w,i));
1809     }
1810     if (content->subvec_array[i] == NULL) {
1811       N_VDestroy(v);
1812       return(NULL);
1813     }
1814   }
1815 
1816   return(v);
1817 }
1818 
1819 
1820 #if SUNDIALS_MPI_ENABLED
1821 /* This function returns the rank of this task in the MPI communicator
1822    associated with the input N_Vector.  If the input N_Vector is MPI-unaware, it
1823    returns 0.  If an error occurs in the call to MPI_Comm_Rank, it returns -1. */
SubvectorMPIRank(N_Vector x)1824 static int SubvectorMPIRank(N_Vector x)
1825 {
1826   void* tmpcomm;
1827   MPI_Comm *comm;
1828   int rank, retval;
1829   tmpcomm = N_VGetCommunicator(x);
1830   if (tmpcomm == NULL) return(0);
1831   comm = (MPI_Comm *) tmpcomm;
1832   if ((*comm) == MPI_COMM_NULL) return(0);
1833   retval = MPI_Comm_rank(*comm, &rank);
1834   if (retval != MPI_SUCCESS)  return(-1);
1835   return(rank);
1836 }
1837 #endif
1838