2 # Highly Optimized Object-oriented Many-particle Dynamics -- Blue Edition
3 # (HOOMD-blue) Open Source Software License Copyright 2009-2014 The Regents of
4 # the University of Michigan All rights reserved.
6 # HOOMD-blue may contain modifications ("Contributions") provided, and to which
7 # copyright is held, by various Contributors who have granted The Regents of the
8 # University of Michigan the right to modify and/or distribute such Contributions.
10 # You may redistribute, use, and create derivate works of HOOMD-blue, in source
11 # and binary forms, provided you abide by the following conditions:
13 # * Redistributions of source code must retain the above copyright notice, this
14 # list of conditions, and the following disclaimer both in the code and
15 # prominently in any materials provided with the distribution.
17 # * Redistributions in binary form must reproduce the above copyright notice, this
18 # list of conditions, and the following disclaimer in the documentation and/or
19 # other materials provided with the distribution.
21 # * All publications and presentations based on HOOMD-blue, including any reports
22 # or published results obtained, in whole or in part, with HOOMD-blue, will
23 # acknowledge its use according to the terms posted at the time of submission on:
24 # http://codeblue.umich.edu/hoomd-blue/citations.html
26 # * Any electronic documents citing HOOMD-Blue will link to the HOOMD-Blue website:
27 # http://codeblue.umich.edu/hoomd-blue/
29 # * Apart from the above required attributions, neither the name of the copyright
30 # holder nor the names of HOOMD-blue's contributors may be used to endorse or
31 # promote products derived from this software without specific prior written
36 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' AND
37 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
38 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND/OR ANY
39 # WARRANTIES THAT THIS SOFTWARE IS FREE OF INFRINGEMENT ARE DISCLAIMED.
41 # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
42 # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
43 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
44 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
45 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
46 # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
47 # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50 # Maintainer: joaander
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:
79 # system = init.read_xml(filename="input.xml")
83 # <h3>Getting/setting the box</h3>
84 # You can access the dimensions of the simulation box like so:
86 # >>> print system.box
87 # Box: Lx=17.3646569289 Ly=17.3646569289 Lz=17.3646569289 xy=0.0 xz=0.0 yz=0.0
89 # and can change it like so:
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
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.
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
106 # >>> len(system.particles)
109 # - A short summary can be printed of the list
111 # >>> print system.particles
112 # Particle Data for 64000 particles of 1 type(s)
114 # - The list of all particle types in the simulation can be accessed
116 # >>> print system.particles.types
119 # - Individual particles can be accessed at random.
122 # >>> p = system.particles[i]
124 # - Various properties can be accessed of any particle
129 # (27.296911239624023, -3.5986068248748779, 10.364067077636719)
131 # (-0.60267972946166992, 2.6205904483795166, -1.7868227958679199)
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:
142 # >>> p.position = (1,2,3)
146 # - Finally, all particles can be easily looped over
148 # for p in system.particles:
149 # p.velocity = (0,0,0)
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.
162 # groupA = group.type(name="a-particles", type='A')
164 # p.velocity = (0,0,0)
168 # <h3>Rigid Body Data</h3>
169 # Rigid Body data can be accessed via the body_data_proxy. Here are examples
173 # >>> b = system.bodies[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]]
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]]
192 # Bonds may be added at any time in the job script.
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)
200 # Individual bonds may be accessed by index.
202 # >>> bnd = system.bonds[0]
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.
220 # >>> del system.bonds[0]
221 # >>> print system.bonds[0]
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.
235 # for b in system.bonds:
236 # if b.a == 2 or b.b == 2:
239 # Then remove each of the bonds by their unique tag.
242 # system.bonds.remove(t)
246 # <h3>Angle, Dihedral, and Improper Data</h3>
247 # Angles, Dihedrals, and Impropers may be added at any time in the job script.
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)
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).
260 # Forces can be accessed in a similar way.
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]
266 # force : (-0.077489577233791351, -0.029512746259570122, -0.13215918838977814)
267 # virial : -0.0931386947632
268 # energy : -0.0469368174672
269 # >>> f0 = lj.forces[0]
271 # (-0.077489577233791351, -0.029512746259570122, -0.13215918838977814)
272 # >>> print f0.virial
274 # >>> print f0.energy
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.
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
287 # p = system.particles[i]
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.
296 # (-21.317455291748047, -23.883811950683594, -22.159387588500977)
301 # (-19.774742126464844, -23.564577102661133, -21.418502807617188)
303 # - Second, it means that copies of the proxy reference cannot be changed independently.
308 # (-19.774742126464844, -23.564577102661133, -21.418502807617188)
309 # >>> p.position = (0,0,0)
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
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:
331 # snapshot = system.take_snapshot(all=True)
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.
343 # b = data.boxdim(L=20);
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.
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):
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)
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);
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
);
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.
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
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.
482 # snapshot = system.take_snapshot()
483 # snapshot = system.take_snapshot(particles=true)
484 # snapshot = system.take_snapshot(bonds=true)
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();
492 if particles
is None:
498 if dihedrals
is None:
500 if impropers
is None:
502 if rigid_bodies
is None:
506 if integrators
is None:
509 if particles
is None and not all
:
511 if bonds
is None and not all
:
513 if angles
is None and not all
:
515 if dihedrals
is None and not all
:
517 if impropers
is None and not all
:
519 if rigid_bodies
is None and not all
:
521 if walls
is None and not all
:
523 if integrators
is None and not all
:
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")
531 cpp_snapshot
= self
.sysdef
.takeSnapshot(particles
,bonds
,angles
,dihedrals
,impropers
,rigid_bodies
,walls
,integrators
)
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
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.
557 def replicate(self
, nx
=1, ny
=1, nz
=1):
558 util
.print_status_line()
564 if nx
== ny
== nz
== 1:
565 globals.msg
.warning("All replication factors == 1. Not replicating system.\n")
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")
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:
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:
601 # system = init.read_xml("some_file.xml")
603 # ... run a simulation ...
605 # snapshot = system.take_snapshot(all=True)
607 # system.restore_snapshot(snapshot)
610 # \sa hoomd_script.data
612 def restore_snapshot(self
, snapshot
):
613 util
.print_status_line();
615 self
.sysdef
.initializeFromSnapshot(snapshot
);
619 # \brief SystemDefinition to which this instance is connected
622 # \brief Translate attribute accesses into the low level API function calls
623 def __setattr__(self
, name
, value
):
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
;
633 # \brief Translate attribute accesses into the low level API function calls
634 def __getattr__(self
, name
):
636 b
= self
.sysdef
.getParticleData().getGlobalBox();
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;
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.
652 # \brief particle_data iterator
653 class particle_data_iterator
:
654 def __init__(self
, data
):
660 if self
.index
== len(self
.data
):
663 result
= self
.data
[self
.index
];
671 # \brief create a particle_data
673 # \param pdata ParticleData to connect
674 def __init__(self
, pdata
):
677 ntypes
= globals.system_definition
.getParticleData().getNTypes();
679 for i
in range(0,ntypes
):
680 self
.types
.append(globals.system_definition
.getParticleData().getNameByType(i
));
684 # \brief ParticleData to which this instance is connected
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:
692 return particle_data_proxy(self
.pdata
, tag
);
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');
702 # \brief Get the number of particles
704 return self
.pdata
.getNGlobal();
707 # \brief Get an informal string representing the object
709 result
= "Particle Data for %d particles of %d type(s)" % (self
.pdata
.getNGlobal(), self
.pdata
.getNTypes());
713 # \brief Return an interator
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
:
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
):
756 # \brief Get an informal string representing the object
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";
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
);
789 image
= self
.pdata
.getImage(self
.tag
);
790 return (image
.x
, image
.y
, image
.z
);
792 return self
.pdata
.getCharge(self
.tag
);
794 return self
.pdata
.getMass(self
.tag
);
795 if name
== "diameter":
796 return self
.pdata
.getDiameter(self
.tag
);
798 return self
.pdata
.getType(self
.tag
);
800 return self
.pdata
.getBody(self
.tag
);
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
);
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;
821 # \brief Translate attribute accesses into the low level API function calls
822 def __setattr__(self
, name
, value
):
823 if name
== "position":
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);
830 if name
== "velocity":
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
);
842 self
.pdata
.setImage(self
.tag
, v
);
845 self
.pdata
.setCharge(self
.tag
, float(value
));
848 self
.pdata
.setMass(self
.tag
, float(value
));
850 if name
== "diameter":
851 self
.pdata
.setDiameter(self
.tag
, value
);
854 self
.pdata
.setBody(self
.tag
, value
);
857 typeid
= self
.pdata
.getTypeByName(value
);
858 self
.pdata
.setType(self
.tag
, typeid
);
861 raise AttributeError;
862 if name
== "acceleration":
863 raise AttributeError;
864 if name
== "orientation":
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
);
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
;
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.
889 # \brief force_data iterator
890 class force_data_iterator
:
891 def __init__(self
, data
):
897 if self
.index
== len(self
.data
):
900 result
= self
.data
[self
.index
];
908 # \brief create a force_data
910 # \param pdata ParticleData to connect
911 def __init__(self
, force
):
916 # \brief ForceCompute to which this instance is connected
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:
924 return force_data_proxy(self
.force
, tag
);
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');
934 # \brief Get the number of particles
936 return globals.system_definition
.getParticleData().getNGlobal();
939 # \brief Get an informal string representing the object
941 result
= "Force Data for %d particles" % (len(self
));
945 # \brief Return an interator
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
:
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
):
974 # \brief Get an informal string representing the object
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";
985 # \brief Translate attribute accesses into the low level API function calls
986 def __getattr__(self
, name
):
988 f
= self
.fdata
.cpp_force
.getForce(self
.tag
);
989 return (f
.x
, f
.y
, f
.z
);
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));
998 energy
= self
.fdata
.cpp_force
.getEnergy(self
.tag
);
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;
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.
1016 # \brief bond_data iterator
1017 class bond_data_iterator
:
1018 def __init__(self
, data
):
1024 if self
.tag
== len(self
.data
):
1025 raise StopIteration;
1027 result
= self
.data
[self
.tag
];
1035 # \brief create a bond_data
1037 # \param bdata BondData to connect
1038 def __init__(self
, bdata
):
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
));
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
);
1059 # \brief BondData to which this instance is connected
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:
1067 return bond_data_proxy(self
.bdata
, tag
);
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');
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:
1082 tag
= self
.bdata
.getNthTag(id);
1083 self
.bdata
.removeBond(tag
);
1086 # \brief Get the number of bonds
1088 return self
.bdata
.getNGlobal();
1091 # \brief Get an informal string representing the object
1093 result
= "Bond Data for %d bonds of %d typeid(s)" % (self
.bdata
.getNGlobal(), self
.bdata
.getNBondTypes());
1097 # \brief Return an interator
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.
1118 class bond_data_proxy
:
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
):
1129 # \brief Get an informal string representing the object
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";
1139 # \brief Translate attribute accesses into the low level API function calls
1140 def __getattr__(self
, name
):
1142 bond
= self
.bdata
.getGroupByTag(self
.tag
);
1145 bond
= self
.bdata
.getGroupByTag(self
.tag
);
1147 if name
== "typeid":
1148 bond
= self
.bdata
.getGroupByTag(self
.tag
);
1151 bond
= self
.bdata
.getGroupByTag(self
.tag
);
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;
1159 # \brief Translate attribute accesses into the low level API function calls
1160 def __setattr__(self
, name
, value
):
1162 raise AttributeError;
1164 raise AttributeError;
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
;
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.
1182 # \brief angle_data iterator
1183 class angle_data_iterator
:
1184 def __init__(self
, data
):
1190 if self
.index
== len(self
.data
):
1191 raise StopIteration;
1193 result
= self
.data
[self
.index
];
1201 # \brief create a angle_data
1203 # \param bdata AngleData to connect
1204 def __init__(self
, adata
):
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
));
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
);
1226 # \brief AngleData to which this instance is connected
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:
1234 return angle_data_proxy(self
.adata
, id);
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');
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:
1250 # Get the tag of the bond to delete
1251 tag
= self
.adata
.getNthTag(id);
1252 self
.adata
.removeBondedGroup(tag
);
1255 # \brief Get the number of angles
1257 return self
.adata
.getNGlobal();
1260 # \brief Get an informal string representing the object
1262 result
= "Angle Data for %d angles of %d typeid(s)" % (self
.adata
.getNGlobal(), self
.adata
.getNTypes());
1266 # \brief Return an interator
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.
1288 class angle_data_proxy
:
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):
1296 self
.tag
= self
.adata
.getNthTag(id);
1299 # \brief Get an informal string representing the object
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";
1311 # \brief Translate attribute accesses into the low level API function calls
1312 def __getattr__(self
, name
):
1314 angle
= self
.adata
.getGroupByTag(self
.tag
);
1317 angle
= self
.adata
.getGroupByTag(self
.tag
);
1320 angle
= self
.adata
.getGroupByTag(self
.tag
);
1322 if name
== "typeid":
1323 angle
= self
.adata
.getGroupByTag(self
.tag
);
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;
1334 # \brief Translate attribute accesses into the low level API function calls
1335 def __setattr__(self
, name
, value
):
1337 raise AttributeError;
1339 raise AttributeError;
1341 raise AttributeError;
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
;
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
:
1359 # \brief dihedral_data iterator
1360 class dihedral_data_iterator
:
1361 def __init__(self
, data
):
1367 if self
.index
== len(self
.data
):
1368 raise StopIteration;
1370 result
= self
.data
[self
.index
];
1378 # \brief create a dihedral_data
1380 # \param bdata DihedralData to connect
1381 def __init__(self
, ddata
):
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
));
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
);
1404 # \brief DihedralData to which this instance is connected
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:
1412 return dihedral_data_proxy(self
.ddata
, id);
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');
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:
1428 # Get the tag of the bond to delete
1429 tag
= self
.ddata
.getNthTag(id);
1430 self
.ddata
.removeBondedGroup(tag
);
1433 # \brief Get the number of angles
1435 return self
.ddata
.getNGlobal();
1438 # \brief Get an informal string representing the object
1440 result
= "Dihedral Data for %d angles of %d typeid(s)" % (self
.ddata
.getNGlobal(), self
.ddata
.getNTypes());
1444 # \brief Return an interator
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
:
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):
1475 self
.tag
= self
.ddata
.getNthTag(id);
1478 # \brief Get an informal string representing the object
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";
1491 # \brief Translate attribute accesses into the low level API function calls
1492 def __getattr__(self
, name
):
1494 dihedral
= self
.ddata
.getGroupByTag(self
.tag
);
1497 dihedral
= self
.ddata
.getGroupByTag(self
.tag
);
1500 dihedral
= self
.ddata
.getGroupByTag(self
.tag
);
1503 dihedral
= self
.ddata
.getGroupByTag(self
.tag
);
1505 if name
== "typeid":
1506 dihedral
= self
.ddata
.getGroupByTag(self
.tag
);
1507 return dihedral
.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;
1517 # \brief Translate attribute accesses into the low level API function calls
1518 def __setattr__(self
, name
, value
):
1520 raise AttributeError;
1522 raise AttributeError;
1524 raise AttributeError;
1526 raise AttributeError;
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
;
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.
1544 # \brief bond_data iterator
1545 class body_data_iterator
:
1546 def __init__(self
, data
):
1552 if self
.index
== len(self
.data
):
1553 raise StopIteration;
1555 result
= self
.data
[self
.index
];
1563 # \brief create a body_data
1565 # \param bdata BodyData to connect
1566 def __init__(self
, 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
1572 self
.bdata
.setRV(True);
1576 # \brief BodyData to which this instance is connected
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:
1584 return body_data_proxy(self
.bdata
, tag
);
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');
1594 # \brief Get the number of bodies
1596 return self
.bdata
.getNumBodies();
1599 # \brief Get an informal string representing the object
1601 result
= "Body Data for %d bodies" % (self
.bdata
.getNumBodies());
1605 # \brief Return an interator
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
:
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.")
1648 # \brief Get an informal string representing the object
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"
1666 # \brief Translate attribute accesses into the low level API function calls
1667 def __getattr__(self
, name
):
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
;
1684 mass
= self
.bdata
.getMass(self
.tag
);
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":
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":
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;
1711 # \brief Translate attribute accesses into the low level API function calls
1712 def __setattr__(self
, name
, value
):
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
);
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
);
1728 self
.bdata
.setMass(self
.tag
, value
);
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
);
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
);
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
);
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
);
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
;