Skip to content

Commit ef5e970

Browse files
author
abacus_fixer
committed
test(parallel): add boundary tests for ParallelPartition
Add 5 boundary test cases covering previously untested scenarios: 1. NonUniformKparPoolRootRank: 10 procs / kpar=3, pools {4,3,3}, verify pool_root_rank returns correct world ranks (0, 4, 7). 2. BndparKparCrossBandGroupRootRank: 12 procs / kpar=3 / bndpar=2, verify band_group_root_rank uses pool 0's slice formula (bg * nproc_in_pool[0] / bndpar), not bg * nproc_in_band_group. 3. OutOfRangeReturnsMinusOne: verify pool_root_rank / band_group_root_rank return -1 on bad indices; also added bounds check to nproc_in_pool(int pool) to return 0 instead of UB on out-of-range. 4. SerialFallbackDefaultConstructor: verify default-constructed ParallelPartition is a safe single-process trivial partition. 5. NonUniformKparSevenProcsThreePools: integration test for create_partition with 7 procs / kpar=3 -> pools {3,2,2}. Total: 13/13 tests PASSED (4-rank mpirun).
1 parent d47dfd0 commit ef5e970

2 files changed

Lines changed: 191 additions & 1 deletion

File tree

source/source_base/parallel_partition.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,12 @@ class ParallelPartition
101101
int my_pool() const { return my_pool_; }
102102
int rank_in_pool() const { return rank_in_pool_; }
103103
const std::vector<int>& nproc_in_pool() const { return nproc_in_pool_; }
104-
int nproc_in_pool(int pool) const { return nproc_in_pool_[pool]; }
104+
int nproc_in_pool(int pool) const
105+
{
106+
return (pool >= 0 && pool < static_cast<int>(nproc_in_pool_.size()))
107+
? nproc_in_pool_[pool]
108+
: 0;
109+
}
105110
/// World rank of the root (rank 0) of a given pool; -1 on bad index.
106111
int pool_root_rank(int pool) const;
107112

source/source_base/test_parallel/parallel_partition_test.cpp

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,191 @@ TEST(ParallelGlobalCreatePartition, FourRanksKpar2Bndpar2DiagNp2)
542542
#endif
543543
}
544544

