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 MSDAnalyzer.cc
53 \brief Defines the MSDAnalyzer class
57 #pragma warning( push )
58 #pragma warning( disable : 4244 )
61 #include "MSDAnalyzer.h"
62 #include "HOOMDInitializer.h"
65 #include "Communicator.h"
68 #include <boost/python.hpp>
69 #include <boost/filesystem/operations.hpp>
70 #include <boost/filesystem/convenience.hpp>
71 using namespace boost::python
;
72 using namespace boost::filesystem
;
77 /*! \param sysdef SystemDefinition containing the Particle data to analyze
78 \param fname File name to write output to
79 \param header_prefix String to print before the file header
80 \param overwrite Will overwite an exiting file if true (default is to append)
82 On construction, the initial coordinates of all parrticles in the system are recoreded. The file is opened
83 (and overwritten if told to). Nothing is initially written to the file, that will occur on the first call to
86 MSDAnalyzer::MSDAnalyzer(boost::shared_ptr
<SystemDefinition
> sysdef
,
88 const std::string
& header_prefix
,
90 : Analyzer(sysdef
), m_delimiter("\t"), m_header_prefix(header_prefix
), m_appending(false),
91 m_columns_changed(false)
93 m_exec_conf
->msg
->notice(5) << "Constructing MSDAnalyzer: " << fname
<< " " << header_prefix
<< " " << overwrite
<< endl
;
95 SnapshotParticleData
snapshot(m_pdata
->getNGlobal());
97 m_pdata
->takeSnapshot(snapshot
);
100 // if we are not the root processor, do not perform file I/O
101 if (m_comm
&& !m_exec_conf
->isRoot())
108 if (exists(fname
) && !overwrite
)
110 m_exec_conf
->msg
->notice(3) << "analyze.msd: Appending msd to existing file \"" << fname
<< "\"" << endl
;
111 m_file
.open(fname
.c_str(), ios_base::in
| ios_base::out
| ios_base::ate
);
116 m_exec_conf
->msg
->notice(3) << "analyze.msd: Creating new msd in file \"" << fname
<< "\"" << endl
;
117 m_file
.open(fname
.c_str(), ios_base::out
);
122 m_exec_conf
->msg
->error() << "analyze.msd: Unable to open file " << fname
<< endl
;
123 throw runtime_error("Error initializing analyze.msd");
126 // record the initial particle positions by tag
127 m_initial_x
.resize(m_pdata
->getNGlobal());
128 m_initial_y
.resize(m_pdata
->getNGlobal());
129 m_initial_z
.resize(m_pdata
->getNGlobal());
131 BoxDim box
= m_pdata
->getGlobalBox();
133 // for each particle in the data
134 for (unsigned int tag
= 0; tag
< snapshot
.size
; tag
++)
136 // save its initial position
137 Scalar3 pos
= snapshot
.pos
[tag
];
138 Scalar3 unwrapped
= box
.shift(pos
, snapshot
.image
[tag
]);
139 m_initial_x
[tag
] = unwrapped
.x
;
140 m_initial_y
[tag
] = unwrapped
.y
;
141 m_initial_z
[tag
] = unwrapped
.z
;
145 MSDAnalyzer::~MSDAnalyzer()
147 m_exec_conf
->msg
->notice(5) << "Destroying MSDAnalyzer" << endl
;
150 /*!\param timestep Current time step of the simulation
152 analyze() will first write out the file header if the columns have changed.
154 On every call, analyze() will write calculate the MSD for each group and write out a row in the file.
156 void MSDAnalyzer::analyze(unsigned int timestep
)
159 m_prof
->push("Analyze MSD");
161 // take particle data snapshot
162 SnapshotParticleData
snapshot(m_pdata
->getNGlobal());
164 m_pdata
->takeSnapshot(snapshot
);
167 // if we are not the root processor, do not perform file I/O
168 if (m_comm
&& !m_exec_conf
->isRoot())
170 if (m_prof
) m_prof
->pop();
176 if (m_columns
.size() == 0)
178 m_exec_conf
->msg
->warning() << "analyze.msd: No columns specified in the MSD analysis" << endl
;
182 // ignore writing the header on the first call when appending the file
183 if (m_columns_changed
&& m_appending
)
186 m_columns_changed
= false;
189 // write out the header only once if the columns change
190 if (m_columns_changed
)
193 m_columns_changed
= false;
196 // write out the row every time
197 writeRow(timestep
, snapshot
);
203 /*! \param delimiter New delimiter to set
205 The delimiter is printed between every element in the row of the output
207 void MSDAnalyzer::setDelimiter(const std::string
& delimiter
)
209 m_delimiter
= delimiter
;
212 /*! \param group Particle group to calculate the MSD of
213 \param name Name to print in the header of the file
215 After a column is added with addColumn(), future calls to analyze() will calculate the MSD of the particles defined
216 in \a group and print out an entry under the \a name header in the file.
218 void MSDAnalyzer::addColumn(boost::shared_ptr
<ParticleGroup
> group
, const std::string
& name
)
220 m_columns
.push_back(column(group
, name
));
221 m_columns_changed
= true;
224 /*! \param xml_fname Name of the XML file to read in to the r0 positions
226 \post \a xml_fname is read and all initial r0 positions are assigned from that file.
228 void MSDAnalyzer::setR0(const std::string
& xml_fname
)
230 // read in the xml file
231 HOOMDInitializer
xml(m_exec_conf
,xml_fname
);
233 // take particle data snapshot
234 SnapshotParticleData
snapshot(m_pdata
->getNGlobal());
236 m_pdata
->takeSnapshot(snapshot
);
239 // if we are not the root processor, do not perform file I/O
240 if (m_pdata
->getDomainDecomposition() && !m_exec_conf
->isRoot())
246 // verify that the input matches the current system size
247 unsigned int nparticles
= m_pdata
->getNGlobal();
248 if (nparticles
!= xml
.getPos().size())
250 m_exec_conf
->msg
->error() << "analyze.msd: Found " << xml
.getPos().size() << " particles in "
251 << xml_fname
<< ", but there are " << nparticles
<< " in the current simulation." << endl
;
252 throw runtime_error("Error setting r0 in analyze.msd");
255 // determine if we have image data
256 bool have_image
= (xml
.getImage().size() == nparticles
);
259 m_exec_conf
->msg
->warning() << "analyze.msd: Image data missing or corrupt in " << xml_fname
260 << ". Computed msd values will not be correct." << endl
;
263 // reset the initial positions
264 BoxDim box
= m_pdata
->getGlobalBox();
266 // for each particle in the data
267 for (unsigned int tag
= 0; tag
< nparticles
; tag
++)
269 // save its initial position
270 HOOMDInitializer::vec pos
= xml
.getPos()[tag
];
271 m_initial_x
[tag
] = pos
.x
;
272 m_initial_y
[tag
] = pos
.y
;
273 m_initial_z
[tag
] = pos
.z
;
275 // adjust the positions by the image flags if we have them
278 HOOMDInitializer::vec_int image
= xml
.getImage()[tag
];
279 Scalar3 pos
= make_scalar3(m_initial_x
[tag
], m_initial_y
[tag
], m_initial_z
[tag
]);
280 int3 image_i
= make_int3(image
.x
, image
.y
, image
.z
);
281 Scalar3 unwrapped
= box
.shift(pos
, image_i
);
282 m_initial_x
[tag
] = unwrapped
.x
;
283 m_initial_y
[tag
] = unwrapped
.y
;
284 m_initial_z
[tag
] = unwrapped
.z
;
289 /*! The entire header row is written to the file. First, timestep is written as every file includes it and then the
290 columns are looped through and their names printed, separated by the delimiter.
292 void MSDAnalyzer::writeHeader()
294 // write out the header prefix
295 m_file
<< m_header_prefix
;
297 // timestep is always output
298 m_file
<< "timestep";
300 if (m_columns
.size() == 0)
302 m_exec_conf
->msg
->warning() << "analyze.msd: No columns specified in the MSD analysis" << endl
;
306 // only print the delimiter after the timestep if there are more columns
307 m_file
<< m_delimiter
;
309 // write all but the last of the quantities separated by the delimiter
310 for (unsigned int i
= 0; i
< m_columns
.size()-1; i
++)
311 m_file
<< m_columns
[i
].m_name
<< m_delimiter
;
312 // write the last one with no delimiter after it
313 m_file
<< m_columns
[m_columns
.size()-1].m_name
<< endl
;
317 /*! \param group Particle group to calculate the MSD of
318 Loop through all particles in the given group and calculate the MSD over them.
319 \returns The calculated MSD
321 Scalar
MSDAnalyzer::calcMSD(boost::shared_ptr
<ParticleGroup
const> group
, const SnapshotParticleData
& snapshot
)
323 BoxDim box
= m_pdata
->getGlobalBox();
325 // initial sum for the average
326 Scalar msd
= Scalar(0.0);
328 // handle the case where there are 0 members gracefully
329 if (group
->getNumMembersGlobal() == 0)
331 m_exec_conf
->msg
->warning() << "analyze.msd: Group has 0 members, reporting a calculated msd of 0.0" << endl
;
335 // for each particle in the group
336 for (unsigned int group_idx
= 0; group_idx
< group
->getNumMembersGlobal(); group_idx
++)
338 // get the tag for the current group member from the group
339 unsigned int tag
= group
->getMemberTag(group_idx
);
340 Scalar3 pos
= snapshot
.pos
[tag
];
341 int3 image
= snapshot
.image
[tag
];
342 Scalar3 unwrapped
= box
.shift(pos
, image
);
343 Scalar dx
= unwrapped
.x
- m_initial_x
[tag
];
344 Scalar dy
= unwrapped
.y
- m_initial_y
[tag
];
345 Scalar dz
= unwrapped
.z
- m_initial_z
[tag
];
347 msd
+= dx
*dx
+ dy
*dy
+ dz
*dz
;
350 // divide to complete the average
351 msd
/= Scalar(group
->getNumMembersGlobal());
355 /*! \param timestep current time step of the simulation
357 Performs all the steps needed in order to calculate the MSDs for all the groups in the columns and writes out an
358 entire row to the file.
360 void MSDAnalyzer::writeRow(unsigned int timestep
, const SnapshotParticleData
& snapshot
)
362 if (m_prof
) m_prof
->push("MSD");
364 // The timestep is always output
365 m_file
<< setprecision(10) << timestep
;
367 // quit now if there is nothing to log
368 if (m_columns
.size() == 0)
373 // only print the delimiter after the timestep if there are more columns
374 m_file
<< m_delimiter
;
376 // write all but the last of the columns separated by the delimiter
377 for (unsigned int i
= 0; i
< m_columns
.size()-1; i
++)
378 m_file
<< setprecision(10) << calcMSD(m_columns
[i
].m_group
, snapshot
) << m_delimiter
;
379 // write the last one with no delimiter after it
380 m_file
<< setprecision(10) << calcMSD(m_columns
[m_columns
.size()-1].m_group
, snapshot
) << endl
;
385 m_exec_conf
->msg
->error() << "analyze.msd: I/O error while writing file" << endl
;
386 throw runtime_error("Error writting msd file");
389 if (m_prof
) m_prof
->pop();
392 void export_MSDAnalyzer()
394 class_
<MSDAnalyzer
, boost::shared_ptr
<MSDAnalyzer
>, bases
<Analyzer
>, boost::noncopyable
>
395 ("MSDAnalyzer", init
< boost::shared_ptr
<SystemDefinition
>, const std::string
&, const std::string
&, bool >())
396 .def("setDelimiter", &MSDAnalyzer::setDelimiter
)
397 .def("addColumn", &MSDAnalyzer::addColumn
)
398 .def("setR0", &MSDAnalyzer::setR0
)
403 #pragma warning( pop )