Update MTK documentation
[hoomd-blue.git] / python-module / hoomd_script / data.py
blob13b36eb2cdbc5d61d5f2541f93cdf99b92cbf436
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_data.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_data.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 sysdef variable (`system.box = new_box`) to set the simulation
340 # box.
342 # boxdim parameters may be accessed directly.
343 # ~~~~
344 # b = data.boxdim(L=20);
345 # b.xy = 1.0;
346 # b.yz = 0.5;
347 # b.Lz = 40;
348 # ~~~~
350 # **Two dimensional systems**
352 # 2D simulations in hoomd are embedded in 3D boxes with short heights in the z direction. To create a 2D box,
353 # set dimensions=2 when creating the boxdim. This will force Lz=1 and xz=yz=0. init commands that support 2D boxes
354 # will pass the dimensionality along to the system. When you assign a new boxdim to an already initialized system,
355 # the dimensionality flag is ignored. Changing the number of dimensions during a simulation run is not supported.
357 # In 2D boxes, *volume* is in units of area.
359 # **Shorthand notation**
361 # data.boxdim accepts the keyword argument *L=x* as shorthand notation for `Lx=x, Ly=x, Lz=x` in 3D
362 # and `Lx=x, Ly=z, Lz=1` in 2D. If you specify both `L=` and `Lx,Ly, or Lz`, then the value for `L` will override
363 # the others.
365 # **Examples:**
367 # There are many ways to define boxes.
369 # * Cubic box with given volume: `data.boxdim(volume=V)`
370 # * Triclinic box in 2D with given area: `data.boxdim(xy=1.0, dimensions=2, volume=A)`
371 # * Rectangular box in 2D with given area and aspect ratio: `data.boxdim(Lx=1, Ly=aspect, dimensions=2, volume=A)`
372 # * Cubic box with given length: `data.boxdim(L=10)`
373 # * Fully define all box parameters: `data.boxdim(Lx=10, Ly=20, Lz=30, xy=1.0, xz=0.5, yz=0.1)`
375 class boxdim:
376 ## Initialize a boxdim object
378 # \param Lx box extent in the x direction (distance units)
379 # \param Ly box extent in the y direction (distance units)
380 # \param Lz box extent in the z direction (distance units)
381 # \param xy tilt factor xy (dimensionless)
382 # \param xz tilt factor xz (dimensionless)
383 # \param yz tilt factor yz (dimensionless)
384 # \param dimensions Number of dimensions in the box (2 or 3).
385 # \param L shorthand for specifying Lx=Ly=Lz=L (distance units)
386 # \param volume Scale the given box dimensions up to the this volume (area if dimensions=2)
388 def __init__(self, Lx=1.0, Ly=1.0, Lz=1.0, xy=0.0, xz=0.0, yz=0.0, dimensions=3, L=None, volume=None):
389 if L is not None:
390 Lx = L;
391 Ly = L;
392 Lz = L;
394 if dimensions == 2:
395 Lz = 1.0;
396 xz = yz = 0.0;
398 self.Lx = Lx;
399 self.Ly = Ly;
400 self.Lz = Lz;
401 self.xy = xy;
402 self.xz = xz;
403 self.yz = yz;
404 self.dimensions = dimensions;
406 if volume is not None:
407 self.set_volume(volume);
409 ## Scale box dimensions
411 # \param sx scale factor in the x direction
412 # \param sy scale factor in the y direction
413 # \param sz scale factor in the z direction
415 # Scales the box by the given scale factors. Tilt factors are not modified.
417 def scale(self, sx, sy, sz):
418 self.Lx = self.Lx * sx;
419 self.Ly = self.Ly * sy;
420 self.Lz = self.Lz * sz;
422 ## Set the box volume
424 # \param volume new box volume (area if dimensions=2)
426 # setVolume() scales the box to the given volume (or area).
428 def set_volume(self, volume):
429 cur_vol = self.get_volume();
431 if self.dimensions == 3:
432 s = (volume / cur_vol)**(1.0/3.0)
433 self.scale(s, s, s);
434 else:
435 s = (volume / cur_vol)**(1.0/2.0)
436 self.scale(s, s, 1.0);
438 ## Get the box volume
440 # Returns the box volume (area in 2D).
442 def get_volume(self):
443 b = self._getBoxDim();
444 return b.getVolume(self.dimensions == 2);
446 ## Get a lattice vector
448 # \param i (=0,1,2) direction of lattice vector
450 # \returns a lattice vector (3-tuple) along direction \a i
452 def get_lattice_vector(self,i):
453 b = self._getBoxDim();
454 v = b.getLatticeVector(int(i))
455 return (v.x, v.y, v.z)
457 ## Wrap a vector using the periodic boundary conditions
459 # \param v The vector to wrap
461 # \returns the wrapped vector
463 def wrap(self,v):
464 u = hoomd.make_scalar3(v[0],v[1],v[2])
465 i = hoomd.make_int3(0,0,0)
466 c = hoomd.make_char3(0,0,0)
467 self._getBoxDim().wrap(u,i,c)
468 return (u.x, u.y, u.z)
470 ## \internal
471 # \brief Get a C++ boxdim
472 def _getBoxDim(self):
473 b = hoomd.BoxDim(self.Lx, self.Ly, self.Lz);
474 b.setTiltFactors(self.xy, self.xz, self.yz);
475 return b
477 def __str__(self):
478 return 'Box: Lx=' + str(self.Lx) + ' Ly=' + str(self.Ly) + ' Lz=' + str(self.Lz) + ' xy=' + str(self.xy) + \
479 ' xz='+ str(self.xz) + ' yz=' + str(self.yz);
481 # \brief Access system data
483 # system_data provides access to the different data structures that define the current state of the simulation.
484 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
485 # system_data, documented by example.
487 class system_data:
488 ## \internal
489 # \brief create a system_data
491 # \param sysdef SystemDefinition to connect
492 def __init__(self, sysdef):
493 self.sysdef = sysdef;
494 self.particles = particle_data(sysdef.getParticleData());
495 self.bonds = bond_data(sysdef.getBondData());
496 self.angles = angle_data(sysdef.getAngleData());
497 self.dihedrals = dihedral_data(sysdef.getDihedralData());
498 self.impropers = dihedral_data(sysdef.getImproperData());
499 self.bodies = body_data(sysdef.getRigidData());
501 ## Take a snapshot of the current system data
503 # This functions returns a snapshot object. It contains the current
504 # partial or complete simulation state. With appropriate options
505 # it is possible to select which data properties should be included
506 # in the snapshot.
508 # \param particles If true, particle data is included in the snapshot
509 # \param bonds If true, bond data is included in the snapshot
510 # \param angles If true, angle data is included in the snapshot
511 # \param dihedrals If true, dihedral data is included in the snapshot
512 # \param impropers If true, dihedral data is included in the snapshot
513 # \param rigid_bodies If true, rigid body data is included in the snapshot
514 # \param walls If true, wall data is included in the snapshot
515 # \param integrators If true, integrator data is included the snapshot
516 # \param all If true, the entire system state is saved in the snapshot
518 # Specific options (such as \b particles=True) take precedence over \b all=True.
520 # \returns the snapshot object.
522 # \code
523 # snapshot = system.take_snapshot()
524 # snapshot = system.take_snapshot(particles=true)
525 # snapshot = system.take_snapshot(bonds=true)
526 # \endcode
528 # \MPI_SUPPORTED
529 def take_snapshot(self,particles=None,bonds=None,angles=None,dihedrals=None, impropers=None, rigid_bodies=None, walls=None, integrators=None, all=None):
530 util.print_status_line();
532 if all is True:
533 if particles is None:
534 particles=True
535 if bonds is None:
536 bonds=True
537 if angles is None:
538 angles=True
539 if dihedrals is None:
540 dihedrals=True
541 if impropers is None:
542 impropers=True
543 if rigid_bodies is None:
544 rigid_bodies=True
545 if walls is None:
546 walls=True
547 if integrators is None:
548 integrators=True
550 if particles is None and not all:
551 particles = False
552 if bonds is None and not all:
553 bonds = False
554 if angles is None and not all:
555 angles = False
556 if dihedrals is None and not all:
557 dihedrals = False
558 if impropers is None and not all:
559 impropers = False
560 if rigid_bodies is None and not all:
561 rigid_bodies = False
562 if walls is None and not all:
563 walls = False
564 if integrators is None and not all:
565 integrators = False
567 if not (particles or bonds or angles or dihedrals or impropers or rigid_bodies or walls or integrators):
568 globals.msg.warning("No options specified. Ignoring request to create an empty snapshot.\n")
569 return None
571 # take the snapshot
572 cpp_snapshot = self.sysdef.takeSnapshot(particles,bonds,angles,dihedrals,impropers,rigid_bodies,walls,integrators)
574 return cpp_snapshot
576 ## Replicates the system along the three spatial dimensions
578 # \param nx Number of times to replicate the system along the x-direction
579 # \param ny Number of times to replicate the system along the y-direction
580 # \param nz Number of times to replicate the system along the z-direction
582 # This method explictly replicates particles along all three spatial directions, as
583 # opposed to replication implied by periodic boundary conditions.
584 # The box is resized and the number of particles is updated so that the new box
585 # holds the specified number of replicas of the old box along all directions.
586 # Particle coordinates are updated accordingly to fit into the new box. All velocities and
587 # other particle properties are replicated as well. Also bonded groups between particles
588 # are replicated.
590 # Example usage:
591 # \code
592 # system = init.read_xml("some_file.xml")
593 # system.replicate(nx=2,ny=2,nz=2)
594 # \endcode
596 # \note Replication of rigid bodies is currently not supported.
598 # \note It is a limitation that in MPI simulations the dimensions of the processor grid
599 # are not updated upon replication. For example, if an initially cubic box is replicated along only one
600 # spatial direction, this could lead to decreased performance if the processor grid was
601 # optimal for the original box dimensions, but not for the new ones.
603 # \MPI_SUPPORTED
604 def replicate(self, nx=1, ny=1, nz=1):
605 util.print_status_line()
607 nx = int(nx)
608 ny = int(ny)
609 nz = int(nz)
611 if nx == ny == nz == 1:
612 globals.msg.warning("All replication factors == 1. Not replicating system.\n")
613 return
615 if nx <= 0 or ny <= 0 or nz <= 0:
616 globals.msg.error("Cannot replicate by zero or by a negative value along any direction.")
617 raise RuntimeError("nx, ny, nz need to be positive integers")
619 # Take a snapshot
620 util._disable_status_lines = True
621 cpp_snapshot = self.take_snapshot(all=True)
622 util._disable_status_lines = False
624 from hoomd_script import comm
625 if comm.get_rank() == 0:
626 # replicate
627 cpp_snapshot.replicate(nx, ny, nz)
629 # restore from snapshot
630 util._disable_status_lines = True
631 self.restore_snapshot(cpp_snapshot)
632 util._disable_status_lines = False
634 ## Re-initializes the system from a snapshot
636 # \param snapshot The snapshot to initialize the system from
638 # Snapshots temporarily store system %data. Snapshots contain the complete simulation state in a
639 # single object. They can be used to restart a simulation.
641 # Example use cases in which a simulation may be restarted from a snapshot include python-script-level
642 # \b Monte-Carlo schemes, where the system state is stored after a move has been accepted (according to
643 # some criterium), and where the system is re-initialized from that same state in the case
644 # when a move is not accepted.
646 # Example for the procedure of taking a snapshot and re-initializing from it:
647 # \code
648 # system = init.read_xml("some_file.xml")
650 # ... run a simulation ...
652 # snapshot = system.take_snapshot(all=True)
653 # ...
654 # system.restore_snapshot(snapshot)
655 # \endcode
657 # \sa hoomd_script.data
658 # \MPI_SUPPORTED
659 def restore_snapshot(self, snapshot):
660 util.print_status_line();
662 self.sysdef.initializeFromSnapshot(snapshot);
664 ## \var sysdef
665 # \internal
666 # \brief SystemDefinition to which this instance is connected
668 ## \internal
669 # \brief Translate attribute accesses into the low level API function calls
670 def __setattr__(self, name, value):
671 if name == "box":
672 if not isinstance(value, boxdim):
673 raise TypeError('box must be a data.boxdim object');
674 self.sysdef.getParticleData().setGlobalBox(value._getBoxDim());
676 # otherwise, consider this an internal attribute to be set in the normal way
677 self.__dict__[name] = value;
679 ## \internal
680 # \brief Translate attribute accesses into the low level API function calls
681 def __getattr__(self, name):
682 if name == "box":
683 b = self.sysdef.getParticleData().getGlobalBox();
684 L = b.getL();
685 return boxdim(Lx=L.x, Ly=L.y, Lz=L.z, xy=b.getTiltFactorXY(), xz=b.getTiltFactorXZ(), yz=b.getTiltFactorYZ());
687 # if we get here, we haven't found any names that match, post an error
688 raise AttributeError;
690 ## \internal
691 # \brief Access particle data
693 # particle_data provides access to the per-particle data of all particles in the system.
694 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
695 # particle_data, documented by example.
697 class particle_data:
698 ## \internal
699 # \brief particle_data iterator
700 class particle_data_iterator:
701 def __init__(self, data):
702 self.data = data;
703 self.index = 0;
704 def __iter__(self):
705 return self;
706 def __next__(self):
707 if self.index == len(self.data):
708 raise StopIteration;
710 result = self.data[self.index];
711 self.index += 1;
712 return result;
714 # support python2
715 next = __next__;
717 ## \internal
718 # \brief create a particle_data
720 # \param pdata ParticleData to connect
721 def __init__(self, pdata):
722 self.pdata = pdata;
724 ntypes = globals.system_definition.getParticleData().getNTypes();
725 self.types = [];
726 for i in range(0,ntypes):
727 self.types.append(globals.system_definition.getParticleData().getNameByType(i));
729 ## \var pdata
730 # \internal
731 # \brief ParticleData to which this instance is connected
733 ## \internal
734 # \brief Get a particle_proxy reference to the particle with tag \a tag
735 # \param tag Particle tag to access
736 def __getitem__(self, tag):
737 if tag >= len(self) or tag < 0:
738 raise IndexError;
739 return particle_data_proxy(self.pdata, tag);
741 ## \internal
742 # \brief Set a particle's properties
743 # \param tag Particle tag to set
744 # \param p Value containing properties to set
745 def __setitem__(self, tag, p):
746 raise RuntimeError('__setitem__ not implemented');
748 ## \internal
749 # \brief Get the number of particles
750 def __len__(self):
751 return self.pdata.getNGlobal();
753 ## \internal
754 # \brief Get an informal string representing the object
755 def __str__(self):
756 result = "Particle Data for %d particles of %d type(s)" % (self.pdata.getNGlobal(), self.pdata.getNTypes());
757 return result
759 ## \internal
760 # \brief Return an interator
761 def __iter__(self):
762 return particle_data.particle_data_iterator(self);
764 ## Access a single particle via a proxy
766 # particle_data_proxy provides access to all of the properties of a single particle in the system.
767 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
768 # particle_data_proxy, documented by example.
770 # The following attributes are read only:
771 # - \c tag : An integer indexing the particle in the system. Tags run from 0 to N-1;
772 # - \c acceleration : A 3-tuple of floats (x, y, z) Note that acceleration is a calculated quantity and cannot be set. (in acceleration units)
773 # - \c typeid : An integer defining the type id
775 # The following attributes can be both read and set
776 # - \c position : A 3-tuple of floats (x, y, z) (in distance units)
777 # - \c image : A 3-tuple of integers (x, y, z)
778 # - \c velocity : A 3-tuple of floats (x, y, z) (in velocity units)
779 # - \c charge : A single float
780 # - \c mass : A single float (in mass units)
781 # - \c diameter : A single float (in distance units)
782 # - \c type : A string naming the type
783 # - \c body : Rigid body id integer (-1 for free particles)
784 # - \c orientation : Orientation of anisotropic particle (quaternion)
785 # - \c net_force : Net force on particle (x, y, z) (in force units)
786 # - \c net_energy : Net contribution of particle to the potential energy (in energy units)
787 # - \c net_torque : Net torque on the particle (x, y, z) (in torque units)
789 # In the current version of the API, only already defined type names can be used. A future improvement will allow
790 # dynamic creation of new type names from within the python API.
792 class particle_data_proxy:
793 ## \internal
794 # \brief create a particle_data_proxy
796 # \param pdata ParticleData to which this proxy belongs
797 # \param tag Tag of this particle in \a pdata
798 def __init__(self, pdata, tag):
799 self.pdata = pdata;
800 self.tag = tag;
802 ## \internal
803 # \brief Get an informal string representing the object
804 def __str__(self):
805 result = "";
806 result += "tag : " + str(self.tag) + "\n"
807 result += "position : " + str(self.position) + "\n";
808 result += "image : " + str(self.image) + "\n";
809 result += "velocity : " + str(self.velocity) + "\n";
810 result += "acceleration: " + str(self.acceleration) + "\n";
811 result += "charge : " + str(self.charge) + "\n";
812 result += "mass : " + str(self.mass) + "\n";
813 result += "diameter : " + str(self.diameter) + "\n";
814 result += "type : " + str(self.type) + "\n";
815 result += "typeid : " + str(self.typeid) + "\n";
816 result += "body : " + str(self.body) + "\n";
817 result += "orientation : " + str(self.orientation) + "\n";
818 result += "net_force : " + str(self.net_force) + "\n";
819 result += "net_energy : " + str(self.net_energy) + "\n";
820 result += "net_torque : " + str(self.net_torque) + "\n";
821 return result;
823 ## \internal
824 # \brief Translate attribute accesses into the low level API function calls
825 def __getattr__(self, name):
826 if name == "position":
827 pos = self.pdata.getPosition(self.tag);
828 return (pos.x, pos.y, pos.z);
829 if name == "velocity":
830 vel = self.pdata.getVelocity(self.tag);
831 return (vel.x, vel.y, vel.z);
832 if name == "acceleration":
833 accel = self.pdata.getAcceleration(self.tag);
834 return (accel.x, accel.y, accel.z);
835 if name == "image":
836 image = self.pdata.getImage(self.tag);
837 return (image.x, image.y, image.z);
838 if name == "charge":
839 return self.pdata.getCharge(self.tag);
840 if name == "mass":
841 return self.pdata.getMass(self.tag);
842 if name == "diameter":
843 return self.pdata.getDiameter(self.tag);
844 if name == "typeid":
845 return self.pdata.getType(self.tag);
846 if name == "body":
847 return self.pdata.getBody(self.tag);
848 if name == "type":
849 typeid = self.pdata.getType(self.tag);
850 return self.pdata.getNameByType(typeid);
851 if name == "orientation":
852 o = self.pdata.getOrientation(self.tag);
853 return (o.x, o.y, o.z, o.w);
854 if name == "net_force":
855 f = self.pdata.getPNetForce(self.tag);
856 return (f.x, f.y, f.z);
857 if name == "net_energy":
858 f = self.pdata.getPNetForce(self.tag);
859 return f.w;
860 if name == "net_torque":
861 f = self.pdata.getNetTorque(self.tag);
862 return (f.x, f.y, f.z);
864 # if we get here, we haven't found any names that match, post an error
865 raise AttributeError;
867 ## \internal
868 # \brief Translate attribute accesses into the low level API function calls
869 def __setattr__(self, name, value):
870 if name == "position":
871 v = hoomd.Scalar3();
872 v.x = float(value[0]);
873 v.y = float(value[1]);
874 v.z = float(value[2]);
875 self.pdata.setPosition(self.tag, v, True);
876 return;
877 if name == "velocity":
878 v = hoomd.Scalar3();
879 v.x = float(value[0]);
880 v.y = float(value[1]);
881 v.z = float(value[2]);
882 self.pdata.setVelocity(self.tag, v);
883 return;
884 if name == "image":
885 v = hoomd.int3();
886 v.x = int(value[0]);
887 v.y = int(value[1]);
888 v.z = int(value[2]);
889 self.pdata.setImage(self.tag, v);
890 return;
891 if name == "charge":
892 self.pdata.setCharge(self.tag, float(value));
893 return;
894 if name == "mass":
895 self.pdata.setMass(self.tag, float(value));
896 return;
897 if name == "diameter":
898 self.pdata.setDiameter(self.tag, value);
899 return;
900 if name == "body":
901 self.pdata.setBody(self.tag, value);
902 return;
903 if name == "type":
904 typeid = self.pdata.getTypeByName(value);
905 self.pdata.setType(self.tag, typeid);
906 return;
907 if name == "typeid":
908 raise AttributeError;
909 if name == "acceleration":
910 raise AttributeError;
911 if name == "orientation":
912 o = hoomd.Scalar4();
913 o.x = float(value[0]);
914 o.y = float(value[1]);
915 o.z = float(value[2]);
916 o.w = float(value[3]);
917 self.pdata.setOrientation(self.tag, o);
918 return;
919 if name == "net_force":
920 raise AttributeError;
921 if name == "net_energy":
922 raise AttributeError;
924 # otherwise, consider this an internal attribute to be set in the normal way
925 self.__dict__[name] = value;
927 ## \internal
928 # Access force data
930 # force_data provides access to the per-particle data of all forces in the system.
931 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
932 # force_data, documented by example.
934 class force_data:
935 ## \internal
936 # \brief force_data iterator
937 class force_data_iterator:
938 def __init__(self, data):
939 self.data = data;
940 self.index = 0;
941 def __iter__(self):
942 return self;
943 def __next__(self):
944 if self.index == len(self.data):
945 raise StopIteration;
947 result = self.data[self.index];
948 self.index += 1;
949 return result;
951 # support python2
952 next = __next__;
954 ## \internal
955 # \brief create a force_data
957 # \param pdata ParticleData to connect
958 def __init__(self, force):
959 self.force = force;
961 ## \var force
962 # \internal
963 # \brief ForceCompute to which this instance is connected
965 ## \internal
966 # \brief Get a force_proxy reference to the particle with tag \a tag
967 # \param tag Particle tag to access
968 def __getitem__(self, tag):
969 if tag >= len(self) or tag < 0:
970 raise IndexError;
971 return force_data_proxy(self.force, tag);
973 ## \internal
974 # \brief Set a particle's properties
975 # \param tag Particle tag to set
976 # \param p Value containing properties to set
977 def __setitem__(self, tag, p):
978 raise RuntimeError('__setitem__ not implemented');
980 ## \internal
981 # \brief Get the number of particles
982 def __len__(self):
983 return globals.system_definition.getParticleData().getNGlobal();
985 ## \internal
986 # \brief Get an informal string representing the object
987 def __str__(self):
988 result = "Force Data for %d particles" % (len(self));
989 return result
991 ## \internal
992 # \brief Return an interator
993 def __iter__(self):
994 return force_data.force_data_iterator(self);
996 ## Access the %force on a single particle via a proxy
998 # force_data_proxy provides access to the current %force, virial, and energy of a single particle due to a single
999 # %force computations.
1001 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
1002 # force_data_proxy, documented by example.
1004 # The following attributes are read only:
1005 # - \c %force : A 3-tuple of floats (x, y, z) listing the current %force on the particle
1006 # - \c virial : A float containing the contribution of this particle to the total virial
1007 # - \c energy : A float containing the contribution of this particle to the total potential energy
1008 # - \c torque : A 3-tuple of floats (x, y, z) listing the current torque on the particle
1010 class force_data_proxy:
1011 ## \internal
1012 # \brief create a force_data_proxy
1014 # \param force ForceCompute to which this proxy belongs
1015 # \param tag Tag of this particle in \a force
1016 def __init__(self, force, tag):
1017 self.fdata = force;
1018 self.tag = tag;
1020 ## \internal
1021 # \brief Get an informal string representing the object
1022 def __str__(self):
1023 result = "";
1024 result += "tag : " + str(self.tag) + "\n"
1025 result += "force : " + str(self.force) + "\n";
1026 result += "virial : " + str(self.virial) + "\n";
1027 result += "energy : " + str(self.energy) + "\n";
1028 result += "torque : " + str(self.torque) + "\n";
1029 return result;
1031 ## \internal
1032 # \brief Translate attribute accesses into the low level API function calls
1033 def __getattr__(self, name):
1034 if name == "force":
1035 f = self.fdata.cpp_force.getForce(self.tag);
1036 return (f.x, f.y, f.z);
1037 if name == "virial":
1038 return (self.fdata.cpp_force.getVirial(self.tag,0),
1039 self.fdata.cpp_force.getVirial(self.tag,1),
1040 self.fdata.cpp_force.getVirial(self.tag,2),
1041 self.fdata.cpp_force.getVirial(self.tag,3),
1042 self.fdata.cpp_force.getVirial(self.tag,4),
1043 self.fdata.cpp_force.getVirial(self.tag,5));
1044 if name == "energy":
1045 energy = self.fdata.cpp_force.getEnergy(self.tag);
1046 return energy;
1047 if name == "torque":
1048 f = self.fdata.cpp_force.getTorque(self.tag);
1049 return (f.x, f.y, f.z)
1051 # if we get here, we haven't found any names that match, post an error
1052 raise AttributeError;
1054 ## \internal
1055 # \brief Access bond data
1057 # bond_data provides access to the bonds in the system.
1058 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
1059 # bond_data, documented by example.
1061 class bond_data:
1062 ## \internal
1063 # \brief bond_data iterator
1064 class bond_data_iterator:
1065 def __init__(self, data):
1066 self.data = data;
1067 self.tag = 0;
1068 def __iter__(self):
1069 return self;
1070 def __next__(self):
1071 if self.tag == len(self.data):
1072 raise StopIteration;
1074 result = self.data[self.tag];
1075 self.tag += 1;
1076 return result;
1078 # support python2
1079 next = __next__;
1081 ## \internal
1082 # \brief create a bond_data
1084 # \param bdata BondData to connect
1085 def __init__(self, bdata):
1086 self.bdata = bdata;
1088 ## \internal
1089 # \brief Add a new bond
1090 # \param type Type name of the bond to add
1091 # \param a Tag of the first particle in the bond
1092 # \param b Tag of the second particle in the bond
1093 # \returns Unique tag identifying this bond
1094 def add(self, type, a, b):
1095 typeid = self.bdata.getTypeByName(type);
1096 return self.bdata.addBondedGroup(hoomd.Bond(typeid, a, b));
1098 ## \internal
1099 # \brief Remove a bond by tag
1100 # \param tag Unique tag of the bond to remove
1101 def remove(self, tag):
1102 self.bdata.removeBondedGroup(tag);
1104 ## \var bdata
1105 # \internal
1106 # \brief BondData to which this instance is connected
1108 ## \internal
1109 # \brief Get a bond_proxy reference to the bond with id \a id
1110 # \param id Bond id to access
1111 def __getitem__(self, id):
1112 if id >= len(self) or id < 0:
1113 raise IndexError;
1114 return bond_data_proxy(self.bdata, id);
1116 ## \internal
1117 # \brief Set a bond's properties
1118 # \param id Bond id to set
1119 # \param b Value containing properties to set
1120 def __setitem__(self, id, b):
1121 raise RuntimeError('Cannot change bonds once they are created');
1123 ## \internal
1124 # \brief Delete a bond by id
1125 # \param id Bond id to delete
1126 def __delitem__(self, id):
1127 if id >= len(self) or id < 0:
1128 raise IndexError;
1129 tag = self.bdata.getNthTag(id);
1130 self.bdata.removeBondedGroup(tag);
1132 ## \internal
1133 # \brief Get the number of bonds
1134 def __len__(self):
1135 return self.bdata.getNGlobal();
1137 ## \internal
1138 # \brief Get an informal string representing the object
1139 def __str__(self):
1140 result = "Bond Data for %d bonds of %d typeid(s)" % (self.bdata.getNGlobal(), self.bdata.getNBondTypes());
1141 return result
1143 ## \internal
1144 # \brief Return an interator
1145 def __iter__(self):
1146 return bond_data.bond_data_iterator(self);
1148 ## Access a single bond via a proxy
1150 # bond_data_proxy provides access to all of the properties of a single bond in the system.
1151 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
1152 # bond_data_proxy, documented by example.
1154 # The following attributes are read only:
1155 # - \c tag : A unique integer attached to each bond (not in any particular range). A bond's tag remans fixed
1156 # during its lifetime. (Tags previously used by removed bonds may be recycled).
1157 # - \c typeid : An integer indexing the bond type of the bond.
1158 # - \c a : An integer indexing the A particle in the bond. Particle tags run from 0 to N-1;
1159 # - \c b : An integer indexing the B particle in the bond. Particle tags run from 0 to N-1;
1160 # - \c type : A string naming the type
1162 # In the current version of the API, only already defined type names can be used. A future improvement will allow
1163 # dynamic creation of new type names from within the python API.
1164 # \MPI_SUPPORTED
1165 class bond_data_proxy:
1166 ## \internal
1167 # \brief create a bond_data_proxy
1169 # \param bdata BondData to which this proxy belongs
1170 # \param id index of this bond in \a bdata (at time of proxy creation)
1171 def __init__(self, bdata, id):
1172 self.bdata = bdata;
1173 self.tag = bdata.getNthTag(id)
1175 ## \internal
1176 # \brief Get an informal string representing the object
1177 def __str__(self):
1178 result = "";
1179 result += "typeid : " + str(self.typeid) + "\n";
1180 result += "a : " + str(self.a) + "\n"
1181 result += "b : " + str(self.b) + "\n"
1182 result += "type : " + str(self.type) + "\n";
1183 return result;
1185 ## \internal
1186 # \brief Translate attribute accesses into the low level API function calls
1187 def __getattr__(self, name):
1188 if name == "a":
1189 bond = self.bdata.getGroupByTag(self.tag);
1190 return bond.a;
1191 if name == "b":
1192 bond = self.bdata.getGroupByTag(self.tag);
1193 return bond.b;
1194 if name == "typeid":
1195 bond = self.bdata.getGroupByTag(self.tag);
1196 return bond.type;
1197 if name == "type":
1198 bond = self.bdata.getGroupByTag(self.tag);
1199 typeid = bond.type;
1200 return self.bdata.getNameByType(typeid);
1202 # if we get here, we haven't found any names that match, post an error
1203 raise AttributeError;
1205 ## \internal
1206 # \brief Translate attribute accesses into the low level API function calls
1207 def __setattr__(self, name, value):
1208 if name == "a":
1209 raise AttributeError;
1210 if name == "b":
1211 raise AttributeError;
1212 if name == "type":
1213 raise AttributeError;
1214 if name == "typeid":
1215 raise AttributeError;
1217 # otherwise, consider this an internal attribute to be set in the normal way
1218 self.__dict__[name] = value;
1220 ## \internal
1221 # \brief Access angle data
1223 # angle_data provides access to the angles in the system.
1224 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
1225 # angle_data, documented by example.
1227 class angle_data:
1228 ## \internal
1229 # \brief angle_data iterator
1230 class angle_data_iterator:
1231 def __init__(self, data):
1232 self.data = data;
1233 self.index = 0;
1234 def __iter__(self):
1235 return self;
1236 def __next__(self):
1237 if self.index == len(self.data):
1238 raise StopIteration;
1240 result = self.data[self.index];
1241 self.index += 1;
1242 return result;
1244 # support python2
1245 next = __next__;
1247 ## \internal
1248 # \brief create a angle_data
1250 # \param bdata AngleData to connect
1251 def __init__(self, adata):
1252 self.adata = adata;
1254 ## \internal
1255 # \brief Add a new angle
1256 # \param type Type name of the angle to add
1257 # \param a Tag of the first particle in the angle
1258 # \param b Tag of the second particle in the angle
1259 # \param c Tag of the thrid particle in the angle
1260 # \returns Unique tag identifying this bond
1261 def add(self, type, a, b, c):
1262 typeid = self.adata.getTypeByName(type);
1263 return self.adata.addBondedGroup(hoomd.Angle(typeid, a, b, c));
1265 ## \internal
1266 # \brief Remove an angle by tag
1267 # \param tag Unique tag of the angle to remove
1268 def remove(self, tag):
1269 self.adata.removeBondedGroup(tag);
1271 ## \var adata
1272 # \internal
1273 # \brief AngleData to which this instance is connected
1275 ## \internal
1276 # \brief Get anm angle_proxy reference to the bond with id \a id
1277 # \param id Angle id to access
1278 def __getitem__(self, id):
1279 if id >= len(self) or id < 0:
1280 raise IndexError;
1281 return angle_data_proxy(self.adata, id);
1283 ## \internal
1284 # \brief Set an angle's properties
1285 # \param id Angle id to set
1286 # \param b Value containing properties to set
1287 def __setitem__(self, id, b):
1288 raise RuntimeError('Cannot change angles once they are created');
1290 ## \internal
1291 # \brief Delete an angle by id
1292 # \param id Angle id to delete
1293 def __delitem__(self, id):
1294 if id >= len(self) or id < 0:
1295 raise IndexError;
1297 # Get the tag of the bond to delete
1298 tag = self.adata.getNthTag(id);
1299 self.adata.removeBondedGroup(tag);
1301 ## \internal
1302 # \brief Get the number of angles
1303 def __len__(self):
1304 return self.adata.getNGlobal();
1306 ## \internal
1307 # \brief Get an informal string representing the object
1308 def __str__(self):
1309 result = "Angle Data for %d angles of %d typeid(s)" % (self.adata.getNGlobal(), self.adata.getNTypes());
1310 return result;
1312 ## \internal
1313 # \brief Return an interator
1314 def __iter__(self):
1315 return angle_data.angle_data_iterator(self);
1317 ## Access a single angle via a proxy
1319 # angle_data_proxy provides access to all of the properties of a single angle in the system.
1320 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
1321 # angle_data_proxy, documented by example.
1323 # The following attributes are read only:
1324 # - \c tag : A unique integer attached to each angle (not in any particular range). A angle's tag remans fixed
1325 # during its lifetime. (Tags previously used by removed angles may be recycled).
1326 # - \c typeid : An integer indexing the angle's type.
1327 # - \c a : An integer indexing the A particle in the angle. Particle tags run from 0 to N-1;
1328 # - \c b : An integer indexing the B particle in the angle. Particle tags run from 0 to N-1;
1329 # - \c c : An integer indexing the C particle in the angle. Particle tags run from 0 to N-1;
1330 # - \c type : A string naming the type
1332 # In the current version of the API, only already defined type names can be used. A future improvement will allow
1333 # dynamic creation of new type names from within the python API.
1334 # \MPI_SUPPORTED
1335 class angle_data_proxy:
1336 ## \internal
1337 # \brief create a angle_data_proxy
1339 # \param adata AngleData to which this proxy belongs
1340 # \param id index of this angle in \a adata (at time of proxy creation)
1341 def __init__(self, adata, id):
1342 self.adata = adata;
1343 self.tag = self.adata.getNthTag(id);
1345 ## \internal
1346 # \brief Get an informal string representing the object
1347 def __str__(self):
1348 result = "";
1349 result += "tag : " + str(self.tag) + "\n";
1350 result += "typeid : " + str(self.typeid) + "\n";
1351 result += "a : " + str(self.a) + "\n"
1352 result += "b : " + str(self.b) + "\n"
1353 result += "c : " + str(self.c) + "\n"
1354 result += "type : " + str(self.type) + "\n";
1355 return result;
1357 ## \internal
1358 # \brief Translate attribute accesses into the low level API function calls
1359 def __getattr__(self, name):
1360 if name == "a":
1361 angle = self.adata.getGroupByTag(self.tag);
1362 return angle.a;
1363 if name == "b":
1364 angle = self.adata.getGroupByTag(self.tag);
1365 return angle.b;
1366 if name == "c":
1367 angle = self.adata.getGroupByTag(self.tag);
1368 return angle.c;
1369 if name == "typeid":
1370 angle = self.adata.getGroupByTag(self.tag);
1371 return angle.type;
1372 if name == "type":
1373 angle = self.adata.getGroupByTag(self.tag);
1374 typeid = angle.type;
1375 return self.adata.getNameByType(typeid);
1377 # if we get here, we haven't found any names that match, post an error
1378 raise AttributeError;
1380 ## \internal
1381 # \brief Translate attribute accesses into the low level API function calls
1382 def __setattr__(self, name, value):
1383 if name == "a":
1384 raise AttributeError;
1385 if name == "b":
1386 raise AttributeError;
1387 if name == "c":
1388 raise AttributeError;
1389 if name == "type":
1390 raise AttributeError;
1391 if name == "typeid":
1392 raise AttributeError;
1394 # otherwise, consider this an internal attribute to be set in the normal way
1395 self.__dict__[name] = value;
1397 ## \internal
1398 # \brief Access dihedral data
1400 # dihedral_data provides access to the dihedrals in the system.
1401 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
1402 # dihedral_data, documented by example.
1404 class dihedral_data:
1405 ## \internal
1406 # \brief dihedral_data iterator
1407 class dihedral_data_iterator:
1408 def __init__(self, data):
1409 self.data = data;
1410 self.index = 0;
1411 def __iter__(self):
1412 return self;
1413 def __next__(self):
1414 if self.index == len(self.data):
1415 raise StopIteration;
1417 result = self.data[self.index];
1418 self.index += 1;
1419 return result;
1421 # support python2
1422 next = __next__;
1424 ## \internal
1425 # \brief create a dihedral_data
1427 # \param bdata DihedralData to connect
1428 def __init__(self, ddata):
1429 self.ddata = ddata;
1431 ## \internal
1432 # \brief Add a new dihedral
1433 # \param type Type name of the dihedral to add
1434 # \param a Tag of the first particle in the dihedral
1435 # \param b Tag of the second particle in the dihedral
1436 # \param c Tag of the thrid particle in the dihedral
1437 # \param d Tag of the fourth particle in the dihedral
1438 # \returns Unique tag identifying this bond
1439 def add(self, type, a, b, c, d):
1440 typeid = self.ddata.getTypeByName(type);
1441 return self.ddata.addBondedGroup(hoomd.Dihedral(typeid, a, b, c, d));
1443 ## \internal
1444 # \brief Remove an dihedral by tag
1445 # \param tag Unique tag of the dihedral to remove
1446 def remove(self, tag):
1447 self.ddata.removeBondedGroup(tag);
1449 ## \var ddata
1450 # \internal
1451 # \brief DihedralData to which this instance is connected
1453 ## \internal
1454 # \brief Get anm dihedral_proxy reference to the dihedral with id \a id
1455 # \param id Dihedral id to access
1456 def __getitem__(self, id):
1457 if id >= len(self) or id < 0:
1458 raise IndexError;
1459 return dihedral_data_proxy(self.ddata, id);
1461 ## \internal
1462 # \brief Set an dihedral's properties
1463 # \param id dihedral id to set
1464 # \param b Value containing properties to set
1465 def __setitem__(self, id, b):
1466 raise RuntimeError('Cannot change angles once they are created');
1468 ## \internal
1469 # \brief Delete an dihedral by id
1470 # \param id Dihedral id to delete
1471 def __delitem__(self, id):
1472 if id >= len(self) or id < 0:
1473 raise IndexError;
1475 # Get the tag of the bond to delete
1476 tag = self.ddata.getNthTag(id);
1477 self.ddata.removeBondedGroup(tag);
1479 ## \internal
1480 # \brief Get the number of angles
1481 def __len__(self):
1482 return self.ddata.getNGlobal();
1484 ## \internal
1485 # \brief Get an informal string representing the object
1486 def __str__(self):
1487 result = "Dihedral Data for %d angles of %d typeid(s)" % (self.ddata.getNGlobal(), self.ddata.getNTypes());
1488 return result;
1490 ## \internal
1491 # \brief Return an interator
1492 def __iter__(self):
1493 return dihedral_data.dihedral_data_iterator(self);
1495 ## Access a single dihedral via a proxy
1497 # dihedral_data_proxy provides access to all of the properties of a single dihedral in the system.
1498 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
1499 # dihedral_data_proxy, documented by example.
1501 # The following attributes are read only:
1502 # - \c tag : A unique integer attached to each dihedral (not in any particular range). A dihedral's tag remans fixed
1503 # during its lifetime. (Tags previously used by removed dihedral may be recycled).
1504 # - \c typeid : An integer indexing the dihedral's type.
1505 # - \c a : An integer indexing the A particle in the angle. Particle tags run from 0 to N-1;
1506 # - \c b : An integer indexing the B particle in the angle. Particle tags run from 0 to N-1;
1507 # - \c c : An integer indexing the C particle in the angle. Particle tags run from 0 to N-1;
1508 # - \c d : An integer indexing the D particle in the dihedral. Particle tags run from 0 to N-1;
1509 # - \c type : A string naming the type
1511 # In the current version of the API, only already defined type names can be used. A future improvement will allow
1512 # dynamic creation of new type names from within the python API.
1513 # \MPI_SUPPORTED
1514 class dihedral_data_proxy:
1515 ## \internal
1516 # \brief create a dihedral_data_proxy
1518 # \param ddata DihedralData to which this proxy belongs
1519 # \param id index of this dihedral in \a ddata (at time of proxy creation)
1520 def __init__(self, ddata, id):
1521 self.ddata = ddata;
1522 self.tag = self.ddata.getNthTag(id);
1524 ## \internal
1525 # \brief Get an informal string representing the object
1526 def __str__(self):
1527 result = "";
1528 result += "tag : " + str(self.tag) + "\n";
1529 result += "typeid : " + str(self.typeid) + "\n";
1530 result += "a : " + str(self.a) + "\n"
1531 result += "b : " + str(self.b) + "\n"
1532 result += "c : " + str(self.c) + "\n"
1533 result += "d : " + str(self.d) + "\n"
1534 result += "type : " + str(self.type) + "\n";
1535 return result;
1537 ## \internal
1538 # \brief Translate attribute accesses into the low level API function calls
1539 def __getattr__(self, name):
1540 if name == "a":
1541 dihedral = self.ddata.getGroupByTag(self.tag);
1542 return dihedral.a;
1543 if name == "b":
1544 dihedral = self.ddata.getGroupByTag(self.tag);
1545 return dihedral.b;
1546 if name == "c":
1547 dihedral = self.ddata.getGroupByTag(self.tag);
1548 return dihedral.c;
1549 if name == "d":
1550 dihedral = self.ddata.getGroupByTag(self.tag);
1551 return dihedral.d;
1552 if name == "typeid":
1553 dihedral = self.ddata.getGroupByTag(self.tag);
1554 return dihedral.type;
1555 if name == "type":
1556 dihedral = self.ddata.getGroupByTag(self.tag);
1557 typeid = dihedral.type;
1558 return self.ddata.getNameByType(typeid);
1560 # if we get here, we haven't found any names that match, post an error
1561 raise AttributeError;
1563 ## \internal
1564 # \brief Translate attribute accesses into the low level API function calls
1565 def __setattr__(self, name, value):
1566 if name == "a":
1567 raise AttributeError;
1568 if name == "b":
1569 raise AttributeError;
1570 if name == "c":
1571 raise AttributeError;
1572 if name == "d":
1573 raise AttributeError;
1574 if name == "type":
1575 raise AttributeError;
1576 if name == "typeid":
1577 raise AttributeError;
1579 # otherwise, consider this an internal attribute to be set in the normal way
1580 self.__dict__[name] = value;
1582 ## \internal
1583 # \brief Access body data
1585 # body_data provides access to the per-body data of all bodies in the system.
1586 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
1587 # body_data, documented by example.
1589 class body_data:
1590 ## \internal
1591 # \brief bond_data iterator
1592 class body_data_iterator:
1593 def __init__(self, data):
1594 self.data = data;
1595 self.index = 0;
1596 def __iter__(self):
1597 return self;
1598 def __next__(self):
1599 if self.index == len(self.data):
1600 raise StopIteration;
1602 result = self.data[self.index];
1603 self.index += 1;
1604 return result;
1606 # support python2
1607 next = __next__;
1609 ## \internal
1610 # \brief create a body_data
1612 # \param bdata BodyData to connect
1613 def __init__(self, bdata):
1614 self.bdata = bdata;
1616 # \brief updates the v and x positions of a rigid body
1617 # \note the second arguement is dt, but the value should not matter as long as not zero
1618 def updateRV(self):
1619 self.bdata.setRV(True);
1621 ## \var bdata
1622 # \internal
1623 # \brief BodyData to which this instance is connected
1625 ## \internal
1626 # \brief Get a body_proxy reference to the body with body index \a tag
1627 # \param tag Body tag to access
1628 def __getitem__(self, tag):
1629 if tag >= len(self) or tag < 0:
1630 raise IndexError;
1631 return body_data_proxy(self.bdata, tag);
1633 ## \internal
1634 # \brief Set a body's properties
1635 # \param tag Body tag to set
1636 # \param p Value containing properties to set
1637 def __setitem__(self, tag, p):
1638 raise RuntimeError('__setitem__ not implemented');
1640 ## \internal
1641 # \brief Get the number of bodies
1642 def __len__(self):
1643 return self.bdata.getNumBodies();
1645 ## \internal
1646 # \brief Get an informal string representing the object
1647 def __str__(self):
1648 result = "Body Data for %d bodies" % (self.bdata.getNumBodies());
1649 return result
1651 ## \internal
1652 # \brief Return an interator
1653 def __iter__(self):
1654 return body_data.body_data_iterator(self);
1656 ## Access a single body via a proxy
1658 # body_data_proxy provides access to all of the properties of a single bond in the system.
1659 # This documentation is intentionally left sparse, see hoomd_script.data for a full explanation of how to use
1660 # body_data_proxy, documented by example.
1662 # The following attributes are read only:
1663 # - \c num_particles : The number of particles (or interaction sites) composing the body
1664 # - \c particle_tags : the tags of the particles (or interaction sites) composing the body
1665 # - \c net_force : Net force acting on the body (x, y, z) (in force units)
1666 # - \c net_torque : Net torque acting on the body (x, y, z) (in units of force * distance)
1668 # The following attributes can be both read and set
1669 # - \c mass : The mass of the body
1670 # - \c COM : The Center of Mass position of the body
1671 # - \c velocity : The velocity vector of the center of mass of the body
1672 # - \c orientation : The orientation of the body (quaternion)
1673 # - \c angular_momentum : The angular momentum of the body in the space frame
1674 # - \c moment_inertia : the principle components of the moment of inertia
1675 # - \c particle_disp : the displacements of the particles (or interaction sites) of the body relative to the COM in the body frame.
1676 # \MPI_NOT_SUPPORTED
1677 class body_data_proxy:
1678 ## \internal
1679 # \brief create a body_data_proxy
1681 # \param bdata RigidData to which this proxy belongs
1682 # \param tag tag of this body in \a bdata
1683 def __init__(self, bdata, tag):
1685 # Error out in MPI simulations
1686 if (hoomd.is_MPI_available()):
1687 if globals.system_definition.getParticleData().getDomainDecomposition():
1688 globals.msg.error("Rigid bodies are not supported in multi-processor simulations.\n\n")
1689 raise RuntimeError("Error accessing body data.")
1691 self.bdata = bdata;
1692 self.tag = tag;
1694 ## \internal
1695 # \brief Get an informal string representing the object
1696 def __str__(self):
1697 result = "";
1698 result += "num_particles : " + str(self.num_particles) + "\n"
1699 result += "mass : " + str(self.mass) + "\n"
1700 result += "COM : " + str(self.COM) + "\n"
1701 result += "velocity : " + str(self.velocity) + "\n"
1702 result += "orientation : " + str(self.orientation) + "\n"
1703 result += "angular_momentum (space frame) : " + str(self.angular_momentum) + "\n"
1704 result += "moment_inertia: " + str(self.moment_inertia) + "\n"
1705 result += "particle_tags : " + str(self.particle_tags) + "\n"
1706 result += "particle_disp : " + str(self.particle_disp) + "\n"
1707 result += "net_force : " + str(self.net_force) + "\n"
1708 result += "net_torque : " + str(self.net_torque) + "\n"
1710 return result;
1712 ## \internal
1713 # \brief Translate attribute accesses into the low level API function calls
1714 def __getattr__(self, name):
1715 if name == "COM":
1716 COM = self.bdata.getBodyCOM(self.tag);
1717 return (COM.x, COM.y, COM.z);
1718 if name == "velocity":
1719 velocity = self.bdata.getBodyVel(self.tag);
1720 return (velocity.x, velocity.y, velocity.z);
1721 if name == "orientation":
1722 orientation = self.bdata.getBodyOrientation(self.tag);
1723 return (orientation.x, orientation.y, orientation.z, orientation.w);
1724 if name == "angular_momentum":
1725 angular_momentum = self.bdata.getBodyAngMom(self.tag);
1726 return (angular_momentum.x, angular_momentum.y, angular_momentum.z);
1727 if name == "num_particles":
1728 num_particles = self.bdata.getBodyNSize(self.tag);
1729 return num_particles;
1730 if name == "mass":
1731 mass = self.bdata.getMass(self.tag);
1732 return mass;
1733 if name == "moment_inertia":
1734 moment_inertia = self.bdata.getBodyMomInertia(self.tag);
1735 return (moment_inertia.x, moment_inertia.y, moment_inertia.z);
1736 if name == "particle_tags":
1737 particle_tags = [];
1738 for i in range(0, self.num_particles):
1739 particle_tags.append(self.bdata.getParticleTag(self.tag, i));
1740 return particle_tags;
1741 if name == "particle_disp":
1742 particle_disp = [];
1743 for i in range(0, self.num_particles):
1744 disp = self.bdata.getParticleDisp(self.tag, i);
1745 particle_disp.append([disp.x, disp.y, disp.z]);
1746 return particle_disp;
1747 if name == "net_force":
1748 f = self.bdata.getBodyNetForce(self.tag);
1749 return (f.x, f.y, f.z);
1750 if name == "net_torque":
1751 t = self.bdata.getBodyNetTorque(self.tag);
1752 return (t.x, t.y, t.z);
1754 # if we get here, we haven't found any names that match, post an error
1755 raise AttributeError;
1757 ## \internal
1758 # \brief Translate attribute accesses into the low level API function calls
1759 def __setattr__(self, name, value):
1760 if name == "COM":
1761 p = hoomd.Scalar3();
1762 p.x = float(value[0]);
1763 p.y = float(value[1]);
1764 p.z = float(value[2]);
1765 self.bdata.setBodyCOM(self.tag, p);
1766 return;
1767 if name == "velocity":
1768 v = hoomd.Scalar3();
1769 v.x = float(value[0]);
1770 v.y = float(value[1]);
1771 v.z = float(value[2]);
1772 self.bdata.setBodyVel(self.tag, v);
1773 return;
1774 if name == "mass":
1775 self.bdata.setMass(self.tag, value);
1776 return;
1777 if name == "orientation":
1778 q = hoomd.Scalar4();
1779 q.x = float(value[0]);
1780 q.y = float(value[1]);
1781 q.z = float(value[2]);
1782 q.w = float(value[3]);
1783 self.bdata.setBodyOrientation(self.tag, q);
1784 return;
1785 if name == "angular_momentum":
1786 p = hoomd.Scalar3();
1787 p.x = float(value[0]);
1788 p.y = float(value[1]);
1789 p.z = float(value[2]);
1790 self.bdata.setAngMom(self.tag, p);
1791 return;
1792 if name == "moment_inertia":
1793 p = hoomd.Scalar3();
1794 p.x = float(value[0]);
1795 p.y = float(value[1]);
1796 p.z = float(value[2]);
1797 self.bdata.setBodyMomInertia(self.tag, p);
1798 return;
1799 if name == "particle_disp":
1800 p = hoomd.Scalar3();
1801 for i in range(0, self.num_particles):
1802 p.x = float(value[i][0]);
1803 p.y = float(value[i][1]);
1804 p.z = float(value[i][2]);
1805 self.bdata.setParticleDisp(self.tag, i, p);
1806 return;
1807 if name == "net_force":
1808 raise AttributeError;
1809 if name == "net_torque":
1810 raise AttributeError;
1812 # otherwise, consider this an internal attribute to be set in the normal way
1813 self.__dict__[name] = value;