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
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
52 /*! \file ComputeThermo.cc
53 \brief Contains code for the ComputeThermo class
56 #include "ComputeThermo.h"
57 #include <boost/python.hpp>
58 using namespace boost::python
;
61 #include "Communicator.h"
68 /*! \param sysdef System for which to compute thermodynamic properties
69 \param group Subset of the system over which properties are calculated
70 \param suffix Suffix to append to all logged quantity names
72 ComputeThermo::ComputeThermo(boost::shared_ptr
<SystemDefinition
> sysdef
,
73 boost::shared_ptr
<ParticleGroup
> group
,
74 const std::string
& suffix
)
75 : Compute(sysdef
), m_group(group
), m_ndof(1)
77 m_exec_conf
->msg
->notice(5) << "Constructing ComputeThermo" << endl
;
80 GPUArray
< Scalar
> properties(thermo_index::num_quantities
, exec_conf
);
81 m_properties
.swap(properties
);
83 m_logname_list
.push_back(string("temperature") + suffix
);
84 m_logname_list
.push_back(string("pressure") + suffix
);
85 m_logname_list
.push_back(string("kinetic_energy") + suffix
);
86 m_logname_list
.push_back(string("potential_energy") + suffix
);
87 m_logname_list
.push_back(string("ndof") + suffix
);
88 m_logname_list
.push_back(string("num_particles") + suffix
);
89 m_logname_list
.push_back(string("pressure_xx") + suffix
);
90 m_logname_list
.push_back(string("pressure_xy") + suffix
);
91 m_logname_list
.push_back(string("pressure_xz") + suffix
);
92 m_logname_list
.push_back(string("pressure_yy") + suffix
);
93 m_logname_list
.push_back(string("pressure_yz") + suffix
);
94 m_logname_list
.push_back(string("pressure_zz") + suffix
);
97 m_properties_reduced
= true;
101 ComputeThermo::~ComputeThermo()
103 m_exec_conf
->msg
->notice(5) << "Destroying ComputeThermo" << endl
;
106 /*! \param ndof Number of degrees of freedom to set
108 void ComputeThermo::setNDOF(unsigned int ndof
)
112 m_exec_conf
->msg
->warning() << "compute.thermo: given a group with 0 degrees of freedom." << endl
113 << " overriding ndof=1 to avoid divide by 0 errors" << endl
;
120 /*! Calls computeProperties if the properties need updating
121 \param timestep Current time step of the simulation
123 void ComputeThermo::compute(unsigned int timestep
)
125 if (!shouldCompute(timestep
))
131 std::vector
< std::string
> ComputeThermo::getProvidedLogQuantities()
133 return m_logname_list
;
136 Scalar
ComputeThermo::getLogValue(const std::string
& quantity
, unsigned int timestep
)
139 if (quantity
== m_logname_list
[0])
141 return getTemperature();
143 else if (quantity
== m_logname_list
[1])
145 return getPressure();
147 else if (quantity
== m_logname_list
[2])
149 return getKineticEnergy();
151 else if (quantity
== m_logname_list
[3])
153 return getPotentialEnergy();
155 else if (quantity
== m_logname_list
[4])
157 return Scalar(m_ndof
);
159 else if (quantity
== m_logname_list
[5])
161 return Scalar(m_group
->getNumMembersGlobal());
163 else if (quantity
== m_logname_list
[6])
165 return Scalar(getPressureTensor().xx
);
167 else if (quantity
== m_logname_list
[7])
169 return Scalar(getPressureTensor().xy
);
171 else if (quantity
== m_logname_list
[8])
173 return Scalar(getPressureTensor().xz
);
175 else if (quantity
== m_logname_list
[9])
177 return Scalar(getPressureTensor().yy
);
179 else if (quantity
== m_logname_list
[10])
181 return Scalar(getPressureTensor().yz
);
183 else if (quantity
== m_logname_list
[11])
185 return Scalar(getPressureTensor().zz
);
189 m_exec_conf
->msg
->error() << "compute.thermo: " << quantity
<< " is not a valid log quantity" << endl
;
190 throw runtime_error("Error getting log value");
194 /*! Computes all thermodynamic properties of the system in one fell swoop.
196 void ComputeThermo::computeProperties()
198 // just drop out if the group is an empty group
199 if (m_group
->getNumMembersGlobal() == 0)
202 unsigned int group_size
= m_group
->getNumMembers();
204 if (m_prof
) m_prof
->push("Thermo");
209 // access the particle data
210 ArrayHandle
<Scalar4
> h_vel(m_pdata
->getVelocities(), access_location::host
, access_mode::read
);
212 // access the net force, pe, and virial
213 const GPUArray
< Scalar4
>& net_force
= m_pdata
->getNetForce();
214 const GPUArray
< Scalar
>& net_virial
= m_pdata
->getNetVirial();
215 ArrayHandle
<Scalar4
> h_net_force(net_force
, access_location::host
, access_mode::read
);
216 ArrayHandle
<Scalar
> h_net_virial(net_virial
, access_location::host
, access_mode::read
);
218 // total kinetic energy
219 double ke_total
= 0.0;
221 PDataFlags flags
= m_pdata
->getFlags();
223 double pressure_kinetic_xx
= 0.0;
224 double pressure_kinetic_xy
= 0.0;
225 double pressure_kinetic_xz
= 0.0;
226 double pressure_kinetic_yy
= 0.0;
227 double pressure_kinetic_yz
= 0.0;
228 double pressure_kinetic_zz
= 0.0;
230 if (flags
[pdata_flag::pressure_tensor
])
232 // Calculate kinetic part of pressure tensor
233 for (unsigned int group_idx
= 0; group_idx
< group_size
; group_idx
++)
235 unsigned int j
= m_group
->getMemberIndex(group_idx
);
236 double mass
= h_vel
.data
[j
].w
;
237 pressure_kinetic_xx
+= mass
*( (double)h_vel
.data
[j
].x
* (double)h_vel
.data
[j
].x
);
238 pressure_kinetic_xy
+= mass
*( (double)h_vel
.data
[j
].x
* (double)h_vel
.data
[j
].y
);
239 pressure_kinetic_xz
+= mass
*( (double)h_vel
.data
[j
].x
* (double)h_vel
.data
[j
].z
);
240 pressure_kinetic_yy
+= mass
*( (double)h_vel
.data
[j
].y
* (double)h_vel
.data
[j
].y
);
241 pressure_kinetic_yz
+= mass
*( (double)h_vel
.data
[j
].y
* (double)h_vel
.data
[j
].z
);
242 pressure_kinetic_zz
+= mass
*( (double)h_vel
.data
[j
].z
* (double)h_vel
.data
[j
].z
);
244 // kinetic energy = 1/2 trace of kinetic part of pressure tensor
245 ke_total
= Scalar(0.5)*(pressure_kinetic_xx
+ pressure_kinetic_yy
+ pressure_kinetic_zz
);
249 // total kinetic energy
250 for (unsigned int group_idx
= 0; group_idx
< group_size
; group_idx
++)
252 unsigned int j
= m_group
->getMemberIndex(group_idx
);
253 ke_total
+= (double)h_vel
.data
[j
].w
*( (double)h_vel
.data
[j
].x
* (double)h_vel
.data
[j
].x
254 + (double)h_vel
.data
[j
].y
* (double)h_vel
.data
[j
].y
255 + (double)h_vel
.data
[j
].z
* (double)h_vel
.data
[j
].z
);
259 ke_total
*= Scalar(0.5);
262 // total potential energy
263 double pe_total
= 0.0;
264 if (flags
[pdata_flag::potential_energy
])
266 for (unsigned int group_idx
= 0; group_idx
< group_size
; group_idx
++)
268 unsigned int j
= m_group
->getMemberIndex(group_idx
);
269 pe_total
+= (double)h_net_force
.data
[j
].w
;
274 double virial_xx
= m_pdata
->getExternalVirial(0);
275 double virial_xy
= m_pdata
->getExternalVirial(1);
276 double virial_xz
= m_pdata
->getExternalVirial(2);
277 double virial_yy
= m_pdata
->getExternalVirial(3);
278 double virial_yz
= m_pdata
->getExternalVirial(4);
279 double virial_zz
= m_pdata
->getExternalVirial(5);
281 if (flags
[pdata_flag::pressure_tensor
])
283 // Calculate upper triangular virial tensor
284 unsigned int virial_pitch
= net_virial
.getPitch();
285 for (unsigned int group_idx
= 0; group_idx
< group_size
; group_idx
++)
287 unsigned int j
= m_group
->getMemberIndex(group_idx
);
288 virial_xx
+= (double)h_net_virial
.data
[j
+0*virial_pitch
];
289 virial_xy
+= (double)h_net_virial
.data
[j
+1*virial_pitch
];
290 virial_xz
+= (double)h_net_virial
.data
[j
+2*virial_pitch
];
291 virial_yy
+= (double)h_net_virial
.data
[j
+3*virial_pitch
];
292 virial_yz
+= (double)h_net_virial
.data
[j
+4*virial_pitch
];
293 virial_zz
+= (double)h_net_virial
.data
[j
+5*virial_pitch
];
296 if (flags
[pdata_flag::isotropic_virial
])
298 // isotropic virial = 1/3 trace of virial tensor
299 W
= Scalar(1./3.) * (virial_xx
+ virial_yy
+ virial_zz
);
302 else if (flags
[pdata_flag::isotropic_virial
])
304 // only sum up isotropic part of virial tensor
305 unsigned int virial_pitch
= net_virial
.getPitch();
306 for (unsigned int group_idx
= 0; group_idx
< group_size
; group_idx
++)
308 unsigned int j
= m_group
->getMemberIndex(group_idx
);
309 W
+= Scalar(1./3.)* ((double)h_net_virial
.data
[j
+0*virial_pitch
] +
310 (double)h_net_virial
.data
[j
+3*virial_pitch
] +
311 (double)h_net_virial
.data
[j
+5*virial_pitch
] );
315 // compute the temperature
316 Scalar temperature
= Scalar(2.0) * Scalar(ke_total
) / Scalar(m_ndof
);
318 // compute the pressure
319 // volume/area & other 2D stuff needed
320 BoxDim global_box
= m_pdata
->getGlobalBox();
322 Scalar3 L
= global_box
.getL();
324 unsigned int D
= m_sysdef
->getNDimensions();
327 // "volume" is area in 2D
329 // W needs to be corrected since the 1/3 factor is built in
330 W
*= Scalar(3.0/2.0);
334 volume
= L
.x
* L
.y
* L
.z
;
337 // pressure: P = (N * K_B * T + W)/V
338 Scalar pressure
= (2.0 * ke_total
/ Scalar(D
) + W
) / volume
;
340 // pressure tensor = (kinetic part + virial) / V
341 Scalar pressure_xx
= (pressure_kinetic_xx
+ virial_xx
) / volume
;
342 Scalar pressure_xy
= (pressure_kinetic_xy
+ virial_xy
) / volume
;
343 Scalar pressure_xz
= (pressure_kinetic_xz
+ virial_xz
) / volume
;
344 Scalar pressure_yy
= (pressure_kinetic_yy
+ virial_yy
) / volume
;
345 Scalar pressure_yz
= (pressure_kinetic_yz
+ virial_yz
) / volume
;
346 Scalar pressure_zz
= (pressure_kinetic_zz
+ virial_zz
) / volume
;
348 // fill out the GPUArray
349 ArrayHandle
<Scalar
> h_properties(m_properties
, access_location::host
, access_mode::overwrite
);
350 h_properties
.data
[thermo_index::temperature
] = temperature
;
351 h_properties
.data
[thermo_index::pressure
] = pressure
;
352 h_properties
.data
[thermo_index::kinetic_energy
] = Scalar(ke_total
);
353 h_properties
.data
[thermo_index::potential_energy
] = Scalar(pe_total
);
354 h_properties
.data
[thermo_index::pressure_xx
] = pressure_xx
;
355 h_properties
.data
[thermo_index::pressure_xy
] = pressure_xy
;
356 h_properties
.data
[thermo_index::pressure_xz
] = pressure_xz
;
357 h_properties
.data
[thermo_index::pressure_yy
] = pressure_yy
;
358 h_properties
.data
[thermo_index::pressure_yz
] = pressure_yz
;
359 h_properties
.data
[thermo_index::pressure_zz
] = pressure_zz
;
362 // in MPI, reduce extensive quantities only when they're needed
363 m_properties_reduced
= !m_pdata
->getDomainDecomposition();
366 if (m_prof
) m_prof
->pop();
370 void ComputeThermo::reduceProperties()
372 if (m_properties_reduced
) return;
375 ArrayHandle
<Scalar
> h_properties(m_properties
, access_location::host
, access_mode::readwrite
);
376 MPI_Allreduce(MPI_IN_PLACE
, h_properties
.data
, thermo_index::num_quantities
, MPI_HOOMD_SCALAR
,
377 MPI_SUM
, m_exec_conf
->getMPICommunicator());
379 m_properties_reduced
= true;
383 void export_ComputeThermo()
385 class_
<ComputeThermo
, boost::shared_ptr
<ComputeThermo
>, bases
<Compute
>, boost::noncopyable
>
386 ("ComputeThermo", init
< boost::shared_ptr
<SystemDefinition
>,
387 boost::shared_ptr
<ParticleGroup
>,
388 const std::string
& >())
389 .def("setNDOF", &ComputeThermo::setNDOF
)
390 .def("getTemperature", &ComputeThermo::getTemperature
)
391 .def("getPressure", &ComputeThermo::getPressure
)
392 .def("getKineticEnergy", &ComputeThermo::getKineticEnergy
)
393 .def("getPotentialEnergy", &ComputeThermo::getPotentialEnergy
)