Zoltan2
Loading...
Searching...
No Matches
directoryTest_KokkosSimple.cpp
Go to the documentation of this file.
1// @HEADER
2//
3// ***********************************************************************
4//
5// Zoltan2: A package of combinatorial algorithms for scientific computing
6// Copyright 2012 Sandia Corporation
7//
8// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
9// the U.S. Government retains certain rights in this software.
10//
11// Redistribution and use in source and binary forms, with or without
12// modification, are permitted provided that the following conditions are
13// met:
14//
15// 1. Redistributions of source code must retain the above copyright
16// notice, this list of conditions and the following disclaimer.
17//
18// 2. Redistributions in binary form must reproduce the above copyright
19// notice, this list of conditions and the following disclaimer in the
20// documentation and/or other materials provided with the distribution.
21//
22// 3. Neither the name of the Corporation nor the names of the
23// contributors may be used to endorse or promote products derived from
24// this software without specific prior written permission.
25//
26// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
27// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
30// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37//
38// Questions? Contact Karen Devine (kddevin@sandia.gov)
39// Erik Boman (egboman@sandia.gov)
40// Siva Rajamanickam (srajama@sandia.gov)
41//
42// ***********************************************************************
43//
44// @HEADER
45
47#include "Tpetra_Core.hpp"
48
49// This type will be used by some of these tests
51 public:
52 // Kokkos will need an empty constructor so either provide one or
53 // make a simple struct with no constructors will also work
55 gid_struct(int v0, int v1, int v2, int v3) {
56 val[0] = v0;
57 val[1] = v1;
58 val[2] = v2;
59 val[3] = v3;
60 }
61 int val[4]; // can be any length but this can't have mem alloc (like std::vector)
62};
63
64// same as gid but for better coverage of testing, make it different
66 public:
67 // Kokkos will need an empty constructor so either provide one or
68 // make a simple struct with no constructors will also work
70 lid_struct(unsigned long v0, unsigned long v1) {
71 val[0] = v0;
72 val[1] = v1;
73 }
74 unsigned long val[2]; // can be any length but this can't have mem alloc (like std::vector)
75};
76
77// a very simple test of replace
78// after calling update and find we do a second round with new gids
79// to make sure repeated update calls will work.
80int test_simple_replace(Teuchos::RCP<const Teuchos::Comm<int> > comm) {
81
82 int err = 0;
83
84 // define a few constants/helpers
85 typedef int test_lid_t; // lid will be ignored in this case
86 typedef int test_gid_t;
87 typedef long user_t;
88
89 // set up the directory type - this is the single user mode (not vector user)
91
92 // create the directory
93 directory_t directory(comm, false, 0);
94
95 // now create some gids to write
96 // to keep things interesting we'll have just proc 0 write 4 Ids and all the
97 // other procs right the same gid (3).
98 std::vector<test_gid_t> writeGIDs;
99
100 if(comm->getRank() == 0) {
101 writeGIDs = { 1, 5, 7, 10 };
102 }
103 else {
104 writeGIDs = { 3 };
105 }
106
107 // now create some user values associated with above gids
108 // for easy reference just make them gid x 10
109 std::vector<user_t> writeUser;
110 if(comm->getRank() == 0) {
111 writeUser = { 10, 50, 70, 100 };
112 }
113 else {
114 writeUser = { 30 };
115 }
116
117 // call update on the directory
118 directory.update(writeGIDs.size(), &writeGIDs[0], NULL, &writeUser[0], NULL,
119 directory_t::Replace);
120
121 // now pick some gids to find
122 std::vector<test_gid_t> findIds = { 3, 5 };
123
124 // now create a user space to accept the values
125 // Setting this empty will turn off this option. The original directory uses
126 // ptrs and null which has some advantages so this API choice might need some
127 // better setup.
128 std::vector<user_t> findUser(findIds.size());
129
130 // now call find which will fill findUser
131 // for special case of mpi proc 1 the find of gid 3 will give a throw
132 // because rank 1 never existed to write it. This test is currently setup
133 // so it will pass for any mpi count so for this special case we turn this
134 // into a test to verify the throw happened. If mpi count is not 1, then
135 // a throw will not happen (or if it does it's a real error).
136 // However maybe it's better to change this so the simple test example
137 // doesn't have this complication ... to do.
138 try {
139 directory.find(findIds.size(), &findIds[0], NULL, &findUser[0], NULL, NULL);
140
141 // now check element 0 in the array - make sure it matches writeUser above
142 // all procs except 0 sent this one
143 if(findUser[0] != 30) {
144 std::cout << "We should have gotten 30 for this value." << std::endl;
145 ++err;
146 }
147
148 // only proc 0 sent the second one - make sure all procs can read it
149 if(findUser[1] != 50) {
150 std::cout << "We should have gotten 50 for this value." << std::endl;
151 ++err;
152 }
153 }
154 catch(std::logic_error &e) {
155 if(comm->getSize() != 1) {
156 err = 1;
157 }
158 else {
159 std::cout << "Successfully detected throw since find failed." << std::endl;
160 }
161 }
162
163 // Phase 2
164 // now let's test further by updating with some more values
165 // we'll try a mix of preexisting values and new values
166 // this adds some unit testing coverage for the case when update is called
167 // twice on the same directory, not implemented in the general unit tests.
168 std::vector<test_gid_t> writeGIDs2;
169
170 if(comm->getRank() == 0) {
171 writeGIDs2 = { 5, 700, 1000 };
172 }
173 else {
174 writeGIDs2 = { 3, 200 };
175 }
176
177 std::vector<user_t> writeUser2;
178 if(comm->getRank() == 0) {
179 writeUser2 = { 50, 7000, 10000 };
180 }
181 else {
182 writeUser2 = { 30, 2000 };
183 }
184
185 // call update on the directory (this will be the second round)
186 directory.update(writeGIDs2.size(), &writeGIDs2[0], NULL, &writeUser2[0], NULL,
187 directory_t::Replace);
188
189 // now pick some gids to find
190 std::vector<test_gid_t> findIds2 = { 1, 5, 1000 };
191
192 // now create a user space to accept the values
193 std::vector<user_t> findUser2(findIds2.size());
194 directory.find(findIds2.size(), &findIds2[0], NULL, &findUser2[0], NULL, NULL);
195
196 // validate the results
197 // index 0 (gid 1) was updated on the first round (not second)
198 // index 1 (gid 5) was updated on both rounds
199 // index 2 (gid 1000) was updated on the second round (not first)
200 if(findUser2[0] != 10) {
201 std::cout << "We should have gotten 10 for this value. " << std::endl;
202 ++err;
203 }
204 if(findUser2[1] != 50) {
205 std::cout << "We should have gotten 50 for this value." << std::endl;
206 ++err;
207 }
208
209 // only proc 0 sent the second one - make sure all procs can read it
210 if(findUser2[2] != 10000) {
211 std::cout << "We should have gotten 10000 for this value." << std::endl;
212 ++err;
213 }
214
215 return err;
216}
217
218// an example of aggregate mode using vector type
219int test_aggregate(Teuchos::RCP<const Teuchos::Comm<int> > comm) {
220
221 int err = 0;
222
223 // define a few constants/helpers
224 typedef int test_lid_t; // lid will be ignored in this case
225 typedef long test_gid_t;
226 typedef std::vector<long long> user_t; // now user is a vector of long long
227
228 // set up the directory type - this is the vector user mode
229 // the way things are setup right now a std::vector user_t must use this
230 // class - maybe eventually this could all be handling by templating but
231 // I think this might be cleaner and more performant
233 directory_t;
234
235 // create the directory
236 directory_t directory(comm, false, 0);
237
238 // now create some gids to write based on the rank
239 std::vector<test_gid_t> writeGIDs;
240 switch(comm->getRank()) {
241 case 0:
242 writeGIDs = { 3, 5 }; // rank 0 writes only 3 and 5
243 break;
244 case 1:
245 writeGIDs = { 3, 7 }; // rank 1 writes only 3 and 7
246 break;
247 default:
248 writeGIDs = { 5 }; // all other ranks write only 5
249 break;
250 }
251
252 // now create some user values associated with above gids
253 // in this case we can make the values different lengths
254 std::vector<user_t> writeUser;
255 switch(comm->getRank()) {
256 case 0:
257 writeUser = { {1,2}, {4,8,10} }; // rank 0 writes only 3 and 5
258 break;
259 case 1:
260 writeUser = { {2,10}, {6,8} }; // rank 1 writes only 3 and 7
261 break;
262 default:
263 writeUser = { {1,2,10} }; // all other ranks write only 5
264 break;
265 }
266
267 // call update on the directory
268 directory.update(writeGIDs.size(), &writeGIDs[0], NULL, &writeUser[0], NULL,
269 directory_t::Aggregate);
270
271 // now pick some gids to find - could make this rank specific
272 std::vector<test_gid_t> findIds = { 3, 5 };
273
274 // now create a user space to accept the values
275 // Setting this empty will turn off this option. The original directory uses
276 // ptrs and null which has some advantages so this API choice might need some
277 // better setup. Note that findUser will be a set of empty std::vector
278 // in this case and each will be individually filled by the directory with
279 // the correct aggregated result
280 std::vector<user_t> findUser(findIds.size());
281
282 // now call find which will fill findUser
283 directory.find(findIds.size(), &findIds[0], NULL, &findUser[0], NULL, NULL);
284
285 // now check element 0 in findUser
286 // that was for gid = 3 so from the above we expect the following:
287 // rank 0 provided user value {1,2}
288 // rank 1 provided user value {2,10}
289 // all other ranks provided nothing for gid 3
290 // Therefore the aggregated result should be {1,2} + {2,10} = {1,2,10}
291 // For a little variation run as MPI 1 proc, then rank 1 will never write
292 // and the expected value is just {1,2}.
293 user_t expectedValue0 =
294 (comm->getSize() == 1) ? user_t({1,2}) : user_t({1,2,10});
295 if(findUser[0] != expectedValue0) {
296 std::cout << "findUser[0] did not match expected." << std::endl;
297 ++err;
298 }
299
300 // now check element 1 in findUser
301 // that was for gid = 5 so from the above we expect the following:
302 // rank 0 provided user value {4,8,10}
303 // rank 1 provided nothing
304 // all other ranks provided {1,2,10}
305 // Therefore the aggregated result should be {4,8,10} + {1,2,10} = {1,2,4,8,10}
306 // Again for variation can run this with proc count < 2, in which case
307 // the expected resut will be just {4,8}.
308 user_t expectedValue1 =
309 (comm->getSize() <= 2) ? user_t({4,8,10}) : user_t({1,2,4,8,10});
310 if(findUser[1] != expectedValue1) {
311 std::cout << "findUser[1] did not match expected." << std::endl;
312 ++err;
313 }
314
315 return err;
316}
317
318// an example of using the directory with a gid defined as a struct
319int test_multiple_gid(Teuchos::RCP<const Teuchos::Comm<int> > comm) {
320
321 int err = 0;
322
323 // define a few constants/helpers
324 typedef int test_lid_t; // lid will be ignored in this case
325 typedef gid_struct test_gid_t;
326 typedef int user_t;
327
328 // set up the directory type - this is the single user mode (not vector user)
330 directory_t;
331
332 // create the directory
333 directory_t directory(comm, false, 0);
334
335 // set up some test gids
336 // in this case the entire set of values is the gid
337 std::vector<test_gid_t> writeGIDs;
338 test_gid_t gid1(1, 8, 7, 3);
339 test_gid_t gid2(1, 8, 7, 4); // this is a completely different gid from the prior
340
341 // let's have rank 0 write gid1 and gid2 while other ranks write gid2 only
342 if(comm->getRank() == 0) {
343 writeGIDs = { gid1, gid2 };
344 }
345 else {
346 writeGIDs = { gid2 };
347 }
348
349 // now create some user values associated with above gids
350 // since this will be an add test, just write 1 for all of them
351 // we expect gid1 to end up unchanged (only rank 0 writes it)
352 // but we expect gid2 to end up with value equal to comm->getSize()
353 // because all ranks will contribute 1 to the sum
354 std::vector<user_t> writeUser;
355 if(comm->getRank() == 0) {
356 writeUser = { 1, 1 }; // two values because rank 0 write both gid1 and gid2
357 }
358 else {
359 writeUser = { 1 }; // one value because other ranks only write gid2
360 }
361
362 // call update on the directory using add mode
363 directory.update(writeGIDs.size(), &writeGIDs[0], NULL, &writeUser[0], NULL,
364 directory_t::Add);
365
366 // now check them on all ranks - this could be different for different ranks
367 std::vector<test_gid_t> findIds = { gid1, gid2 };
368
369 // create a user space to accept the values
370 // Setting this empty will turn off this option. The original directory uses
371 // ptrs and null which has some advantages so this API choice might need some
372 // better setup.
373 std::vector<user_t> findUser(findIds.size());
374
375 // now call find which will fill findUser
376 directory.find(findIds.size(), &findIds[0], NULL, &findUser[0], NULL, NULL);
377
378 // now check element 0 in the array which should have value 1
379 if(findUser[0] != 1) {
380 std::cout << "We should have gotten 1 for gid1. Only rank 0 wrote to gid1"
381 " so the sum should have been 1." << std::endl;
382 ++err;
383 }
384
385 // now check element 1 in the array should have value comm->getSize()
386 // that is because each rank contributed 1 and we are in Add mode
387 if(findUser[1] != comm->getSize()) {
388 std::cout << "We should have gotten the proc count " << comm->getSize()
389 << " since every rank wrote to gid2." << std::endl;
390 ++err;
391 }
392
393 return err;
394}
395
396// an example of using the directory with an lid defined as a struct
397// this is similar to the prior test but here we will use both
398// gid and lid as different structs and also do a find with user data
399// set ignored just to get some more coverage of the possibilities.
400int test_multiple_lid(Teuchos::RCP<const Teuchos::Comm<int> > comm) {
401
402 int err = 0;
403
404 // define a few constants/helpers
405 typedef gid_struct test_gid_t;
406 typedef lid_struct test_lid_t;
407 typedef int user_t;
408
410 directory_t;
411
412 // create the directory
413 directory_t directory(comm, true, 0);
414
415 // set up some test gids
416 // in this case the entire set of values is the gid
417 std::vector<test_gid_t> writeGIDs;
418 test_gid_t gid1(1, 8, 7, 3);
419 test_gid_t gid2(1, 8, 7, 4); // this is a completely different gid from the prior
420
421 // set up some test lids
422 // in this case the entire set of values is the lid
423 std::vector<test_lid_t> writeLIDs;
424 test_lid_t lid1(500, 2009);
425 test_lid_t lid2(500, 8000); // this is a completely different lid from the prior
426
427 // let's have rank 0 write gid1 and gid2 while other ranks write gid2 only
428 if(comm->getRank() == 0) {
429 writeGIDs = { gid1, gid2 };
430 writeLIDs = { lid1, lid2 };
431 }
432 else {
433 writeGIDs = { gid2 };
434 writeLIDs = { lid2 };
435 }
436
437 std::vector<user_t> writeUser;
438 if(comm->getRank() == 0) {
439 writeUser = { 1, 1 }; // two values because rank 0 write both gid1 and gid2
440 }
441 else {
442 writeUser = { 1 }; // one value because other ranks only write gid2
443 }
444
445 // call update on the directory using add mode
446 directory.update(writeGIDs.size(), &writeGIDs[0], &writeLIDs[0], &writeUser[0],
447 NULL, directory_t::Replace);
448
449 // now check them on all ranks - this could be different for different ranks
450 std::vector<test_gid_t> findGIDs = { gid1, gid2 };
451
452 // create lid space to accept the lid values
453 std::vector<test_lid_t> findLIDs(findGIDs.size());
454
455 // now call find which will fill findLIDs
456 directory.find(findGIDs.size(), &findGIDs[0], &findLIDs[0], NULL, NULL, NULL);
457
458 // now check element 0 in the array is matched to lid1
459 if(findLIDs[0].val[0] != lid1.val[0] || findLIDs[0].val[1] != lid1.val[1]) {
460 std::cout << "We should have gotten [500,2009] for lid1." << std::endl;
461 ++err;
462 }
463
464 // now check element 1 in the array is matched to lid2
465 if(findLIDs[1].val[0] != lid2.val[0] || findLIDs[1].val[1] != lid2.val[1]) {
466 std::cout << "We should have gotten [500,8000] for lid1." << std::endl;
467 ++err;
468 }
469
470 // create user space to accept the user values
471 std::vector<user_t> findUser(findGIDs.size());
472
473 // now call find which will fill findLIDs and findUser
474 directory.find(findGIDs.size(), &findGIDs[0], &findLIDs[0], &findUser[0],
475 NULL, NULL);
476
477 // now check element 0 in the array which should have value 1
478 if(findUser[0] != 1) {
479 std::cout << "We should have gotten 1 for gid1. Only rank 0 wrote to gid1"
480 " so the sum should have been 1." << std::endl;
481 ++err;
482 }
483
484 // now check element 1 in the array should have value comm->getSize()
485 // that is because each rank contributed 1 and we are in Add mode
486 if(findUser[1] != 1) {
487 std::cout << "We should have gotten the proc count " << comm->getSize()
488 << " since every rank wrote to gid2." << std::endl;
489 ++err;
490 }
491
492 return err;
493}
494
495int main(int narg, char **arg) {
496 Tpetra::ScopeGuard tscope(&narg, &arg);
497 Teuchos::RCP<const Teuchos::Comm<int> > comm =
498 Teuchos::DefaultComm<int>::getComm();
499
500 // will reduce err on all ranks
501 int err = 0;
502
503 // run some tests
504 err += test_simple_replace(comm);
505 err += test_aggregate(comm);
506 err += test_multiple_gid(comm);
507 err += test_multiple_lid(comm);
508
509 // Get the global err results so we fail properly if any proc failed
510 int errGlobal;
511 Teuchos::reduceAll<int>(*comm,Teuchos::REDUCE_SUM, err,
512 Teuchos::outArg(errGlobal));
513
514 // this proc is ok
515 comm->barrier();
516 if(comm->getRank() == 0) {
517 if(errGlobal == 0) {
518 std::cout << "Passed" << std::endl;
519 }
520 else {
521 std::cout << "FAILED!" << std::endl;
522 }
523 }
524
525 return errGlobal;
526}
Zoltan2::BasicUserTypes< zscalar_t, zlno_t, zgno_t > user_t
Definition: Metric.cpp:74
int main()
gid_struct(int v0, int v1, int v2, int v3)
lid_struct(unsigned long v0, unsigned long v1)
unsigned long val[2]
int test_aggregate(Teuchos::RCP< const Teuchos::Comm< int > > comm)
int test_multiple_gid(Teuchos::RCP< const Teuchos::Comm< int > > comm)
int test_multiple_lid(Teuchos::RCP< const Teuchos::Comm< int > > comm)
int test_simple_replace(Teuchos::RCP< const Teuchos::Comm< int > > comm)