Merge branch 'maint'
[hoomd-blue.git] / libhoomd / computes / HarmonicDihedralForceCompute.cc
blob6e350cd7a285a4ed0dbb81bdc357324ea2e488ea
1 /*
2 Highly Optimized Object-oriented Many-particle Dynamics -- Blue Edition
3 (HOOMD-blue) Open Source Software License Copyright 2009-2014 The Regents of
4 the University of Michigan All rights reserved.
6 HOOMD-blue may contain modifications ("Contributions") provided, and to which
7 copyright is held, by various Contributors who have granted The Regents of the
8 University of Michigan the right to modify and/or distribute such Contributions.
10 You may redistribute, use, and create derivate works of HOOMD-blue, in source
11 and binary forms, provided you abide by the following conditions:
13 * Redistributions of source code must retain the above copyright notice, this
14 list of conditions, and the following disclaimer both in the code and
15 prominently in any materials provided with the distribution.
17 * Redistributions in binary form must reproduce the above copyright notice, this
18 list of conditions, and the following disclaimer in the documentation and/or
19 other materials provided with the distribution.
21 * All publications and presentations based on HOOMD-blue, including any reports
22 or published results obtained, in whole or in part, with HOOMD-blue, will
23 acknowledge its use according to the terms posted at the time of submission on:
24 http://codeblue.umich.edu/hoomd-blue/citations.html
26 * Any electronic documents citing HOOMD-Blue will link to the HOOMD-Blue website:
27 http://codeblue.umich.edu/hoomd-blue/
29 * Apart from the above required attributions, neither the name of the copyright
30 holder nor the names of HOOMD-blue's contributors may be used to endorse or
31 promote products derived from this software without specific prior written
32 permission.
34 Disclaimer
36 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' AND
37 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
38 WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND/OR ANY
39 WARRANTIES THAT THIS SOFTWARE IS FREE OF INFRINGEMENT ARE DISCLAIMED.
41 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
42 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
43 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
44 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
45 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
46 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
47 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50 // Maintainer: dnlebard
52 #ifdef WIN32
53 #pragma warning( push )
54 #pragma warning( disable : 4244 )
55 #endif
57 #include <boost/python.hpp>
58 using namespace boost::python;
60 #include "HarmonicDihedralForceCompute.h"
62 #include <iostream>
63 #include <sstream>
64 #include <stdexcept>
65 #include <math.h>
67 using namespace std;
69 // SMALL a relatively small number
70 #define SMALL Scalar(0.001)
72 /*! \file HarmonicDihedralForceCompute.cc
73 \brief Contains code for the HarmonicDihedralForceCompute class
76 /*! \param sysdef System to compute forces on
77 \post Memory is allocated, and forces are zeroed.
79 HarmonicDihedralForceCompute::HarmonicDihedralForceCompute(boost::shared_ptr<SystemDefinition> sysdef)
80 : ForceCompute(sysdef), m_K(NULL), m_sign(NULL), m_multi(NULL)
82 m_exec_conf->msg->notice(5) << "Constructing HarmonicDihedralForceCompute" << endl;
84 // access the dihedral data for later use
85 m_dihedral_data = m_sysdef->getDihedralData();
87 // check for some silly errors a user could make
88 if (m_dihedral_data->getNTypes() == 0)
90 m_exec_conf->msg->error() << "dihedral.harmonic: No dihedral types specified" << endl;
91 throw runtime_error("Error initializing HarmonicDihedralForceCompute");
94 // allocate the parameters
95 m_K = new Scalar[m_dihedral_data->getNTypes()];
96 m_sign = new Scalar[m_dihedral_data->getNTypes()];
97 m_multi = new Scalar[m_dihedral_data->getNTypes()];
101 HarmonicDihedralForceCompute::~HarmonicDihedralForceCompute()
103 m_exec_conf->msg->notice(5) << "Destroying HarmonicDihedralForceCompute" << endl;
105 delete[] m_K;
106 delete[] m_sign;
107 delete[] m_multi;
108 m_K = NULL;
109 m_sign = NULL;
110 m_multi = NULL;
113 /*! \param type Type of the dihedral to set parameters for
114 \param K Stiffness parameter for the force computation
115 \param sign the sign of the cosign term
116 \param multiplicity of the dihedral itself
118 Sets parameters for the potential of a particular dihedral type
120 void HarmonicDihedralForceCompute::setParams(unsigned int type, Scalar K, int sign, unsigned int multiplicity)
122 // make sure the type is valid
123 if (type >= m_dihedral_data->getNTypes())
125 m_exec_conf->msg->error() << "dihedral.harmonic: Invalid dihedral type specified" << endl;
126 throw runtime_error("Error setting parameters in HarmonicDihedralForceCompute");
129 m_K[type] = K;
130 m_sign[type] = (Scalar)sign;
131 m_multi[type] = (Scalar)multiplicity;
133 // check for some silly errors a user could make
134 if (K <= 0)
135 m_exec_conf->msg->warning() << "dihedral.harmonic: specified K <= 0" << endl;
136 if (sign != 1 && sign != -1)
137 m_exec_conf->msg->warning() << "dihedral.harmonic: a non unitary sign was specified" << endl;
140 /*! DihedralForceCompute provides
141 - \c dihedral_harmonic_energy
143 std::vector< std::string > HarmonicDihedralForceCompute::getProvidedLogQuantities()
145 vector<string> list;
146 list.push_back("dihedral_harmonic_energy");
147 return list;
150 /*! \param quantity Name of the quantity to get the log value of
151 \param timestep Current time step of the simulation
153 Scalar HarmonicDihedralForceCompute::getLogValue(const std::string& quantity, unsigned int timestep)
155 if (quantity == string("dihedral_harmonic_energy"))
157 compute(timestep);
158 return calcEnergySum();
160 else
162 m_exec_conf->msg->error() << "dihedral.harmonic: " << quantity << " is not a valid log quantity" << endl;
163 throw runtime_error("Error getting log value");
167 /*! Actually perform the force computation
168 \param timestep Current time step
170 void HarmonicDihedralForceCompute::computeForces(unsigned int timestep)
172 if (m_prof) m_prof->push("Harmonic Dihedral");
174 assert(m_pdata);
175 // access the particle data arrays
176 ArrayHandle<Scalar4> h_pos(m_pdata->getPositions(), access_location::host, access_mode::read);
177 ArrayHandle<unsigned int> h_rtag(m_pdata->getRTags(), access_location::host, access_mode::read);
179 ArrayHandle<Scalar4> h_force(m_force,access_location::host, access_mode::overwrite);
180 ArrayHandle<Scalar> h_virial(m_virial,access_location::host, access_mode::overwrite);
182 // Zero data for force calculation.
183 memset((void*)h_force.data,0,sizeof(Scalar4)*m_force.getNumElements());
184 memset((void*)h_virial.data,0,sizeof(Scalar)*m_virial.getNumElements());
186 // there are enough other checks on the input data: but it doesn't hurt to be safe
187 assert(h_force.data);
188 assert(h_virial.data);
189 assert(h_pos.data);
190 assert(h_rtag.data);
192 unsigned int virial_pitch = m_virial.getPitch();
194 // get a local copy of the simulation box too
195 const BoxDim& box = m_pdata->getBox();
197 // for each of the dihedrals
198 const unsigned int size = (unsigned int)m_dihedral_data->getN();
199 for (unsigned int i = 0; i < size; i++)
201 // lookup the tag of each of the particles participating in the dihedral
202 const ImproperData::members_t& dihedral = m_dihedral_data->getMembersByIndex(i);
203 assert(dihedral.tag[0] < m_pdata->getNGlobal());
204 assert(dihedral.tag[1] < m_pdata->getNGlobal());
205 assert(dihedral.tag[2] < m_pdata->getNGlobal());
206 assert(dihedral.tag[3] < m_pdata->getNGlobal());
208 // transform a, b, and c into indicies into the particle data arrays
209 // MEM TRANSFER: 6 ints
210 unsigned int idx_a = h_rtag.data[dihedral.tag[0]];
211 unsigned int idx_b = h_rtag.data[dihedral.tag[1]];
212 unsigned int idx_c = h_rtag.data[dihedral.tag[2]];
213 unsigned int idx_d = h_rtag.data[dihedral.tag[3]];
215 // throw an error if this angle is incomplete
216 if (idx_a == NOT_LOCAL|| idx_b == NOT_LOCAL || idx_c == NOT_LOCAL || idx_d == NOT_LOCAL)
218 this->m_exec_conf->msg->error() << "dihedral.harmonic: dihedral " <<
219 dihedral.tag[0] << " " << dihedral.tag[1] << " " << dihedral.tag[2] << " " << dihedral.tag[3]
220 << " incomplete." << endl << endl;
221 throw std::runtime_error("Error in dihedral calculation");
224 assert(idx_a < m_pdata->getN() + m_pdata->getNGhosts());
225 assert(idx_b < m_pdata->getN() + m_pdata->getNGhosts());
226 assert(idx_c < m_pdata->getN() + m_pdata->getNGhosts());
227 assert(idx_d < m_pdata->getN() + m_pdata->getNGhosts());
229 // calculate d\vec{r}
230 Scalar3 dab;
231 dab.x = h_pos.data[idx_a].x - h_pos.data[idx_b].x;
232 dab.y = h_pos.data[idx_a].y - h_pos.data[idx_b].y;
233 dab.z = h_pos.data[idx_a].z - h_pos.data[idx_b].z;
235 Scalar3 dcb;
236 dcb.x = h_pos.data[idx_c].x - h_pos.data[idx_b].x;
237 dcb.y = h_pos.data[idx_c].y - h_pos.data[idx_b].y;
238 dcb.z = h_pos.data[idx_c].z - h_pos.data[idx_b].z;
240 Scalar3 ddc;
241 ddc.x = h_pos.data[idx_d].x - h_pos.data[idx_c].x;
242 ddc.y = h_pos.data[idx_d].y - h_pos.data[idx_c].y;
243 ddc.z = h_pos.data[idx_d].z - h_pos.data[idx_c].z;
245 // apply periodic boundary conditions
246 dab = box.minImage(dab);
247 dcb = box.minImage(dcb);
248 ddc = box.minImage(ddc);
250 Scalar3 dcbm;
251 dcbm.x = -dcb.x;
252 dcbm.y = -dcb.y;
253 dcbm.z = -dcb.z;
255 dcbm = box.minImage(dcbm);
257 Scalar aax = dab.y*dcbm.z - dab.z*dcbm.y;
258 Scalar aay = dab.z*dcbm.x - dab.x*dcbm.z;
259 Scalar aaz = dab.x*dcbm.y - dab.y*dcbm.x;
261 Scalar bbx = ddc.y*dcbm.z - ddc.z*dcbm.y;
262 Scalar bby = ddc.z*dcbm.x - ddc.x*dcbm.z;
263 Scalar bbz = ddc.x*dcbm.y - ddc.y*dcbm.x;
265 Scalar raasq = aax*aax + aay*aay + aaz*aaz;
266 Scalar rbbsq = bbx*bbx + bby*bby + bbz*bbz;
267 Scalar rgsq = dcbm.x*dcbm.x + dcbm.y*dcbm.y + dcbm.z*dcbm.z;
268 Scalar rg = sqrt(rgsq);
270 Scalar rginv, raa2inv, rbb2inv;
271 rginv = raa2inv = rbb2inv = Scalar(0.0);
272 if (rg > Scalar(0.0)) rginv = Scalar(1.0)/rg;
273 if (raasq > Scalar(0.0)) raa2inv = Scalar(1.0)/raasq;
274 if (rbbsq > Scalar(0.0)) rbb2inv = Scalar(1.0)/rbbsq;
275 Scalar rabinv = sqrt(raa2inv*rbb2inv);
277 Scalar c_abcd = (aax*bbx + aay*bby + aaz*bbz)*rabinv;
278 Scalar s_abcd = rg*rabinv*(aax*ddc.x + aay*ddc.y + aaz*ddc.z);
280 if (c_abcd > 1.0) c_abcd = 1.0;
281 if (c_abcd < -1.0) c_abcd = -1.0;
283 unsigned int dihedral_type = m_dihedral_data->getTypeByIndex(i);
284 int multi = (int)m_multi[dihedral_type];
285 Scalar p = Scalar(1.0);
286 Scalar dfab = Scalar(0.0);
287 Scalar ddfab;
289 for (int j = 0; j < multi; j++)
291 ddfab = p*c_abcd - dfab*s_abcd;
292 dfab = p*s_abcd + dfab*c_abcd;
293 p = ddfab;
296 /////////////////////////
297 // FROM LAMMPS: sin_shift is always 0... so dropping all sin_shift terms!!!!
298 /////////////////////////
300 Scalar sign = m_sign[dihedral_type];
301 p = p*sign;
302 dfab = dfab*sign;
303 dfab *= (Scalar)-multi;
304 p += Scalar(1.0);
306 if (multi == 0)
308 p = Scalar(1.0) + sign;
309 dfab = Scalar(0.0);
313 Scalar fg = dab.x*dcbm.x + dab.y*dcbm.y + dab.z*dcbm.z;
314 Scalar hg = ddc.x*dcbm.x + ddc.y*dcbm.y + ddc.z*dcbm.z;
316 Scalar fga = fg*raa2inv*rginv;
317 Scalar hgb = hg*rbb2inv*rginv;
318 Scalar gaa = -raa2inv*rg;
319 Scalar gbb = rbb2inv*rg;
321 Scalar dtfx = gaa*aax;
322 Scalar dtfy = gaa*aay;
323 Scalar dtfz = gaa*aaz;
324 Scalar dtgx = fga*aax - hgb*bbx;
325 Scalar dtgy = fga*aay - hgb*bby;
326 Scalar dtgz = fga*aaz - hgb*bbz;
327 Scalar dthx = gbb*bbx;
328 Scalar dthy = gbb*bby;
329 Scalar dthz = gbb*bbz;
331 // Scalar df = -m_K[dihedral.type] * dfab;
332 Scalar df = -m_K[dihedral_type] * dfab * Scalar(0.500); // the 0.5 term is for 1/2K in the forces
334 Scalar sx2 = df*dtgx;
335 Scalar sy2 = df*dtgy;
336 Scalar sz2 = df*dtgz;
338 Scalar ffax = df*dtfx;
339 Scalar ffay= df*dtfy;
340 Scalar ffaz = df*dtfz;
342 Scalar ffbx = sx2 - ffax;
343 Scalar ffby = sy2 - ffay;
344 Scalar ffbz = sz2 - ffaz;
346 Scalar ffdx = df*dthx;
347 Scalar ffdy = df*dthy;
348 Scalar ffdz = df*dthz;
350 Scalar ffcx = -sx2 - ffdx;
351 Scalar ffcy = -sy2 - ffdy;
352 Scalar ffcz = -sz2 - ffdz;
354 // Now, apply the force to each individual atom a,b,c,d
355 // and accumlate the energy/virial
356 // compute 1/4 of the energy, 1/4 for each atom in the dihedral
357 //Scalar dihedral_eng = p*m_K[dihedral.type]*Scalar(1.0/4.0);
358 Scalar dihedral_eng = p*m_K[dihedral_type]*Scalar(0.125); // the .125 term is (1/2)K * 1/4
360 // compute 1/4 of the virial, 1/4 for each atom in the dihedral
361 // upper triangular version of virial tensor
362 Scalar dihedral_virial[6];
363 dihedral_virial[0] = (1./4.)*(dab.x*ffax + dcb.x*ffcx + (ddc.x+dcb.x)*ffdx);
364 dihedral_virial[1] = (1./4.)*(dab.y*ffax + dcb.y*ffcx + (ddc.y+dcb.y)*ffdx);
365 dihedral_virial[2] = (1./4.)*(dab.z*ffax + dcb.z*ffcx + (ddc.z+dcb.z)*ffdx);
366 dihedral_virial[3] = (1./4.)*(dab.y*ffay + dcb.y*ffcy + (ddc.y+dcb.y)*ffdy);
367 dihedral_virial[4] = (1./4.)*(dab.z*ffay + dcb.z*ffcy + (ddc.z+dcb.z)*ffdy);
368 dihedral_virial[5] = (1./4.)*(dab.z*ffaz + dcb.z*ffcz + (ddc.z+dcb.z)*ffdz);
370 h_force.data[idx_a].x += ffax;
371 h_force.data[idx_a].y += ffay;
372 h_force.data[idx_a].z += ffaz;
373 h_force.data[idx_a].w += dihedral_eng;
374 for (int k = 0; k < 6; k++)
375 h_virial.data[virial_pitch*k+idx_a] += dihedral_virial[k];
377 h_force.data[idx_b].x += ffbx;
378 h_force.data[idx_b].y += ffby;
379 h_force.data[idx_b].z += ffbz;
380 h_force.data[idx_b].w += dihedral_eng;
381 for (int k = 0; k < 6; k++)
382 h_virial.data[virial_pitch*k+idx_b] += dihedral_virial[k];
384 h_force.data[idx_c].x += ffcx;
385 h_force.data[idx_c].y += ffcy;
386 h_force.data[idx_c].z += ffcz;
387 h_force.data[idx_c].w += dihedral_eng;
388 for (int k = 0; k < 6; k++)
389 h_virial.data[virial_pitch*k+idx_c] += dihedral_virial[k];
391 h_force.data[idx_d].x += ffdx;
392 h_force.data[idx_d].y += ffdy;
393 h_force.data[idx_d].z += ffdz;
394 h_force.data[idx_d].w += dihedral_eng;
395 for (int k = 0; k < 6; k++)
396 h_virial.data[virial_pitch*k+idx_d] += dihedral_virial[k];
399 if (m_prof) m_prof->pop();
402 void export_HarmonicDihedralForceCompute()
404 class_<HarmonicDihedralForceCompute, boost::shared_ptr<HarmonicDihedralForceCompute>, bases<ForceCompute>, boost::noncopyable >
405 ("HarmonicDihedralForceCompute", init< boost::shared_ptr<SystemDefinition> >())
406 .def("setParams", &HarmonicDihedralForceCompute::setParams)
410 #ifdef WIN32
411 #pragma warning( pop )
412 #endif