Skip to content

Commit ec0b84c

Browse files
authored
Merge pull request #1925 from glotzerlab/fix-muvt-gibbs
Ensure that partitions have different seeds in MuVT.
2 parents dc042e5 + dcba850 commit ec0b84c

File tree

3 files changed

+94
-37
lines changed

3 files changed

+94
-37
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Change Log
1414

1515
* Correct compile errors with ``-DENABLE_GPU=on -DHOOMD_GPU_PLATFORM=HIP``
1616
(`#1920 <https://github.com/glotzerlab/hoomd-blue/pull/1915>`__)
17+
* Ensure that users set unique seeds on all partitions when performing Gibbs ensemble simulations
18+
(`#1925 <https://github.com/glotzerlab/hoomd-blue/pull/1925>`__)
1719

1820
4.9.0 (2024-10-29)
1921
^^^^^^^^^^^^^^^^^^

hoomd/RNGIdentifiers.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,16 @@ struct RNGIdentifier
3434
static const uint8_t UpdaterClusters = 8;
3535
static const uint8_t UpdaterClustersPairwise = 9;
3636
static const uint8_t UpdaterExternalFieldWall = 10;
37-
static const uint8_t UpdaterMuVT = 11;
37+
static const uint8_t UpdaterMuVTGroup = 11;
3838
static const uint8_t UpdaterMuVTDepletants1 = 12;
3939
static const uint8_t UpdaterMuVTDepletants2 = 13;
4040
static const uint8_t UpdaterMuVTDepletants3 = 14;
4141
static const uint8_t UpdaterMuVTDepletants4 = 15;
4242
static const uint8_t UpdaterMuVTDepletants5 = 16;
4343
static const uint8_t UpdaterMuVTDepletants6 = 17;
4444
static const uint8_t UpdaterMuVTPoisson = 18;
45-
static const uint8_t UpdaterMuVTBox1 = 19;
46-
static const uint8_t UpdaterMuVTBox2 = 20;
45+
static const uint8_t UpdaterMuVTInsertRemove = 19;
46+
static const uint8_t Unused1 = 20;
4747
static const uint8_t ActiveForceCompute = 21;
4848
static const uint8_t EvaluatorPairDPDThermo = 22;
4949
static const uint8_t IntegrationMethodTwoStep = 23;

hoomd/hpmc/UpdaterMuVT.h

Lines changed: 89 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ template<class Shape> class UpdaterMuVT : public Updater
156156
m_mc; //!< The MC Integrator this Updater is associated with
157157
unsigned int m_npartition; //!< The number of partitions to use for Gibbs ensemble
158158
bool m_gibbs; //!< True if we simulate a Gibbs ensemble
159+
uint16_t m_move_type_seed; //!< Random number seed to use for move types.
159160

160161
GPUVector<Scalar4> m_postype_backup; //!< Backup of postype array
161162

@@ -347,6 +348,8 @@ UpdaterMuVT<Shape>::UpdaterMuVT(std::shared_ptr<SystemDefinition> sysdef,
347348
m_gibbs = true;
348349
}
349350

