imdclient 0.1.2__py3-none-any.whl → 0.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -83,9 +83,8 @@ class IMDReader(StreamReaderBase):
83
83
 
84
84
  try:
85
85
  imdf = self._imdclient.get_imdframe()
86
- except EOFError:
87
- # Not strictly necessary, but for clarity
88
- raise StopIteration
86
+ except EOFError as e:
87
+ raise e
89
88
 
90
89
  self._frame = frame
91
90
  self._load_imdframe_into_ts(imdf)
imdclient/IMDClient.py CHANGED
@@ -25,6 +25,7 @@ import time
25
25
  import numpy as np
26
26
  from typing import Union, Dict
27
27
  import signal
28
+ import atexit
28
29
 
29
30
  logger = logging.getLogger(__name__)
30
31
 
@@ -42,7 +43,9 @@ class IMDClient:
42
43
  socket_bufsize : int, (optional)
43
44
  Size of the socket buffer in bytes. Default is to use the system default
44
45
  buffer_size : int (optional)
45
- IMDFramebuffer will be filled with as many :class:`IMDFrame` fit in `buffer_size` [``10MB``]
46
+ IMDFramebuffer will be filled with as many :class:`IMDFrame` fit in `buffer_size` bytes [``10MB``]
47
+ timeout : int, optional
48
+ Timeout for the socket in seconds [``5``]
46
49
  **kwargs : dict (optional)
47
50
  Additional keyword arguments to pass to the :class:`BaseIMDProducer` and :class:`IMDFrameBuffer`
48
51
  """
@@ -68,8 +71,10 @@ class IMDClient:
68
71
  n_atoms,
69
72
  **kwargs,
70
73
  )
74
+ self._error_queue = queue.Queue()
71
75
  else:
72
76
  self._buf = None
77
+ self._error_queue = None
73
78
  if self._imdsinfo.version == 2:
74
79
  self._producer = IMDProducerV2(
75
80
  self._conn,
@@ -77,6 +82,7 @@ class IMDClient:
77
82
  self._imdsinfo,
78
83
  n_atoms,
79
84
  multithreaded,
85
+ self._error_queue,
80
86
  **kwargs,
81
87
  )
82
88
  elif self._imdsinfo.version == 3:
@@ -86,23 +92,60 @@ class IMDClient:
86
92
  self._imdsinfo,
87
93
  n_atoms,
88
94
  multithreaded,
95
+ self._error_queue,
89
96
  **kwargs,
90
97
  )
91
98
 
92
99
  self._go()
93
100
 
94
101
  if self._multithreaded:
102
+ # Disconnect MUST occur. This covers typical cases (Python, IPython interpreter)
95
103
  signal.signal(signal.SIGINT, self.signal_handler)
104
+ signal.signal(signal.SIGTERM, self.signal_handler)
105
+
106
+ # Disconnect and socket shutdown MUST occur. This covers Jupyter use
107
+ # since in jupyter, the signal handler is reset to the default
108
+ # by pre- and post- hooks
109
+ # https://stackoverflow.com/questions/70841648/jupyter-reverts-signal-handler-to-default-when-running-next-cell
110
+ try:
111
+ import IPython
112
+ except ImportError:
113
+ has_ipython = False
114
+ else:
115
+ has_ipython = True
116
+
117
+ if has_ipython:
118
+ try:
119
+ from IPython import get_ipython
120
+
121
+ if get_ipython() is not None:
122
+ kernel = get_ipython().kernel
123
+ kernel.pre_handler_hook = lambda: None
124
+ kernel.post_handler_hook = lambda: None
125
+ logger.debug("Running in Jupyter")
126
+ except NameError:
127
+ logger.debug("Running in non-jupyter IPython environment")
128
+
129
+ # Final case: error is raised outside of IMDClient code
130
+ logger.debug("Registering atexit")
131
+ atexit.register(self.stop)
132
+
96
133
  self._producer.start()
97
134
 
98
135
  def signal_handler(self, sig, frame):
99
136
  """Catch SIGINT to allow clean shutdown on CTRL+C
