def addSolvent(universe, solvent, density, scale_factor=4.): """ Scales up the universe and adds as many solvent molecules as are necessary to obtain the specified solvent density, taking account of the solute molecules that are already present in the universe. The molecules are placed at random positions in the scaled-up universe, but without overlaps between any two molecules. :param universe: a finite universe :type universe: :class:~MMTK.Universe.Universe :param solvent: a molecule, or the name of a molecule in the database :type solvent: :class:~MMTK.ChemicalObjects.Molecule or str :param density: the density of the solvent (amu/nm**3) :type density: float :param scale_factor: the factor by which the initial universe is expanded before adding the solvent molecules :type scale_factor: float """ # Calculate number of solvent molecules and universe size if isinstance(solvent, basestring): solvent = ChemicalObjects.Molecule(solvent) cell_volume = universe.cellVolume() if cell_volume is None: raise TypeError("universe volume is undefined") solute = copy.copy(universe._objects) solute_volume = 0. excluded_regions = [] for o in solute: solute_volume = solute_volume + surfaceAndVolume(o)[1] excluded_regions.append(o.boundingSphere()) n_solvent = int( round(density * (cell_volume - solute_volume) / solvent.mass())) solvent_volume = n_solvent * solvent.mass() / density cell_volume = solvent_volume + solute_volume universe.translateBy(-solute.position()) universe.scaleSize((cell_volume / universe.cellVolume())**(1. / 3.)) # Scale up the universe and add solvent molecules at random positions universe.scaleSize(scale_factor) universe.scale_factor = scale_factor for i in range(n_solvent): m = copy.copy(solvent) m.translateTo(universe.randomPoint()) while True: s = m.boundingSphere() collision = False for region in excluded_regions: if (s.center - region.center).length() < s.radius + region.radius: collision = True break if not collision: break m.translateTo(universe.randomPoint()) universe.addObject(m) excluded_regions.append(s)
def createMolecule(self, name=None): """ :returns: a :class:~MMTK.ChemicalObjects.Molecule object corresponding to the molecule in the PDB file. The parameter name specifies the molecule name as defined in the chemical database. It can be left out for known molecules (currently only water). :rtype: :class:~MMTK.ChemicalObjects.Molecule """ if name is None: name = molecule_names[self.name] m = ChemicalObjects.Molecule(name) setConfiguration(m, [self]) return m
def makeChemicalObjects(self, template, top_level): self.groups[template.name].locked = True if top_level: if template.attributes.has_key('sequence'): object = ChemicalObjects.ChainMolecule(None) else: object = ChemicalObjects.Molecule(None) else: object = ChemicalObjects.Group(None) object.atoms = [] object.bonds = Bonds.BondList([]) object.groups = [] object.type = self.groups[template.name] object.parent = None child_objects = [] for child in template.children: if isinstance(child, GroupTemplate): group = self.makeChemicalObjects(child, False) object.groups.append(group) object.atoms.extend(group.atoms) object.bonds.extend(group.bonds) group.parent = object child_objects.append(group) else: atom = ChemicalObjects.Atom(child.element) object.atoms.append(atom) atom.parent = object child_objects.append(atom) for name, index in template.names.items(): setattr(object, name, child_objects[index]) child_objects[index].name = name for name, value in template.attributes.items(): path = name.split('.') setattr(self.namePath(object, path[:-1]), path[-1], value) for atom1, atom2 in template.bonds: atom1 = self.namePath(object, atom1) atom2 = self.namePath(object, atom2) object.bonds.append(Bonds.Bond((atom1, atom2))) for name, vector in template.positions.items(): path = name.split('.') self.namePath(object, path).setPosition(vector) return object
def numberOfSolventMolecules(universe, solvent, density): """ :param universe: a finite universe :type universe: :class:`~MMTK.Universe.Universe` :param solvent: a molecule, or the name of a molecule in the database :type solvent: :class:`~MMTK.ChemicalObjects.Molecule` or str :param density: the density of the solvent (amu/nm**3) :type density: float :returns: the number of solvent molecules that must be added to the universe, in addition to whatever it already contains, to obtain the given solvent density. :rtype: int """ if isinstance(solvent, basestring): solvent = ChemicalObjects.Molecule(solvent) cell_volume = universe.cellVolume() if cell_volume is None: raise TypeError("universe volume is undefined") solute_volume = 0. for o in universe._objects: solute_volume = solute_volume + surfaceAndVolume(o)[1] return int(round(density*(cell_volume-solute_volume)/solvent.mass()))
def createMolecules(self, names = None, permit_undefined=True): """ :param names: If a list of molecule names (as defined in the chemical database) and/or PDB residue names, only molecules mentioned in this list will be constructed. If a dictionary, it is used to map PDB residue names to molecule names. With the default (None), only water molecules are built. :type names: list :param permit_undefined: If False, an exception is raised when a PDB residue is encountered for which no molecule name is supplied in names. If True, an AtomCluster object is constructed for each unknown molecule. :returns: a collection of :class:~MMTK.ChemicalObjects.Molecule objects, one for each molecule in the PDB file. Each PDB residue not describing an amino acid or nucleotide residue is considered a molecule. :rtype: :class:~MMTK.Collections.Collection """ collection = Collections.Collection() mol_dicts = [molecule_names] if type(names) == type({}): mol_dicts.append(names) names = None for name in self.molecules.keys(): full_name = None for dict in mol_dicts: full_name = dict.get(name, None) if names is None or name in names or full_name in names: if full_name is None and not permit_undefined: raise ValueError("no definition for molecule " + name) for molecule in self.molecules[name]: if full_name: m = ChemicalObjects.Molecule(full_name) setConfiguration(m, [molecule]) else: pdbdict = {} atoms = [] i = 0 for atom in molecule: aname = atom.name while aname[0] in string.digits: aname = aname[1:] + aname[0] try: element = atom['element'].strip() a = ChemicalObjects.Atom(element, name = aname) except KeyError: try: a = ChemicalObjects.Atom(aname[:2].strip(), name = aname) except IOError: a = ChemicalObjects.Atom(aname[:1], name = aname) a.setPosition(atom.position) atoms.append(a) pdbdict[atom.name] = Database.AtomReference(i) i += 1 m = ChemicalObjects.AtomCluster(atoms, name = name) if len(pdbdict) == len(molecule): # pdbmap is correct only if the AtomCluster has # unique atom names m.pdbmap = [(name, pdbdict)] setConfiguration(m, [molecule]) collection.addObject(m) return collection