Skip to content

Commit 095ab02

Browse files
clpetixmphoward
andauthored
Compute bond without using minimum image convention (#108)
* Add PotentialBond from HOOMD. * Make export file and update names to make matches. * Add to module.cc and cmake lists. * Apply reviewer suggestions. * Fix mismatch between class names. * Add tests to ImageHarmonic bond. * Add ImageHarmonic to documentation * Fix formatting and update example for ImageHarmonic class * Try MPI fix by adding comm flag for image. * Consolidate and expand ImageHarmonic tests. * Refactor ImageHarmonic bond test into a class structure with methods for each kind of bond. * Move parametrization to test. * Test only first unit test conditions under MPI. * Try MPI fix. * Fix MPI issue. * Apply suggestions from code review Co-authored-by: Michael Howard <[email protected]> * Consolidate image bond test from class to function. * Rename ImageBondPotentialHarmonic to ImagePotentialBondHarmonic. * Use inheritence to make ImagePotentialBond * Apply suggested changes. * Implement GPU support files and links. * Fix GPU implementation. * Apply reviewer suggestions. * Update to use gpu namespace. * Remove unnessary m_flags. --------- Co-authored-by: Michael Howard <[email protected]>
1 parent 3b79e89 commit 095ab02

11 files changed

+961
-0
lines changed

doc/module-azplugins-bond.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ azplugins.bond
1313
:nosignatures:
1414

1515
DoubleWell
16+
ImageHarmonic
1617
Quartic
1718

1819
.. rubric:: Details
1920

2021
.. automodule:: hoomd.azplugins.bond
2122
:synopsis: Bond potentials.
2223
:members: DoubleWell,
24+
ImageHarmonic,
2325
Quartic
2426
:no-inherited-members:
2527
:show-inheritance:

src/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,21 @@ set(COMPONENT_NAME azplugins)
44
# TODO: List all host C++ source code files in _${COMPONENT_NAME}_sources.
55
set(_${COMPONENT_NAME}_sources
66
ConstantFlow.cc
7+
export_ImagePotentialBondHarmonic.cc
78
module.cc
89
ParabolicFlow.cc
910
VelocityCompute.cc
1011
)
1112
if (ENABLE_HIP)
1213
list(APPEND _${COMPONENT_NAME}_sources
14+
export_ImagePotentialBondHarmonicGPU.cc
1315
VelocityComputeGPU.cc
1416
)
1517
endif()
1618

1719
# TODO: List all GPU C++ source code files in _${COMPONENT_NAME}_cu_sources.
1820
set(_${COMPONENT_NAME}_cu_sources
21+
ImagePotentialBondGPUKernel.cu
1922
VelocityComputeGPU.cu
2023
VelocityFieldComputeGPU.cu
2124
)

src/ImagePotentialBond.h

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
// Copyright (c) 2018-2020, Michael P. Howard
2+
// Copyright (c) 2021-2025, Auburn University
3+
// Part of azplugins, released under the BSD 3-Clause License.
4+
5+
// File modified from HOOMD-blue
6+
// Copyright (c) 2009-2025 The Regents of the University of Michigan.
7+
// Part of HOOMD-blue, released under the BSD 3-Clause License.
8+
9+
#include "hoomd/ForceCompute.h"
10+
#include "hoomd/GPUArray.h"
11+
#include "hoomd/MeshDefinition.h"
12+
#include "hoomd/md/PotentialBond.h"
13+
#include <memory>
14+
15+
#include <vector>
16+
17+
/*! \file ImagePotentialBond.h
18+
\brief Declares ImagePotentialBond
19+
*/
20+
21+
#ifdef __HIPCC__
22+
#error This header cannot be compiled by nvcc
23+
#endif
24+
25+
#include <pybind11/pybind11.h>
26+
27+
#ifndef AZPLUGINS_IMAGE_POTENTIAL_BOND_H_
28+
#define AZPLUGINS_IMAGE_POTENTIAL_BOND_H_
29+
30+
namespace hoomd
31+
{
32+
namespace azplugins
33+
{
34+
/*! Bond potential using unwrapped coordinates
35+
36+
\ingroup computes
37+
*/
38+
template<class evaluator, class Bonds>
39+
class ImagePotentialBond : public md::PotentialBond<evaluator, Bonds>
40+
{
41+
public:
42+
//! Inherit constructors from base class
43+
using md::PotentialBond<evaluator, Bonds>::PotentialBond;
44+
45+
#ifdef ENABLE_MPI
46+
//! Get ghost particle fields requested by this pair potential
47+
CommFlags getRequestedCommFlags(uint64_t timestep) override;
48+
#endif
49+
50+
protected:
51+
void computeForces(uint64_t timestep) override;
52+
};
53+
54+
/*! Actually perform the force computation
55+
\param timestep Current time step
56+
*/
57+
template<class evaluator, class Bonds>
58+
void ImagePotentialBond<evaluator, Bonds>::computeForces(uint64_t timestep)
59+
{
60+
// access the particle data arrays
61+
ArrayHandle<Scalar4> h_pos(this->m_pdata->getPositions(),
62+
access_location::host,
63+
access_mode::read);
64+
ArrayHandle<int3> h_image(this->m_pdata->getImages(), access_location::host, access_mode::read);
65+
ArrayHandle<unsigned int> h_rtag(this->m_pdata->getRTags(),
66+
access_location::host,
67+
access_mode::read);
68+
ArrayHandle<Scalar> h_charge(this->m_pdata->getCharges(),
69+
access_location::host,
70+
access_mode::read);
71+
72+
ArrayHandle<Scalar4> h_force(this->m_force, access_location::host, access_mode::readwrite);
73+
ArrayHandle<Scalar> h_virial(this->m_virial, access_location::host, access_mode::readwrite);
74+
75+
// access the parameters
76+
ArrayHandle<typename evaluator::param_type> h_params(this->m_params,
77+
access_location::host,
78+
access_mode::read);
79+
80+
// Zero data for force calculation
81+
this->m_force.zeroFill();
82+
this->m_virial.zeroFill();
83+
84+
const BoxDim box = this->m_pdata->getGlobalBox();
85+
86+
PDataFlags flags = this->m_pdata->getFlags();
87+
bool compute_virial = flags[pdata_flag::pressure_tensor];
88+
89+
Scalar bond_virial[6];
90+
for (unsigned int i = 0; i < 6; i++)
91+
bond_virial[i] = Scalar(0.0);
92+
93+
ArrayHandle<typename Bonds::members_t> h_bonds(this->m_bond_data->getMembersArray(),
94+
access_location::host,
95+
access_mode::read);
96+
ArrayHandle<typeval_t> h_typeval(this->m_bond_data->getTypeValArray(),
97+
access_location::host,
98+
access_mode::read);
99+
100+
unsigned int max_local = this->m_pdata->getN() + this->m_pdata->getNGhosts();
101+
102+
// for each of the bonds
103+
const unsigned int size = (unsigned int)this->m_bond_data->getN();
104+
105+
for (unsigned int i = 0; i < size; i++)
106+
{
107+
// lookup the tag of each of the particles participating in the bond
108+
const typename Bonds::members_t& bond = h_bonds.data[i];
109+
110+
// transform a and b into indices into the particle data arrays
111+
// (MEM TRANSFER: 4 integers)
112+
unsigned int idx_a = h_rtag.data[bond.tag[0]];
113+
unsigned int idx_b = h_rtag.data[bond.tag[1]];
114+
115+
// throw an error if this bond is incomplete
116+
if (idx_a >= max_local || idx_b >= max_local)
117+
{
118+
std::ostringstream stream;
119+
stream << "Error: bond " << bond.tag[0] << " " << bond.tag[1] << " is incomplete.";
120+
throw std::runtime_error(stream.str());
121+
}
122+
123+
// calculate d\vec{r}
124+
// (MEM TRANSFER: 6 Scalars / FLOPS: 3)
125+
Scalar3 posa = make_scalar3(h_pos.data[idx_a].x, h_pos.data[idx_a].y, h_pos.data[idx_a].z);
126+
Scalar3 posb = make_scalar3(h_pos.data[idx_b].x, h_pos.data[idx_b].y, h_pos.data[idx_b].z);
127+
128+
// access charge (if needed)
129+
Scalar charge_a = Scalar(0.0);
130+
Scalar charge_b = Scalar(0.0);
131+
if (evaluator::needsCharge())
132+
{
133+
charge_a = h_charge.data[idx_a];
134+
charge_b = h_charge.data[idx_b];
135+
}
136+
137+
// get images of particles
138+
const int3 img_a = h_image.data[idx_a];
139+
const int3 img_b = h_image.data[idx_b];
140+
141+
// get relative vector between particles
142+
const Scalar3 dx = box.shift(posb - posa, img_b - img_a);
143+
144+
// calculate r_ab squared
145+
Scalar rsq = dot(dx, dx);
146+
147+
// compute the force and potential energy
148+
Scalar force_divr = Scalar(0.0);
149+
Scalar bond_eng = Scalar(0.0);
150+
evaluator eval(rsq, h_params.data[h_typeval.data[i].type]);
151+
if (evaluator::needsCharge())
152+
eval.setCharge(charge_a, charge_b);
153+
154+
bool evaluated = eval.evalForceAndEnergy(force_divr, bond_eng);
155+
156+
// Bond energy must be halved
157+
bond_eng *= Scalar(0.5);
158+
159+
if (evaluated)
160+
{
161+
// calculate virial
162+
if (compute_virial)
163+
{
164+
Scalar force_div2r = Scalar(1.0 / 2.0) * force_divr;
165+
bond_virial[0] = dx.x * dx.x * force_div2r; // xx
166+
bond_virial[1] = dx.x * dx.y * force_div2r; // xy
167+
bond_virial[2] = dx.x * dx.z * force_div2r; // xz
168+
bond_virial[3] = dx.y * dx.y * force_div2r; // yy
169+
bond_virial[4] = dx.y * dx.z * force_div2r; // yz
170+
bond_virial[5] = dx.z * dx.z * force_div2r; // zz
171+
}
172+
173+
// add the force to the particles (only for non-ghost particles)
174+
if (idx_b < this->m_pdata->getN())
175+
{
176+
h_force.data[idx_b].x += force_divr * dx.x;
177+
h_force.data[idx_b].y += force_divr * dx.y;
178+
h_force.data[idx_b].z += force_divr * dx.z;
179+
h_force.data[idx_b].w += bond_eng;
180+
if (compute_virial)
181+
for (unsigned int i = 0; i < 6; i++)
182+
h_virial.data[i * this->m_virial_pitch + idx_b] += bond_virial[i];
183+
}
184+
185+
if (idx_a < this->m_pdata->getN())
186+
{
187+
h_force.data[idx_a].x -= force_divr * dx.x;
188+
h_force.data[idx_a].y -= force_divr * dx.y;
189+
h_force.data[idx_a].z -= force_divr * dx.z;
190+
h_force.data[idx_a].w += bond_eng;
191+
if (compute_virial)
192+
for (unsigned int i = 0; i < 6; i++)
193+
h_virial.data[i * this->m_virial_pitch + idx_a] += bond_virial[i];
194+
}
195+
}
196+
else
197+
{
198+
this->m_exec_conf->msg->error()
199+
<< "bond." << evaluator::getName() << ": bond out of bounds" << std::endl
200+
<< std::endl;
201+
throw std::runtime_error("Error in bond calculation");
202+
}
203+
}
204+
}
205+
206+
#ifdef ENABLE_MPI
207+
/*! \param timestep Current time step
208+
*/
209+
template<class evaluator, class Bonds>
210+
CommFlags ImagePotentialBond<evaluator, Bonds>::getRequestedCommFlags(uint64_t timestep)
211+
{
212+
CommFlags flags = md::PotentialBond<evaluator, Bonds>::getRequestedCommFlags(timestep);
213+
flags[comm_flag::image] = 1;
214+
215+
return flags;
216+
}
217+
#endif
218+
219+
namespace detail
220+
{
221+
//! Exports the ImagePotentialBond class to python
222+
/*! \param name Name of the class in the exported python module
223+
\tparam T Evaluator type to export.
224+
*/
225+
template<class T> void export_ImagePotentialBond(pybind11::module& m, const std::string& name)
226+
{
227+
pybind11::class_<ImagePotentialBond<T, BondData>,
228+
md::PotentialBond<T, BondData>,
229+
std::shared_ptr<ImagePotentialBond<T, BondData>>>(m, name.c_str())
230+
.def(pybind11::init<std::shared_ptr<SystemDefinition>>());
231+
}
232+
233+
} // end namespace detail
234+
} // end namespace azplugins
235+
} // end namespace hoomd
236+
237+
#endif

0 commit comments

Comments
 (0)