351+
m_move_type_seed = m_sysdef->getSeed();
352+
350353
#ifdef ENABLE_MPI
351354
if (m_gibbs)
352355
{
@@ -361,6 +364,52 @@ UpdaterMuVT<Shape>::UpdaterMuVT(std::shared_ptr<SystemDefinition> sysdef,
361364

362365
m_exec_conf->msg->notice(5) << "Constructing UpdaterMuVT: Gibbs ensemble with "
363366
<< m_npartition << " partitions" << std::endl;
367+
368+
// Ensure that the user sets unique seeds on all partitions so that local trial moves
369+
// are decorrelated.
370+
unsigned int my_partition = this->m_exec_conf->getPartition();
371+
unsigned int my_group = this->m_exec_conf->getPartition() / npartition;
372+
uint16_t my_seed = this->m_sysdef->getSeed();
373+
374+
for (unsigned int check_partition = 0;
375+
check_partition < this->m_exec_conf->getNPartitions();
376+
check_partition++)
377+
{
378+
unsigned int check_group = check_partition / npartition;
379+
uint16_t check_seed = my_seed;
380+
MPI_Bcast(&check_seed,
381+
1,
382+
MPI_UINT16_T,
383+
check_partition * m_exec_conf->getMPIConfig()->getNRanks(),
384+
m_exec_conf->getHOOMDWorldMPICommunicator());
385+
386+
if (my_group == check_group && my_partition != check_partition && my_seed == check_seed)
387+
{
388+
std::ostringstream s;
389+
s << "Each partition within a group must set a unique seed. ";
390+
s << "Partition " << check_partition << "'s " << "seed (" << check_seed << ") ";
391+
s << "matches partition " << my_partition << "'s";
392+
throw std::runtime_error(s.str());
393+
}
394+
}
395+
396+
// synchronize move types across all ranks within each group
397+
for (unsigned int group = 0; group < this->m_exec_conf->getNPartitions() / npartition;
398+
group++)
399+
{
400+
uint16_t tmp = m_move_type_seed;
401+
MPI_Bcast(&tmp,
402+
1,
403+
MPI_UINT16_T,
404+
group * npartition * this->m_exec_conf->getNRanks(),
405+
m_exec_conf->getHOOMDWorldMPICommunicator());
406+
407+
unsigned int my_group = this->m_exec_conf->getPartition() / npartition;
408+
if (my_group == group)
409+
{
410+
m_move_type_seed = tmp;
411+
}
412+
}
364413
}
365414
else
366415
#endif
@@ -865,9 +914,11 @@ template<class Shape> void UpdaterMuVT<Shape>::update(uint64_t timestep)
865914

866915
// initialize random number generator
867916
unsigned int group = (m_exec_conf->getPartition() / m_npartition);
917+
unsigned int partition = (m_exec_conf->getPartition() % m_npartition);
868918

869-
hoomd::RandomGenerator rng(
870-
hoomd::Seed(hoomd::RNGIdentifier::UpdaterMuVT, timestep, this->m_sysdef->getSeed()),
919+
// Make a RNG that is seeded the same across the whole group
920+
hoomd::RandomGenerator rng_group(
921+
hoomd::Seed(hoomd::RNGIdentifier::UpdaterMuVTGroup, timestep, m_move_type_seed),
871922
hoomd::Counter(group));
872923

873924
bool active = true;
@@ -884,31 +935,30 @@ template<class Shape> void UpdaterMuVT<Shape>::update(uint64_t timestep)
884935
// the other MPI partition
885936
if (m_gibbs)
886937
{
887-
unsigned int p = m_exec_conf->getPartition() % m_npartition;
888-
889938
// choose a random pair of communicating boxes
890-
src = hoomd::UniformIntDistribution(m_npartition - 1)(rng);
891-
dest = hoomd::UniformIntDistribution(m_npartition - 2)(rng);
939+
src = hoomd::UniformIntDistribution(m_npartition - 1)(rng_group);
940+
dest = hoomd::UniformIntDistribution(m_npartition - 2)(rng_group);
892941
if (src <= dest)
893942
dest++;
894943

895-
if (p == src)
944+
if (partition == src)
896945
{
897946
m_gibbs_other = (dest + group * m_npartition) * m_exec_conf->getNRanks();
898947
mod = 0;
899948
}
900-
if (p == dest)
949+
if (partition == dest)
901950
{
902951
m_gibbs_other = (src + group * m_npartition) * m_exec_conf->getNRanks();
903952
mod = 1;
904953
}
905-
if (p != src && p != dest)
954+
if (partition != src && partition != dest)
906955
{
907956
active = false;
908957
}
909958

910959
// order the expanded ensembles
911-
volume_move = hoomd::detail::generate_canonical<Scalar>(rng) < m_volume_move_probability;
960+
Scalar r = hoomd::detail::generate_canonical<Scalar>(rng_group);
961+
volume_move = r < m_volume_move_probability;
912962

913963
if (active && m_exec_conf->getRank() == 0)
914964
{
@@ -973,7 +1023,14 @@ template<class Shape> void UpdaterMuVT<Shape>::update(uint64_t timestep)
9731023
#endif
9741024

9751025
// whether we insert or remove a particle
976-
bool insert = m_gibbs ? mod : hoomd::UniformIntDistribution(1)(rng);
1026+
bool insert = m_gibbs ? mod : hoomd::UniformIntDistribution(1)(rng_group);
1027+
1028+
// Use a partition specific RNG stream on each partition in Gibbs ensembles.
1029+
hoomd::RandomGenerator rng_insert_remove(
1030+
hoomd::Seed(hoomd::RNGIdentifier::UpdaterMuVTInsertRemove,
1031+
timestep,
1032+
this->m_sysdef->getSeed()),
1033+
hoomd::Counter(group, partition));
9771034

9781035
if (insert)
9791036
{
@@ -992,7 +1049,7 @@ template<class Shape> void UpdaterMuVT<Shape>::update(uint64_t timestep)
9921049
{
9931050
// choose a random particle type out of those being inserted or removed
9941051
type = m_transfer_types[hoomd::UniformIntDistribution(
995-
(unsigned int)(m_transfer_types.size() - 1))(rng)];
1052+
(unsigned int)(m_transfer_types.size() - 1))(rng_insert_remove)];
9961053
}
9971054
else
9981055
{
@@ -1045,23 +1102,23 @@ template<class Shape> void UpdaterMuVT<Shape>::update(uint64_t timestep)
10451102

10461103
// Propose a random position uniformly in the box
10471104
Scalar3 f;
1048-
f.x = hoomd::detail::generate_canonical<Scalar>(rng);
1049-
f.y = hoomd::detail::generate_canonical<Scalar>(rng);
1105+
f.x = hoomd::detail::generate_canonical<Scalar>(rng_insert_remove);
1106+
f.y = hoomd::detail::generate_canonical<Scalar>(rng_insert_remove);
10501107
if (m_sysdef->getNDimensions() == 2)
10511108
{
10521109
f.z = Scalar(0.5);
10531110
}
10541111
else
10551112
{
1056-
f.z = hoomd::detail::generate_canonical<Scalar>(rng);
1113+
f.z = hoomd::detail::generate_canonical<Scalar>(rng_insert_remove);
10571114
}
10581115
vec3<Scalar> pos_test = vec3<Scalar>(m_pdata->getGlobalBox().makeCoordinates(f));
10591116

10601117
Shape shape_test(quat<Scalar>(), param);
10611118
if (shape_test.hasOrientation())
10621119
{
10631120
// set particle orientation
1064-
shape_test.orientation = generateRandomOrientation(rng, ndim);
1121+
shape_test.orientation = generateRandomOrientation(rng_insert_remove, ndim);
10651122
}
10661123

10671124
if (m_gibbs)
@@ -1140,7 +1197,8 @@ template<class Shape> void UpdaterMuVT<Shape>::update(uint64_t timestep)
11401197
bool accept = false;
11411198
if (nonzero)
11421199
{
1143-
accept = (hoomd::detail::generate_canonical<double>(rng) < exp(lnboltzmann));
1200+
accept = (hoomd::detail::generate_canonical<double>(rng_insert_remove)
1201+
< exp(lnboltzmann));
11441202
}
11451203

11461204
#ifdef ENABLE_MPI
@@ -1190,24 +1248,19 @@ template<class Shape> void UpdaterMuVT<Shape>::update(uint64_t timestep)
11901248
// try removing a particle
11911249
unsigned int tag = UINT_MAX;
11921250

1193-
// in Gibbs ensemble, we should not use correlated random numbers with box 1
1194-
hoomd::RandomGenerator rng_local(hoomd::Seed(hoomd::RNGIdentifier::UpdaterMuVTBox1,
1195-
timestep,
1196-
this->m_sysdef->getSeed()),
1197-
hoomd::Counter(group));
1198-
11991251
// choose a random particle type out of those being transferred
12001252
assert(m_transfer_types.size() > 0);
12011253
unsigned int type = m_transfer_types[hoomd::UniformIntDistribution(
1202-
(unsigned int)(m_transfer_types.size() - 1))(rng_local)];
1254+
(unsigned int)(m_transfer_types.size() - 1))(rng_insert_remove)];
12031255

12041256
// choose a random particle of that type
12051257
unsigned int nptl_type = getNumParticlesType(type);
12061258

12071259
if (nptl_type)
12081260
{
12091261
// get random tag of given type
1210-
unsigned int type_offset = hoomd::UniformIntDistribution(nptl_type - 1)(rng_local);
1262+
unsigned int type_offset
1263+
= hoomd::UniformIntDistribution(nptl_type - 1)(rng_insert_remove);
12111264
tag = getNthTypeTag(type, type_offset);
12121265
}
12131266

@@ -1319,8 +1372,8 @@ template<class Shape> void UpdaterMuVT<Shape>::update(uint64_t timestep)
13191372
// apply acceptance criterion
13201373
if (nonzero)
13211374
{
1322-
accept
1323-
= (hoomd::detail::generate_canonical<double>(rng_local) < exp(lnboltzmann));
1375+
accept = (hoomd::detail::generate_canonical<double>(rng_insert_remove)
1376+
< exp(lnboltzmann));
13241377
}
13251378
else
13261379
{
@@ -1410,18 +1463,20 @@ template<class Shape> void UpdaterMuVT<Shape>::update(uint64_t timestep)
14101463

14111464
if (mod == 0)
14121465
{
1413-
Scalar ln_V_new = log(V / V_other)
1414-
+ (hoomd::detail::generate_canonical<Scalar>(rng) - Scalar(0.5))
1415-
* m_max_vol_rescale;
1466+
Scalar ln_V_new
1467+
= log(V / V_other)
1468+
+ (hoomd::detail::generate_canonical<Scalar>(rng_group) - Scalar(0.5))
1469+
* m_max_vol_rescale;
14161470
V_new = (V + V_other) * exp(ln_V_new) / (Scalar(1.0) + exp(ln_V_new));
14171471
V_new_other
14181472
= (V + V_other) * (Scalar(1.0) - exp(ln_V_new) / (Scalar(1.0) + exp(ln_V_new)));
14191473
}
14201474
else
14211475
{
1422-
Scalar ln_V_new = log(V_other / V)
1423-
+ (hoomd::detail::generate_canonical<Scalar>(rng) - Scalar(0.5))
1424-
* m_max_vol_rescale;
1476+
Scalar ln_V_new
1477+
= log(V_other / V)
1478+
+ (hoomd::detail::generate_canonical<Scalar>(rng_group) - Scalar(0.5))
1479+
* m_max_vol_rescale;
14251480
V_new
14261481
= (V + V_other) * (Scalar(1.0) - exp(ln_V_new) / (Scalar(1.0) + exp(ln_V_new)));
14271482
}
@@ -1538,7 +1593,7 @@ template<class Shape> void UpdaterMuVT<Shape>::update(uint64_t timestep)
15381593
+ log(V_new_other / V_other) * (Scalar)(other_ndof + 1) + lnb
15391594
+ other_lnb;
15401595

1541-
accept = hoomd::detail::generate_canonical<double>(rng) < exp(arg);
1596+
accept = hoomd::detail::generate_canonical<double>(rng_group) < exp(arg);
15421597
accept &= !(has_overlaps || other_result);
15431598

15441599
// communicate if accepted

0 commit comments

Comments
 (0)