Fixed replicate python command
[hoomd-blue.git] / python-module / hoomd_script / data.py
blob9f11268f67dc09e8768d09853902ad9aa0b238b5
1 # -- start license --
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.
48 # -- end license --
50 # Maintainer: joaander
52 import hoomd
53 from hoomd_script import globals
54 from hoomd_script import util
56 ## \package hoomd_script.data
57 # \brief Access particles, bonds, and other state information inside scripts
59 # Code in the data package provides high-level access to all of the particle, bond and other %data that define the
60 # current state of the system. By writing python code that modifies this %data, any conceivable initialization of the
61 # system can be achieved without needing to invoke external tools or generate xml files. Data can be read and additional
62 # analysis performed during or after simulation runs as well. Basically, the user's imagination is the limit to what can
63 # be done with the %data.
65 # The only thing to be aware of is that accessing the %data in this way can slow a simulation significantly if performed
66 # too often. As a general guideline, consider writing a high performance C++ / GPU plugin (\ref sec_build_plugin)
67 # if particle %data needs to accessed more often than once every few thousand time steps.
69 # If modifications need to be done on more than just a few particles, e.g.
70 # setting new positions for all particles, or updating the velocities, etc., \b snapshots can be used.
71 # \ref data_snapshot store the entire system state in a single (currently opaque) object and can
72 # be used to re-initialize the system system.restore_snapshot().
74 # <h2>Documentation by example</h2>
76 # For most of the cases below, it is assumed that the result of the initialization command was saved at the beginning
77 # of the script, like so:
78 # \code
79 # system = init.read_xml(filename="input.xml")
80 # \endcode
82 # <hr>
83 # <h3>Getting/setting the box</h3>
84 # You can access the dimensions of the simulation box like so:
85 # \code
86 # >>> print system.box
87 # Box: Lx=17.3646569289 Ly=17.3646569289 Lz=17.3646569289 xy=0.0 xz=0.0 yz=0.0
88 # \endcode
89 # and can change it like so:
90 # \code
91 # >>> system.box = data.boxdim(Lx=10, Ly=20, Lz=30, xy=1.0, xz=0.1, yz=2.0)
92 # >>> print system.box
93 # Box: Lx=10 Ly=20 Lz=30 xy=1.0 xz=0.1 yz=2.0
94 # \endcode
95 # \b All particles must \b always remain inside the box. If a box is set in this way such that a particle ends up outside of the box, expect
96 # errors to be thrown or for hoomd to just crash. The dimensionality of the system cannot change after initialization.
97 # <hr>
98 # <h3>Particle properties</h3>
99 # For a list of all particle properties that can be read and/or set, see the particle_data_proxy. The examples
100 # here only demonstrate changing a few of them.
102 # With the result of an init command saved in the variable \c system (see above), \c system.particles is a window
103 # into all of the particles in the system. It behaves like standard python list in many ways.
104 # - Its length (the number of particles in the system) can be queried
105 # \code
106 # >>> len(system.particles)
107 # 64000
108 # \endcode
109 # - A short summary can be printed of the list
110 # \code
111 # >>> print system.particles
112 # Particle Data for 64000 particles of 1 type(s)
113 # \endcode
114 # - The list of all particle types in the simulation can be accessed
115 # \code
116 # >>> print system.particles.types
117 # ['A']
118 # \endcode
119 # - Individual particles can be accessed at random.
120 # \code
121 # >>> i = 4
122 # >>> p = system.particles[i]
123 # \endcode
124 # - Various properties can be accessed of any particle
125 # \code
126 # >>> p.tag
128 # >>> p.position
129 # (27.296911239624023, -3.5986068248748779, 10.364067077636719)
130 # >>> p.velocity
131 # (-0.60267972946166992, 2.6205904483795166, -1.7868227958679199)
132 # >>> p.mass
133 # 1.0
134 # >>> p.diameter
135 # 1.0
136 # >>> p.type
137 # 'A'
138 # \endcode
139 # (note that p can be replaced with system.particles.[i] above and the results are the same)
140 # - Particle properties can be set in the same way:
141 # \code
142 # >>> p.position = (1,2,3)
143 # >>> p.position
144 # (1.0, 2.0, 3.0)
145 # \endcode
146 # - Finally, all particles can be easily looped over
147 # \code
148 # for p in system.particles:
149 # p.velocity = (0,0,0)
150 # \endcode
152 # Performance is decent, but not great. The for loop above that sets all velocities to 0 takes 0.86 seconds to execute
153 # on a 2.93 GHz core2 iMac. The interface has been designed to be flexible and easy to use for the widest variety of
154 # initialization tasks, not efficiency.
155 # For doing modifications that operate on the whole system data efficiently, snapshots have been
156 # designed. Their usage is described below.
158 # There is a second way to access the particle data. Any defined group can be used in exactly the same way as
159 # \c system.particles above, only the particles accessed will be those just belonging to the group. For a specific
160 # example, the following will set the velocity of all particles of type A to 0.
161 # \code
162 # groupA = group.type(name="a-particles", type='A')
163 # for p in groupA:
164 # p.velocity = (0,0,0)
165 # \endcode
167 # <hr>
168 # <h3>Rigid Body Data</h3>
169 # Rigid Body data can be accessed via the body_data_proxy. Here are examples
171 #\code
173 # >>> b = system.bodies[0]
174 # >>> print b
175 #num_particles : 5
176 #mass : 5.0
177 # COM : (0.33264800906181335, -2.495814800262451, -1.2669427394866943)
178 # velocity : (0.0, 0.0, 0.0)
179 # orientation : (0.9244732856750488, -0.3788720965385437, -0.029276784509420395, 0.0307924821972847)
180 # angular_momentum (space frame) : (0.0, 0.0, 0.0)
181 # moment_inertia: (10.000000953674316, 10.0, 0.0)
182 # particle_tags : [0, 1, 2, 3, 4]
183 # particle_disp : [[-3.725290298461914e-09, -4.172325134277344e-07, 2.0], [-2.421438694000244e-08, -2.086162567138672e-07, 0.9999998211860657], [-2.6206091519043184e-08, -2.073889504572435e-09, -3.361484459674102e-07], [-5.029141902923584e-08, 2.682209014892578e-07, -1.0000004768371582], [-3.3527612686157227e-08, -2.980232238769531e-07, -2.0]]
184 # >>> print b.COM
185 # (0.33264800906181335, -2.495814800262451, -1.2669427394866943)
186 # >>> b.particle_disp = [[0,0,0], [0,0,0], [0,0,0.0], [0,0,0], [0,0,0]]
188 #\endcode
190 # <hr>
191 # <h3>Bond Data</h3>
192 # Bonds may be added at any time in the job script.
193 # \code
194 # >>> system.bonds.add("bondA", 0, 1)
195 # >>> system.bonds.add("bondA", 1, 2)
196 # >>> system.bonds.add("bondA", 2, 3)
197 # >>> system.bonds.add("bondA", 3, 4)
198 # \endcode
200 # Individual bonds may be accessed by index.
201 # \code
202 # >>> bnd = system.bonds[0]
203 # >>> print bnd
204 # tag : 0
205 # typeid : 0
206 # a : 0
207 # b : 1
208 # type : bondA
209 # >>> print bnd.type
210 # bondA
211 # >>> print bnd.a
213 # >>> print bnd.b
215 # \endcode
216 # \note The order in which bonds appear by index is not static and may change at any time!
218 # Bonds may be deleted by index.
219 # \code
220 # >>> del system.bonds[0]
221 # >>> print system.bonds[0]
222 # tag : 3
223 # typeid : 0
224 # a : 3
225 # b : 4
226 # type : bondA
227 # \endcode
228 # \note Regarding the previous note: see how the last bond added is now at index 0. No guarantee is made about how the
229 # order of bonds by index will or will not change, so do not write any job scripts which assume a given ordering.
231 # To access bonds in an index-independent manner, use their tags. For example, to delete all bonds which connect to
232 # particle 2, first loop through the bonds and build a list of bond tags that match the criteria.
233 # \code
234 # tags = []
235 # for b in system.bonds:
236 # if b.a == 2 or b.b == 2:
237 # tags.append(b.tag)
238 # \endcode
239 # Then remove each of the bonds by their unique tag.
240 # \code
241 # for t in tags:
242 # system.bonds.remove(t)
243 # \endcode
245 # <hr>
246 # <h3>Angle, Dihedral, and Improper Data</h3>
247 # Angles, Dihedrals, and Impropers may be added at any time in the job script.
248 # \code
249 # >>> system.angles.add("angleA", 0, 1, 2)
250 # >>> system.dihedrals.add("dihedralA", 1, 2, 3, 4)
251 # >>> system.impropers.add("dihedralA", 2, 3, 4, 5)
252 # \endcode
254 # Individual angles, dihedrals, and impropers may be accessed, deleted by index or removed by tag with the same syntax
255 # as described for bonds, just replace \em bonds with \em angles, \em dihedrals, or, \em impropers and access the
256 # appropriate number of tag elements (a,b,c for angles) (a,b,c,d for dihedrals/impropers).
257 # <hr>
259 # <h3>Forces</h3>
260 # Forces can be accessed in a similar way.
261 # \code
262 # >>> lj = pair.lj(r_cut=3.0)
263 # >>> lj.pair_coeff.set('A', 'A', epsilon=1.0, sigma=1.0)
264 # >>> print lj.forces[0]
265 # tag : 0
266 # force : (-0.077489577233791351, -0.029512746259570122, -0.13215918838977814)
267 # virial : -0.0931386947632
268 # energy : -0.0469368174672
269 # >>> f0 = lj.forces[0]
270 # >>> print f0.force
271 # (-0.077489577233791351, -0.029512746259570122, -0.13215918838977814)
272 # >>> print f0.virial
273 # -0.0931386947632
274 # >>> print f0.energy
275 # -0.0469368174672
276 # \endcode
278 # In this manner, forces due to the lj %pair %force, bonds, and any other %force commands in hoomd can be accessed
279 # independently from one another. See force_data_proxy for a definition of each parameter accessed.
281 # <hr>
282 # <h3>Proxy references</h3>
284 # For advanced code using the particle data access from python, it is important to understand that the hoomd_script
285 # particles, forces, bonds, et cetera, are accessed as proxies. This means that after
286 # \code
287 # p = system.particles[i]
288 # \endcode
289 # is executed, \a p \b doesn't store the position, velocity, ... of particle \a i. Instead, it just stores \a i and
290 # provides an interface to get/set the properties on demand. This has some side effects. They aren't necessarily
291 # bad side effects, just some to be aware of.
292 # - First, it means that \a p (or any other proxy reference) always references the current state of the particle.
293 # As an example, note how the position of particle p moves after the run() command.
294 # \code
295 # >>> p.position
296 # (-21.317455291748047, -23.883811950683594, -22.159387588500977)
297 # >>> run(1000)
298 # ** starting run **
299 # ** run complete **
300 # >>> p.position
301 # (-19.774742126464844, -23.564577102661133, -21.418502807617188)
302 # \endcode
303 # - Second, it means that copies of the proxy reference cannot be changed independently.
304 # \code
305 # p.position
306 # >>> a = p
307 # >>> a.position
308 # (-19.774742126464844, -23.564577102661133, -21.418502807617188)
309 # >>> p.position = (0,0,0)
310 # >>> a.position
311 # (0.0, 0.0, 0.0)
312 # \endcode
314 # If you need to store some particle properties at one time in the simulation and access them again later, you will need
315 # to make copies of the actual property values themselves and not of the proxy references.
317 # \section data_snapshot Snapshots
318 # <hr>
319 # <h3>Snapshots</h3>
321 # A snaphot of the current system state is obtained using system_data.take_snapshot(). It contains information
322 # about the simulation box, particles, bonds, angles, dihedrals, impropers, walls and rigid bodies.
323 # Once taken, it is not updated anymore (as opposed to the particle %data proxies, which always
324 # return the current state). Instead, it can be used to restart the simulation
325 # using system.restore_snapshot().
327 # In future releases it will be possible to modify or %analyze the contents of a snapshot.
329 # Example for taking a snapshot:
330 # \code
331 # snapshot = system.take_snapshot(all=True)
332 # \endcode
334 ## Define box dimensions
336 # Simulation boxes in hoomd are specified by six parameters, *Lx*, *Ly*, *Lz*, *xy*, *xz* and *yz*. For full details,
337 # see \ref page_box. A boxdim provides a way to specify all six parameters for a given box and perform some common
338 # operations with them. Modifying a boxdim does not modify the underlying simulation box in hoomd. A boxdim can be passed
339 # to an initialization method or to assigned to a saved system.
341 # boxdim parameters may be accessed directly.
342 # ~~~~
343 # b = data.boxdim(L=20);
344 # b.xy = 1.0;
345 # b.yz = 0.5;
346 # b.Lz = 40;
347 # ~~~~
349 # **Two dimensional systems**
351 # 2D simulations in hoomd are embedded in 3D boxes with short heights in the z direction. To create a 2D box,
352 # set dimensions=2 when creating the boxdim. This will force Lz=1 and xz=yz=0. init commands that support 2D boxes
353 # will pass the dimensionality along to the system. When you assign a new boxdim to an already initialized system,
354 # the dimensionality flag is ignored. Changing the number of dimensions during a simulation run is not supported.
356 # In 2D boxes, "volume" refers to area.
358 class boxdim:
359 ## Initialize a boxdim object
361 # \param L shorthand for specifying Lx=Ly=Lz=L (distance units)
362 # \param Lx box extent in the x direction (distance units)
363 # \param Ly box extent in the y direction (distance units)
364 # \param Lz box extent in the z direction (distance units)
365 # \param xy tilt factor xy (dimensionless)
366 # \param xz tilt factor xz (dimensionless)
367 # \param yz tilt factor yz (dimensionless)
368 # \param dimensions Number of dimensions in the box (2 or 3).
369 # \param volume Scale the given box dimensions up to the this volume (area if dimensions=2)
371 def __init__(self, L=None, Lx=1.0, Ly=1.0, Lz=1.0, xy=0.0, xz=0.0, yz=0.0, dimensions=3, volume=None):
372 if L is not None:
373 Lx = L;
374 Ly = L;
375 Lz = L;
377 if dimensions == 2:
378 Lz = 1.0;
379 xz = yz = 0.0;
381 self.Lx = Lx;
382 self.Ly = Ly;
383 self.Lz = Lz;
384 self.xy = xy;
385 self.xz = xz;
386 self.yz = yz;
387 self.dimensions = dimensions;
389 if volume is not None:
390 self.set_volume(volume);
392 ## Scale box dimensions
394 # \param sx scale factor in the x direction
395 # \param sy scale factor in the y direction
396 # \param sz scale factor in the z direction
398 # Scales the box by the given scale factors. Tilt factors are not modified.
400 def scale(self, sx, sy, sz):
401 self.Lx = self.Lx * sx;
402 self.Ly = self.Ly * sy;
403 self.Lz = self.Lz * sz;
405 ## Set the box volume
407 # \param volume new box volume (area if dimensions=2)
409 # setVolume() scales the box to the given volume (or area).
411 def set_volume(self, volume):
412 cur_vol = self.get_volume();
414 if self.dimensions == 3:
415 s = (volume / cur_vol)**(1.0/3.0)
416 self.scale(s, s, s);
417 else:
418 s = (volume / cur_vol)**(1.0/2.0)
419 self.scale(s, s, 1.0);
421 ## Get the box volume
423 # Returns the box volume (area in 2D).
425 def get_volume(self):
426 b = self._getBoxDim();
427 return b.getVolume(self.dimensions == 2);
429 ## \internal
430 # \brief Get a C++ boxdim
431 def _getBoxDim(self):
432 b = hoomd.BoxDim(self.Lx, self.Ly, self.Lz);
433 b.setTiltFactors(self.xy, self.xz, self.yz);
434 return b
436 def __str__(self):
437 return 'Box: Lx=' + str(self.Lx) + ' Ly=' + str(self.Ly) + ' Lz=' + str(self.Lz) + ' xy=' + str(self.xy) + \
438 ' xz='+ str(self.xz) + ' yz=' + str(self.yz);
440 # \brief Access system data
442 # system_data provides access to the different data structures that define the current state of the simulation.
443 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
444 # system_data, documented by example.
446 class system_data:
447 ## \internal
448 # \brief create a system_data
450 # \param sysdef SystemDefinition to connect
451 def __init__(self, sysdef):
452 self.sysdef = sysdef;
453 self.particles = particle_data(sysdef.getParticleData());
454 self.bonds = bond_data(sysdef.getBondData());
455 self.angles = angle_data(sysdef.getAngleData());
456 self.dihedrals = dihedral_data(sysdef.getDihedralData());
457 self.impropers = dihedral_data(sysdef.getImproperData());
458 self.bodies = body_data(sysdef.getRigidData());
460 ## Take a snapshot of the current system data
462 # This functions returns a snapshot object. It contains the current
463 # partial or complete simulation state. With appropriate options
464 # it is possible to select which data properties should be included
465 # in the snapshot.
467 # \param particles If true, particle data is included in the snapshot
468 # \param bonds If true, bond data is included in the snapshot
469 # \param angles If true, angle data is included in the snapshot
470 # \param dihedrals If true, dihedral data is included in the snapshot
471 # \param impropers If true, dihedral data is included in the snapshot
472 # \param rigid_bodies If true, rigid body data is included in the snapshot
473 # \param walls If true, wall data is included in the snapshot
474 # \param integrators If true, integrator data is included the snapshot
475 # \param all If true, the entire system state is saved in the snapshot
477 # Specific options (such as \b particles=True) take precedence over \b all=True.
479 # \returns the snapshot object.
481 # \code
482 # snapshot = system.take_snapshot()
483 # snapshot = system.take_snapshot(particles=true)
484 # snapshot = system.take_snapshot(bonds=true)
485 # \endcode
487 # \MPI_SUPPORTED
488 def take_snapshot(self,particles=None,bonds=None,angles=None,dihedrals=None, impropers=None, rigid_bodies=None, walls=None, integrators=None, all=None):
489 util.print_status_line();
491 if all is True:
492 if particles is None:
493 particles=True
494 if bonds is None:
495 bonds=True
496 if angles is None:
497 angles=True
498 if dihedrals is None:
499 dihedrals=True
500 if impropers is None:
501 impropers=True
502 if rigid_bodies is None:
503 rigid_bodies=True
504 if walls is None:
505 walls=True
506 if integrators is None:
507 integrators=True
509 if particles is None and not all:
510 particles = False
511 if bonds is None and not all:
512 bonds = False
513 if angles is None and not all:
514 angles = False
515 if dihedrals is None and not all:
516 dihedrals = False
517 if impropers is None and not all:
518 impropers = False
519 if rigid_bodies is None and not all:
520 rigid_bodies = False
521 if walls is None and not all:
522 walls = False
523 if integrators is None and not all:
524 integrators = False
526 if not (particles or bonds or angles or dihedrals or impropers or rigid_bodies or walls or integrators):
527 globals.msg.warning("No options specified. Ignoring request to create an empty snapshot.\n")
528 return None
530 # take the snapshot
531 cpp_snapshot = self.sysdef.takeSnapshot(particles,bonds,angles,dihedrals,impropers,rigid_bodies,walls,integrators)
533 return cpp_snapshot
535 ## Replicates the system along the three spatial dimensions
537 # \param nx Number of times to replicate the system along the x-direction
538 # \param ny Number of times to replicate the system along the y-direction
539 # \param nz Number of times to replicate the system along the z-direction
541 # This method explictly replicates particles along all three spatial directions, as
542 # opposed to replication implied by periodic boundary conditions.
543 # The box is resized and the number of particles is updated so that the new box
544 # holds the specified number of replicas of the old box along all directions.
545 # Particle coordinates are updated accordingly to fit into the new box. All velocities and
546 # other particle properties are replicated as well. Also bonded groups between particles
547 # are replicated.
549 # \note Replication of rigid bodies is currently not supported.
551 # \note It is a limitation that in MPI simulations the dimensions of the processor grid
552 # are not updated upon replication. For example, if an initially cubic box is replicated along only one
553 # spatial direction, this could lead to decreased performance if the processor grid was
554 # optimal for the original box dimensions, but not for the new ones.
556 # \MPI_SUPPORTED
557 def replicate(self, nx=1, ny=1, nz=1):
558 util.print_status_line()
560 nx = int(nx)
561 ny = int(ny)
562 nz = int(nz)
564 if nx == ny == nz == 1:
565 globals.msg.warning("All replication factors == 1. Not replicating system.\n")
566 return
568 if nx <= 0 or ny <= 0 or nz <= 0:
569 globals.msg.error("Cannot replicate by zero or by a negative value along any direction.")
570 raise RuntimeError("nx, ny, nz need to be positive integers")
572 # Take a snapshot
573 util._disable_status_lines = True
574 cpp_snapshot = self.take_snapshot(all=True)
575 util._disable_status_lines = False
577 from hoomd_script import comm
578 if comm.get_rank() == 0:
579 # replicate
580 cpp_snapshot.replicate(nx, ny, nz)
582 # restore from snapshot
583 util._disable_status_lines = True
584 self.restore_snapshot(cpp_snapshot)
585 util._disable_status_lines = False
587 ## Re-initializes the system from a snapshot
589 # \param snapshot The snapshot to initialize the system from
591 # Snapshots temporarily store system %data. Snapshots contain the complete simulation state in a
592 # single object. They can be used to restart a simulation.
594 # Example use cases in which a simulation may be restarted from a snapshot include python-script-level
595 # \b Monte-Carlo schemes, where the system state is stored after a move has been accepted (according to
596 # some criterium), and where the system is re-initialized from that same state in the case
597 # when a move is not accepted.
599 # Example for the procedure of taking a snapshot and re-initializing from it:
600 # \code
601 # system = init.read_xml("some_file.xml")
603 # ... run a simulation ...
605 # snapshot = system.take_snapshot(all=True)
606 # ...
607 # system.restore_snapshot(snapshot)
608 # \endcode
610 # \sa hoomd_script.data
611 # \MPI_SUPPORTED
612 def restore_snapshot(self, snapshot):
613 util.print_status_line();
615 self.sysdef.initializeFromSnapshot(snapshot);
617 ## \var sysdef
618 # \internal
619 # \brief SystemDefinition to which this instance is connected
621 ## \internal
622 # \brief Translate attribute accesses into the low level API function calls
623 def __setattr__(self, name, value):
624 if name == "box":
625 if not isinstance(value, boxdim):
626 raise TypeError('box must be a data.boxdim object');
627 self.sysdef.getParticleData().setGlobalBox(value._getBoxDim());
629 # otherwise, consider this an internal attribute to be set in the normal way
630 self.__dict__[name] = value;
632 ## \internal
633 # \brief Translate attribute accesses into the low level API function calls
634 def __getattr__(self, name):
635 if name == "box":
636 b = self.sysdef.getParticleData().getGlobalBox();
637 L = b.getL();
638 return boxdim(Lx=L.x, Ly=L.y, Lz=L.z, xy=b.getTiltFactorXY(), xz=b.getTiltFactorXZ(), yz=b.getTiltFactorYZ());
640 # if we get here, we haven't found any names that match, post an error
641 raise AttributeError;
643 ## \internal
644 # \brief Access particle data
646 # particle_data provides access to the per-particle data of all particles in the system.
647 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
648 # particle_data, documented by example.
650 class particle_data:
651 ## \internal
652 # \brief particle_data iterator
653 class particle_data_iterator:
654 def __init__(self, data):
655 self.data = data;
656 self.index = 0;
657 def __iter__(self):
658 return self;
659 def __next__(self):
660 if self.index == len(self.data):
661 raise StopIteration;
663 result = self.data[self.index];
664 self.index += 1;
665 return result;
667 # support python2
668 next = __next__;
670 ## \internal
671 # \brief create a particle_data
673 # \param pdata ParticleData to connect
674 def __init__(self, pdata):
675 self.pdata = pdata;
677 ntypes = globals.system_definition.getParticleData().getNTypes();
678 self.types = [];
679 for i in range(0,ntypes):
680 self.types.append(globals.system_definition.getParticleData().getNameByType(i));
682 ## \var pdata
683 # \internal
684 # \brief ParticleData to which this instance is connected
686 ## \internal
687 # \brief Get a particle_proxy reference to the particle with tag \a tag
688 # \param tag Particle tag to access
689 def __getitem__(self, tag):
690 if tag >= len(self) or tag < 0:
691 raise IndexError;
692 return particle_data_proxy(self.pdata, tag);
694 ## \internal
695 # \brief Set a particle's properties
696 # \param tag Particle tag to set
697 # \param p Value containing properties to set
698 def __setitem__(self, tag, p):
699 raise RuntimeError('__setitem__ not implemented');
701 ## \internal
702 # \brief Get the number of particles
703 def __len__(self):
704 return self.pdata.getNGlobal();
706 ## \internal
707 # \brief Get an informal string representing the object
708 def __str__(self):
709 result = "Particle Data for %d particles of %d type(s)" % (self.pdata.getNGlobal(), self.pdata.getNTypes());
710 return result
712 ## \internal
713 # \brief Return an interator
714 def __iter__(self):
715 return particle_data.particle_data_iterator(self);
717 ## Access a single particle via a proxy
719 # particle_data_proxy provides access to all of the properties of a single particle in the system.
720 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
721 # particle_data_proxy, documented by example.
723 # The following attributes are read only:
724 # - \c tag : An integer indexing the particle in the system. Tags run from 0 to N-1;
725 # - \c acceleration : A 3-tuple of floats (x, y, z) Note that acceleration is a calculated quantity and cannot be set. (in acceleration units)
726 # - \c typeid : An integer defining the type id
728 # The following attributes can be both read and set
729 # - \c position : A 3-tuple of floats (x, y, z) (in distance units)
730 # - \c image : A 3-tuple of integers (x, y, z)
731 # - \c velocity : A 3-tuple of floats (x, y, z) (in velocity units)
732 # - \c charge : A single float
733 # - \c mass : A single float (in mass units)
734 # - \c diameter : A single float (in distance units)
735 # - \c type : A string naming the type
736 # - \c body : Rigid body id integer (-1 for free particles)
737 # - \c orientation : Orientation of anisotropic particle (quaternion)
738 # - \c net_force : Net force on particle (x, y, z) (in force units)
739 # - \c net_energy : Net contribution of particle to the potential energy (in energy units)
740 # - \c net_torque : Net torque on the particle (x, y, z) (in torque units)
742 # In the current version of the API, only already defined type names can be used. A future improvement will allow
743 # dynamic creation of new type names from within the python API.
745 class particle_data_proxy:
746 ## \internal
747 # \brief create a particle_data_proxy
749 # \param pdata ParticleData to which this proxy belongs
750 # \param tag Tag of this particle in \a pdata
751 def __init__(self, pdata, tag):
752 self.pdata = pdata;
753 self.tag = tag;
755 ## \internal
756 # \brief Get an informal string representing the object
757 def __str__(self):
758 result = "";
759 result += "tag : " + str(self.tag) + "\n"
760 result += "position : " + str(self.position) + "\n";
761 result += "image : " + str(self.image) + "\n";
762 result += "velocity : " + str(self.velocity) + "\n";
763 result += "acceleration: " + str(self.acceleration) + "\n";
764 result += "charge : " + str(self.charge) + "\n";
765 result += "mass : " + str(self.mass) + "\n";
766 result += "diameter : " + str(self.diameter) + "\n";
767 result += "type : " + str(self.type) + "\n";
768 result += "typeid : " + str(self.typeid) + "\n";
769 result += "body : " + str(self.body) + "\n";
770 result += "orientation : " + str(self.orientation) + "\n";
771 result += "net_force : " + str(self.net_force) + "\n";
772 result += "net_energy : " + str(self.net_energy) + "\n";
773 result += "net_torque : " + str(self.net_torque) + "\n";
774 return result;
776 ## \internal
777 # \brief Translate attribute accesses into the low level API function calls
778 def __getattr__(self, name):
779 if name == "position":
780 pos = self.pdata.getPosition(self.tag);
781 return (pos.x, pos.y, pos.z);
782 if name == "velocity":
783 vel = self.pdata.getVelocity(self.tag);
784 return (vel.x, vel.y, vel.z);
785 if name == "acceleration":
786 accel = self.pdata.getAcceleration(self.tag);
787 return (accel.x, accel.y, accel.z);
788 if name == "image":
789 image = self.pdata.getImage(self.tag);
790 return (image.x, image.y, image.z);
791 if name == "charge":
792 return self.pdata.getCharge(self.tag);
793 if name == "mass":
794 return self.pdata.getMass(self.tag);
795 if name == "diameter":
796 return self.pdata.getDiameter(self.tag);
797 if name == "typeid":
798 return self.pdata.getType(self.tag);
799 if name == "body":
800 return self.pdata.getBody(self.tag);
801 if name == "type":
802 typeid = self.pdata.getType(self.tag);
803 return self.pdata.getNameByType(typeid);
804 if name == "orientation":
805 o = self.pdata.getOrientation(self.tag);
806 return (o.x, o.y, o.z, o.w);
807 if name == "net_force":
808 f = self.pdata.getPNetForce(self.tag);
809 return (f.x, f.y, f.z);
810 if name == "net_energy":
811 f = self.pdata.getPNetForce(self.tag);
812 return f.w;
813 if name == "net_torque":
814 f = self.pdata.getNetTorque(self.tag);
815 return (f.x, f.y, f.z);
817 # if we get here, we haven't found any names that match, post an error
818 raise AttributeError;
820 ## \internal
821 # \brief Translate attribute accesses into the low level API function calls
822 def __setattr__(self, name, value):
823 if name == "position":
824 v = hoomd.Scalar3();
825 v.x = float(value[0]);
826 v.y = float(value[1]);
827 v.z = float(value[2]);
828 self.pdata.setPosition(self.tag, v, True);
829 return;
830 if name == "velocity":
831 v = hoomd.Scalar3();
832 v.x = float(value[0]);
833 v.y = float(value[1]);
834 v.z = float(value[2]);
835 self.pdata.setVelocity(self.tag, v);
836 return;
837 if name == "image":
838 v = hoomd.int3();
839 v.x = int(value[0]);
840 v.y = int(value[1]);
841 v.z = int(value[2]);
842 self.pdata.setImage(self.tag, v);
843 return;
844 if name == "charge":
845 self.pdata.setCharge(self.tag, float(value));
846 return;
847 if name == "mass":
848 self.pdata.setMass(self.tag, float(value));
849 return;
850 if name == "diameter":
851 self.pdata.setDiameter(self.tag, value);
852 return;
853 if name == "body":
854 self.pdata.setBody(self.tag, value);
855 return;
856 if name == "type":
857 typeid = self.pdata.getTypeByName(value);
858 self.pdata.setType(self.tag, typeid);
859 return;
860 if name == "typeid":
861 raise AttributeError;
862 if name == "acceleration":
863 raise AttributeError;
864 if name == "orientation":
865 o = hoomd.Scalar4();
866 o.x = float(value[0]);
867 o.y = float(value[1]);
868 o.z = float(value[2]);
869 o.w = float(value[3]);
870 self.pdata.setOrientation(self.tag, o);
871 return;
872 if name == "net_force":
873 raise AttributeError;
874 if name == "net_energy":
875 raise AttributeError;
877 # otherwise, consider this an internal attribute to be set in the normal way
878 self.__dict__[name] = value;
880 ## \internal
881 # Access force data
883 # force_data provides access to the per-particle data of all forces in the system.
884 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
885 # force_data, documented by example.
887 class force_data:
888 ## \internal
889 # \brief force_data iterator
890 class force_data_iterator:
891 def __init__(self, data):
892 self.data = data;
893 self.index = 0;
894 def __iter__(self):
895 return self;
896 def __next__(self):
897 if self.index == len(self.data):
898 raise StopIteration;
900 result = self.data[self.index];
901 self.index += 1;
902 return result;
904 # support python2
905 next = __next__;
907 ## \internal
908 # \brief create a force_data
910 # \param pdata ParticleData to connect
911 def __init__(self, force):
912 self.force = force;
914 ## \var force
915 # \internal
916 # \brief ForceCompute to which this instance is connected
918 ## \internal
919 # \brief Get a force_proxy reference to the particle with tag \a tag
920 # \param tag Particle tag to access
921 def __getitem__(self, tag):
922 if tag >= len(self) or tag < 0:
923 raise IndexError;
924 return force_data_proxy(self.force, tag);
926 ## \internal
927 # \brief Set a particle's properties
928 # \param tag Particle tag to set
929 # \param p Value containing properties to set
930 def __setitem__(self, tag, p):
931 raise RuntimeError('__setitem__ not implemented');
933 ## \internal
934 # \brief Get the number of particles
935 def __len__(self):
936 return globals.system_definition.getParticleData().getNGlobal();
938 ## \internal
939 # \brief Get an informal string representing the object
940 def __str__(self):
941 result = "Force Data for %d particles" % (len(self));
942 return result
944 ## \internal
945 # \brief Return an interator
946 def __iter__(self):
947 return force_data.force_data_iterator(self);
949 ## Access the %force on a single particle via a proxy
951 # force_data_proxy provides access to the current %force, virial, and energy of a single particle due to a single
952 # %force computations.
954 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
955 # force_data_proxy, documented by example.
957 # The following attributes are read only:
958 # - \c %force : A 3-tuple of floats (x, y, z) listing the current %force on the particle
959 # - \c virial : A float containing the contribution of this particle to the total virial
960 # - \c energy : A float containing the contribution of this particle to the total potential energy
961 # - \c torque : A 3-tuple of floats (x, y, z) listing the current torque on the particle
963 class force_data_proxy:
964 ## \internal
965 # \brief create a force_data_proxy
967 # \param force ForceCompute to which this proxy belongs
968 # \param tag Tag of this particle in \a force
969 def __init__(self, force, tag):
970 self.fdata = force;
971 self.tag = tag;
973 ## \internal
974 # \brief Get an informal string representing the object
975 def __str__(self):
976 result = "";
977 result += "tag : " + str(self.tag) + "\n"
978 result += "force : " + str(self.force) + "\n";
979 result += "virial : " + str(self.virial) + "\n";
980 result += "energy : " + str(self.energy) + "\n";
981 result += "torque : " + str(self.torque) + "\n";
982 return result;
984 ## \internal
985 # \brief Translate attribute accesses into the low level API function calls
986 def __getattr__(self, name):
987 if name == "force":
988 f = self.fdata.cpp_force.getForce(self.tag);
989 return (f.x, f.y, f.z);
990 if name == "virial":
991 return (self.fdata.cpp_force.getVirial(self.tag,0),
992 self.fdata.cpp_force.getVirial(self.tag,1),
993 self.fdata.cpp_force.getVirial(self.tag,2),
994 self.fdata.cpp_force.getVirial(self.tag,3),
995 self.fdata.cpp_force.getVirial(self.tag,4),
996 self.fdata.cpp_force.getVirial(self.tag,5));
997 if name == "energy":
998 energy = self.fdata.cpp_force.getEnergy(self.tag);
999 return energy;
1000 if name == "torque":
1001 f = self.fdata.cpp_force.getTorque(self.tag);
1002 return (f.x, f.y, f.z)
1004 # if we get here, we haven't found any names that match, post an error
1005 raise AttributeError;
1007 ## \internal
1008 # \brief Access bond data
1010 # bond_data provides access to the bonds in the system.
1011 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
1012 # bond_data, documented by example.
1014 class bond_data:
1015 ## \internal
1016 # \brief bond_data iterator
1017 class bond_data_iterator:
1018 def __init__(self, data):
1019 self.data = data;
1020 self.tag = 0;
1021 def __iter__(self):
1022 return self;
1023 def __next__(self):
1024 if self.tag == len(self.data):
1025 raise StopIteration;
1027 result = self.data[self.tag];
1028 self.tag += 1;
1029 return result;
1031 # support python2
1032 next = __next__;
1034 ## \internal
1035 # \brief create a bond_data
1037 # \param bdata BondData to connect
1038 def __init__(self, bdata):
1039 self.bdata = bdata;
1041 ## \internal
1042 # \brief Add a new bond
1043 # \param type Type name of the bond to add
1044 # \param a Tag of the first particle in the bond
1045 # \param b Tag of the second particle in the bond
1046 # \returns Unique tag identifying this bond
1047 def add(self, type, a, b):
1048 typeid = self.bdata.getTypeByName(type);
1049 return self.bdata.addBondedGroup(hoomd.Bond(typeid, a, b));
1051 ## \internal
1052 # \brief Remove a bond by tag
1053 # \param tag Unique tag of the bond to remove
1054 def remove(self, tag):
1055 self.bdata.removeBondedGroup(tag);
1057 ## \var bdata
1058 # \internal
1059 # \brief BondData to which this instance is connected
1061 ## \internal
1062 # \brief Get a bond_proxy reference to the bond with id \a id
1063 # \param id Bond id to access
1064 def __getitem__(self, tag):
1065 if tag >= len(self) or tag < 0:
1066 raise IndexError;
1067 return bond_data_proxy(self.bdata, tag);
1069 ## \internal
1070 # \brief Set a bond's properties
1071 # \param id Bond id to set
1072 # \param b Value containing properties to set
1073 def __setitem__(self, id, b):
1074 raise RuntimeError('Cannot change bonds once they are created');
1076 ## \internal
1077 # \brief Delete a bond by id
1078 # \param id Bond id to delete
1079 def __delitem__(self, id):
1080 if id >= len(self) or id < 0:
1081 raise IndexError;
1082 tag = self.bdata.getNthTag(id);
1083 self.bdata.removeBond(tag);
1085 ## \internal
1086 # \brief Get the number of bonds
1087 def __len__(self):
1088 return self.bdata.getNGlobal();
1090 ## \internal
1091 # \brief Get an informal string representing the object
1092 def __str__(self):
1093 result = "Bond Data for %d bonds of %d typeid(s)" % (self.bdata.getNGlobal(), self.bdata.getNBondTypes());
1094 return result
1096 ## \internal
1097 # \brief Return an interator
1098 def __iter__(self):
1099 return bond_data.bond_data_iterator(self);
1101 ## Access a single bond via a proxy
1103 # bond_data_proxy provides access to all of the properties of a single bond in the system.
1104 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
1105 # bond_data_proxy, documented by example.
1107 # The following attributes are read only:
1108 # - \c tag : A unique integer attached to each bond (not in any particular range). A bond's tag remans fixed
1109 # during its lifetime. (Tags previously used by removed bonds may be recycled).
1110 # - \c typeid : An integer indexing the bond type of the bond.
1111 # - \c a : An integer indexing the A particle in the bond. Particle tags run from 0 to N-1;
1112 # - \c b : An integer indexing the B particle in the bond. Particle tags run from 0 to N-1;
1113 # - \c type : A string naming the type
1115 # In the current version of the API, only already defined type names can be used. A future improvement will allow
1116 # dynamic creation of new type names from within the python API.
1117 # \MPI_SUPPORTED
1118 class bond_data_proxy:
1119 ## \internal
1120 # \brief create a bond_data_proxy
1122 # \param bdata BondData to which this proxy belongs
1123 # \param id index of this bond in \a bdata (at time of proxy creation)
1124 def __init__(self, bdata, tag):
1125 self.bdata = bdata;
1126 self.tag = tag
1128 ## \internal
1129 # \brief Get an informal string representing the object
1130 def __str__(self):
1131 result = "";
1132 result += "typeid : " + str(self.typeid) + "\n";
1133 result += "a : " + str(self.a) + "\n"
1134 result += "b : " + str(self.b) + "\n"
1135 result += "type : " + str(self.type) + "\n";
1136 return result;
1138 ## \internal
1139 # \brief Translate attribute accesses into the low level API function calls
1140 def __getattr__(self, name):
1141 if name == "a":
1142 bond = self.bdata.getGroupByTag(self.tag);
1143 return bond.a;
1144 if name == "b":
1145 bond = self.bdata.getGroupByTag(self.tag);
1146 return bond.b;
1147 if name == "typeid":
1148 bond = self.bdata.getGroupByTag(self.tag);
1149 return bond.type;
1150 if name == "type":
1151 bond = self.bdata.getGroupByTag(self.tag);
1152 typeid = bond.type;
1153 return self.bdata.getNameByType(typeid);
1155 # if we get here, we haven't found any names that match, post an error
1156 raise AttributeError;
1158 ## \internal
1159 # \brief Translate attribute accesses into the low level API function calls
1160 def __setattr__(self, name, value):
1161 if name == "a":
1162 raise AttributeError;
1163 if name == "b":
1164 raise AttributeError;
1165 if name == "type":
1166 raise AttributeError;
1167 if name == "typeid":
1168 raise AttributeError;
1170 # otherwise, consider this an internal attribute to be set in the normal way
1171 self.__dict__[name] = value;
1173 ## \internal
1174 # \brief Access angle data
1176 # angle_data provides access to the angles in the system.
1177 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
1178 # angle_data, documented by example.
1180 class angle_data:
1181 ## \internal
1182 # \brief angle_data iterator
1183 class angle_data_iterator:
1184 def __init__(self, data):
1185 self.data = data;
1186 self.index = 0;
1187 def __iter__(self):
1188 return self;
1189 def __next__(self):
1190 if self.index == len(self.data):
1191 raise StopIteration;
1193 result = self.data[self.index];
1194 self.index += 1;
1195 return result;
1197 # support python2
1198 next = __next__;
1200 ## \internal
1201 # \brief create a angle_data
1203 # \param bdata AngleData to connect
1204 def __init__(self, adata):
1205 self.adata = adata;
1207 ## \internal
1208 # \brief Add a new angle
1209 # \param type Type name of the angle to add
1210 # \param a Tag of the first particle in the angle
1211 # \param b Tag of the second particle in the angle
1212 # \param c Tag of the thrid particle in the angle
1213 # \returns Unique tag identifying this bond
1214 def add(self, type, a, b, c):
1215 typeid = self.adata.getTypeByName(type);
1216 return self.adata.addBondedGroup(hoomd.Angle(typeid, a, b, c));
1218 ## \internal
1219 # \brief Remove an angle by tag
1220 # \param tag Unique tag of the angle to remove
1221 def remove(self, tag):
1222 self.adata.removeBondedGroup(tag);
1224 ## \var adata
1225 # \internal
1226 # \brief AngleData to which this instance is connected
1228 ## \internal
1229 # \brief Get anm angle_proxy reference to the bond with id \a id
1230 # \param id Angle id to access
1231 def __getitem__(self, id):
1232 if id >= len(self) or id < 0:
1233 raise IndexError;
1234 return angle_data_proxy(self.adata, id);
1236 ## \internal
1237 # \brief Set an angle's properties
1238 # \param id Angle id to set
1239 # \param b Value containing properties to set
1240 def __setitem__(self, id, b):
1241 raise RuntimeError('Cannot change angles once they are created');
1243 ## \internal
1244 # \brief Delete an angle by id
1245 # \param id Angle id to delete
1246 def __delitem__(self, id):
1247 if id >= len(self) or id < 0:
1248 raise IndexError;
1250 # Get the tag of the bond to delete
1251 tag = self.adata.getNthTag(id);
1252 self.adata.removeBondedGroup(tag);
1254 ## \internal
1255 # \brief Get the number of angles
1256 def __len__(self):
1257 return self.adata.getNGlobal();
1259 ## \internal
1260 # \brief Get an informal string representing the object
1261 def __str__(self):
1262 result = "Angle Data for %d angles of %d typeid(s)" % (self.adata.getNGlobal(), self.adata.getNTypes());
1263 return result;
1265 ## \internal
1266 # \brief Return an interator
1267 def __iter__(self):
1268 return angle_data.angle_data_iterator(self);
1270 ## Access a single angle via a proxy
1272 # angle_data_proxy provides access to all of the properties of a single angle in the system.
1273 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
1274 # angle_data_proxy, documented by example.
1276 # The following attributes are read only:
1277 # - \c tag : A unique integer attached to each angle (not in any particular range). A angle's tag remans fixed
1278 # during its lifetime. (Tags previously used by removed angles may be recycled).
1279 # - \c typeid : An integer indexing the angle's type.
1280 # - \c a : An integer indexing the A particle in the angle. Particle tags run from 0 to N-1;
1281 # - \c b : An integer indexing the B particle in the angle. Particle tags run from 0 to N-1;
1282 # - \c c : An integer indexing the C particle in the angle. Particle tags run from 0 to N-1;
1283 # - \c type : A string naming the type
1285 # In the current version of the API, only already defined type names can be used. A future improvement will allow
1286 # dynamic creation of new type names from within the python API.
1287 # \MPI_SUPPORTED
1288 class angle_data_proxy:
1289 ## \internal
1290 # \brief create a angle_data_proxy
1292 # \param adata AngleData to which this proxy belongs
1293 # \param id index of this angle in \a adata (at time of proxy creation)
1294 def __init__(self, adata, id):
1295 self.adata = adata;
1296 self.tag = self.adata.getNthTag(id);
1298 ## \internal
1299 # \brief Get an informal string representing the object
1300 def __str__(self):
1301 result = "";
1302 result += "tag : " + str(self.tag) + "\n";
1303 result += "typeid : " + str(self.typeid) + "\n";
1304 result += "a : " + str(self.a) + "\n"
1305 result += "b : " + str(self.b) + "\n"
1306 result += "c : " + str(self.c) + "\n"
1307 result += "type : " + str(self.type) + "\n";
1308 return result;
1310 ## \internal
1311 # \brief Translate attribute accesses into the low level API function calls
1312 def __getattr__(self, name):
1313 if name == "a":
1314 angle = self.adata.getGroupByTag(self.tag);
1315 return angle.a;
1316 if name == "b":
1317 angle = self.adata.getGroupByTag(self.tag);
1318 return angle.b;
1319 if name == "c":
1320 angle = self.adata.getGroupByTag(self.tag);
1321 return angle.c;
1322 if name == "typeid":
1323 angle = self.adata.getGroupByTag(self.tag);
1324 return angle.type;
1325 if name == "type":
1326 angle = self.adata.getGroupByTag(self.tag);
1327 typeid = angle.type;
1328 return self.adata.getNameByType(typeid);
1330 # if we get here, we haven't found any names that match, post an error
1331 raise AttributeError;
1333 ## \internal
1334 # \brief Translate attribute accesses into the low level API function calls
1335 def __setattr__(self, name, value):
1336 if name == "a":
1337 raise AttributeError;
1338 if name == "b":
1339 raise AttributeError;
1340 if name == "c":
1341 raise AttributeError;
1342 if name == "type":
1343 raise AttributeError;
1344 if name == "typeid":
1345 raise AttributeError;
1347 # otherwise, consider this an internal attribute to be set in the normal way
1348 self.__dict__[name] = value;
1350 ## \internal
1351 # \brief Access dihedral data
1353 # dihedral_data provides access to the dihedrals in the system.
1354 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
1355 # dihedral_data, documented by example.
1357 class dihedral_data:
1358 ## \internal
1359 # \brief dihedral_data iterator
1360 class dihedral_data_iterator:
1361 def __init__(self, data):
1362 self.data = data;
1363 self.index = 0;
1364 def __iter__(self):
1365 return self;
1366 def __next__(self):
1367 if self.index == len(self.data):
1368 raise StopIteration;
1370 result = self.data[self.index];
1371 self.index += 1;
1372 return result;
1374 # support python2
1375 next = __next__;
1377 ## \internal
1378 # \brief create a dihedral_data
1380 # \param bdata DihedralData to connect
1381 def __init__(self, ddata):
1382 self.ddata = ddata;
1384 ## \internal
1385 # \brief Add a new dihedral
1386 # \param type Type name of the dihedral to add
1387 # \param a Tag of the first particle in the dihedral
1388 # \param b Tag of the second particle in the dihedral
1389 # \param c Tag of the thrid particle in the dihedral
1390 # \param d Tag of the fourth particle in the dihedral
1391 # \returns Unique tag identifying this bond
1392 def add(self, type, a, b, c, d):
1393 typeid = self.ddata.getTypeByName(type);
1394 return self.ddata.addBondedGroup(hoomd.Dihedral(typeid, a, b, c, d));
1396 ## \internal
1397 # \brief Remove an dihedral by tag
1398 # \param tag Unique tag of the dihedral to remove
1399 def remove(self, tag):
1400 self.ddata.removeBondedGroup(tag);
1402 ## \var ddata
1403 # \internal
1404 # \brief DihedralData to which this instance is connected
1406 ## \internal
1407 # \brief Get anm dihedral_proxy reference to the dihedral with id \a id
1408 # \param id Dihedral id to access
1409 def __getitem__(self, id):
1410 if id >= len(self) or id < 0:
1411 raise IndexError;
1412 return dihedral_data_proxy(self.ddata, id);
1414 ## \internal
1415 # \brief Set an dihedral's properties
1416 # \param id dihedral id to set
1417 # \param b Value containing properties to set
1418 def __setitem__(self, id, b):
1419 raise RuntimeError('Cannot change angles once they are created');
1421 ## \internal
1422 # \brief Delete an dihedral by id
1423 # \param id Dihedral id to delete
1424 def __delitem__(self, id):
1425 if id >= len(self) or id < 0:
1426 raise IndexError;
1428 # Get the tag of the bond to delete
1429 tag = self.ddata.getNthTag(id);
1430 self.ddata.removeBondedGroup(tag);
1432 ## \internal
1433 # \brief Get the number of angles
1434 def __len__(self):
1435 return self.ddata.getNGlobal();
1437 ## \internal
1438 # \brief Get an informal string representing the object
1439 def __str__(self):
1440 result = "Dihedral Data for %d angles of %d typeid(s)" % (self.ddata.getNGlobal(), self.ddata.getNTypes());
1441 return result;
1443 ## \internal
1444 # \brief Return an interator
1445 def __iter__(self):
1446 return dihedral_data.dihedral_data_iterator(self);
1448 ## Access a single dihedral via a proxy
1450 # dihedral_data_proxy provides access to all of the properties of a single dihedral in the system.
1451 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
1452 # dihedral_data_proxy, documented by example.
1454 # The following attributes are read only:
1455 # - \c tag : A unique integer attached to each dihedral (not in any particular range). A dihedral's tag remans fixed
1456 # during its lifetime. (Tags previously used by removed dihedral may be recycled).
1457 # - \c typeid : An integer indexing the dihedral's type.
1458 # - \c a : An integer indexing the A particle in the angle. Particle tags run from 0 to N-1;
1459 # - \c b : An integer indexing the B particle in the angle. Particle tags run from 0 to N-1;
1460 # - \c c : An integer indexing the C particle in the angle. Particle tags run from 0 to N-1;
1461 # - \c d : An integer indexing the D particle in the dihedral. Particle tags run from 0 to N-1;
1462 # - \c type : A string naming the type
1464 # In the current version of the API, only already defined type names can be used. A future improvement will allow
1465 # dynamic creation of new type names from within the python API.
1466 # \MPI_NOT_SUPPORTED
1467 class dihedral_data_proxy:
1468 ## \internal
1469 # \brief create a dihedral_data_proxy
1471 # \param ddata DihedralData to which this proxy belongs
1472 # \param id index of this dihedral in \a ddata (at time of proxy creation)
1473 def __init__(self, ddata, id):
1474 self.ddata = ddata;
1475 self.tag = self.ddata.getNthTag(id);
1477 ## \internal
1478 # \brief Get an informal string representing the object
1479 def __str__(self):
1480 result = "";
1481 result += "tag : " + str(self.tag) + "\n";
1482 result += "typeid : " + str(self.typeid) + "\n";
1483 result += "a : " + str(self.a) + "\n"
1484 result += "b : " + str(self.b) + "\n"
1485 result += "c : " + str(self.c) + "\n"
1486 result += "d : " + str(self.d) + "\n"
1487 result += "type : " + str(self.type) + "\n";
1488 return result;
1490 ## \internal
1491 # \brief Translate attribute accesses into the low level API function calls
1492 def __getattr__(self, name):
1493 if name == "a":
1494 dihedral = self.ddata.getGroupByTag(self.tag);
1495 return dihedral.a;
1496 if name == "b":
1497 dihedral = self.ddata.getGroupByTag(self.tag);
1498 return dihedral.b;
1499 if name == "c":
1500 dihedral = self.ddata.getGroupByTag(self.tag);
1501 return dihedral.c;
1502 if name == "d":
1503 dihedral = self.ddata.getGroupByTag(self.tag);
1504 return dihedral.d;
1505 if name == "typeid":
1506 dihedral = self.ddata.getGroupByTag(self.tag);
1507 return dihedral.type;
1508 if name == "type":
1509 dihedral = self.ddata.getGroupByTag(self.tag);
1510 typeid = dihedral.type;
1511 return self.ddata.getNameByType(typeid);
1513 # if we get here, we haven't found any names that match, post an error
1514 raise AttributeError;
1516 ## \internal
1517 # \brief Translate attribute accesses into the low level API function calls
1518 def __setattr__(self, name, value):
1519 if name == "a":
1520 raise AttributeError;
1521 if name == "b":
1522 raise AttributeError;
1523 if name == "c":
1524 raise AttributeError;
1525 if name == "d":
1526 raise AttributeError;
1527 if name == "type":
1528 raise AttributeError;
1529 if name == "typeid":
1530 raise AttributeError;
1532 # otherwise, consider this an internal attribute to be set in the normal way
1533 self.__dict__[name] = value;
1535 ## \internal
1536 # \brief Access body data
1538 # body_data provides access to the per-body data of all bodies in the system.
1539 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
1540 # body_data, documented by example.
1542 class body_data:
1543 ## \internal
1544 # \brief bond_data iterator
1545 class body_data_iterator:
1546 def __init__(self, data):
1547 self.data = data;
1548 self.index = 0;
1549 def __iter__(self):
1550 return self;
1551 def __next__(self):
1552 if self.index == len(self.data):
1553 raise StopIteration;
1555 result = self.data[self.index];
1556 self.index += 1;
1557 return result;
1559 # support python2
1560 next = __next__;
1562 ## \internal
1563 # \brief create a body_data
1565 # \param bdata BodyData to connect
1566 def __init__(self, bdata):
1567 self.bdata = bdata;
1569 # \brief updates the v and x positions of a rigid body
1570 # \note the second arguement is dt, but the value should not matter as long as not zero
1571 def updateRV(self):
1572 self.bdata.setRV(True);
1574 ## \var bdata
1575 # \internal
1576 # \brief BodyData to which this instance is connected
1578 ## \internal
1579 # \brief Get a body_proxy reference to the body with body index \a tag
1580 # \param tag Body tag to access
1581 def __getitem__(self, tag):
1582 if tag >= len(self) or tag < 0:
1583 raise IndexError;
1584 return body_data_proxy(self.bdata, tag);
1586 ## \internal
1587 # \brief Set a body's properties
1588 # \param tag Body tag to set
1589 # \param p Value containing properties to set
1590 def __setitem__(self, tag, p):
1591 raise RuntimeError('__setitem__ not implemented');
1593 ## \internal
1594 # \brief Get the number of bodies
1595 def __len__(self):
1596 return self.bdata.getNumBodies();
1598 ## \internal
1599 # \brief Get an informal string representing the object
1600 def __str__(self):
1601 result = "Body Data for %d bodies" % (self.bdata.getNumBodies());
1602 return result
1604 ## \internal
1605 # \brief Return an interator
1606 def __iter__(self):
1607 return body_data.body_data_iterator(self);
1609 ## Access a single body via a proxy
1611 # body_data_proxy provides access to all of the properties of a single bond in the system.
1612 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
1613 # body_data_proxy, documented by example.
1615 # The following attributes are read only:
1616 # - \c num_particles : The number of particles (or interaction sites) composing the body
1617 # - \c particle_tags : the tags of the particles (or interaction sites) composing the body
1618 # - \c net_force : Net force acting on the body (x, y, z) (in force units)
1619 # - \c net_torque : Net torque acting on the body (x, y, z) (in units of force * distance)
1621 # The following attributes can be both read and set
1622 # - \c mass : The mass of the body
1623 # - \c COM : The Center of Mass position of the body
1624 # - \c velocity : The velocity vector of the center of mass of the body
1625 # - \c orientation : The orientation of the body (quaternion)
1626 # - \c angular_momentum : The angular momentum of the body in the space frame
1627 # - \c moment_inertia : the principle components of the moment of inertia
1628 # - \c particle_disp : the displacements of the particles (or interaction sites) of the body relative to the COM in the body frame.
1629 # \MPI_NOT_SUPPORTED
1630 class body_data_proxy:
1631 ## \internal
1632 # \brief create a body_data_proxy
1634 # \param bdata RigidData to which this proxy belongs
1635 # \param tag tag of this body in \a bdata
1636 def __init__(self, bdata, tag):
1638 # Error out in MPI simulations
1639 if (hoomd.is_MPI_available()):
1640 if globals.system_definition.getParticleData().getDomainDecomposition():
1641 globals.msg.error("Rigid bodies are not supported in multi-processor simulations.\n\n")
1642 raise RuntimeError("Error accessing body data.")
1644 self.bdata = bdata;
1645 self.tag = tag;
1647 ## \internal
1648 # \brief Get an informal string representing the object
1649 def __str__(self):
1650 result = "";
1651 result += "num_particles : " + str(self.num_particles) + "\n"
1652 result += "mass : " + str(self.mass) + "\n"
1653 result += "COM : " + str(self.COM) + "\n"
1654 result += "velocity : " + str(self.velocity) + "\n"
1655 result += "orientation : " + str(self.orientation) + "\n"
1656 result += "angular_momentum (space frame) : " + str(self.angular_momentum) + "\n"
1657 result += "moment_inertia: " + str(self.moment_inertia) + "\n"
1658 result += "particle_tags : " + str(self.particle_tags) + "\n"
1659 result += "particle_disp : " + str(self.particle_disp) + "\n"
1660 result += "net_force : " + str(self.net_force) + "\n"
1661 result += "net_torque : " + str(self.net_torque) + "\n"
1663 return result;
1665 ## \internal
1666 # \brief Translate attribute accesses into the low level API function calls
1667 def __getattr__(self, name):
1668 if name == "COM":
1669 COM = self.bdata.getBodyCOM(self.tag);
1670 return (COM.x, COM.y, COM.z);
1671 if name == "velocity":
1672 velocity = self.bdata.getBodyVel(self.tag);
1673 return (velocity.x, velocity.y, velocity.z);
1674 if name == "orientation":
1675 orientation = self.bdata.getBodyOrientation(self.tag);
1676 return (orientation.x, orientation.y, orientation.z, orientation.w);
1677 if name == "angular_momentum":
1678 angular_momentum = self.bdata.getBodyAngMom(self.tag);
1679 return (angular_momentum.x, angular_momentum.y, angular_momentum.z);
1680 if name == "num_particles":
1681 num_particles = self.bdata.getBodyNSize(self.tag);
1682 return num_particles;
1683 if name == "mass":
1684 mass = self.bdata.getMass(self.tag);
1685 return mass;
1686 if name == "moment_inertia":
1687 moment_inertia = self.bdata.getBodyMomInertia(self.tag);
1688 return (moment_inertia.x, moment_inertia.y, moment_inertia.z);
1689 if name == "particle_tags":
1690 particle_tags = [];
1691 for i in range(0, self.num_particles):
1692 particle_tags.append(self.bdata.getParticleTag(self.tag, i));
1693 return particle_tags;
1694 if name == "particle_disp":
1695 particle_disp = [];
1696 for i in range(0, self.num_particles):
1697 disp = self.bdata.getParticleDisp(self.tag, i);
1698 particle_disp.append([disp.x, disp.y, disp.z]);
1699 return particle_disp;
1700 if name == "net_force":
1701 f = self.bdata.getBodyNetForce(self.tag);
1702 return (f.x, f.y, f.z);
1703 if name == "net_torque":
1704 t = self.bdata.getBodyNetTorque(self.tag);
1705 return (t.x, t.y, t.z);
1707 # if we get here, we haven't found any names that match, post an error
1708 raise AttributeError;
1710 ## \internal
1711 # \brief Translate attribute accesses into the low level API function calls
1712 def __setattr__(self, name, value):
1713 if name == "COM":
1714 p = hoomd.Scalar3();
1715 p.x = float(value[0]);
1716 p.y = float(value[1]);
1717 p.z = float(value[2]);
1718 self.bdata.setBodyCOM(self.tag, p);
1719 return;
1720 if name == "velocity":
1721 v = hoomd.Scalar3();
1722 v.x = float(value[0]);
1723 v.y = float(value[1]);
1724 v.z = float(value[2]);
1725 self.bdata.setBodyVel(self.tag, v);
1726 return;
1727 if name == "mass":
1728 self.bdata.setMass(self.tag, value);
1729 return;
1730 if name == "orientation":
1731 q = hoomd.Scalar4();
1732 q.x = float(value[0]);
1733 q.y = float(value[1]);
1734 q.z = float(value[2]);
1735 q.w = float(value[3]);
1736 self.bdata.setBodyOrientation(self.tag, q);
1737 return;
1738 if name == "angular_momentum":
1739 p = hoomd.Scalar3();
1740 p.x = float(value[0]);
1741 p.y = float(value[1]);
1742 p.z = float(value[2]);
1743 self.bdata.setAngMom(self.tag, p);
1744 return;
1745 if name == "moment_inertia":
1746 p = hoomd.Scalar3();
1747 p.x = float(value[0]);
1748 p.y = float(value[1]);
1749 p.z = float(value[2]);
1750 self.bdata.setBodyMomInertia(self.tag, p);
1751 return;
1752 if name == "particle_disp":
1753 p = hoomd.Scalar3();
1754 for i in range(0, self.num_particles):
1755 p.x = float(value[i][0]);
1756 p.y = float(value[i][1]);
1757 p.z = float(value[i][2]);
1758 self.bdata.setParticleDisp(self.tag, i, p);
1759 return;
1760 if name == "net_force":
1761 raise AttributeError;
1762 if name == "net_torque":
1763 raise AttributeError;
1765 # otherwise, consider this an internal attribute to be set in the normal way
1766 self.__dict__[name] = value;