100
137
  This also ensures that main thread execution doesn't get stuck
101
138
  waiting in buf.pop_full_imdframe()"""
139
+ logger.debug("Intercepted signal")
102
140
  self.stop()
141
+ logger.debug("Shutdown success")
103
142
 
104
143
  def get_imdframe(self):
105
144
  """
145
+ Returns
146
+ -------
147
+ IMDFrame
148
+ The next frame from the IMD server
106
149
  Raises
107
150
  ------
108
151
  EOFError
@@ -116,6 +159,9 @@ class IMDClient:
116
159
  # and doesn't need to be notified
117
160
  self._disconnect()
118
161
  self._stopped = True
162
+
163
+ if self._error_queue.qsize():
164
+ raise EOFError(f"{self._error_queue.get()}")
119
165
  raise EOFError
120
166
  else:
121
167
  try:
@@ -125,14 +171,23 @@ class IMDClient:
125
171
  raise EOFError
126
172
 
127
173
  def get_imdsessioninfo(self):
174
+ """
175
+ Returns
176
+ -------
177
+ IMDSessionInfo
178
+ Information about the IMD session
179
+ """
128
180
  return self._imdsinfo
129
181
 
130
182
  def stop(self):
183
+ """
184
+ Stop the client and close the connection
185
+ """
131
186
  if self._multithreaded:
132
187
  if not self._stopped:
133
- self._buf.notify_consumer_finished()
134
- self._disconnect()
135
188
  self._stopped = True
189
+ self._disconnect()
190
+ self._buf.notify_consumer_finished()
136
191
  else:
137
192
  self._disconnect()
138
193
 
@@ -146,9 +201,7 @@ class IMDClient:
146
201
  # /proc/sys/net/core/rmem_default
147
202
  # Max (linux):
148
203
  # /proc/sys/net/core/rmem_max
149
- conn.setsockopt(
150
- socket.SOL_SOCKET, socket.SO_RCVBUF, socket_bufsize
151
- )
204
+ conn.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, socket_bufsize)
152
205
  try:
153
206
  logger.debug(f"IMDClient: Connecting to {host}:{port}")
154
207
  conn.connect((host, port))
@@ -254,6 +307,13 @@ class IMDClient:
254
307
  finally:
255
308
  self._conn.close()
256
309
 
310
+ def __enter__(self):
311
+ return self
312
+
313
+ def __exit__(self, exc_type, exc_val, exc_tb):
314
+ self.stop()
315
+ return False
316
+
257
317
 
258
318
  class BaseIMDProducer(threading.Thread):
259
319
  """
@@ -269,11 +329,14 @@ class BaseIMDProducer(threading.Thread):
269
329
  Information about the IMD session
270
330
  n_atoms : int
271
331
  Number of atoms in the simulation
272
- multithreaded : bool, optional
332
+ multithreaded : bool
273
333
  If True, socket interaction will occur in a separate thread &
274
334
  frames will be buffered. Single-threaded, blocking IMDClient
275
- should only be used in testing [[``True``]]
276
-
335
+ should only be used in testing
336
+ error_queue: queue.Queue
337
+ Queue to hold errors produced by the producer thread
338
+ timeout : int, optional
339
+ Timeout for the socket in seconds [``5``]
277
340
  """
278
341
 
279
342
  def __init__(
@@ -282,7 +345,8 @@ class BaseIMDProducer(threading.Thread):
282
345
  buffer,
283
346
  sinfo,
284
347
  n_atoms,
285
- multithreaded=True,
348
+ multithreaded,
349
+ error_queue,
286
350
  timeout=5,
287
351
  **kwargs,
288
352
  ):
@@ -291,6 +355,7 @@ class BaseIMDProducer(threading.Thread):
291
355
  self._imdsinfo = sinfo
