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 DCDDumpWriter.cc
53 \brief Defines the DCDDumpWriter class and related helper functions
57 #pragma warning( push )
58 #pragma warning( disable : 4244 )
63 #include "DCDDumpWriter.h"
67 #include "Communicator.h"
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
;
77 // File position of NFILE in DCD header
79 // File position of NSTEP in DCD header
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
95 static unsigned int read_int(fstream
&file
)
98 file
.read((char *)&val
, sizeof(unsigned int));
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
,
118 boost::shared_ptr
<ParticleGroup
> group
,
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
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
);
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");
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
)
181 m_prof
->push("Dump DCD");
183 // take particle data snapshot
184 SnapshotParticleData
snapshot(m_pdata
->getNGlobal());
186 m_pdata
->takeSnapshot(snapshot
);
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();
197 if (! m_is_initialized
)
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
);
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
;
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
);
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
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
266 write_int(file
, 0); // timestep (unused)
267 write_int(file
, 1); // include unit cell
276 write_int(file
, 24); // Pretend to be CHARMM version 24
278 write_int(file
, 164);
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);
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);
297 unsigned int nparticles
= m_group
->getNumMembersGlobal();
298 write_int(file
, nparticles
);
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
)
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
);
332 // box angles are 90 degrees
338 file
.write((char *)unitcell
, 48);
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
);
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");
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
);
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
);
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
);
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
430 if (snapshot
.orientation
[i
].w
< 0)
433 m_staging_buffer
[group_idx
] = acosf(snapshot
.orientation
[i
].x
) * 2 * s
;
438 write_int(file
, nparticles
* sizeof(float));
439 file
.write((char *)m_staging_buffer
, nparticles
* sizeof(float));
440 write_int(file
, nparticles
* sizeof(float));
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
)
476 #pragma warning( pop )