Merge branch 'maint'
[hoomd-blue.git] / libhoomd / computes / ForceCompute.cc
blob6ec2e84cbcdfd33ad01ba42ceee705c85e11a712
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: joaander, grva, baschult
52 /*! \file ForceCompute.cc
53 \brief Defines the ForceCompute class
56 #ifdef WIN32
57 #pragma warning( push )
58 #pragma warning( disable : 4244 )
59 #endif
61 #include "ForceCompute.h"
62 #include <iostream>
63 using namespace std;
65 #include <boost/python.hpp>
66 using namespace boost::python;
68 #include <boost/shared_ptr.hpp>
69 #include <boost/bind.hpp>
70 using namespace boost;
72 #ifdef ENABLE_MPI
73 #include "Communicator.h"
74 #endif
76 /*! \param sysdef System to compute forces on
77 \post The Compute is initialized and all memory needed for the forces is allocated
78 \post \c force and \c virial GPUarrays are initialized
79 \post All forces are initialized to 0
81 ForceCompute::ForceCompute(boost::shared_ptr<SystemDefinition> sysdef) : Compute(sysdef), m_particles_sorted(false)
83 assert(m_pdata);
84 assert(m_pdata->getMaxN() > 0);
86 // allocate data on the host
87 unsigned int max_num_particles = m_pdata->getMaxN();
88 GPUArray<Scalar4> force(max_num_particles,exec_conf);
89 GPUArray<Scalar> virial(max_num_particles,6,exec_conf);
90 GPUArray<Scalar4> torque(max_num_particles,exec_conf);
91 m_force.swap(force);
92 m_virial.swap(virial);
93 m_torque.swap(torque);
95 m_virial_pitch = m_virial.getPitch();
97 // connect to the ParticleData to recieve notifications when particles change order in memory
98 m_sort_connection = m_pdata->connectParticleSort(bind(&ForceCompute::setParticlesSorted, this));
100 // connect to the ParticleData to receive notifications when the maximum number of particles changes
101 m_max_particle_num_change_connection = m_pdata->connectMaxParticleNumberChange(bind(&ForceCompute::reallocate, this));
103 // reset external virial
104 for (unsigned int i = 0; i < 6; ++i)
105 m_external_virial[i] = Scalar(0.0);
109 /*! \post m_force, m_virial and m_torque are resized to the current maximum particle number
111 void ForceCompute::reallocate()
113 m_force.resize(m_pdata->getMaxN());
114 m_virial.resize(m_pdata->getMaxN(),6);
115 m_torque.resize(m_pdata->getMaxN());
117 // the pitch of the virial array may have changed
118 m_virial_pitch = m_virial.getPitch();
121 /*! Frees allocated memory
123 ForceCompute::~ForceCompute()
125 m_sort_connection.disconnect();
126 m_max_particle_num_change_connection.disconnect();
129 /*! Sums the total potential energy calculated by the last call to compute() and returns it.
131 Scalar ForceCompute::calcEnergySum()
133 ArrayHandle<Scalar4> h_force(m_force,access_location::host,access_mode::read);
134 // always perform the sum in double precision for better accuracy
135 // this is cheating and is really just a temporary hack to get logging up and running
136 // the potential accuracy loss in simulations needs to be evaluated here and a proper
137 // summation algorithm put in place
138 double pe_total = 0.0;
139 for (unsigned int i=0; i < m_pdata->getN(); i++)
141 pe_total += (double)h_force.data[i].w;
143 #ifdef ENABLE_MPI
144 if (m_comm)
146 // reduce potential energy on all processors
147 MPI_Allreduce(MPI_IN_PLACE, &pe_total, 1, MPI_DOUBLE, MPI_SUM, m_exec_conf->getMPICommunicator());
149 #endif
150 return Scalar(pe_total);
153 /*! Performs the force computation.
154 \param timestep Current Timestep
155 \note If compute() has previously been called with a value of timestep equal to
156 the current value, the forces are assumed to already have been computed and nothing will
157 be done
160 void ForceCompute::compute(unsigned int timestep)
162 // skip if we shouldn't compute this step
163 if (!m_particles_sorted && !shouldCompute(timestep))
164 return;
166 computeForces(timestep);
167 m_particles_sorted = false;
170 /*! \param num_iters Number of iterations to average for the benchmark
171 \returns Milliseconds of execution time per calculation
173 Calls computeForces repeatedly to benchmark the force compute.
176 double ForceCompute::benchmark(unsigned int num_iters)
178 ClockSource t;
179 // warm up run
180 computeForces(0);
182 #ifdef ENABLE_CUDA
183 if (exec_conf->isCUDAEnabled())
185 cudaThreadSynchronize();
186 CHECK_CUDA_ERROR();
188 #endif
190 // benchmark
191 uint64_t start_time = t.getTime();
192 for (unsigned int i = 0; i < num_iters; i++)
193 computeForces(0);
195 #ifdef ENABLE_CUDA
196 if (exec_conf->isCUDAEnabled())
197 cudaThreadSynchronize();
198 #endif
199 uint64_t total_time_ns = t.getTime() - start_time;
201 // convert the run time to milliseconds
202 return double(total_time_ns) / 1e6 / double(num_iters);
205 /*! \param tag Global particle tag
206 \returns Torque of particle referenced by tag
208 Scalar4 ForceCompute::getTorque(unsigned int tag)
210 unsigned int i = m_pdata->getRTag(tag);
211 bool found = (i < m_pdata->getN());
212 Scalar4 result = make_scalar4(0.0,0.0,0.0,0.0);
213 if (found)
215 ArrayHandle<Scalar4> h_torque(m_torque, access_location::host, access_mode::read);
216 result = h_torque.data[i];
218 #ifdef ENABLE_MPI
219 if (m_pdata->getDomainDecomposition())
221 unsigned int owner_rank = m_pdata->getOwnerRank(tag);
222 MPI_Bcast(&result, sizeof(Scalar4), MPI_BYTE, owner_rank, m_exec_conf->getMPICommunicator());
224 #endif
225 return result;
228 /*! \param tag Global particle tag
229 \returns Force of particle referenced by tag
231 Scalar3 ForceCompute::getForce(unsigned int tag)
233 unsigned int i = m_pdata->getRTag(tag);
234 bool found = (i < m_pdata->getN());
235 Scalar3 result = make_scalar3(0.0,0.0,0.0);
236 if (found)
238 ArrayHandle<Scalar4> h_force(m_force, access_location::host, access_mode::read);
239 result = make_scalar3(h_force.data[i].x, h_force.data[i].y, h_force.data[i].z);
241 #ifdef ENABLE_MPI
242 if (m_pdata->getDomainDecomposition())
244 unsigned int owner_rank = m_pdata->getOwnerRank(tag);
245 MPI_Bcast(&result, sizeof(Scalar3), MPI_BYTE, owner_rank, m_exec_conf->getMPICommunicator());
247 #endif
248 return result;
251 /*! \param tag Global particle tag
252 \param component Virial component (0=xx, 1=xy, 2=xz, 3=yy, 4=yz, 5=zz)
253 \returns Force of particle referenced by tag
255 Scalar ForceCompute::getVirial(unsigned int tag, unsigned int component)
257 unsigned int i = m_pdata->getRTag(tag);
258 bool found = (i < m_pdata->getN());
259 Scalar result = Scalar(0.0);
260 if (found)
262 ArrayHandle<Scalar> h_virial(m_virial, access_location::host, access_mode::read);
263 result = h_virial.data[m_virial_pitch*component+i];
265 #ifdef ENABLE_MPI
266 if (m_pdata->getDomainDecomposition())
268 unsigned int owner_rank = m_pdata->getOwnerRank(tag);
269 MPI_Bcast(&result, sizeof(Scalar), MPI_BYTE, owner_rank, m_exec_conf->getMPICommunicator());
271 #endif
272 return result;
275 /*! \param tag Global particle tag
276 \returns Energy of particle referenced by tag
278 Scalar ForceCompute::getEnergy(unsigned int tag)
280 unsigned int i = m_pdata->getRTag(tag);
281 bool found = (i < m_pdata->getN());
282 Scalar result = Scalar(0.0);
283 if (found)
285 ArrayHandle<Scalar4> h_force(m_force, access_location::host, access_mode::read);
286 result = h_force.data[i].w;
288 #ifdef ENABLE_MPI
289 if (m_pdata->getDomainDecomposition())
291 unsigned int owner_rank = m_pdata->getOwnerRank(tag);
292 MPI_Bcast(&result, sizeof(Scalar), MPI_BYTE, owner_rank, m_exec_conf->getMPICommunicator());
294 #endif
295 return result;
298 //! Wrapper class for wrapping pure virtual methodos of ForceCompute in python
299 class ForceComputeWrap : public ForceCompute, public wrapper<ForceCompute>
301 public:
302 //! Constructor
303 /*! \param sysdef Particle data passed to the base class */
304 ForceComputeWrap(boost::shared_ptr<SystemDefinition> sysdef) : ForceCompute(sysdef) { }
305 protected:
306 //! Calls the overidden ForceCompute::computeForces()
307 /*! \param timestep parameter to pass on to the overidden method
309 void computeForces(unsigned int timestep)
311 this->get_override("computeForces")(timestep);
315 void export_ForceCompute()
317 class_< ForceComputeWrap, boost::shared_ptr<ForceComputeWrap>, bases<Compute>, boost::noncopyable >
318 ("ForceCompute", init< boost::shared_ptr<SystemDefinition> >())
319 .def("getForce", &ForceCompute::getForce)
320 .def("getTorque", &ForceCompute::getTorque)
321 .def("getVirial", &ForceCompute::getVirial)
322 .def("getEnergy", &ForceCompute::getEnergy)
326 #ifdef WIN32
327 #pragma warning( pop )
328 #endif