292
356
  self._paused = False
293
357
 
358
+ self.error_queue = error_queue
294
359
  # Timeout for first frame should be longer
295
360
  # than rest of frames
296
361
  self._timeout = timeout
@@ -385,6 +450,7 @@ class BaseIMDProducer(threading.Thread):
385
450
  logger.debug("IMDProducer: Simulation ended normally, cleaning up")
386
451
  except Exception as e:
387
452
  logger.debug("IMDProducer: An unexpected error occurred: %s", e)
453
+ self.error_queue.put(e)
388
454
  finally:
389
455
  logger.debug("IMDProducer: Stopping run loop")
390
456
  # Tell consumer not to expect more frames to be added
@@ -400,9 +466,19 @@ class BaseIMDProducer(threading.Thread):
400
466
  )
401
467
  # Sometimes we do not care what the value is
402
468
  if expected_value is not None and header.length != expected_value:
403
- raise RuntimeError(
404
- f"IMDProducer: Expected header value {expected_value}, got {header.length}"
405
- )
469
+ if expected_type in [
470
+ IMDHeaderType.IMD_FCOORDS,
471
+ IMDHeaderType.IMD_VELOCITIES,
472
+ IMDHeaderType.IMD_FORCES,
473
+ ]:
474
+ raise RuntimeError(
475
+ f"IMDProducer: Expected n_atoms value {expected_value}, got {header.length}. "
476
+ + "Ensure you are using the correct topology file."
477
+ )
478
+ else:
479
+ raise RuntimeError(
480
+ f"IMDProducer: Expected header value {expected_value}, got {header.length}"
481
+ )
406
482
 
407
483
  def _get_header(self):
408
484
  self._read(self._header)
@@ -422,9 +498,11 @@ class BaseIMDProducer(threading.Thread):
422
498
 
423
499
 
424
500
  class IMDProducerV2(BaseIMDProducer):
425
- def __init__(self, conn, buffer, sinfo, n_atoms, multithreaded, **kwargs):
501
+ def __init__(
502
+ self, conn, buffer, sinfo, n_atoms, multithreaded, error_queue, **kwargs
503
+ ):
426
504
  super(IMDProducerV2, self).__init__(
427
- conn, buffer, sinfo, n_atoms, multithreaded, **kwargs
505
+ conn, buffer, sinfo, n_atoms, multithreaded, error_queue, **kwargs
428
506
  )
429
507
 
430
508
  self._energies = bytearray(IMDENERGYPACKETLENGTH)
@@ -517,6 +595,7 @@ class IMDProducerV3(BaseIMDProducer):
517
595
  sinfo,
518
596
  n_atoms,
519
597
  multithreaded,
598
+ error_queue,
520
599
  **kwargs,
521
600
  ):
522
601
  super(IMDProducerV3, self).__init__(
@@ -525,6 +604,7 @@ class IMDProducerV3(BaseIMDProducer):
525
604
  sinfo,
526
605
  n_atoms,
527
606
  multithreaded,
607
+ error_queue,
528
608
  **kwargs,
529
609
  )
530
610
  # The body of an x/v/f packet should contain
@@ -682,9 +762,7 @@ class IMDFrameBuffer:
682
762
  raise ValueError("pause_empty_proportion must be between 0 and 1")
683
763
  self._pause_empty_proportion = pause_empty_proportion
684
764
  if unpause_empty_proportion < 0 or unpause_empty_proportion > 1:
685
- raise ValueError(
686
- "unpause_empty_proportion must be between 0 and 1"
687
- )
765
+ raise ValueError("unpause_empty_proportion must be between 0 and 1")
688
766
  self._unpause_empty_proportion = unpause_empty_proportion
689
767
 
690
768
  if buffer_size <= 0:
@@ -751,9 +829,7 @@ class IMDFrameBuffer:
751
829
  logger.debug("IMDProducer: Noticing consumer finished")
752
830
  raise EOFError
