def _prepare_atoms(topology, compute_cycles=False): """Compute cycles and add white-/blacklists to atoms.""" atom1 = next(topology.atoms()) has_whitelists = hasattr(atom1, 'whitelist') has_cycles = hasattr(atom1, 'cycles') compute_cycles = compute_cycles and not has_cycles if compute_cycles or not has_whitelists: for atom in topology.atoms(): if compute_cycles: atom.cycles = set() if not has_whitelists: atom.whitelist = OrderedSet() atom.blacklist = OrderedSet() if compute_cycles: bond_graph = nx.Graph() bond_graph.add_nodes_from(topology.atoms()) bond_graph.add_edges_from(topology.bonds()) cycles = nx.cycle_basis(bond_graph) for cycle in cycles: # NOTE: Only considering cycles containing less than 8 atoms if len(cycle) > 8: continue for atom in cycle: atom.cycles.add(tuple(cycle))
def find_atomtypes(atoms, forcefield, debug=False): """Determine atomtypes for all atoms. This function is fairly general in that it can function on any list of atom objects as long as they have a property `neighbors`, which is a list of other atoms that they are bonded to as well as the attributes `whitelist` and `blacklist` which are sets - if they are ordered sets it simplifies debugging a little bit. Parameters ---------- atoms : list of Atom objects The atoms whose atomtypes you are looking for. forcefield : simtk.openmm.app.Forcefield object The forcefield object. debug : bool, default=True Provides debug information about the logical consistency of the atomtyping rules. See also -------- _sanitize """ for atom in atoms: atom.whitelist = OrderedSet() atom.blacklist = OrderedSet() _load_rules(forcefield) # if debug: # _sanitize() _iterate_rules(atoms, max_iter=10) _resolve_atomtypes(atoms)
def write_gmx_topology(system, filename, header=""): """ Write out gmx topology file Parameters ---------- system : mb.Compound() filename : str header : str, optional Header string for include statements etc Notes ----- Molecule names are based on the one-level-up parents of the particles """ molecules = OrderedSet() for p in system.particles(): if p.parent is not None: molecules.add(p.parent) with open(filename, 'w') as f: f.write("{}\n\n".format(header)) f.write("[ system ]\n") f.write("mBuild Bilayer System\n\n") f.write("[ molecules ]\n") f.write("\n".join([ "{:<8s} {}".format(name, sum(1 for _ in g)) for name, g in groupby([c.name for c in molecules]) ]))
def abbreviate_names_uniquely(names): """Given a list of strings, return a list of strings with abbreviations for each that are unique among the strings given. Strings occuring later in the iterable will have longer names if there are conflicts with shorter abbreviations. If an unordered iterable is passed, the abbreviations assigned may be different each time. So this is really only meant to be used with ordered iterables. Args: names (iterable of str): Iterable of names to abbreviate. Return: abbrevs (dict): Mapping from names given to their abbreviations. """ abbrevs = {} unique = OrderedSet() for name in names: words = name.split('_') n_words = len(words) abbrev = abbreviate_name_upton(name, n_words) # Sometimest last character is a parameter distinguisher, such as in # theta0, theta1, theta2, or as in lambda_B, lambda_W. The second case # falls under the typical use case, so should be accounted for to avoid # repeated last characters. The first case is atypical; without an # underscore preceding it, the last character is normally be discarded. last_char = name[-1] last_char_important = last_char.isupper() or last_char.isdigit() last_char_not_last_word = len(words[-1]) > 1 add_last_char = last_char_important and last_char_not_last_word if add_last_char: abbrev += last_char while abbrev in unique: n_words += 1 abbrev = abbreviate_name_upton(name, n_words) if add_last_char: abbrev += last_char unique.add(abbrev) abbrevs[name] = abbrev return abbrevs
def from_mbuild(cls, compound): """ Instantiates a CG_Compound and follows mb.Compound.deep_copy to copy particles and bonds to CG_Compound Parameters ---------- compound : mb.Compound to be compied Returns ------- CG_Compound """ comp = cls() clone_dict = {} comp.name = deepcopy(compound.name) comp.periodicity = deepcopy(compound.periodicity) comp._pos = deepcopy(compound._pos) comp.port_particle = deepcopy(compound.port_particle) comp._check_if_contains_rigid_bodies = deepcopy( compound._check_if_contains_rigid_bodies ) comp._contains_rigid = deepcopy(compound._contains_rigid) comp._rigid_id = deepcopy(compound._rigid_id) comp._charge = deepcopy(compound._charge) if compound.children is None: comp.children = None else: comp.children = OrderedSet() # Parent should be None initially. comp.parent = None comp.labels = OrderedDict() comp.referrers = set() comp.bond_graph = None for p in compound.particles(): new_particle = mb.Particle(name=p.name, pos=p.xyz.flatten()) comp.add(new_particle) clone_dict[p] = new_particle for c1, c2 in compound.bonds(): try: comp.add_bond((clone_dict[c1], clone_dict[c2])) except KeyError: raise MBuildError( "Cloning failed. Compound contains bonds to " "Particles outside of its containment hierarchy." ) return comp
def _prepare_atoms(topology, compute_cycles=False): """Compute cycles and add white-/blacklists to atoms.""" atom1 = next(topology.atoms()) has_whitelists = hasattr(atom1, 'whitelist') has_cycles = hasattr(atom1, 'cycles') compute_cycles = compute_cycles and not has_cycles if compute_cycles or not has_whitelists: for atom in topology.atoms(): if compute_cycles: atom.cycles = set() if not has_whitelists: atom.whitelist = OrderedSet() atom.blacklist = OrderedSet() if compute_cycles: bond_graph = nx.Graph() bond_graph.add_nodes_from(topology.atoms()) bond_graph.add_edges_from(topology.bonds()) all_cycles = _find_chordless_cycles(bond_graph, max_cycle_size=8) for atom, cycles in zip(bond_graph.nodes, all_cycles): for cycle in cycles: atom.cycles.add(tuple(cycle))
def __init__(self, subcompounds=None, name=None, pos=None, charge=0.0, periodicity=None, port_particle=False): super(Compound, self).__init__() if name: if not isinstance(name, string_types): raise ValueError( 'Compound.name should be a string. You passed ' '{}'.format(name)) self.name = name else: self.name = self.__class__.__name__ # A periodocity of zero in any direction is treated as non-periodic. if periodicity is None: self._periodicity = np.array([0.0, 0.0, 0.0]) else: self._periodicity = np.asarray(periodicity) if pos is not None: self._pos = np.asarray(pos, dtype=float) else: self._pos = np.zeros(3) self.charge = charge self.parent = None self.children = OrderedSet() self.labels = OrderedDict() self.referrers = set() self.bond_graph = None self.port_particle = port_particle # self.add() must be called after labels and children are initialized. if subcompounds: self.add(subcompounds)
def matches(self, atom): return self._matches(atom, atom_pattern=self.start_atom_pattern(), visited_atoms=OrderedSet(), labeled_atoms=dict())
def unique(iter): """ Yields a stream of deduplicated elements. Elements are returned in the same order as the input iterator. """ yield from OrderedSet(iter)
def write_forcefield(structure, filename, ref_distance=1.0, ref_energy=1.0): """Output force field information in JSON format. Parameters ---------- structure : parmed.GromacsTopologyFile Parmed structure object filename : str Path of the output file. ref_distance : float, default=1.0 Reference distance for conversion to reduced units ref_energy : float, default=1.0 Reference energy for conversion to reduced units """ params = pmd.ParameterSet.from_structure(structure) ff_data = OrderedDict() styles = OrderedDict() with open(filename, 'w') as f: # Pair charges = np.any([atom.charge for atom in structure.atoms]) if charges: styles['pair'] = 'lj/coul' else: styles['pair'] = 'lj' pair_data = dict() for key in params.atom_types.items(): temp_dict = OrderedDict() temp_dict['element'] = { enum: ename for ename, enum in AtomicNum.items() }[key[1].atomic_number] temp_dict['epsilon'] = round(key[1].epsilon, 3) / ref_energy temp_dict['sigma'] = round(key[1].sigma, 3) / ref_distance pair_data[key[0]] = temp_dict pair_data = OrderedDict( sorted(pair_data.items(), key=lambda p: int(p[0].split('_')[1]))) # Bonds bonds = [bond for bond in structure.bonds] if bonds: styles['bond'] = 'harmonic' unique_bond_types = dict( enumerate( OrderedSet([(round(bond.type.k, 3), round(bond.type.req, 3)) for bond in structure.bonds]))) bond_data = OrderedDict() for idx, key in unique_bond_types.items(): temp_dict = OrderedDict() temp_dict['k'] = round(key[0] * 2, 3) * ( (ref_distance**2) / ref_energy) temp_dict['r0'] = round(key[1], 3) / ref_distance bond_data[str(idx)] = temp_dict # Angles angles = [angle for angle in structure.angles] if angles: styles['angle'] = 'harmonic' unique_angle_types = dict( enumerate( OrderedSet([(round(angle.type.k, 3), round(angle.type.theteq, 3)) for angle in structure.angles]))) angle_data = OrderedDict() for idx, key in unique_angle_types.items(): temp_dict = OrderedDict() temp_dict['k'] = round(key[0] * 2, 3) / ref_energy temp_dict['t0'] = round(key[1], 3) * (np.pi / 180) angle_data[str(idx)] = temp_dict # Dihedrals dihedrals = [dihedral for dihedral in structure.rb_torsions] if dihedrals: styles['dihedral'] = 'opls' unique_dihedral_types = dict( enumerate( OrderedSet([(round(dihedral.type.c0, 3), round(dihedral.type.c1, 3), round(dihedral.type.c2, 3), round(dihedral.type.c3, 3), round(dihedral.type.c4, 3), round(dihedral.type.c5, 3), round(dihedral.type.scee, 1), round(dihedral.type.scnb, 1)) for dihedral in structure.rb_torsions]))) dihedrals_opls = [ RB_to_OPLS(y[0], y[1], y[2], y[3], y[4], y[5]) for x, y in unique_dihedral_types.items() ] dihedral_data = OrderedDict() for idx, key in enumerate(dihedrals_opls): temp_dict = OrderedDict() temp_dict['k1'] = round(key[0], 3) / ref_energy temp_dict['k2'] = round(key[1], 3) / ref_energy temp_dict['k3'] = round(key[2], 3) / ref_energy temp_dict['k4'] = round(key[3], 3) / ref_energy dihedral_data[str(idx)] = temp_dict ff_data['styles'] = styles ff_data['pair_coeffs'] = pair_data if bonds: ff_data['bond_coeffs'] = bond_data if angles: ff_data['angle_coeffs'] = angle_data if dihedrals: ff_data['dihedral_coeffs'] = dihedral_data json.dump(ff_data, f, indent=4)
def _init(self, maxsize): self.queue = OrderedSet()
def _clone(self, clone_of=None, root_container=None): """A faster alternative to deepcopying. Does not resolve circular dependencies. This should be safe provided you never try to add the top of a Compound hierarchy to a sub-Compound. Clones compound hierarchy only, not the bonds. """ if root_container is None: root_container = self if clone_of is None: clone_of = dict() # If this compound has already been cloned, return that. if self in clone_of: return clone_of[self] # Otherwise we make a new clone. cls = self.__class__ newone = cls.__new__(cls) # Remember that we're cloning the new one of self. clone_of[self] = newone newone.name = deepcopy(self.name) newone.wrapped = clone(self.wrapped) if hasattr(self, 'index'): newone.index = deepcopy(self.index) if self.children is None: newone.children = None else: newone.children = OrderedSet() # Parent should be None initially. newone.parent = None newone.labels = OrderedDict() newone.referrers = set() newone.bond_graph = None # Add children to clone. if self.children: for child in self.children: newchild = child._clone(clone_of, root_container) newone.children.add(newchild) newchild.parent = newone # Copy labels, except bonds with atoms outside the hierarchy. if self.labels: for label, compound in self.labels.items(): if not isinstance(compound, list): newone.labels[label] = compound._clone( clone_of, root_container) compound.referrers.add(clone_of[compound]) else: # compound is a list of compounds, so we create an empty # list, and add the clones of the original list elements. newone.labels[label] = [] for subpart in compound: newone.labels[label].append( subpart._clone(clone_of, root_container)) # Referrers must have been handled already, or the will # be handled return newone
def add(self, new_child, label=None, containment=True, replace=False, inherit_periodicity=True): """Add a part to the Compound. Note: This does not necessarily add the part to self.children but may instead be used to add a reference to the part to self.labels. See 'containment' argument. Parameters ---------- new_child : mb.Compound or list-like of mb.Compound The object(s) to be added to this Compound. label : str, optional A descriptive string for the part. containment : bool, optional, default=True Add the part to self.children. replace : bool, optional, default=True Replace the label if it already exists. """ # Support batch add via lists, tuples and sets. if (isinstance(new_child, collections.Iterable) and not isinstance(new_child, string_types)): for child in new_child: self.add(child) return if not isinstance(new_child, Compound): raise ValueError('Only objects that inherit from mbuild.Compound ' 'can be added to Compounds. You tried to add ' '"{}".'.format(new_child)) # Create children and labels on the first add operation if self.children is None: self.children = OrderedSet() if self.labels is None: self.labels = OrderedDict() if containment: if new_child.parent is not None: raise MBuildError('Part {} already has a parent: {}'.format( new_child, new_child.parent)) self.children.add(new_child) new_child.parent = self if new_child.bond_graph is not None: if self.root.bond_graph is None: self.root.bond_graph = new_child.bond_graph else: self.root.bond_graph.compose(new_child.bond_graph) new_child.bond_graph = None # Add new_part to labels. Does not currently support batch add. if label is None: label = '{0}[$]'.format(new_child.__class__.__name__) if label.endswith('[$]'): label = label[:-3] if label not in self.labels: self.labels[label] = [] label_pattern = label + '[{}]' count = len(self.labels[label]) self.labels[label].append(new_child) label = label_pattern.format(count) if not replace and label in self.labels: raise MBuildError('Label "{0}" already exists in {1}.'.format( label, self)) else: self.labels[label] = new_child new_child.referrers.add(self) if (inherit_periodicity and isinstance(new_child, Compound) and new_child.periodicity.any()): self.periodicity = new_child.periodicity
def write_gsd(structure, filename, forcefield, box, ref_distance=1.0, ref_mass=1.0, ref_energy=1.0, write_ff=True): """Output a GSD file (HOOMD default data format). Parameters ---------- structure : parmed.GromacsTopologyFile Parmed structure object filename : str Path of the output file. forcefield : str, default=None Name of the force field to be applied to the compound box : mb.Box Box information to save to XML file ref_distance : float, default=1.0 Reference distance for conversion to reduced units ref_mass : float, default=1.0 Reference mass for conversion to reduced units ref_energy : float, default=1.0 Reference energy for conversion to reduced units write_ff : boolean, default=True Write forcefield parameters to a JSON file, 'parameters.json' """ import_('gsd') import gsd.hoomd xyz = np.array([[atom.xx, atom.xy, atom.xz] for atom in structure.atoms]) # Center box at origin and remap coordinates into box box.lengths *= 10.0 box.maxs *= 10.0 box.mins *= 10.0 box_init = deepcopy(box) box.mins = np.array([-d / 2 for d in box_init.lengths]) box.maxs = np.array([d / 2 for d in box_init.lengths]) shift = [box_init.maxs[i] - max for i, max in enumerate(box.maxs)] for i, pos in enumerate(xyz): for j, coord in enumerate(pos): xyz[i, j] -= shift[j] rep = floor((xyz[i, j] - box.mins[j]) / box.lengths[j]) xyz[i, j] -= (rep * box.lengths[j]) gsd_file = gsd.hoomd.Snapshot() gsd_file.configuration.step = 0 gsd_file.configuration.dimensions = 3 gsd_file.configuration.box = np.hstack( (box.lengths / ref_distance, np.zeros(3))) gsd_file.particles.N = len(structure.atoms) gsd_file.particles.position = xyz / ref_distance if forcefield: types = [atom.type for atom in structure.atoms] else: types = [atom.name for atom in structure.atoms] unique_types = list(set(types)) unique_types.sort(key=_natural_sort) typeids = np.array([unique_types.index(t) for t in types]) gsd_file.particles.types = unique_types gsd_file.particles.typeid = typeids masses = np.array([atom.mass for atom in structure.atoms]) masses[masses == 0] = 1.0 gsd_file.particles.mass = masses / ref_mass charges = np.array([atom.charge for atom in structure.atoms]) e0 = 2.39725e-4 ''' Permittivity of free space = 2.39725e-4 e^2/((kcal/mol)(angstrom)), where e is the elementary charge ''' charge_factor = (4.0 * np.pi * e0 * ref_distance * ref_energy)**0.5 gsd_file.particles.charge = charges / charge_factor bonds = [[bond.atom1.idx, bond.atom2.idx] for bond in structure.bonds] if bonds: bonds = np.asarray(bonds) gsd_file.bonds.N = len(bonds) if len(structure.bond_types) == 0: bond_types = np.zeros(len(bonds), dtype=int) gsd_file.bonds.types = ['0'] else: unique_bond_types = dict( enumerate( OrderedSet([(round(bond.type.k, 3), round(bond.type.req, 3)) for bond in structure.bonds]))) unique_bond_types = OrderedDict([ (y, x) for x, y in unique_bond_types.items() ]) bond_types = [ unique_bond_types[(round(bond.type.k, 3), round(bond.type.req, 3))] for bond in structure.bonds ] gsd_file.bonds.types = [ str(y) for x, y in unique_bond_types.items() ] gsd_file.bonds.typeid = bond_types gsd_file.bonds.group = bonds angles = [[angle.atom1.idx, angle.atom2.idx, angle.atom3.idx] for angle in structure.angles] if angles: angles = np.asarray(angles) gsd_file.angles.N = len(angles) unique_angle_types = dict( enumerate( OrderedSet([(round(angle.type.k, 3), round(angle.type.theteq, 3)) for angle in structure.angles]))) unique_angle_types = OrderedDict([ (y, x) for x, y in unique_angle_types.items() ]) angle_types = [ unique_angle_types[(round(angle.type.k, 3), round(angle.type.theteq, 3))] for angle in structure.angles ] gsd_file.angles.types = [str(y) for x, y in unique_angle_types.items()] gsd_file.angles.typeid = angle_types gsd_file.angles.group = angles dihedrals = [[ dihedral.atom1.idx, dihedral.atom2.idx, dihedral.atom3.idx, dihedral.atom4.idx ] for dihedral in structure.rb_torsions] if dihedrals: dihedrals = np.asarray(dihedrals) gsd_file.dihedrals.N = len(dihedrals) unique_dihedral_types = dict( enumerate( OrderedSet([(round(dihedral.type.c0, 3), round(dihedral.type.c1, 3), round(dihedral.type.c2, 3), round(dihedral.type.c3, 3), round(dihedral.type.c4, 3), round(dihedral.type.c5, 3), round(dihedral.type.scee, 1), round(dihedral.type.scnb, 1)) for dihedral in structure.rb_torsions]))) unique_dihedral_types = OrderedDict([ (y, x) for x, y in unique_dihedral_types.items() ]) dihedral_types = [ unique_dihedral_types[(round(dihedral.type.c0, 3), round(dihedral.type.c1, 3), round(dihedral.type.c2, 3), round(dihedral.type.c3, 3), round(dihedral.type.c4, 3), round(dihedral.type.c5, 3), round(dihedral.type.scee, 1), round(dihedral.type.scnb, 1))] for dihedral in structure.rb_torsions ] gsd_file.dihedrals.types = [ str(y) for x, y in unique_dihedral_types.items() ] gsd_file.dihedrals.typeid = dihedral_types gsd_file.dihedrals.group = dihedrals gsd.hoomd.create(filename, gsd_file) if write_ff: write_forcefield(structure, 'ff.json', ref_distance=ref_distance, ref_energy=ref_energy)