Skip to content

Commit 23ebdda

Browse files
authored
Merge pull request #1970 from MartinGirard/WangFrenkel
Wang frenkel
2 parents 937bfbd + db96b5d commit 23ebdda

File tree

14 files changed

+460
-3
lines changed

14 files changed

+460
-3
lines changed

CHANGELOG.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ Change Log
99

1010
*Added*
1111

12+
* The ``WangFrenkel`` potential
13+
(`#1970 <https://github.com/glotzerlab/hoomd-blue/pull/1970>`__).
1214
* ``mpcd.update.ReverseNonequilibriumShearFlow``
1315
(`#1983 <https://github.com/glotzerlab/hoomd-blue/pull/1983>`__).
1416

17+
1518
5.0.1 (2025-01-20)
1619
^^^^^^^^^^^^^^^^^^
1720

hoomd/HOOMDMath.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,56 @@ inline HOSTDEVICE double pow(double x, double y)
349349
return ::exp(y * log(x));
350350
}
351351

352+
//! Compute the pow of x,y in double precision when y is an integer using squaring
353+
inline HOSTDEVICE double pow(double x, unsigned int y)
354+
{
355+
double result = 1.0;
356+
for (;;)
357+
{
358+
if (y & 1)
359+
result *= x;
360+
y >>= 1;
361+
if (!y)
362+
break;
363+
x *= x;
364+
}
365+
return result;
366+
}
367+
368+
inline HOSTDEVICE double pow(double x, int y)
369+
{
370+
unsigned int _y = abs(y);
371+
if (y < 0)
372+
return 1.0 / pow(x, _y);
373+
else
374+
return pow(x, _y);
375+
}
376+
377+
//! Compute the pow of x,y in single precision when y is an integer using squaring
378+
inline HOSTDEVICE float pow(float x, unsigned int y)
379+
{
380+
float result = 1.0f;
381+
for (;;)
382+
{
383+
if (y & 1)
384+
result *= x;
385+
y >>= 1;
386+
if (!y)
387+
break;
388+
x *= x;
389+
}
390+
return result;
391+
}
392+
393+
inline HOSTDEVICE float pow(float x, int y)
394+
{
395+
unsigned int _y = abs(y);
396+
if (y < 0)
397+
return 1.0f / pow(x, _y);
398+
else
399+
return pow(x, _y);
400+
}
401+
352402
//! Compute the exp of x
353403
inline HOSTDEVICE float exp(float x)
354404
{

hoomd/data/typeconverter.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,21 @@ def nonnegative_real(number):
106106
return float_number
107107

108108

109+
def positive_int(number):
110+
"""Ensures that a value is a positive integer"""
111+
if isinstance(number, float) and not number.is_integer():
112+
raise TypeConversionError(
113+
f"Expected integer, {number} has a non-zero decimal part"
114+
)
115+
try:
116+
int_number = int(number)
117+
except Exception as err:
118+
raise TypeConversionError(f"{number} is not convertible to int.") from err
119+
if int_number <= 0:
120+
raise TypeConversionError("Expected a positive integer")
121+
return int_number
122+
123+
109124
def identity(value):
110125
"""Return the given value."""
111126
return value

hoomd/md/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ set(_md_headers ActiveForceComputeGPU.h
126126
EvaluatorPairTable.h
127127
EvaluatorPairTWF.h
128128
EvaluatorPairYukawa.h
129+
EvaluatorPairWangFrenkel.h
129130
EvaluatorPairZBL.h
130131
EvaluatorTersoff.h
131132
EvaluatorWalls.h
@@ -556,7 +557,8 @@ set(_pair_evaluators Buckingham
556557
LJGauss
557558
ForceShiftedLJ
558559
Table
559-
ExpandedGaussian)
560+
ExpandedGaussian
561+
WangFrenkel)
560562

561563

562564
foreach(_evaluator ${_pair_evaluators})
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
// Copyright (c) 2009-2025 The Regents of the University of Michigan.
2+
// Part of HOOMD-blue, released under the BSD 3-Clause License.
3+
4+
#ifndef __PAIR_EVALUATOR_WANGFRENKEL_H__
5+
#define __PAIR_EVALUATOR_WANGFRENKEL_H__
6+
7+
#ifndef __HIPCC__
8+
#include <string>
9+
#endif
10+
11+
#include "hoomd/HOOMDMath.h"
12+
13+
/*! \file EvaluatorPairWangFrenkel.h
14+
\brief Defines the pair evaluator class for Wang-Frenkel potentials
15+
*/
16+
17+
// need to declare these class methods with __device__ qualifiers when building in nvcc
18+
// DEVICE is __host__ __device__ when included in nvcc and blank when included into the host
19+
// compiler
20+
#ifdef __HIPCC__
21+
#define DEVICE __device__
22+
#define HOSTDEVICE __host__ __device__
23+
#else
24+
#define DEVICE
25+
#define HOSTDEVICE
26+
#endif
27+
28+
namespace hoomd
29+
{
30+
namespace md
31+
{
32+
//! Class for evaluating the Wang-Frenkel pair potential
33+
/*! <b>General Overview</b>
34+
35+
See EvaluatorPairLJ.
36+
37+
<b>Wang-Frenkel specifics</b>
38+
39+
EvaluatorPairWang-Frenkel evaluates the function:
40+
\f[ \epsilon \alpha \left( \left[\frac{\sigma}{r}\right]^{2\mu}
41+
-1\right)\left(\left[\frac{R}{r}\right]^{2\mu} -1 \left)^{2\nu} \f]
42+
*/
43+
class EvaluatorPairWangFrenkel
44+
{
45+
public:
46+
//! Define the parameter type used by this pair potential evaluator
47+
struct param_type
48+
{
49+
Scalar prefactor;
50+
Scalar sigma_pow_2m;
51+
Scalar R_pow_2m;
52+
int mu;
53+
int nu;
54+
55+
DEVICE void load_shared(char*& ptr, unsigned int& available_bytes) { }
56+
57+
HOSTDEVICE void allocate_shared(char*& ptr, unsigned int& available_bytes) const { }
58+
59+
#ifdef ENABLE_HIP
60+
// set CUDA memory hints
61+
void set_memory_hint() const { }
62+
#endif
63+
64+
#ifndef __HIPCC__
65+
param_type() : prefactor(0.0), sigma_pow_2m(0.0), R_pow_2m(0.0), mu(0.0), nu(0.0) { }
66+
67+
param_type(pybind11::dict v, bool managed = false)
68+
{
69+
// should probably send a warning if exponents are large (eg >256)
70+
mu = v["mu"].cast<unsigned int>();
71+
nu = v["nu"].cast<unsigned int>();
72+
73+
if (nu == 0 || mu == 0)
74+
throw std::invalid_argument("Cannot set exponents nu or mu to zero");
75+
76+
Scalar epsilon = v["epsilon"].cast<Scalar>();
77+
Scalar sigma = v["sigma"].cast<Scalar>();
78+
Scalar sigma_sq = sigma * sigma;
79+
Scalar rcut = v["R"].cast<Scalar>();
80+
Scalar rcutsq = rcut * rcut;
81+
Scalar left = (1 + 2 * nu) / (2 * nu * (fast::pow(rcutsq / sigma_sq, mu) - 1));
82+
Scalar alpha = 2 * nu * fast::pow(rcutsq / sigma_sq, mu) * fast::pow(left, 2 * nu + 1);
83+
84+
prefactor = epsilon * alpha;
85+
R_pow_2m = fast::pow(rcutsq, mu);
86+
sigma_pow_2m = fast::pow(sigma_sq, mu);
87+
}
88+
89+
pybind11::dict asDict()
90+
{
91+
pybind11::dict v;
92+
v["mu"] = mu;
93+
v["nu"] = nu;
94+
Scalar sigma = fast::pow(sigma_pow_2m, 1.0 / (2.0 * Scalar(mu)));
95+
v["sigma"] = sigma;
96+
Scalar sigma_sq = sigma * sigma;
97+
v["R"] = fast::pow(R_pow_2m, 1 / Scalar(2 * mu));
98+
Scalar rcutsq = fast::pow(R_pow_2m, 1 / Scalar(mu));
99+
Scalar left = (1 + 2 * nu) / (2 * nu * (fast::pow(rcutsq / sigma_sq, mu) - 1));
100+
Scalar alpha = 2 * nu * fast::pow(rcutsq / sigma_sq, mu) * fast::pow(left, 2 * nu + 1);
101+
102+
v["epsilon"] = prefactor / alpha;
103+
104+
return v;
105+
}
106+
#endif
107+
} __attribute__((aligned(16)));
108+
109+
//! Constructs the pair potential evaluator
110+
/*! \param _rsq Squared distance between the particles
111+
\param _rcutsq Squared distance at which the potential goes to 0
112+
\param _params Per type pair parameters of this potential
113+
*/
114+
DEVICE EvaluatorPairWangFrenkel(Scalar _rsq, Scalar _rcutsq, const param_type& _params)
115+
: rsq(_rsq), rcutsq(_rcutsq), prefactor(_params.prefactor),
116+
sigma_pow_2m(_params.sigma_pow_2m), R_pow_2m(_params.R_pow_2m), mu(_params.mu),
117+
nu(_params.nu)
118+
{
119+
}
120+
121+
//! Mie doesn't use charge
122+
DEVICE static bool needsCharge()
123+
{
124+
return false;
125+
}
126+
//! Accept the optional charge values.
127+
/*! \param qi Charge of particle i
128+
\param qj Charge of particle j
129+
*/
130+
DEVICE void setCharge(Scalar qi, Scalar qj) { }
131+
132+
//! Evaluate the force and energy
133+
/*! \param force_divr Output parameter to write the computed force divided by r.
134+
\param pair_eng Output parameter to write the computed pair energy
135+
\param energy_shift If true, the potential must be shifted so that V(r) is continuous at the
136+
cutoff \note There is no need to check if rsq < rcutsq in this method. Cutoff tests are
137+
performed in PotentialPair.
138+
139+
\return True if they are evaluated or false if they are not because we are beyond the cutoff
140+
*/
141+
DEVICE bool evalForceAndEnergy(Scalar& force_divr, Scalar& pair_eng, bool energy_shift)
142+
{
143+
// compute the force divided by r in force_divr
144+
if (rsq < rcutsq && prefactor != 0)
145+
{
146+
Scalar r2inv = Scalar(1.0) / rsq;
147+
Scalar rinv_pow_2m = fast::pow(r2inv, mu);
148+
Scalar sigma_over_r_pow_2m = sigma_pow_2m * rinv_pow_2m;
149+
Scalar R_over_r_pow_2m = R_pow_2m * rinv_pow_2m;
150+
151+
Scalar sigma_term = sigma_over_r_pow_2m - 1;
152+
Scalar R_term = R_over_r_pow_2m - 1;
153+
154+
Scalar R_term_2num1 = fast::pow(R_term, 2 * nu - 1);
155+
Scalar R_term_2nu = R_term_2num1 * R_term;
156+
157+
pair_eng = prefactor * sigma_term * R_term_2nu;
158+
force_divr = 2 * prefactor * mu * R_term_2num1
159+
* (2 * nu * R_over_r_pow_2m * sigma_term + R_term * sigma_over_r_pow_2m)
160+
* r2inv;
161+
162+
if (energy_shift)
163+
{
164+
Scalar rcinv_pow_2m = fast::pow(Scalar(1.0) / rcutsq, mu);
165+
Scalar sigma_over_rc_pow_2m = sigma_pow_2m * rcinv_pow_2m;
166+
Scalar R_over_rc_pow_2m = R_pow_2m * rcinv_pow_2m;
167+
Scalar rc_R_term_2nu = fast::pow(R_over_rc_pow_2m - 1, 2 * nu);
168+
pair_eng -= prefactor * (sigma_over_rc_pow_2m - 1) * rc_R_term_2nu;
169+
}
170+
return true;
171+
}
172+
else
173+
return false;
174+
}
175+
176+
DEVICE Scalar evalPressureLRCIntegral()
177+
{
178+
return 0;
179+
}
180+
181+
DEVICE Scalar evalEnergyLRCIntegral()
182+
{
183+
return 0;
184+
}
185+
186+
#ifndef __HIPCC__
187+
//! Get the name of this potential
188+
/*! \returns The potential name.
189+
*/
190+
static std::string getName()
191+
{
192+
return std::string("wangfrenkel");
193+
}
194+
195+
std::string getShapeSpec() const
196+
{
197+
throw std::runtime_error("Shape definition not supported for this pair potential.");
198+
}
199+
#endif
200+
201+
protected:
202+
Scalar rsq; //!< Stored rsq from the constructor
203+
Scalar rcutsq; //!< Stored rcutsq from the constructor
204+
205+
Scalar prefactor; //!< Prefactor (epsilon * alpha)
206+
Scalar sigma_pow_2m; //!< sigma^(2m) stored
207+
Scalar R_pow_2m; //!< R^(2m) stored
208+
unsigned int mu; //!< mu exponent
209+
unsigned int nu; //!< nu exponent
210+
};
211+
212+
} // end namespace md
213+
} // end namespace hoomd
214+
215+
#endif // __PAIR_EVALUATOR_WANGFRENKEL_H__

hoomd/md/module-md.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ void export_PotentialPairTWF(pybind11::module& m);
6666
void export_PotentialPairLJGauss(pybind11::module& m);
6767
void export_PotentialPairForceShiftedLJ(pybind11::module& m);
6868
void export_PotentialPairTable(pybind11::module& m);
69+
void export_PotentialPairWangFrenkel(pybind11::module& m);
6970

7071
void export_AnisoPotentialPairALJ2D(pybind11::module& m);
7172
void export_AnisoPotentialPairALJ3D(pybind11::module& m);
@@ -221,6 +222,7 @@ void export_PotentialPairDLVOGPU(pybind11::module& m);
221222
void export_PotentialPairFourierGPU(pybind11::module& m);
222223
void export_PotentialPairOPPGPU(pybind11::module& m);
223224
void export_PotentialPairTWFGPU(pybind11::module& m);
225+
void export_PotentialPairWangFrenkelGPU(pybind11::module&);
224226
void export_PotentialPairLJGaussGPU(pybind11::module& m);
225227
void export_PotentialPairForceShiftedLJGPU(pybind11::module& m);
226228
void export_PotentialPairTableGPU(pybind11::module& m);
@@ -364,6 +366,7 @@ PYBIND11_MODULE(_md, m)
364366
export_PotentialPairLJGauss(m);
365367
export_PotentialPairForceShiftedLJ(m);
366368
export_PotentialPairTable(m);
369+
export_PotentialPairWangFrenkel(m);
367370

368371
export_AlchemicalMDParticles(m);
369372

@@ -461,6 +464,7 @@ PYBIND11_MODULE(_md, m)
461464
export_PotentialPairLJGaussGPU(m);
462465
export_PotentialPairForceShiftedLJGPU(m);
463466
export_PotentialPairTableGPU(m);
467+
export_PotentialPairWangFrenkelGPU(m);
464468
export_PotentialPairConservativeDPDGPU(m);
465469

466470
export_PotentialTersoffGPU(m);

hoomd/md/pair/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@
158158
Table,
159159
TWF,
160160
LJGauss,
161+
WangFrenkel,
161162
)
162163

163164
__all__ = [
@@ -186,6 +187,7 @@
186187
"Pair",
187188
"ReactionField",
188189
"Table",
190+
"WangFrenkel",
189191
"Yukawa",
190192
"aniso",
191193
]

0 commit comments

Comments
 (0)