753
831
  except Exception as e:
754
- logger.debug(
755
- f"IMDProducer: Error waiting for space in buffer: {e}"
756
- )
832
+ logger.debug(f"IMDProducer: Error waiting for space in buffer: {e}")
757
833
 
758
834
  def pop_empty_imdframe(self):
759
835
  logger.debug("IMDProducer: Getting empty frame")
@@ -799,9 +875,7 @@ class IMDFrameBuffer:
799
875
  imdf = self._full_q.get()
800
876
  else:
801
877
  with self._full_imdf_avail:
802
- while (
803
- self._full_q.qsize() == 0 and not self._producer_finished
804
- ):
878
+ while self._full_q.qsize() == 0 and not self._producer_finished:
805
879
  self._full_imdf_avail.wait()
806
880
 
807
881
  if self._producer_finished and self._full_q.qsize() == 0:
@@ -0,0 +1,58 @@
1
+ title = PRODUCTION IN NPT
2
+ ld-seed = 1
3
+ ; Run parameters
4
+ integrator = md ; leap-frog integrator
5
+ nsteps = 100 ; 1 * 1000 = 1 ps
6
+ dt = 0.001 ; 1 fs
7
+ ; Output control
8
+ nstxout = 8 ; save coordinates every 1 fs
9
+ nstvout = 8 ; save velocities every 1 fs
10
+ nstfout = 8
11
+ nstenergy = 8 ; save energies every 1 fs
12
+ nstlog = 10 ; update log file every 1 ps
13
+ ; Center of mass (COM) motion
14
+ nstcomm = 10 ; remove COM motion every 10 steps
15
+ comm-mode = Linear ; remove only COM translation (liquids in PBC)
16
+ ; Bond parameters
17
+ continuation = yes ; first dynamics run
18
+ constraint_algorithm = lincs ; holonomic constraints
19
+ constraints = all-bonds ; all bonds lengths are constrained
20
+ lincs_iter = 1 ; accuracy of LINCS
21
+ lincs_order = 4 ; also related to accuracy
22
+ ; Nonbonded settings
23
+ cutoff-scheme = Verlet ; Buffered neighbor searching
24
+ ns_type = grid ; search neighboring grid cells
25
+ nstlist = 10 ; 10 fs, largely irrelevant with Verlet
26
+ rcoulomb = 1.0 ; short-range electrostatic cutoff (in nm)
27
+ rvdw = 1.0 ; short-range van der Waals cutoff (in nm)
28
+ DispCorr = EnerPres ; account for cut-off vdW scheme
29
+ ; Electrostatics
30
+ coulombtype = PME ; Particle Mesh Ewald for long-range electrostatics
31
+ pme_order = 4 ; cubic interpolation
32
+ fourierspacing = 0.12 ; grid spacing for FFT
33
+ ; Temperature coupling is on
34
+ tcoupl = Nose-Hoover ; good for production, after equilibration
35
+ ; we define separate thermostats for the solute and solvent (need to adapt)
36
+ ; see default groups defined by Gromacs for your system or define your own (make_ndx)
37
+ tc-grps = Protein SOL ; the separate groups for the thermostats
38
+ tau-t = 1.0 1.0 ; time constants for thermostats (ps)
39
+ ref-t = 300 300 ; reference temperature for thermostats (K)
40
+ ; Pressure coupling is off
41
+ pcoupl = Parrinello-Rahman ; good for production, after equilibration
42
+ tau-p = 2.0 ; time constant for barostat (ps)
43
+ compressibility = 4.5e-5 ; compressibility (1/bar) set to water at ~300K
44
+ ref-p = 1.0 ; reference pressure for barostat (bar)
45
+ ; Periodic boundary conditions
46
+ pbc = xyz ; 3-D PBC
47
+ ; Velocity generation
48
+ gen_vel = no
49
+ IMD-group = System
50
+ IMD-nst = 8
51
+ IMD-version = 3
52
+ IMD-time = yes
53
+ IMD-box = yes
54
+ IMD-coords = yes
55
+ IMD-unwrap = no
56
+ IMD-vels = yes
57
+ IMD-forces = yes
58
+ IMD-energies = no
@@ -52,8 +52,8 @@ velocity all create 300 102939 dist gaussian mom yes rot yes
52
52
  fix 1 all nve
