def make_snapshot(particle_types=['A'], a=1, n=7, r=0): """Make a snapshot with particles in a fcc structure. Args: particle_types: List of particle type names a: Lattice constant n: Number of unit cells along each box edge r: Amount to randomly perturb particles in x,y,z Place particles in a fcc structure. The box is cubic with a side length of ``n * a``. There will be ``4 * n**3`` particles in the snapshot. """ s = Snapshot(device.communicator) if s.communicator.rank == 0: # make one unit cell s.configuration.box = [a, a, a, 0, 0, 0] s.particles.N = 4 s.particles.types = particle_types s.particles.position[:] = [ [0, 0, 0], [0, a / 2, a / 2], [a / 2, 0, a / 2], [a / 2, a / 2, 0], ] # and replicate it s.replicate(n, n, n) # perturb the positions if r > 0: shift = numpy.random.uniform(-r, r, size=(s.particles.N, 3)) s.particles.position[:] += shift return s
def create_state_from_gsd(self, filename, frame=-1): """Create the simulation state from a GSD file. Args: filename (str): GSD file to read frame (int): Index of the frame to read from the file. Negative values index back from the last frame in the file. """ if self.state is not None: raise RuntimeError("Cannot initialize more than once\n") filename = _hoomd.mpi_bcast_str(filename, self.device._cpp_exec_conf) # Grab snapshot and timestep reader = _hoomd.GSDReader(self.device._cpp_exec_conf, filename, abs(frame), frame < 0) snapshot = Snapshot._from_cpp_snapshot(reader.getSnapshot(), self.device.communicator) step = reader.getTimeStep() if self.timestep is None else self.timestep self._state = State(self, snapshot) reader.clearSnapshot() # Store System and Reader for Operations self._cpp_sys = _hoomd.System(self.state._cpp_sys_def, step) self._init_communicator() self.operations._store_reader(reader)
def snapshot(self): r"""hoomd.Snapshot: All data of a simulation's current microstate. `State.snapshot` should be used when all of a simulation's state information is desired in a single object. When accessed, data across all MPI ranks and from GPUs is gathered on the root MPI rank's memory. When accessing data in MPI simulations, it is recommended to use a ``if snapshot.exists:`` conditional to prevent attempting to access data on a non-root rank. This property can be set to replace the system state with the given `hoomd.Snapshot` object. Example use cases in which a simulation's state may be reset from a snapshot include Monte Carlo schemes implemented at the Python script level, where the current snapshot is passed to the Monte Carlo simulation before being passed back after running some Monte Carlo steps. Warning: Using `State.snapshot` multiple times will gather data across MPI ranks and GPUs every time. If the snapshot is needed for more than one use, it is recommended to store it in a variable. Note: Setting or getting a snapshot is an order :math:`O(N_{particles} + N_{bonds} + \ldots)` operation. """ cpp_snapshot = self._cpp_sys_def.takeSnapshot_double() return Snapshot._from_cpp_snapshot(cpp_snapshot, self._simulation.device.communicator)
def test_from_gsd_snapshot_populated(s, device): if s.exists: s.configuration.box = [10, 12, 7, 0.1, 0.4, 0.2] for section in ('particles', 'bonds', 'angles', 'dihedrals', 'impropers', 'pairs'): setattr(getattr(s, section), 'N', 5) setattr(getattr(s, section), 'types', ['A', 'B']) for prop in ('angmom', 'body', 'charge', 'diameter', 'image', 'mass', 'moment_inertia', 'orientation', 'position', 'typeid', 'velocity'): attr = getattr(s.particles, prop) if attr.dtype == numpy.float64: attr[:] = numpy.random.rand(*attr.shape) else: attr[:] = numpy.random.randint(3, size=attr.shape) for section in ('bonds', 'angles', 'dihedrals', 'impropers', 'pairs'): for prop in ('group', 'typeid'): attr = getattr(getattr(s, section), prop) attr[:] = numpy.random.randint(3, size=attr.shape) s.constraints.N = 3 for prop in ('group', 'value'): attr = getattr(s.constraints, prop) if attr.dtype == numpy.float64: attr[:] = numpy.random.rand(*attr.shape) else: attr[:] = numpy.random.randint(3, size=attr.shape) gsd_snap = make_gsd_snapshot(s) hoomd_snap = Snapshot.from_gsd_snapshot(gsd_snap, device.communicator) assert_equivalent_snapshots(gsd_snap, hoomd_snap)
def get_snapshot(self): """Make a copy of the simulation current state. `State.get_snapshot` makes a copy of the simulation state and makes it available in a single object. `State.set_snapshot` resets the internal state to that in the given snapshot. Use these methods to implement techniques like hybrid MD/MC or umbrella sampling where entire system configurations need to be reset to a previous one after a rejected move. Note: Data across all MPI ranks and from GPUs is gathered on the root MPI rank's memory. When accessing data in MPI simulations, use a ``if snapshot.communicator.rank == 0:`` conditional to access data arrays only on the root rank. Note: `State.get_snapshot` is an order :math:`O(N_{particles} + N_{bonds} + \\ldots)` operation. See Also: `set_snapshot` Returns: hoomd.Snapshot: The current simulation state """ cpp_snapshot = self._cpp_sys_def.takeSnapshot_double() return Snapshot._from_cpp_snapshot( cpp_snapshot, self._simulation.device.communicator)
def make_snapshot(particle_types=['A'], dimensions=3, a=1, n=7, r=0): s = Snapshot(device.communicator) if s.exists: box = [n * a, n * a, n * a, 0, 0, 0] if dimensions == 2: box[2] = 0 s.configuration.box = box s.particles.N = n**dimensions s.particles.types = particle_types # create the lattice range_ = numpy.arange(-n / 2, n / 2) if dimensions == 2: pos = list(itertools.product(range_, range_, [0])) else: pos = list(itertools.product(range_, repeat=3)) pos = numpy.array(pos) * a pos[:, 0] += a / 2 pos[:, 1] += a / 2 if dimensions == 3: pos[:, 2] += a / 2 # perturb the positions if r > 0: shift = numpy.random.uniform(-r, r, size=(s.particles.N, 3)) if dimensions == 2: shift[:, 2] = 0 pos += shift s.particles.position[:] = pos return s
def make_snapshot(particle_types=['A'], dimensions=3, d=1, L=20): """Make the snapshot. Args: particle_types: List of particle type names dimensions: Number of dimensions (2 or 3) d: Distance apart to place particles L: Box length The two particles are placed at (-d/2, 0, 0) and (d/2,0,0). When, dimensions==3, the box is L by L by L. When dimensions==2, the box is L by L by 0. """ s = Snapshot(device.communicator) N = 2 if s.communicator.rank == 0: box = [L, L, L, 0, 0, 0] if dimensions == 2: box[2] = 0 s.configuration.box = box s.particles.N = N # shift particle positions slightly in z so MPI tests pass s.particles.position[:] = [[-d / 2, 0, .1], [d / 2, 0, .1]] s.particles.types = particle_types if dimensions == 2: box[2] = 0 s.particles.position[:] = [[-d / 2, 0.1, 0], [d / 2, 0.1, 0]] return s
def create_state_from_snapshot(self, snapshot): """Create the simulations state from a `Snapshot`. Args: snapshot (Snapshot or gsd.hoomd.Snapshot): Snapshot to initialize the state from. A `gsd.hoomd.Snapshot` will first be converted to a `hoomd.Snapshot`. When `timestep` is `None` before calling, `create_state_from_snapshot` sets `timestep` to 0. """ if self.state is not None: raise RuntimeError("Cannot initialize more than once\n") if isinstance(snapshot, Snapshot): # snapshot is hoomd.Snapshot self._state = State(self, snapshot) elif _match_class_path(snapshot, 'gsd.hoomd.Snapshot'): # snapshot is gsd.hoomd.Snapshot snapshot = Snapshot.from_gsd_snapshot(snapshot, self._device.communicator) self._state = State(self, snapshot) else: raise TypeError( "Snapshot must be a hoomd.Snapshot or gsd.hoomd.Snapshot.") step = 0 if self.timestep is not None: step = self.timestep self._init_system(step)
def filter_snapshot(n=10, particle_types=['A']): s = Snapshot(device.communicator) if s.communicator.rank == 0: s.configuration.box = [20, 20, 20, 0, 0, 0] s.particles.N = n s.particles.position[:] = np.random.uniform(-10, 10, size=(n, 3)) s.particles.types = particle_types return s
def create_state_from_snapshot(self, snapshot, domain_decomposition=(None, None, None)): """Create the simulation state from a `Snapshot`. Args: snapshot (Snapshot or gsd.hoomd.Snapshot): Snapshot to initialize the state from. A `gsd.hoomd.Snapshot` will first be converted to a `hoomd.Snapshot`. domain_decomposition (tuple): Choose how to distribute the state across MPI ranks with domain decomposition. Provide a tuple of 3 integers indicating the number of evenly spaced domains in the x, y, and z directions (e.g. ``(8,4,2)``). Provide a tuple of 3 lists of floats to set the fraction of the simulation box to include in each domain. The sum of each list of floats must be 1.0 (e.g. ``([0.25, 0.75], [0.2, 0.8], [1.0])``). When `timestep` is `None` before calling, `create_state_from_snapshot` sets `timestep` to 0. Note: Set any or all of the ``domain_decomposition`` tuple elements to `None` and `create_state_from_gsd` will select a value that minimizes the surface area between the domains (e.g. ``(2,None,None)``). The domains are spaced evenly along each automatically selected direction. The default value of ``(None, None, None)`` will automatically select the number of domains in all directions. See Also: `State.get_snapshot` `State.set_snapshot` """ if self._state is not None: raise RuntimeError("Cannot initialize more than once\n") if isinstance(snapshot, Snapshot): # snapshot is hoomd.Snapshot self._state = State(self, snapshot, domain_decomposition) elif _match_class_path(snapshot, 'gsd.hoomd.Snapshot'): # snapshot is gsd.hoomd.Snapshot snapshot = Snapshot.from_gsd_snapshot(snapshot, self._device.communicator) self._state = State(self, snapshot, domain_decomposition) else: raise TypeError( "Snapshot must be a hoomd.Snapshot or gsd.hoomd.Snapshot.") step = 0 if self.timestep is not None: step = self.timestep self._init_system(step)
def make_snapshot(particle_types=['A'], dimensions=3, a=1, n=7, r=0): """Make the snapshot. Args: particle_types: List of particle type names dimensions: Number of dimensions (2 or 3) a: Lattice constant n: Number of particles along each box edge. Pass a tuple for different lengths in each dimension. r: Fraction of `a` to randomly perturb particles Place particles on a simple cubic (dimensions==3) or square (dimensions==2) lattice. The box is cubic (or square) with a side length of `n * a`. Set `r` to randomly perturb particles a small amount off their lattice positions. This is useful in MD simulation testing so that forces do not cancel out by symmetry. """ if isinstance(n, int): n = (n, ) * dimensions if dimensions == 2: n += (1, ) s = Snapshot(device.communicator) if s.communicator.rank == 0: box = [n[0] * a, n[1] * a, n[2] * a, 0, 0, 0] if dimensions == 2: box[2] = 0 s.configuration.box = box s.particles.N = numpy.product(n) s.particles.types = particle_types if any(nx == 0 for nx in n): return s # create the lattice ranges = [numpy.arange(-nx / 2, nx / 2) for nx in n] x, y, z = numpy.meshgrid(*ranges) lattice_position = numpy.vstack( (x.flatten(), y.flatten(), z.flatten())).T pos = (lattice_position + 0.5) * a if dimensions == 2: pos[:, 2] = 0 # perturb the positions if r > 0: shift = numpy.random.uniform(-r, r, size=(s.particles.N, 3)) if dimensions == 2: shift[:, 2] = 0 pos += shift s.particles.position[:] = pos return s
def make_snapshot(particle_types=['A'], dimensions=3, a=1, n=7, r=0): """Make the snapshot. Args: particle_types: List of particle type names dimensions: Number of dimensions (2 or 3) a: Lattice constant n: Number of particles along each box edge r: Fraction of `a` to randomly perturb particles Place particles on a simple cubic (dimensions==3) or square (dimensions==2) lattice. The box is cubic (or square) with a side length of `n * a`. Set `r` to randomly perturb particles a small amount off their lattice positions. This is useful in MD simulation testing so that forces do not cancel out by symmetry. """ s = Snapshot(device.communicator) if s.communicator.rank == 0: box = [n * a, n * a, n * a, 0, 0, 0] if dimensions == 2: box[2] = 0 s.configuration.box = box s.particles.N = n**dimensions s.particles.types = particle_types # create the lattice if n > 0: range_ = numpy.arange(-n / 2, n / 2) if dimensions == 2: pos = list(itertools.product(range_, range_, [0])) else: pos = list(itertools.product(range_, repeat=3)) pos = numpy.array(pos) * a pos[:, 0] += a / 2 pos[:, 1] += a / 2 if dimensions == 3: pos[:, 2] += a / 2 # perturb the positions if r > 0: shift = numpy.random.uniform(-r, r, size=(s.particles.N, 3)) if dimensions == 2: shift[:, 2] = 0 pos += shift s.particles.position[:] = pos return s
def make_snapshot(particle_types=['A'], dimensions=3, d=1, L=20): s = Snapshot(device.communicator) N = 2 if s.exists: box = [L, L, L, 0, 0, 0] if dimensions == 2: box[2] = 1 s.configuration.box = box s.configuration.dimensions = dimensions s.particles.N = N # shift particle positions slightly in z so MPI tests pass s.particles.position[:] = [[-d / 2, 0, .1], [d / 2, 0, .1]] s.particles.types = particle_types return s
def create_state_from_gsd(self, filename, frame=-1, domain_decomposition=(None, None, None)): """Create the simulation state from a GSD file. Args: filename (str): GSD file to read frame (int): Index of the frame to read from the file. Negative values index back from the last frame in the file. domain_decomposition (tuple): Choose how to distribute the state across MPI ranks with domain decomposition. Provide a tuple of 3 integers indicating the number of evenly spaced domains in the x, y, and z directions (e.g. ``(8,4,2)``). Provide a tuple of 3 lists of floats to set the fraction of the simulation box to include in each domain. The sum of each list of floats must be 1.0 (e.g. ``([0.25, 0.75], [0.2, 0.8], [1.0])``). When `timestep` is `None` before calling, `create_state_from_gsd` sets `timestep` to the value in the selected GSD frame in the file. Note: Set any or all of the ``domain_decomposition`` tuple elements to `None` and `create_state_from_gsd` will select a value that minimizes the surface area between the domains (e.g. ``(2,None,None)``). The domains are spaced evenly along each automatically selected direction. The default value of ``(None, None, None)`` will automatically select the number of domains in all directions. """ if self._state is not None: raise RuntimeError("Cannot initialize more than once\n") filename = _hoomd.mpi_bcast_str(filename, self.device._cpp_exec_conf) # Grab snapshot and timestep reader = _hoomd.GSDReader(self.device._cpp_exec_conf, filename, abs(frame), frame < 0) snapshot = Snapshot._from_cpp_snapshot(reader.getSnapshot(), self.device.communicator) step = reader.getTimeStep() if self.timestep is None else self.timestep self._state = State(self, snapshot, domain_decomposition) reader.clearSnapshot() self._init_system(step)
def make_snapshot(particle_types=['A'], dimensions=3, position=(0, 0, 0), orientation=(1, 0, 0, 0), L=20): """Make the snapshot. Args: particle_types: List of particle type names dimensions: Number of dimensions (2 or 3) position: Position to place the particle orientation: Orientation quaternion to assign to the particle L: Box length The arguments position and orientation define the position and orientation of the particle. When dimensions==3, the box is a cubic box with dimensions L by L by L. When dimensions==2, the box is a square box with dimensions L by L by 0. """ s = Snapshot(device.communicator) N = 1 if dimensions == 2 and position[2] != 0: raise ValueError( 'z component of position must be zero for 2D simulation.') if s.communicator.rank == 0: box = [L, L, L, 0, 0, 0] if dimensions == 2: box[2] = 0 s.configuration.box = box s.particles.N = N # shift particle positions slightly in z so MPI tests pass s.particles.position[0] = position s.particles.orientation[0] = orientation s.particles.types = particle_types return s
def test_from_gsd_snapshot_empty(s, device): gsd_snap = make_gsd_snapshot(s) hoomd_snap = Snapshot.from_gsd_snapshot(gsd_snap, device.communicator) assert_equivalent_snapshots(gsd_snap, hoomd_snap)
def snap(device): s = Snapshot(device.communicator) N = 1000 if s.exists: s.configuration.box = [20, 20, 20, 0, 0, 0] s.particles.N = N s.particles.position[:] = numpy.random.uniform(-10, 10, size=(N, 3)) s.particles.velocity[:] = numpy.random.uniform(-1, 1, size=(N, 3)) s.particles.typeid[:] = numpy.random.randint(0, 3, size=N) s.particles.mass[:] = numpy.random.uniform(1, 2, size=N) s.particles.charge[:] = numpy.random.uniform(-2, 2, size=N) s.particles.image[:] = numpy.random.randint(-8, 8, size=(N, 3)) s.particles.orientation[:] = numpy.random.uniform(-1, 1, size=(N, 4)) s.particles.moment_inertia[:] = numpy.random.uniform(1, 5, size=(N, 3)) s.particles.angmom[:] = numpy.random.uniform(-1, 1, size=(N, 4)) s.particles.types = ['A', 'B', 'C', 'D'] s.bonds.N = N - 1 for i in range(s.bonds.N): s.bonds.group[i, :] = [i, i + 1] s.bonds.typeid[:] = numpy.random.randint(0, 3, size=s.bonds.N) s.bonds.types = ['bondA', 'bondB', 'bondC', 'bondD'] s.angles.N = N - 2 for i in range(s.angles.N): s.angles.group[i, :] = [i, i + 1, i + 2] s.angles.typeid[:] = numpy.random.randint(0, 3, size=s.angles.N) s.angles.types = ['angleA', 'angleB', 'angleC', 'angleD'] s.dihedrals.N = N - 3 for i in range(s.dihedrals.N): s.dihedrals.group[i, :] = [i, i + 1, i + 2, i + 3] s.dihedrals.typeid[:] = numpy.random.randint(0, 3, size=s.dihedrals.N) s.dihedrals.types = [ 'dihedralA', 'dihedralB', 'dihedralC', 'dihedralD' ] s.impropers.N = N - 3 for i in range(s.impropers.N): s.impropers.group[i, :] = [i, i + 1, i + 2, i + 3] s.impropers.typeid[:] = numpy.random.randint(0, 3, size=s.impropers.N) s.impropers.types = [ 'improperA', 'improperB', 'improperC', 'improperD' ] s.pairs.N = N - 1 for i in range(s.pairs.N): s.pairs.group[i, :] = [i, i + 1] s.pairs.typeid[:] = numpy.random.randint(0, 3, size=s.pairs.N) s.pairs.types = ['pairA', 'pairB', 'pairC', 'pairD'] s.constraints.N = N - 1 for i in range(s.constraints.N): s.constraints.group[i, :] = [i, i + 1] s.constraints.value[:] = numpy.random.uniform(1, 10, size=s.constraints.N) return s
def s(): return Snapshot()