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_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:
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_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:
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 sysdef variable (`system.box = new_box`) to set the simulation
342 # boxdim parameters may be accessed directly.
344 # b = data.boxdim(L=20);
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
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)`
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):
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)
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
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
)
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
);
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.
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
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.
523 # snapshot = system.take_snapshot()
524 # snapshot = system.take_snapshot(particles=true)
525 # snapshot = system.take_snapshot(bonds=true)
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();
533 if particles
is None:
539 if dihedrals
is None:
541 if impropers
is None:
543 if rigid_bodies
is None:
547 if integrators
is None:
550 if particles
is None and not all
:
552 if bonds
is None and not all
:
554 if angles
is None and not all
:
556 if dihedrals
is None and not all
:
558 if impropers
is None and not all
:
560 if rigid_bodies
is None and not all
:
562 if walls
is None and not all
:
564 if integrators
is None and not all
:
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")
572 cpp_snapshot
= self
.sysdef
.takeSnapshot(particles
,bonds
,angles
,dihedrals
,impropers
,rigid_bodies
,walls
,integrators
)
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
592 # system = init.read_xml("some_file.xml")
593 # system.replicate(nx=2,ny=2,nz=2)
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.
604 def replicate(self
, nx
=1, ny
=1, nz
=1):
605 util
.print_status_line()
611 if nx
== ny
== nz
== 1:
612 globals.msg
.warning("All replication factors == 1. Not replicating system.\n")
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")
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:
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:
648 # system = init.read_xml("some_file.xml")
650 # ... run a simulation ...
652 # snapshot = system.take_snapshot(all=True)
654 # system.restore_snapshot(snapshot)
657 # \sa hoomd_script.data
659 def restore_snapshot(self
, snapshot
):
660 util
.print_status_line();
662 self
.sysdef
.initializeFromSnapshot(snapshot
);
666 # \brief SystemDefinition to which this instance is connected
669 # \brief Translate attribute accesses into the low level API function calls
670 def __setattr__(self
, name
, value
):
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
;
680 # \brief Translate attribute accesses into the low level API function calls
681 def __getattr__(self
, name
):
683 b
= self
.sysdef
.getParticleData().getGlobalBox();
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;
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.
699 # \brief particle_data iterator
700 class particle_data_iterator
:
701 def __init__(self
, data
):
707 if self
.index
== len(self
.data
):
710 result
= self
.data
[self
.index
];
718 # \brief create a particle_data
720 # \param pdata ParticleData to connect
721 def __init__(self
, pdata
):
724 ntypes
= globals.system_definition
.getParticleData().getNTypes();
726 for i
in range(0,ntypes
):
727 self
.types
.append(globals.system_definition
.getParticleData().getNameByType(i
));
731 # \brief ParticleData to which this instance is connected
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:
739 return particle_data_proxy(self
.pdata
, tag
);
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');
749 # \brief Get the number of particles
751 return self
.pdata
.getNGlobal();
754 # \brief Get an informal string representing the object
756 result
= "Particle Data for %d particles of %d type(s)" % (self
.pdata
.getNGlobal(), self
.pdata
.getNTypes());
760 # \brief Return an interator
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
:
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
):
803 # \brief Get an informal string representing the object
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";
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
);
836 image
= self
.pdata
.getImage(self
.tag
);
837 return (image
.x
, image
.y
, image
.z
);
839 return self
.pdata
.getCharge(self
.tag
);
841 return self
.pdata
.getMass(self
.tag
);
842 if name
== "diameter":
843 return self
.pdata
.getDiameter(self
.tag
);
845 return self
.pdata
.getType(self
.tag
);
847 return self
.pdata
.getBody(self
.tag
);
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
);
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;
868 # \brief Translate attribute accesses into the low level API function calls
869 def __setattr__(self
, name
, value
):
870 if name
== "position":
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);
877 if name
== "velocity":
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
);
889 self
.pdata
.setImage(self
.tag
, v
);
892 self
.pdata
.setCharge(self
.tag
, float(value
));
895 self
.pdata
.setMass(self
.tag
, float(value
));
897 if name
== "diameter":
898 self
.pdata
.setDiameter(self
.tag
, value
);
901 self
.pdata
.setBody(self
.tag
, value
);
904 typeid
= self
.pdata
.getTypeByName(value
);
905 self
.pdata
.setType(self
.tag
, typeid
);
908 raise AttributeError;
909 if name
== "acceleration":
910 raise AttributeError;
911 if name
== "orientation":
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
);
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
;
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.
936 # \brief force_data iterator
937 class force_data_iterator
:
938 def __init__(self
, data
):
944 if self
.index
== len(self
.data
):
947 result
= self
.data
[self
.index
];
955 # \brief create a force_data
957 # \param pdata ParticleData to connect
958 def __init__(self
, force
):
963 # \brief ForceCompute to which this instance is connected
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:
971 return force_data_proxy(self
.force
, tag
);
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');
981 # \brief Get the number of particles
983 return globals.system_definition
.getParticleData().getNGlobal();
986 # \brief Get an informal string representing the object
988 result
= "Force Data for %d particles" % (len(self
));
992 # \brief Return an interator
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
:
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
):
1021 # \brief Get an informal string representing the object
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";
1032 # \brief Translate attribute accesses into the low level API function calls
1033 def __getattr__(self
, name
):
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
);
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;
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.
1063 # \brief bond_data iterator
1064 class bond_data_iterator
:
1065 def __init__(self
, data
):
1071 if self
.tag
== len(self
.data
):
1072 raise StopIteration;
1074 result
= self
.data
[self
.tag
];
1082 # \brief create a bond_data
1084 # \param bdata BondData to connect
1085 def __init__(self
, bdata
):
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
));
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
);
1106 # \brief BondData to which this instance is connected
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:
1114 return bond_data_proxy(self
.bdata
, id);
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');
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:
1129 tag
= self
.bdata
.getNthTag(id);
1130 self
.bdata
.removeBondedGroup(tag
);
1133 # \brief Get the number of bonds
1135 return self
.bdata
.getNGlobal();
1138 # \brief Get an informal string representing the object
1140 result
= "Bond Data for %d bonds of %d typeid(s)" % (self
.bdata
.getNGlobal(), self
.bdata
.getNBondTypes());
1144 # \brief Return an interator
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.
1165 class bond_data_proxy
:
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):
1173 self
.tag
= bdata
.getNthTag(id)
1176 # \brief Get an informal string representing the object
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";
1186 # \brief Translate attribute accesses into the low level API function calls
1187 def __getattr__(self
, name
):
1189 bond
= self
.bdata
.getGroupByTag(self
.tag
);
1192 bond
= self
.bdata
.getGroupByTag(self
.tag
);
1194 if name
== "typeid":
1195 bond
= self
.bdata
.getGroupByTag(self
.tag
);
1198 bond
= self
.bdata
.getGroupByTag(self
.tag
);
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;
1206 # \brief Translate attribute accesses into the low level API function calls
1207 def __setattr__(self
, name
, value
):
1209 raise AttributeError;
1211 raise AttributeError;
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
;
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.
1229 # \brief angle_data iterator
1230 class angle_data_iterator
:
1231 def __init__(self
, data
):
1237 if self
.index
== len(self
.data
):
1238 raise StopIteration;
1240 result
= self
.data
[self
.index
];
1248 # \brief create a angle_data
1250 # \param bdata AngleData to connect
1251 def __init__(self
, adata
):
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
));
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
);
1273 # \brief AngleData to which this instance is connected
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:
1281 return angle_data_proxy(self
.adata
, id);
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');
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:
1297 # Get the tag of the bond to delete
1298 tag
= self
.adata
.getNthTag(id);
1299 self
.adata
.removeBondedGroup(tag
);
1302 # \brief Get the number of angles
1304 return self
.adata
.getNGlobal();
1307 # \brief Get an informal string representing the object
1309 result
= "Angle Data for %d angles of %d typeid(s)" % (self
.adata
.getNGlobal(), self
.adata
.getNTypes());
1313 # \brief Return an interator
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.
1335 class angle_data_proxy
:
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):
1343 self
.tag
= self
.adata
.getNthTag(id);
1346 # \brief Get an informal string representing the object
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";
1358 # \brief Translate attribute accesses into the low level API function calls
1359 def __getattr__(self
, name
):
1361 angle
= self
.adata
.getGroupByTag(self
.tag
);
1364 angle
= self
.adata
.getGroupByTag(self
.tag
);
1367 angle
= self
.adata
.getGroupByTag(self
.tag
);
1369 if name
== "typeid":
1370 angle
= self
.adata
.getGroupByTag(self
.tag
);
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;
1381 # \brief Translate attribute accesses into the low level API function calls
1382 def __setattr__(self
, name
, value
):
1384 raise AttributeError;
1386 raise AttributeError;
1388 raise AttributeError;
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
;
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
:
1406 # \brief dihedral_data iterator
1407 class dihedral_data_iterator
:
1408 def __init__(self
, data
):
1414 if self
.index
== len(self
.data
):
1415 raise StopIteration;
1417 result
= self
.data
[self
.index
];
1425 # \brief create a dihedral_data
1427 # \param bdata DihedralData to connect
1428 def __init__(self
, ddata
):
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
));
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
);
1451 # \brief DihedralData to which this instance is connected
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:
1459 return dihedral_data_proxy(self
.ddata
, id);
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');
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:
1475 # Get the tag of the bond to delete
1476 tag
= self
.ddata
.getNthTag(id);
1477 self
.ddata
.removeBondedGroup(tag
);
1480 # \brief Get the number of angles
1482 return self
.ddata
.getNGlobal();
1485 # \brief Get an informal string representing the object
1487 result
= "Dihedral Data for %d angles of %d typeid(s)" % (self
.ddata
.getNGlobal(), self
.ddata
.getNTypes());
1491 # \brief Return an interator
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.
1514 class dihedral_data_proxy
:
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):
1522 self
.tag
= self
.ddata
.getNthTag(id);
1525 # \brief Get an informal string representing the object
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";
1538 # \brief Translate attribute accesses into the low level API function calls
1539 def __getattr__(self
, name
):
1541 dihedral
= self
.ddata
.getGroupByTag(self
.tag
);
1544 dihedral
= self
.ddata
.getGroupByTag(self
.tag
);
1547 dihedral
= self
.ddata
.getGroupByTag(self
.tag
);
1550 dihedral
= self
.ddata
.getGroupByTag(self
.tag
);
1552 if name
== "typeid":
1553 dihedral
= self
.ddata
.getGroupByTag(self
.tag
);
1554 return dihedral
.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;
1564 # \brief Translate attribute accesses into the low level API function calls
1565 def __setattr__(self
, name
, value
):
1567 raise AttributeError;
1569 raise AttributeError;
1571 raise AttributeError;
1573 raise AttributeError;
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
;
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.
1591 # \brief bond_data iterator
1592 class body_data_iterator
:
1593 def __init__(self
, data
):
1599 if self
.index
== len(self
.data
):
1600 raise StopIteration;
1602 result
= self
.data
[self
.index
];
1610 # \brief create a body_data
1612 # \param bdata BodyData to connect
1613 def __init__(self
, 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
1619 self
.bdata
.setRV(True);
1623 # \brief BodyData to which this instance is connected
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:
1631 return body_data_proxy(self
.bdata
, tag
);
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');
1641 # \brief Get the number of bodies
1643 return self
.bdata
.getNumBodies();
1646 # \brief Get an informal string representing the object
1648 result
= "Body Data for %d bodies" % (self
.bdata
.getNumBodies());
1652 # \brief Return an interator
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
:
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.")
1695 # \brief Get an informal string representing the object
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"
1713 # \brief Translate attribute accesses into the low level API function calls
1714 def __getattr__(self
, name
):
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
;
1731 mass
= self
.bdata
.getMass(self
.tag
);
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":
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":
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;
1758 # \brief Translate attribute accesses into the low level API function calls
1759 def __setattr__(self
, name
, value
):
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
);
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
);
1775 self
.bdata
.setMass(self
.tag
, value
);
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
);
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
);
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
);
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
);
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
;