53
53
 
54
54
  # Create source of truth trajectory
55
- # dump h5md1 all h5md 1 lammps_trj.h5md position velocity force box yes
56
- # dump_modify h5md1 unwrap no
55
+ dump h5md1 all h5md 1 lammps_trj.h5md position velocity force box yes
56
+ dump_modify h5md1 unwrap no
57
57
 
58
58
  ## IMD settings
59
59
  # https://docs.lammps.org/fix_imd.html
@@ -63,7 +63,7 @@ fix 2 all imd 8888 version 3 unwrap off nowait off
63
63
  run 100
64
64
 
65
65
  # Stop dumping information to the dump file.
66
- # undump h5md1
66
+ undump h5md1
67
67
 
68
68
  # Unfix the NVE. Additional lines if any will assume that this fix is off.
69
69
  unfix 1
@@ -0,0 +1,71 @@
1
+ ## Setup
2
+ units metal
3
+ boundary p p p #Specify periodic boundary condition are needed in all three faces
4
+ atom_style atomic #What style of atoms is to be used in the simulation
5
+ log logfile.txt #Write the log file to this text file. All thermodynamic information applicable to the entire system
6
+
7
+ ## Create Box
8
+ #Refers to an abstract geometric region of space. units box refers to the fact that the size of the box is specified in the units as given in the units command.
9
+ # The name "forbox" refers to the region ID so that you can refer to it somewhere else in this input script.
10
+ region forbox block 0 45.8 0 45.8 0 45.8 units box
11
+ create_box 1 forbox
12
+ # Since we have given fcc as lattice type no need to mention basis for this
13
+ lattice fcc 4.58
14
+
15
+ ## Create atoms & define interactions
16
+ # basis arg defines which atoms are created based on their lattice position (all are atom type 1)
17
+ create_atoms 1 region forbox basis 1 1 basis 2 1 basis 3 1 basis 4 1 units box
18
+ # Mass of atom type 1 is 39.48 [mass units grams/mole]
19
+ mass 1 39.948
20
+ # lj potential describes potential energy between two atoms as function of the dist between them
21
+ # don't apply lj interactions beyond cutoff dist
22
+ pair_style lj/cut 10
23
+ # The coefficient of the lj potential for the interactions of atom type 1 with atom type 1
24
+ pair_coeff 1 1 0.01006418 3.3952
25
+
26
+ ## Create atom group for argon atoms
27
+ group ar type 1 #Group all the argon types (argon type is of type 1). All atoms of type 1 are in group with the name 'ar'
28
+
29
+
30
+ ## Write initial configuration
31
+ dump dump_1 all custom 1 dump_initial_config.dump id type x y z ix iy iz vx vy vz
32
+
33
+
34
+ ## Perform energy minimization
35
+ run 1
36
+ # Stop dumping to this file
37
+ undump dump_1
38
+ # Minimize the energy using a conjugate gradient step.
39
+ minimize 1e-25 1e-19 10000 10000
40
+ print "Finished Minimizing"
41
+ variable ener equal pe
42
+
43
+ ## Output the topology after minimization
44
+ write_data topology_after_min.data
45
+
46
+ ## Prepare MD simulation
47
+ timestep 0.001
48
+ # Set the velocities of all the atoms so that the temperature of the system
49
+ # is 300K. Make the distribution Gaussian.
50
+ velocity all create 300 102939 dist gaussian mom yes rot yes
51
+ # this is equlibration process.
52
+ fix 1 all nve
53
+
54
+ # Create source of truth trajectory
55
+ dump h5md1 all h5md 8 lammps_trj.h5md position velocity force box yes
56
+ dump_modify h5md1 unwrap no
57
+
58
+ ## IMD settings
59
+ # https://docs.lammps.org/fix_imd.html
60
+ fix 2 all imd 8888 version 3 unwrap off nowait off trate 8
61
+
62
+ ## Run MD sim
63
+ run 100
64
+
65
+ # Stop dumping information to the dump file.
66
+ undump h5md1
67
+
68
+ # Unfix the NVE. Additional lines if any will assume that this fix is off.
69
+ unfix 1
70
+
71
+ #End
@@ -17,8 +17,20 @@ switchdist 7.0
17
17
  cutoff 8.0