545+
// ================================================================
546+
// Boundary tests: pure arithmetic, no MPI needed.
547+
// ================================================================
548+
549+
// Non-uniform kpar: 10 procs / kpar=3 -> pools {4, 3, 3}.
550+
// pool_root_rank must return the world rank of rank 0 in each pool.
551+
TEST(ParallelPartition, NonUniformKparPoolRootRank)
552+
{
553+
const std::vector<int> pool_sizes = {4, 3, 3};
554+
const ParallelPartition t(/*world=*/10,
555+
/*my_rank=*/5,
556+
/*kpar=*/3,
557+
/*my_pool=*/1,
558+
/*rank_in_pool=*/2,
559+
pool_sizes,
560+
/*bndpar=*/1,
561+
/*my_band_group=*/0,
562+
/*rank_in_band_group=*/5,
563+
/*nproc_in_band_group=*/10
564+
#ifdef __MPI
565+
,
566+
MPI_COMM_SELF, MPI_COMM_NULL, MPI_COMM_SELF,
567+
MPI_COMM_NULL, MPI_COMM_NULL, MPI_COMM_NULL,
568+
MPI_COMM_NULL, MPI_COMM_NULL
569+
#endif
570+
);
571+
572+
EXPECT_EQ(t.kpar(), 3);
573+
EXPECT_EQ(t.nproc_in_pool(), pool_sizes);
574+
EXPECT_EQ(t.nproc_in_pool(0), 4);
575+
EXPECT_EQ(t.nproc_in_pool(1), 3);
576+
EXPECT_EQ(t.nproc_in_pool(2), 3);
577+
578+
// Pool roots: pool 0 starts at wr 0; pool 1 at wr 4; pool 2 at wr 7.
579+
EXPECT_EQ(t.pool_root_rank(0), 0);
580+
EXPECT_EQ(t.pool_root_rank(1), 4);
581+
EXPECT_EQ(t.pool_root_rank(2), 7);
582+
}
583+
584+
// bndpar>1 + kpar>1 cross: 12 procs / kpar=3 / bndpar=2.
585+
// Each pool {4,4,4}, each pool split into 2 band-groups of 2.
586+
TEST(ParallelPartition, BndparKparCrossBandGroupRootRank)
587+
{
588+
const std::vector<int> pool_sizes = {4, 4, 4};
589+
const ParallelPartition t(/*world=*/12,
590+
/*my_rank=*/5,
591+
/*kpar=*/3,
592+
/*my_pool=*/1,
593+
/*rank_in_pool=*/1,
594+
pool_sizes,
595+
/*bndpar=*/2,
596+
/*my_band_group=*/1,
597+
/*rank_in_band_group=*/5,
598+
/*nproc_in_band_group=*/6
599+
#ifdef __MPI
600+
,
601+
MPI_COMM_SELF, MPI_COMM_NULL, MPI_COMM_SELF,
602+
MPI_COMM_NULL, MPI_COMM_NULL, MPI_COMM_NULL,
603+
MPI_COMM_NULL, MPI_COMM_NULL
604+
#endif
605+
);
606+
607+
// nproc_in_band_group = kpar * (nproc_in_pool[0] / bndpar) = 3 * 2 = 6
608+
EXPECT_EQ(t.nproc_in_band_group(), 6);
609+
610+
// band_group_root_rank uses pool 0's slice:
611+
// bg0 root = 0 * (4/2) = 0
612+
// bg1 root = 1 * (4/2) = 2
613+
// NOT band_group * nproc_in_band_group (would give 6, wrong).
614+
EXPECT_EQ(t.band_group_root_rank(0), 0);
615+
EXPECT_EQ(t.band_group_root_rank(1), 2);
616+
}
617+
618+
// Out-of-range indices must return -1, not crash.
619+
TEST(ParallelPartition, OutOfRangeReturnsMinusOne)
620+
{
621+
const std::vector<int> pool_sizes = {4, 4, 4};
622+
const ParallelPartition t(/*world=*/12,
623+
/*my_rank=*/0,
624+
/*kpar=*/3,
625+
/*my_pool=*/0,
626+
/*rank_in_pool=*/0,
627+
pool_sizes,
628+
/*bndpar=*/2,
629+
/*my_band_group=*/0,
630+
/*rank_in_band_group=*/0,
631+
/*nproc_in_band_group=*/6
632+
#ifdef __MPI
633+
,
634+
MPI_COMM_SELF, MPI_COMM_NULL, MPI_COMM_SELF,
635+
MPI_COMM_NULL, MPI_COMM_NULL, MPI_COMM_NULL,
636+
MPI_COMM_NULL, MPI_COMM_NULL
637+
#endif
638+
);
639+
640+
// pool_root_rank: valid 0..2, invalid otherwise
641+
EXPECT_EQ(t.pool_root_rank(-1), -1);
642+
EXPECT_EQ(t.pool_root_rank(0), 0);
643+
EXPECT_EQ(t.pool_root_rank(2), 8);
644+
EXPECT_EQ(t.pool_root_rank(3), -1);
645+
EXPECT_EQ(t.pool_root_rank(99), -1);
646+
647+
// band_group_root_rank: valid 0..1, invalid otherwise
648+
EXPECT_EQ(t.band_group_root_rank(-1), -1);
649+
EXPECT_EQ(t.band_group_root_rank(0), 0);
650+
EXPECT_EQ(t.band_group_root_rank(1), 2);
651+
EXPECT_EQ(t.band_group_root_rank(2), -1);
652+
EXPECT_EQ(t.band_group_root_rank(99), -1);
653+
654+
// nproc_in_pool(int pool): valid 0..2 returns size, invalid returns 0
655+
EXPECT_EQ(t.nproc_in_pool(0), 4);
656+
EXPECT_EQ(t.nproc_in_pool(2), 4);
657+
EXPECT_EQ(t.nproc_in_pool(3), 0);
658+
EXPECT_EQ(t.nproc_in_pool(99), 0);
659+
EXPECT_EQ(t.nproc_in_pool(-1), 0);
660+
}
661+
662+
// Serial fallback: default-constructed partition is a safe trivial
663+
// single-process topology. This test runs even without MPI.
664+
TEST(ParallelPartition, SerialFallbackDefaultConstructor)
665+
{
666+
ParallelPartition t;
667+
EXPECT_EQ(t.world_size(), 1);
668+
EXPECT_EQ(t.world_rank(), 0);
669+
EXPECT_EQ(t.kpar(), 1);
670+
EXPECT_EQ(t.my_pool(), 0);
671+
EXPECT_EQ(t.rank_in_pool(), 0);
672+
EXPECT_EQ(t.nproc_in_pool(), std::vector<int>{1});
673+
EXPECT_EQ(t.nproc_in_pool(0), 1);
674+
EXPECT_EQ(t.pool_root_rank(0), 0);
675+
EXPECT_EQ(t.bndpar(), 1);
676+
EXPECT_EQ(t.my_band_group(), 0);
677+
EXPECT_EQ(t.rank_in_band_group(), 0);
678+
EXPECT_EQ(t.nproc_in_band_group(), 1);
679+
EXPECT_EQ(t.band_group_root_rank(0), 0);
680+
EXPECT_EQ(t.band_group_root_rank(1), -1); // only bg 0 exists
681+
#ifdef __MPI
682+
EXPECT_EQ(t.pw_world_comm(), MPI_COMM_SELF);
683+
EXPECT_EQ(t.matrix_world_comm(), MPI_COMM_NULL);
684+
EXPECT_EQ(t.atom_world_comm(), MPI_COMM_NULL);
685+
#endif
686+
}
687+
688+
// Integration: create_partition with non-uniform kpar (7 procs / kpar=3
689+
// -> pools {3,2,2}). Verifies the factory's nproc_in_pool vector matches
690+
// the arithmetic from divide_mpi_groups(even=false).
691+
TEST(ParallelGlobalCreatePartition, NonUniformKparSevenProcsThreePools)
692+
{
693+
int world_size = 1;
694+
int world_rank = 0;
695+
#ifdef __MPI
696+
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
697+
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
698+
#endif
699+
if (world_size != 7)
700+
{
701+
GTEST_SKIP() << "non-uniform kpar integration case requires exactly 7 MPI ranks (got "
702+
<< world_size << ").";
703+
}
704+
705+
const int kpar = 3;
706+
const int bndpar = 1;
707+
const int diag_np = 1;
708+
const ParallelPartition t = Parallel_Global::create_partition(
709+
world_size, world_rank, kpar, bndpar, diag_np, diag_np);
710+
711+
// divide_mpi_groups(7, 3, even=false) -> base=2, extra=1
712+
// group 0: 3 procs, group 1: 2, group 2: 2
713+
const std::vector<int> expected_pools = {3, 2, 2};
714+
EXPECT_EQ(t.kpar(), 3);
715+
EXPECT_EQ(t.nproc_in_pool(), expected_pools);
716+
EXPECT_EQ(t.world_size(), 7);
717+
718+
// Pool roots: 0, 3, 5
719+
EXPECT_EQ(t.pool_root_rank(0), 0);
720+
EXPECT_EQ(t.pool_root_rank(1), 3);
721+
EXPECT_EQ(t.pool_root_rank(2), 5);
722+
723+
// Every rank must end up in exactly one pool whose size matches.
724+
const int my_pool = t.my_pool();
725+
ASSERT_GE(my_pool, 0);
726+
ASSERT_LT(my_pool, kpar);
727+
EXPECT_EQ(t.nproc_in_pool(my_pool), expected_pools[my_pool]);
728+
}
729+
545730
int main(int argc, char** argv)
546731
{
547732
#ifdef __MPI

0 commit comments

Comments
 (0)