Enable parallel tests.
[hoomd-blue.git] / libhoomd / analyzers / IMDInterface.cc
blob52436539b210f9946d77449ab77bf4781dda939f
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 IMDInterface.cc
53 \brief Defines the IMDInterface class
56 #ifdef WIN32
57 #pragma warning( push )
58 #pragma warning( disable : 4103 4244 )
59 #endif
61 #include <boost/python.hpp>
62 #include <boost/shared_array.hpp>
63 using namespace boost::python;
64 using namespace boost;
66 #include "IMDInterface.h"
67 #include "SignalHandler.h"
69 #ifdef ENABLE_MPI
70 #include "Communicator.h"
71 #include "HOOMDMPI.h"
72 #endif
74 #include "vmdsock.h"
75 #include "imd.h"
77 #include <stdexcept>
79 using namespace std;
81 /*! After construction, IMDInterface is listening for connections on port \a port.
82 analyze() must be called to handle any incoming connections.
83 \param sysdef SystemDefinition containing the ParticleData that will be transmitted to VMD
84 \param port port number to listen for connections on
85 \param pause Set to true to pause the simulation and waith for IMD_GO before continuing
86 \param rate Initial rate at which to send data
87 \param force Constant force used to apply forces received from VMD
88 \param force_scale Factor by which to scale all forces from IMD
90 IMDInterface::IMDInterface(boost::shared_ptr<SystemDefinition> sysdef,
91 int port,
92 bool pause,
93 unsigned int rate,
94 boost::shared_ptr<ConstForceCompute> force,
95 float force_scale)
96 : Analyzer(sysdef)
98 m_exec_conf->msg->notice(5) << "Constructing IMDInterface: " << port << " " << pause << " " << rate << " " << force_scale << endl;
100 if (port <= 0)
102 m_exec_conf->msg->error() << "analyze.imd: Invalid port specified" << endl;
103 throw runtime_error("Error initializing IMDInterface");
106 // initialize state
107 m_active = false;
108 m_paused = pause;
109 m_trate = rate;
110 m_count = 0;
111 m_force = force;
112 m_force_scale = force_scale;
113 m_port = port;
114 if (m_force)
115 m_force->setForce(0,0,0);
117 // TCP socket will be initialized later
118 m_is_initialized = false;
121 void IMDInterface::initConnection()
123 int err = 0;
125 // start by initializing memory
126 m_tmp_coords = new float[m_pdata->getNGlobal() * 3];
128 // intialize the listening socket
129 vmdsock_init();
130 m_listen_sock = vmdsock_create();
132 // check for errors
133 if (m_listen_sock == NULL)
135 m_exec_conf->msg->error() << "analyze.imd: Unable to create listening socket" << endl;
136 throw runtime_error("Error initializing IMDInterface");
139 // bind the socket and start listening for connections on that port
140 m_connected_sock = NULL;
141 err = vmdsock_bind(m_listen_sock, m_port);
143 if (err == -1)
145 m_exec_conf->msg->error() << "analyze.imd: Unable to bind listening socket" << endl;
146 throw runtime_error("Error initializing IMDInterface");
149 err = vmdsock_listen(m_listen_sock);
151 if (err == -1)
153 m_exec_conf->msg->error() << "analyze.imd: Unable to listen on listening socket" << endl;
154 throw runtime_error("Error initializing IMDInterface");
157 m_exec_conf->msg->notice(2) << "analyze.imd: listening on port " << m_port << endl;
159 m_is_initialized = true;
162 IMDInterface::~IMDInterface()
164 m_exec_conf->msg->notice(5) << "Destroying IMDInterface" << endl;
166 if (m_is_initialized)
168 // free all used memory
169 delete[] m_tmp_coords;
170 vmdsock_destroy(m_connected_sock);
171 vmdsock_destroy(m_listen_sock);
173 m_tmp_coords = NULL;
174 m_connected_sock = NULL;
175 m_listen_sock = NULL;
179 /*! If there is no active connection, analyze() will check to see if a connection attempt
180 has been made since the last call. If so, it will attempt to handshake with VMD and
181 on success will start transmitting data every time analyze() is called
183 \param timestep Current time step of the simulation
185 void IMDInterface::analyze(unsigned int timestep)
187 if (m_prof)
188 m_prof->push("IMD");
190 #ifdef ENABLE_MPI
191 bool is_root = true;
192 if (m_comm)
193 is_root = m_exec_conf->isRoot();
195 if (is_root && ! m_is_initialized)
196 initConnection();
198 if (is_root)
199 #else
200 if (! m_is_initialized)
201 initConnection();
202 #endif
204 m_count++;
208 // establish a connection if one has not been made
209 if (m_connected_sock == NULL)
210 establishConnectionAttempt();
212 // dispatch incoming commands
213 if (m_connected_sock)
217 dispatch();
219 while (m_connected_sock && messagesAvailable());
222 // quit if cntrl-C was pressed
223 if (g_sigint_recvd)
225 g_sigint_recvd = 0;
226 throw runtime_error("SIG INT received while paused in IMD");
229 while (m_paused);
232 #ifdef ENABLE_MPI
233 unsigned char send_coords = 0;
234 if (is_root && m_connected_sock && m_active && (m_trate == 0 || m_count % m_trate == 0))
235 send_coords = 1;
237 if (m_comm)
239 bcast(send_coords, 0, m_exec_conf->getMPICommunicator());
242 if (send_coords)
243 sendCoords(timestep);
244 #else
245 // send data when active, connected, and the rate matches
246 if (m_connected_sock && m_active && (m_trate == 0 || m_count % m_trate == 0))
247 sendCoords(timestep);
248 #endif
250 if (m_prof)
251 m_prof->pop();
254 /*! \pre \a m_connected_sock is connected and handshaking has occured
256 void IMDInterface::dispatch()
258 assert(m_connected_sock != NULL);
260 // wait for messages, but only when paused
261 int timeout = 0;
262 if (m_paused)
263 timeout = 5;
265 // begin by checking to see if any commands have been received
266 int length;
267 int res = vmdsock_selread(m_connected_sock, timeout);
268 // check to see if there are any errors
269 if (res == -1)
271 m_exec_conf->msg->notice(3) << "analyze.imd: connection appears to have been terminated" << endl;
272 processDeadConnection();
273 return;
275 // if a command is waiting
276 if (res == 1)
278 // receive the header
279 IMDType header = imd_recv_header(m_connected_sock, &length);
281 switch (header)
283 case IMD_DISCONNECT:
284 processIMD_DISCONNECT();
285 break;
286 case IMD_GO:
287 processIMD_GO();
288 break;
289 case IMD_KILL:
290 processIMD_KILL();
291 break;
292 case IMD_MDCOMM:
293 processIMD_MDCOMM(length);
294 break;
295 case IMD_TRATE:
296 processIMD_TRATE(length);
297 break;
298 case IMD_PAUSE:
299 processIMD_PAUSE();
300 break;
301 case IMD_IOERROR:
302 processIMD_IOERROR();
303 break;
304 default:
305 m_exec_conf->msg->notice(3) << "analyze.imd: received an unimplemented command (" << header << "), disconnecting" << endl;
306 processDeadConnection();
307 break;
310 // otherwise no message was received, do nothing
313 /*! \pre m_connected_sock is connected
315 bool IMDInterface::messagesAvailable()
317 int res = vmdsock_selread(m_connected_sock, 0);
319 if (res == -1)
321 m_exec_conf->msg->notice(3) << "analyze.imd: connection appears to have been terminated" << endl;
322 processDeadConnection();
323 return false;
325 if (res == 1)
326 return true;
327 else
328 return false;
331 void IMDInterface::processIMD_DISCONNECT()
333 // cleanly disconnect and continue running the simulation. This is no different than what we do with a dead
334 // connection
335 processDeadConnection();
338 void IMDInterface::processIMD_GO()
340 // unpause and start transmitting data
341 m_paused = false;
342 m_active = true;
343 m_exec_conf->msg->notice(3) << "analyze.imd: Received IMD_GO, transmitting data now" << endl;
346 void IMDInterface::processIMD_KILL()
348 // disconnect (no different from handling a dead connection)
349 processDeadConnection();
350 // terminate the simulation
351 m_exec_conf->msg->notice(3) << "analyze.imd: Received IMD_KILL message, stopping the simulation" << endl;
352 throw runtime_error("Received IMD_KILL message");
355 void IMDInterface::processIMD_MDCOMM(unsigned int n)
357 // mdcomm is not currently handled
358 shared_array<int32> indices(new int32[n]);
359 shared_array<float> forces(new float[3*n]);
361 int err = imd_recv_mdcomm(m_connected_sock, n, &indices[0], &forces[0]);
363 if (err)
365 m_exec_conf->msg->error() << "analyze.imd: Error receiving mdcomm data, disconnecting" << endl;
366 processDeadConnection();
367 return;
370 #ifdef ENABLE_MPI
371 if (m_comm)
373 m_exec_conf->msg->warning() << "analyze.imd: mdcomm currently not supported in MPI simulations." << endl;
375 #endif
377 if (m_force)
379 ArrayHandle< unsigned int > h_rtag(m_pdata->getRTags(), access_location::host, access_mode::read);
380 m_force->setForce(0,0,0);
381 for (unsigned int i = 0; i < n; i++)
383 unsigned int j = h_rtag.data[indices[i]];
384 m_force->setParticleForce(j,
385 forces[3*i+0]*m_force_scale,
386 forces[3*i+1]*m_force_scale,
387 forces[3*i+2]*m_force_scale);
390 else
392 m_exec_conf->msg->warning() << "analyze.imd: Receiving forces over IMD, but no force was given to analyze.imd. Doing nothing" << endl;
396 void IMDInterface::processIMD_TRATE(int rate)
398 m_exec_conf->msg->notice(3) << "analyze.imd: Received IMD_TRATE, setting trate to " << rate << endl;
399 m_trate = rate;
402 void IMDInterface::processIMD_PAUSE()
404 if (!m_paused)
406 m_exec_conf->msg->notice(3) << "analyze.imd: Received IMD_PAUSE, pausing simulation" << endl;
407 m_paused = true;
409 else
411 m_exec_conf->msg->notice(3) << "analyze.imd: Received IMD_PAUSE, unpausing simulation" << endl;
412 m_paused = false;
416 void IMDInterface::processIMD_IOERROR()
418 // disconnect (no different from handling a dead connection)
419 processDeadConnection();
420 // terminate the simulation
421 m_exec_conf->msg->error() << "analyze.imd: Received IMD_IOERROR message, dropping the connection" << endl;
424 void IMDInterface::processDeadConnection()
426 vmdsock_destroy(m_connected_sock);
427 m_connected_sock = NULL;
428 m_active = false;
429 m_paused = false;
430 if (m_force)
431 m_force->setForce(0,0,0);
434 /*! \pre \a m_connected_sock is not connected
435 \pre \a m_listen_sock is listening
437 \a m_listen_sock is checked for any incoming connections. If an incoming connection is found, a handshake is made
438 and \a m_connected_sock is set. If no connection is established, \a m_connected_sock is set to NULL.
440 void IMDInterface::establishConnectionAttempt()
442 assert(m_listen_sock != NULL);
443 assert(m_connected_sock == NULL);
445 // wait for messages, but only when paused
446 int timeout = 0;
447 if (m_paused)
448 timeout = 5;
450 // check to see if there is an incoming connection
451 if (vmdsock_selread(m_listen_sock, timeout) > 0)
453 // create the connection
454 m_connected_sock = vmdsock_accept(m_listen_sock);
455 if (imd_handshake(m_connected_sock))
457 vmdsock_destroy(m_connected_sock);
458 m_connected_sock = NULL;
459 return;
461 else
463 m_exec_conf->msg->notice(2) << "analyze.imd: accepted connection" << endl;
468 /*! \param timestep Current time step of the simulation
469 \pre A connection has been established
471 Sends the current coordinates to VMD for display.
473 void IMDInterface::sendCoords(unsigned int timestep)
475 // take a snapshot of the particle data
476 SnapshotParticleData snapshot(m_pdata->getNGlobal());
477 m_pdata->takeSnapshot(snapshot);
479 #ifdef ENABLE_MPI
480 // return now if not root rank
481 if (m_comm)
482 if (! m_exec_conf->isRoot()) return;
483 #endif
485 assert(m_connected_sock != NULL);
487 // setup and send the energies structure
488 IMDEnergies energies;
489 energies.tstep = timestep;
490 energies.T = 0.0f;
491 energies.Etot = 0.0f;
492 energies.Epot = 0.0f;
493 energies.Evdw = 0.0f;
494 energies.Eelec = 0.0f;
495 energies.Ebond = 0.0f;
496 energies.Eangle = 0.0f;
497 energies.Edihe = 0.0f;
498 energies.Eimpr = 0.0f;
500 int err = imd_send_energies(m_connected_sock, &energies);
501 if (err)
503 m_exec_conf->msg->error() << "analyze.imd: I/O error while sending energies, disconnecting" << endl;
504 processDeadConnection();
505 return;
508 // copy the particle data to the holding array and send it
509 for (unsigned int tag = 0; tag < m_pdata->getNGlobal(); tag++)
511 m_tmp_coords[tag*3] = float(snapshot.pos[tag].x);
512 m_tmp_coords[tag*3 + 1] = float(snapshot.pos[tag].y);
513 m_tmp_coords[tag*3 + 2] = float(snapshot.pos[tag].z);
515 err = imd_send_fcoords(m_connected_sock, m_pdata->getNGlobal(), m_tmp_coords);
517 if (err)
519 m_exec_conf->msg->error() << "analyze.imd: I/O error while sending coordinates, disconnecting" << endl;
520 processDeadConnection();
521 return;
525 void export_IMDInterface()
527 class_<IMDInterface, boost::shared_ptr<IMDInterface>, bases<Analyzer>, boost::noncopyable>
528 ("IMDInterface", init< boost::shared_ptr<SystemDefinition>, int, bool, unsigned int, boost::shared_ptr<ConstForceCompute> >())
532 #ifdef WIN32
533 #pragma warning( pop )
534 #endif