Enable parallel tests.
[hoomd-blue.git] / libhoomd / analyzers / DCDDumpWriter.cc
blobb7e9fc4416626f467e72e504d632e060b60f716e
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
52 /*! \file DCDDumpWriter.cc
53 \brief Defines the DCDDumpWriter class and related helper functions
56 #ifdef WIN32
57 #pragma warning( push )
58 #pragma warning( disable : 4244 )
59 #endif
61 #include <stdexcept>
63 #include "DCDDumpWriter.h"
64 #include "time.h"
66 #ifdef ENABLE_MPI
67 #include "Communicator.h"
68 #endif
70 #include <boost/python.hpp>
71 #include <boost/filesystem/operations.hpp>
72 #include <boost/filesystem/convenience.hpp>
73 using boost::filesystem::exists;
74 using namespace boost::python;
75 using namespace std;
77 // File position of NFILE in DCD header
78 #define NFILE_POS 8L
79 // File position of NSTEP in DCD header
80 #define NSTEP_POS 20L
82 //! simple helper function to write an integer
83 /*! \param file file to write to
84 \param val integer to write
86 static void write_int(fstream &file, unsigned int val)
88 file.write((char *)&val, sizeof(unsigned int));
91 //! simple helper function to read in integer
92 /*! \param file file to read from
93 \returns integer read
95 static unsigned int read_int(fstream &file)
97 unsigned int val;
98 file.read((char *)&val, sizeof(unsigned int));
99 return val;
102 /*! Constructs the DCDDumpWriter. After construction, settings are set. No file operations are
103 attempted until analyze() is called.
105 \param sysdef SystemDefinition containing the ParticleData to dump
106 \param fname File name to write DCD data to
107 \param period Period which analyze() is going to be called at
108 \param group Group of particles to include in the output
109 \param overwrite If false, existing files will be appended to. If true, existing files will be overwritten.
111 \note You must call analyze() with the same period specified in the constructor or
112 the time step inforamtion in the file will be invalid. analyze() will print a warning
113 if it is called out of sequence.
115 DCDDumpWriter::DCDDumpWriter(boost::shared_ptr<SystemDefinition> sysdef,
116 const std::string &fname,
117 unsigned int period,
118 boost::shared_ptr<ParticleGroup> group,
119 bool overwrite)
120 : Analyzer(sysdef), m_fname(fname), m_start_timestep(0), m_period(period), m_group(group),
121 m_rigid_data(sysdef->getRigidData()), m_num_frames_written(0), m_last_written_step(0), m_appending(false),
122 m_unwrap_full(false), m_unwrap_rigid(false), m_angle(false),
123 m_overwrite(overwrite), m_is_initialized(false)
125 m_exec_conf->msg->notice(5) << "Constructing DCDDumpWriter: " << fname << " " << period << " " << overwrite << endl;
128 //! Initializes the output file for writing
129 void DCDDumpWriter::initFileIO()
131 // handle appending to an existing file if it is requested
132 if (!m_overwrite && exists(m_fname))
134 m_exec_conf->msg->notice(3) << "dump.dcd: Appending to existing DCD file \"" << m_fname << "\"" << endl;
136 // open the file and get data from the header
137 fstream file;
138 file.open(m_fname.c_str(), ios::ate | ios::in | ios::out | ios::binary);
139 file.seekp(NFILE_POS);
141 m_num_frames_written = read_int(file);
142 m_start_timestep = read_int(file);
143 unsigned int file_period = read_int(file);
145 // warn the user if we are now dumping at a different period
146 if (file_period != m_period)
147 m_exec_conf->msg->warning() << "dump.dcd: appending to a file that has period " << file_period << " that is not the same as the requested period of " << m_period << endl;
149 m_last_written_step = read_int(file);
151 // check for errors
152 if (!file.good())
154 m_exec_conf->msg->error() << "dump.dcd: I/O error while reading DCD header data" << endl;
155 throw runtime_error("Error appending to DCD file");
158 m_appending = true;
161 m_staging_buffer = new float[m_pdata->getNGlobal()];
162 m_is_initialized = true;
165 DCDDumpWriter::~DCDDumpWriter()
167 m_exec_conf->msg->notice(5) << "Destroying DCDDumpWriter" << endl;
169 if (m_is_initialized)
170 delete[] m_staging_buffer;
173 /*! \param timestep Current time step of the simulation
174 The very first call to analyze() will result in the creation (or overwriting) of the
175 file fname and the writing of the current timestep snapshot. After that, each call to analyze
176 will add a new snapshot to the end of the file.
178 void DCDDumpWriter::analyze(unsigned int timestep)
180 if (m_prof)
181 m_prof->push("Dump DCD");
183 // take particle data snapshot
184 SnapshotParticleData snapshot(m_pdata->getNGlobal());
186 m_pdata->takeSnapshot(snapshot);
188 #ifdef ENABLE_MPI
189 // if we are not the root processor, do not perform file I/O
190 if (m_comm && !m_exec_conf->isRoot())
192 if (m_prof) m_prof->pop();
193 return;
195 #endif
197 if (! m_is_initialized)
198 initFileIO();
200 // the file object
201 fstream file;
203 // initialize the file on the first frame written
204 if (m_num_frames_written == 0)
206 // open the file and truncate it
207 file.open(m_fname.c_str(), ios::trunc | ios::out | ios::binary);
209 // write the file header
210 m_start_timestep = timestep;
211 write_file_header(file);
213 else
215 if (m_appending && timestep <= m_last_written_step)
217 m_exec_conf->msg->warning() << "dump.dcd: not writing output at timestep " << timestep << " because the file reports that it already has data up to step " << m_last_written_step << endl;
219 if (m_prof)
220 m_prof->pop();
221 return;
224 // open the file and move the file pointer to the end
225 file.open(m_fname.c_str(), ios::ate | ios::in | ios::out | ios::binary);
227 // verify the period on subsequent frames
228 if ( (timestep - m_start_timestep) % m_period != 0)
229 m_exec_conf->msg->warning() << "dump.dcd: writing time step " << timestep << " which is not specified in the period of the DCD file: " << m_start_timestep << " + i * " << m_period << endl;
232 // write the data for the current time step
233 write_frame_header(file);
234 write_frame_data(file, snapshot);
236 // update the header with the number of frames written
237 m_num_frames_written++;
238 write_updated_header(file, timestep);
239 file.close();
241 if (m_prof)
242 m_prof->pop();
245 /*! \param file File to write to
246 Writes the initial DCD header to the beginning of the file. This must be
247 called on a newly created (or truncated file).
249 void DCDDumpWriter::write_file_header(std::fstream &file)
251 // the first 4 bytes in the file must be 84
252 write_int(file, 84);
254 // the next 4 bytes in the file must be "CORD"
255 char cord_data[] = "CORD";
256 file.write(cord_data, 4);
257 write_int(file, 0); // Number of frames in file, none written yet
258 write_int(file, m_start_timestep); // Starting timestep
259 write_int(file, m_period); // Timesteps between frames written to the file
260 write_int(file, 0); // Number of timesteps in simulation
261 write_int(file, 0);
262 write_int(file, 0);
263 write_int(file, 0);
264 write_int(file, 0);
265 write_int(file, 0);
266 write_int(file, 0); // timestep (unused)
267 write_int(file, 1); // include unit cell
268 write_int(file, 0);
269 write_int(file, 0);
270 write_int(file, 0);
271 write_int(file, 0);
272 write_int(file, 0);
273 write_int(file, 0);
274 write_int(file, 0);
275 write_int(file, 0);
276 write_int(file, 24); // Pretend to be CHARMM version 24
277 write_int(file, 84);
278 write_int(file, 164);
279 write_int(file, 2);
281 char title_string[81];
282 memset(title_string, 0, 81);
283 char remarks[] = "Created by HOOMD";
284 strncpy(title_string, remarks, 80);
285 title_string[79] = '\0';
286 file.write(title_string, 80);
288 char time_str[81];
289 memset(time_str, 0, 81);
290 time_t cur_time = time(NULL);
291 tm *tmbuf=localtime(&cur_time);
292 strftime(time_str, 80, "REMARKS Created %d %B, %Y at %H:%M", tmbuf);
293 file.write(time_str, 80);
295 write_int(file, 164);
296 write_int(file, 4);
297 unsigned int nparticles = m_group->getNumMembersGlobal();
298 write_int(file, nparticles);
299 write_int(file, 4);
301 // check for errors
302 if (!file.good())
304 m_exec_conf->msg->error() << "dump.dcd: I/O rrror when writing DCD header" << endl;
305 throw runtime_error("Error writing DCD file");
309 /*! \param file File to write to
310 Writes the header that precedes each snapshot in the file. This header
311 includes information on the box size of the simulation.
313 void DCDDumpWriter::write_frame_header(std::fstream &file)
315 double unitcell[6];
316 BoxDim box = m_pdata->getGlobalBox();
317 // set box dimensions
318 Scalar a,b,c,alpha,beta,gamma;
319 Scalar3 va = box.getLatticeVector(0);
320 Scalar3 vb = box.getLatticeVector(1);
321 Scalar3 vc = box.getLatticeVector(2);
322 a = sqrt(dot(va,va));
323 b = sqrt(dot(vb,vb));
324 c = sqrt(dot(vc,vc));
325 alpha = dot(vb,vc)/(b*c);
326 beta = dot(va,vc)/(a*c);
327 gamma = dot(va,vb)/(a*b);
329 unitcell[0] = a;
330 unitcell[2] = b;
331 unitcell[5] = c;
332 // box angles are 90 degrees
333 unitcell[1] = gamma;
334 unitcell[3] = beta;
335 unitcell[4] = alpha;
337 write_int(file, 48);
338 file.write((char *)unitcell, 48);
339 write_int(file, 48);
341 // check for errors
342 if (!file.good())
344 m_exec_conf->msg->error() << "dump.dcd: I/O rrror while writing DCD frame header" << endl;
345 throw runtime_error("Error writing DCD file");
349 /*! \param file File to write to
350 \param snapshot Snapshot to write
351 Writes the actual particle positions for all particles at the current time step
353 void DCDDumpWriter::write_frame_data(std::fstream &file, const SnapshotParticleData& snapshot)
355 // we need to unsort the positions and write in tag order
356 assert(m_staging_buffer);
358 #ifdef ENABLE_MPI
359 if (m_comm && m_unwrap_rigid)
361 m_exec_conf->msg->error() << "dump.dcd: Unwrap of rigid bodies in DCD files is currently not supported in MPI simulations" << endl;
362 throw runtime_error("Error writing DCD file");
364 #endif
366 ArrayHandle<int3> body_image_handle(m_rigid_data->getBodyImage(),access_location::host,access_mode::read);
367 BoxDim box = m_pdata->getGlobalBox();
369 unsigned int nparticles = m_group->getNumMembersGlobal();
371 // Create a tmp copy of the particle data and unwrap particles
372 std::vector<Scalar3> tmp_pos(snapshot.pos);
373 for (unsigned int group_idx = 0; group_idx < nparticles; group_idx++)
375 unsigned int i = m_group->getMemberTag(group_idx);
377 if (m_unwrap_full)
379 tmp_pos[i] = box.shift(tmp_pos[i], snapshot.image[i]);
381 else if (m_unwrap_rigid && snapshot.body[i] != NO_BODY)
383 int body_ix = body_image_handle.data[snapshot.body[i]].x;
384 int body_iy = body_image_handle.data[snapshot.body[i]].y;
385 int body_iz = body_image_handle.data[snapshot.body[i]].z;
386 int3 particle_img = snapshot.image[i];
387 int3 img_diff = make_int3(particle_img.x - body_ix,
388 particle_img.y - body_iy,
389 particle_img.z - body_iz);
391 tmp_pos[i] = box.shift(tmp_pos[i], img_diff);
395 // prepare x coords for writing, looping in tag order
396 for (unsigned int group_idx = 0; group_idx < nparticles; group_idx++)
398 unsigned int i = m_group->getMemberTag(group_idx);
399 m_staging_buffer[group_idx] = float(tmp_pos[i].x);
402 // write x coords
403 write_int(file, nparticles * sizeof(float));
404 file.write((char *)m_staging_buffer, nparticles * sizeof(float));
405 write_int(file, nparticles * sizeof(float));
407 // prepare y coords for writing
408 for (unsigned int group_idx = 0; group_idx < nparticles; group_idx++)
410 unsigned int i = m_group->getMemberTag(group_idx);
411 m_staging_buffer[group_idx] = float(tmp_pos[i].y);
414 // write y coords
415 write_int(file, nparticles * sizeof(float));
416 file.write((char *)m_staging_buffer, nparticles * sizeof(float));
417 write_int(file, nparticles * sizeof(float));
419 // prepare z coords for writing
420 for (unsigned int group_idx = 0; group_idx < nparticles; group_idx++)
422 unsigned int i = m_group->getMemberTag(group_idx);
423 m_staging_buffer[group_idx] = float(tmp_pos[i].z);
425 // m_angle set to True turns on a hack where the particle orientation angle is written out to the z component
426 // this only works in 2D simulations, obviously
427 if (m_angle)
429 Scalar s = 1;
430 if (snapshot.orientation[i].w < 0)
431 s = -1;
433 m_staging_buffer[group_idx] = acosf(snapshot.orientation[i].x) * 2 * s;
437 // write z coords
438 write_int(file, nparticles * sizeof(float));
439 file.write((char *)m_staging_buffer, nparticles * sizeof(float));
440 write_int(file, nparticles * sizeof(float));
442 // check for errors
443 if (!file.good())
445 m_exec_conf->msg->error() << "I/O error while writing DCD frame data" << endl;
446 throw runtime_error("Error writing DCD file");
450 /*! \param file File to write to
451 \param timestep Current time step of the simulation
453 Updates the pointers in the main file header to reflect the current number of frames
454 written and the last time step written.
456 void DCDDumpWriter::write_updated_header(std::fstream &file, unsigned int timestep)
458 file.seekp(NFILE_POS);
459 write_int(file, m_num_frames_written);
461 file.seekp(NSTEP_POS);
462 write_int(file, timestep);
465 void export_DCDDumpWriter()
467 class_<DCDDumpWriter, boost::shared_ptr<DCDDumpWriter>, bases<Analyzer>, boost::noncopyable>
468 ("DCDDumpWriter", init< boost::shared_ptr<SystemDefinition>, std::string, unsigned int, boost::shared_ptr<ParticleGroup>, bool>())
469 .def("setUnwrapFull", &DCDDumpWriter::setUnwrapFull)
470 .def("setUnwrapRigid", &DCDDumpWriter::setUnwrapRigid)
471 .def("setAngleZ", &DCDDumpWriter::setAngleZ)
475 #ifdef WIN32
476 #pragma warning( pop )
477 #endif