18
18
  pairlistdist 9.0
19
19
 
20
- dcdfile alanin.dcd
21
- dcdfreq 1
20
+ # Add box dimensions
21
+ cellBasisVector1 32.76 0.0 0.0
22
+ cellBasisVector2 0.0 31.66 0.0
23
+ cellBasisVector3 0.0 0.0 32.89
24
+
25
+ DCDfile alanin.dcd
26
+ DCDfreq 1
27
+ DCDUnitCell yes
28
+ velDcdFile alanin.vel.dcd
29
+ velDcdFreq 1
30
+ forceDcdFile alanin.force.dcd
31
+ forceDcdFreq 1
32
+ XSTFile alanin.xst
33
+ xstFreq 1
22
34
 
23
35
  #restartname alanin.restart
24
36
  #restartfreq 10
@@ -40,8 +52,8 @@ IMDwait on
40
52
  IMDversion 3
41
53
  IMDsendPositions yes
42
54
  IMDsendEnergies yes
43
- #IMDsendTime yes
44
- #IMDsendBoxDimensions yes
55
+ IMDsendTime yes
56
+ IMDsendBoxDimensions yes
45
57
  IMDsendVelocities yes
46
- #IMDsendForces yes
58
+ IMDsendForces yes
47
59
  IMDwrapPositions yes
@@ -0,0 +1,59 @@
1
+ # This is a test namd configuration file
2
+
3
+ timestep 0.5
4
+ numsteps 100
5
+ structure alanin.psf
6
+ parameters alanin.params
7
+ coordinates alanin.pdb
8
+ exclude scaled1-4
9
+ 1-4scaling 0.4
10
+ outputname output[myReplica]
11
+ margin 1.0
12
+ stepspercycle 3
13
+ temperature 0
14
+
15
+ switching on
16
+ switchdist 7.0
17
+ cutoff 8.0
18
+ pairlistdist 9.0
19
+
20
+ # Add box dimensions
21
+ cellBasisVector1 32.76 0.0 0.0
22
+ cellBasisVector2 0.0 31.66 0.0
23
+ cellBasisVector3 0.0 0.0 32.89
24
+
25
+ DCDfile alanin.dcd
26
+ DCDfreq 8
27
+ DCDUnitCell yes
28
+ velDcdFile alanin.vel.dcd
29
+ velDcdFreq 8
30
+ forceDcdFile alanin.force.dcd
31
+ forceDcdFreq 8
32
+ XSTFile alanin.xst
33
+ xstFreq 8
34
+
35
+ #restartname alanin.restart
36
+ #restartfreq 10
37
+
38
+ #langevin on
39
+ #langevinTemp 300.0
40
+ #langevincol O
41
+
42
+ #constraints on
43
+
44
+ #fma on
45
+
46
+ seed 12345
47
+
48
+ IMDon yes
49
+ IMDport 8888
50
+ IMDfreq 8
51
+ IMDwait on
52
+ IMDversion 3
53
+ IMDsendPositions yes
54
+ IMDsendEnergies yes
55
+ IMDsendTime yes
56
+ IMDsendBoxDimensions yes
57
+ IMDsendVelocities yes
58
+ IMDsendForces yes
59
+ IMDwrapPositions yes