def _new_property(self, key, value): """ Create a new list of properties for each frame. To facilitate analysis, will try to create this list as one of the following classes (in order of preference): 1) resizeable numpy array 2) resizeable MdtQuantity array 3) list The list of properties will be backfilled with ``None`` if this property wasn't already present """ assert key not in self.properties if self.num_frames != 0: proplist = [None] * self.num_frames proplist.append(value) else: try: proplist = self.unit_system.convert(u.array([value])) except TypeError: proplist = [value] else: proplist.make_resizable() if proplist.dimensionless: proplist = proplist._magnitude self.properties[key] = proplist
def rmsd(self, atoms=None, reference=None): r""" Calculate root-mean-square displacement for each frame in the trajectory. The RMSD between times :math:`t` and :math:`t0` is given by :math:`\text{RMSD}(t;t_0) =\sqrt{\sum_{i \in \text{atoms}} \left( \mathbf{R}_i(t) - \mathbf{R}_i(t_0) \right)^2}`, where :math:`\mathbf{R}_i(t)` is the position of atom *i* at time *t*. Args: atoms (list[mdt.Atom]): list of atoms to calculate the RMSD for (all atoms in the ``Molecule``) reference (u.Vector[length]): Reference positions for RMSD. (default: ``traj.frames[0].positions``) Returns: u.Vector[length]: list of RMSD displacements for each frame in the trajectory """ if reference is None: refpos = self.frames[0].positions else: refpos = reference.positions atoms = mdt.utils.if_not_none(atoms, self.mol.atoms) indices = np.array([atom.index for atom in atoms]) rmsds = [] for f in self.frames: diff = (refpos[indices] - f.positions[indices]) rmsds.append(np.sqrt((diff * diff).sum() / len(atoms))) return u.array(rmsds).defunits()
def rmsd(self, atoms=None, reference=None): r""" Calculate root-mean-square displacement for each frame in the trajectory. The RMSD between times :math:`t` and :math:`t0` is given by :math:`\text{RMSD}(t;t_0) =\sqrt{\sum_{i \in \text{atoms}} \left( \mathbf{R}_i(t) - \mathbf{R}_i(t_0) \right)^2}`, where :math:`\mathbf{R}_i(t)` is the position of atom *i* at time *t*. Args: atoms (list[mdt.Atom]): list of atoms to calculate the RMSD for (all atoms in the ``Molecule``) reference (u.Vector[length]): Reference positions for RMSD. (default: ``traj.frames[0].positions``) Returns: u.Vector[length]: list of RMSD displacements for each frame in the trajectory """ if reference is None: refpos = self.frames[0].positions else: refpos = reference.positions atoms = mdt.utils.if_not_none(atoms, self.mol.atoms) indices = np.array([atom.index for atom in atoms]) rmsds = [] for f in self.frames: diff = (refpos[indices] - f.positions[indices]) rmsds.append(np.sqrt((diff*diff).sum()/len(atoms))) return u.array(rmsds).defunits()
def test_bond_alignment_on_axis(benzene): mol = benzene.copy() directions = [ 'x', 'y', 'z', [1, 2, 3.0], [0, 1, 0], [0.1, 0.1, 0.1] * u.angstrom ] for i, dir in enumerate(directions): bond = mdt.Bond(*random.sample(mol.atoms, 2)) center = (i % 2) == 0.0 bond.align(dir, centered=center) if center: np.testing.assert_allclose(bond.midpoint, np.zeros(3), atol=1.e-12) np.testing.assert_allclose(bond.a1.position.defunits_value(), -bond.a2.position.defunits_value(), atol=1e-10) if isinstance(dir, str): if dir == 'x': d = np.array([1.0, 0, 0]) elif dir == 'y': d = np.array([0.0, 1.0, 0.0]) elif dir == 'z': d = np.array([0.0, 0.0, 1.0]) else: raise WtfError() else: d = normalized(u.array(dir)) newvec = (bond.a2.position - bond.a1.position).normalized() assert abs(1.0 - d.dot(newvec)) < 1e-10
def calculate(self, requests): dvec = self.mol.atoms[1].position - self.mol.atoms[0].position dist = np.sqrt(dvec.dot(dvec)) pe = 0.5 * self.k * (dist - self.d0)**2 f = self.k * dvec * (dist - self.d0) / dist forcearray = u.array([f, -f]) return {'potential_energy': pe, 'forces': forcearray}
def test_container_properties(fixturename, request): obj = request.getfixturevalue(fixturename) assert obj.mass == sum([atom.mass for atom in obj.atoms]) np.testing.assert_array_equal( obj.positions.defunits(), u.array([atom.position for atom in obj.atoms]).defunits()) assert obj.num_atoms == len(obj.atoms)
def test_bond_alignment_on_axis(benzene): mol = benzene.copy() directions = ['x', 'y', 'z', [1,2,3.0], [0,1,0], [0.1, 0.1, 0.1] * u.angstrom] for i, dir in enumerate(directions): bond = mdt.Bond(*random.sample(mol.atoms, 2)) center = (i % 2) == 0.0 bond.align(dir, centered=center) if center: np.testing.assert_allclose(bond.midpoint, np.zeros(3), atol=1.e-12) np.testing.assert_allclose(bond.a1.position.defunits_value(), -bond.a2.position.defunits_value(), atol=1e-10) if isinstance(dir, str): if dir == 'x': d = np.array([1.0, 0, 0]) elif dir == 'y': d = np.array([0.0, 1.0, 0.0]) elif dir == 'z': d = np.array([0.0, 0.0, 1.0]) else: raise WtfError() else: d = normalized(u.array(dir)) newvec = (bond.a2.position - bond.a1.position).normalized() assert abs(1.0 - d.dot(newvec)) < 1e-10
def calculate(self, requests): dvec = self.mol.atoms[1].position - self.mol.atoms[0].position dist = np.sqrt(dvec.dot(dvec)) pe = 0.5 * self.params.k * (dist - self.params.d0)**2 f = self.params.k * dvec * (dist - self.params.d0) / dist forcearray = u.array([f, -f]) return {'potential_energy': pe, 'forces': forcearray}
def test_default_unit_conversions(): my_list = [1.0 * units.angstrom, 1.0 * units.nm, 1.0 * units.a0] my_array = units.array(my_list).defunits() assert my_array.get_units() == units.default.convert(my_array).get_units() result = my_array.value_in(units.nm) np.testing.assert_almost_equal(result[0], 0.1, 9) np.testing.assert_almost_equal(result[1], 1.0, 9) np.testing.assert_almost_equal(result[2], 0.05291772, 7)
def test_dimensionless_array_from_mixed_data(): data = [ np.arange(3), [1.0 * u.dimensionless, 1.0 * u.nm / u.angstrom, 3.0], [1, 1, 1] * u.ureg.kg / u.ureg.g ] arr = u.array(data) np.testing.assert_allclose( arr, np.array([[0, 1, 2], [1, 10, 3], [1000, 1000, 1000]], dtype='float')) assert isinstance(arr, np.ndarray)
def transform(self, matrix): """ Transform this object's coordinates using the provided 4x4 matrix Args: matrix (numpy.ndarray): transformation matrix, shape=(4,4) """ # TODO: deal with units ... hard because the matrix has diff units for diff columns assert matrix.shape == (4, 4) positions = u.array(self.atoms.position) newpos = mathutils.apply_4x4_transform(matrix, positions) for atom, r in zip(self.atoms, newpos): atom.position = r
def __getattr__(self, item): """ Returns an array of atomic properties, e.g., an array of all atomic masses is returned by ``AtomContainer.mass = AtomContainer.__getattr__('mass')`` """ if item.startswith('__'): raise AttributeError atom_attrs = [getattr(atom, item) for atom in self.atoms] try: return u.array(atom_attrs) except (TypeError, StopIteration): return atom_attrs
def colormap(cats, mplmap='auto', categorical=None): """ Map a series of categories to hex colors, using a matplotlib colormap Generates both categorical and numerical colormaps. Args: cats (Iterable): list of categories or numerical values mplmap (str): name of matplotlib colormap object categorical (bool): If None (the default) interpret data as numerical only if it can be cast to float. If True, interpret this data as categorical. If False, cast the data to float. Returns: List[str]: List of hexadecimal RGB color values in the in the form ``'#000102'`` """ # Should automatically choose the right colormaps for: # categorical data # sequential data (low, high important) # diverging data (low, mid, high important) global DEF_SEQUENTIAL from matplotlib import cm if hasattr(cm, 'inferno'): DEF_SEQUENTIAL = 'inferno' else: DEF_SEQUENTIAL = 'BrBG' # strip units units = None # TODO: build a color bar with units if hasattr(cats[0], 'magnitude'): arr = u.array(cats) units = arr.units cats = arr.magnitude is_categorical = False else: is_categorical = not isinstance(cats[0], (float, int)) if categorical is not None: is_categorical = categorical if is_categorical: values = _map_categories_to_ints(cats) if mplmap == 'auto': mplmap = DEF_CATEGORICAL else: values = np.array(list(map(float, cats))) if mplmap == 'auto': mplmap = DEF_SEQUENTIAL rgb = _cmap_to_rgb(mplmap, values) hexcolors = [webcolors.rgb_to_hex(np.array(c)) for c in rgb] return hexcolors
def test_dimensional_array_from_mixed_data(): data = [ np.arange(3) * u.angstrom, [1.0 * u.nm, 1.0 * u.nm**2 / u.angstrom, 3.0 * u.ureg.km], [1, 1, 1] * u.bohr ] arr = u.array(data) bohr_in_ang = (1.0 * u.bohr).value_in(u.angstrom) expected = u.angstrom * [[0, 1, 2], [10.0, 100.0, 3.0e13], [bohr_in_ang, bohr_in_ang, bohr_in_ang]] np.testing.assert_allclose(arr, expected) assert isinstance(arr, units.MdtQuantity)
def __getattr__(self, item): # TODO: remove and replace all __getattr__ if item in ('traj', 'index', 'real_atom'): raise AttributeError('_TrajAtom.%s not assigned (pickle issue?)' % item) try: # try to get a time-dependent version trajslice = getattr(self.traj, item) except AttributeError: # not time-dependent - look for an atomic property return getattr(self.real_atom, item) if trajslice[0]['type'] != 'atomic': raise ValueError('%s is not an atomic quantity' % item) else: return u.array([f[self.real_atom] for f in trajslice])
def colormap(cats, mplmap='auto', categorical=None): """ Map a series of categories to hex colors, using a matplotlib colormap Generates both categorical and numerical colormaps. Args: cats (Iterable): list of categories or numerical values mplmap (str): name of matplotlib colormap object categorical (bool): if True, interpret this data as categorical. If False, interpret the data as numerical values (data must be convertible to float) Returns: List[str]: List of hexadecimal RGB color values in the in the form ``'#000102'`` """ # Should automatically choose the right colormaps for: # categorical data # sequential data (low, high important) # diverging data (low, mid, high important) global DEF_SEQUENTIAL from matplotlib import cm if hasattr(cm, 'inferno'): DEF_SEQUENTIAL = 'inferno' else: DEF_SEQUENTIAL = 'BrBG' # strip units units = None # TODO: build a color bar with units if hasattr(cats[0], 'magnitude'): arr = u.array(cats) units = arr.units cats = arr.magnitude is_categorical = False else: is_categorical = not isinstance(cats[0], float) if categorical is not None: is_categorical = categorical if is_categorical: values = _map_categories_to_ints(cats) if mplmap == 'auto': mplmap = DEF_CATEGORICAL else: values = np.array(map(float, cats)) if mplmap == 'auto': mplmap = DEF_SEQUENTIAL rgb = _cmap_to_rgb(mplmap, values) hexcolors = [webcolors.rgb_to_hex(np.array(c)) for c in rgb] return hexcolors
def test_vectorized_gaussian_function_evaluations(objkey, request): g = request.getfuncargvalue(objkey) coords = np.zeros((5, g.ndims)) * g.center.units for i in xrange(5): coords[i] = g.center randoffset = 4.0 * (random.random() - 0.5) * g.exp**-0.5 idim = random.randrange(g.ndims) coords[i, idim] += randoffset vector_results = g(coords) expected = u.array([g(c) for c in coords]) _assert_almost_equal(vector_results, expected, decimal=8)
def test_pyscf_and_mdt_overlaps_are_the_same(h2_rhf_augccpvdz): mol = h2_rhf_augccpvdz basis = mol.wfn.aobasis calc_overlap_mat = [] for i in range(len(basis)): calc_overlap_mat.append( [basis[i].overlap(basis[j]) for j in range(len(basis))]) overlaps = u.array(calc_overlap_mat) assert isinstance(overlaps, np.ndarray) or overlaps.units == u.dimensionless np.testing.assert_allclose(mol.wfn.aobasis.overlaps, overlaps, atol=5.0e-7)
def kinetic_energy(self): convert_units = True energies = [] for frame in self.frames: if 'momenta' in frame: energies.append( helpers.kinetic_energy(frame.momenta, self.mol.dim_masses)) else: convert_units = False energies.append(None) if convert_units: arr = u.array(energies) return u.default.convert(arr) else: return energies
def kinetic_temperature(self): convert_units = True temps = [] energies = self.kinetic_energy dof = self.mol.dynamic_dof for energy, frame in zip(energies, self.frames): if energy is not None: temps.append(helpers.kinetic_temperature(energy, dof)) else: convert_units = False temps.append(None) if convert_units: arr = u.array(temps) return u.default.convert(arr) else: return temps
def test_pyscf_and_mdt_overlaps_are_the_same(h2_rhf_augccpvdz): mol = h2_rhf_augccpvdz basis = mol.wfn.aobasis calc_overlap_mat = [] for i in range(len(basis)): calc_overlap_mat.append( [basis[i].overlap(basis[j]) for j in range(len(basis))] ) overlaps = u.array(calc_overlap_mat) assert isinstance(overlaps, np.ndarray) or overlaps.units == u.dimensionless np.testing.assert_allclose(mol.wfn.aobasis.overlaps, overlaps, atol=5.0e-7)
def colormap(cats, mplmap='auto'): # should make it easy to choose one for: # categorical data # sequential (low, high important) # diverging data (low, mid, high important) # Can deal with numerical and categorical data # we'll treat ints as categories for now global DEF_SEQUENTIAL from matplotlib import cm if hasattr(cm, 'inferno'): DEF_SEQUENTIAL = 'inferno' else: DEF_SEQUENTIAL = 'BrBG' # strip units units = None if hasattr(cats[0], 'magnitude'): arr = u.array(cats) units = arr.units cats = arr.magnitude if not isinstance(cats, np.ndarray) and not isinstance(cats[0], float): # treat as # categorical values = np.zeros(len(cats), dtype='float') to_int = collections.OrderedDict() for i, item in enumerate(cats): if item not in to_int: to_int[item] = len(to_int) values[i] = to_int[item] if mplmap == 'auto': mplmap = DEF_CATEGORICAL else: # it's numerical values = np.array(cats, dtype='float') if mplmap == 'auto': mplmap = DEF_SEQUENTIAL cmap = getattr(cm, mplmap) mx = values.max() mn = values.min() r = (values - mn) / (mx - mn) # rescale to [0.0,1.0] rgb = cmap(r) hexcolors = [webcolors.rgb_to_hex(np.array(r[:3]) * 256) for r in rgb] return hexcolors
def test_volumetric_grid_point_list(key, request): ranges = request.getfixturevalue(key) grid = mathutils.VolumetricGrid(*ranges, xpoints=3, ypoints=4, zpoints=5) assert (grid.xpoints, grid.ypoints, grid.zpoints) == (3,4,5) pl = list(grid.iter_points()) pa = grid.allpoints() assert (u.array(pl) == pa).all() for i in range(3): assert pa[:,i].min() == ranges[i][0] assert pa[:,i].max() == ranges[i][1] assert grid.npoints == 3*4*5 assert len(pl) == grid.npoints for idim in range(3): for ipoint in range(1,grid.points[idim]): helpers.assert_almost_equal(grid.spaces[idim][ipoint] - grid.spaces[idim][ipoint-1], grid.deltas[idim])
def test_volumetric_grid_point_list(key, request): ranges = request.getfixturevalue(key) grid = mathutils.VolumetricGrid(*ranges, xpoints=3, ypoints=4, zpoints=5) assert (grid.xpoints, grid.ypoints, grid.zpoints) == (3, 4, 5) pl = list(grid.iter_points()) pa = grid.allpoints() assert (u.array(pl) == pa).all() for i in range(3): assert pa[:, i].min() == ranges[i][0] assert pa[:, i].max() == ranges[i][1] assert grid.npoints == 3 * 4 * 5 assert len(pl) == grid.npoints for idim in range(3): for ipoint in range(1, grid.points[idim]): helpers.assert_almost_equal( grid.spaces[idim][ipoint] - grid.spaces[idim][ipoint - 1], grid.deltas[idim])
def test_pyscf_orbital_grid_works(h2_rhf_augccpvdz): """ Tests the basic input/output of the pyscf basis_values function Doesn't actually test the values directly - just that the answers are mathematically consistent """ mol = h2_rhf_augccpvdz wfn = mol.wfn nbasis = len(wfn.aobasis) coords = u.array([ mol.com, np.zeros(3) * u.angstrom, 10.0 * np.ones(3) * u.angstrom, np.ones(3) * u.nm ]) # First - check that the shape is appropriate if called without orbital coefficients values_nocoeffs = basis_values(mol, wfn.aobasis, coords) assert values_nocoeffs.shape == (len(coords), nbasis) assert (values_nocoeffs[-1] == values_nocoeffs[-2] ).all() # these 2 coordinates are the same # Second - explicitly send orbital coefficients for first 2 basis functions coeffs = np.zeros((2, nbasis)) coeffs[:2, :2] = np.identity(2) vals_b0 = basis_values(mol, wfn.aobasis, coords, coeffs=coeffs) assert vals_b0.shape == (len(coords), len(coeffs)) np.testing.assert_allclose(values_nocoeffs[:, :2], vals_b0) # Third - send symmetric and anti-symmetric combinations of basis functions and check answers plusminus = np.zeros((2, nbasis)) plusminus[:2, :2] = 1.0 / np.sqrt(2) plusminus[1, 1] = -1.0 / np.sqrt(2) vals_plusminus = basis_values(mol, wfn.aobasis, coords, coeffs=plusminus) assert vals_plusminus.shape == (len(coords), len(coeffs)) helpers.assert_almost_equal(vals_plusminus[:, 0], (vals_b0[:, 0] + vals_b0[:, 1]) / np.sqrt(2)) helpers.assert_almost_equal(vals_plusminus[:, 1], (vals_b0[:, 0] - vals_b0[:, 1]) / np.sqrt(2))
def slice_frames(self, key, missing=None): """ Return an array of giving the value of ``key`` at each frame. Args: key (str): name of the property, e.g., time, potential_energy, annotation, etc missing: value to return if a given frame does not have this property Returns: moldesign.units.Vector: vector containing the value at each frame, or the value given in the ``missing`` keyword) (len= `len(self)` ) """ has_units = True result = [] for f in self.frames: val = f.get(key, None) if not issubclass(type(val), u.MdtQuantity): has_units = False result.append(val) if has_units: result = u.array([frame.get(key, None) for frame in self.frames]) return u.default.convert(result) else: return np.array(result)
def __getattr__(self, item): if item in ('traj', 'index', 'real_atom'): raise AttributeError('_TrajAtom.%s not assigned (pickle issue?)' % item) if item in self.ATOMIC_ARRAYS: is_array = True item = self.ATOMIC_ARRAYS[item] else: is_array = False try: # try to get a time-dependent version trajslice = getattr(self.traj, item) except AttributeError: # not time-dependent - look for an atomic property return getattr(self.real_atom, item) if is_array: return trajslice[:, self.index, :] else: if trajslice[0]['type'] != 'atomic': raise ValueError('%s is not an atomic quantity' % item) else: return u.array([f[self.real_atom] for f in trajslice])
def test_pyscf_orbital_grid_works(h2_rhf_augccpvdz): """ Tests the basic input/output of the pyscf basis_values function Doesn't actually test the values directly - just that the answers are mathematically consistent """ mol = h2_rhf_augccpvdz wfn = mol.wfn nbasis = len(wfn.aobasis) coords = u.array([mol.com, np.zeros(3)*u.angstrom, 10.0 * np.ones(3) * u.angstrom, np.ones(3)*u.nm]) # First - check that the shape is appropriate if called without orbital coefficients values_nocoeffs = basis_values(mol, wfn.aobasis, coords) assert values_nocoeffs.shape == (len(coords), nbasis) assert (values_nocoeffs[-1] == values_nocoeffs[-2]).all() # these 2 coordinates are the same # Second - explicitly send orbital coefficients for first 2 basis functions coeffs = np.zeros((2, nbasis)) coeffs[:2, :2] = np.identity(2) vals_b0 = basis_values(mol, wfn.aobasis, coords, coeffs=coeffs) assert vals_b0.shape == (len(coords), len(coeffs)) np.testing.assert_allclose(values_nocoeffs[:,:2], vals_b0) # Third - send symmetric and anti-symmetric combinations of basis functions and check answers plusminus = np.zeros((2, nbasis)) plusminus[:2, :2] = 1.0 / np.sqrt(2) plusminus[1,1] = -1.0 / np.sqrt(2) vals_plusminus = basis_values(mol, wfn.aobasis, coords, coeffs=plusminus) assert vals_plusminus.shape == (len(coords), len(coeffs)) helpers.assert_almost_equal(vals_plusminus[:,0], (vals_b0[:,0] + vals_b0[:,1])/np.sqrt(2)) helpers.assert_almost_equal(vals_plusminus[:,1], (vals_b0[:,0] - vals_b0[:,1])/np.sqrt(2))
def test_container_properties(fixturename, request): obj = request.getfixturevalue(fixturename) assert obj.mass == sum([atom.mass for atom in obj.atoms]) np.testing.assert_array_equal(obj.positions.defunits(), u.array([atom.position for atom in obj.atoms]).defunits()) assert obj.num_atoms == len(obj.atoms)
def test_inconsistent_array_units_raises_dimensionality_error(): with pytest.raises(units.DimensionalityError): u.array([[1, 2, 3] * u.angstrom, [3 * u.bohr, 4 * u.eV, 5 * u.bohr]])
def __get__(self, instance, owner): return u.array( [getattr(atom, self.attrname) for atom in instance.atoms])
def test_mixed_units_and_nonunits_raises_dimensionality_error(): with pytest.raises(units.DimensionalityError): u.array([[1, 2, 3] * u.angstrom, [3 * u.bohr, 4, 5 * u.bohr]])
def test_no_nonsquare_arrays(): with pytest.raises(ValueError): u.array([[1, 2], [3]])
def __get__(self, instance, owner): return u.array([getattr(atom, self.attrname) for atom in instance.atoms])
def parameterize(mol, charges='esp', ffname='gaff2', **kwargs): """Parameterize ``mol``, typically using GAFF parameters. This will both assign a forcefield to the molecule (at ``mol.ff``) and produce the parameters so that they can be used in other systems (e.g., so that this molecule can be simulated embedded in a larger protein) Note: 'am1-bcc' and 'gasteiger' partial charges will be automatically computed if necessary. Other charge types must be precomputed. Args: mol (moldesign.Molecule): charges (str or dict): what partial charges to use? Can be a dict (``{atom:charge}``) OR a string, in which case charges will be read from ``mol.properties.[charges name]``; typical values will be 'esp', 'mulliken', 'am1-bcc', etc. Use 'zero' to set all charges to 0 (for QM/MM and testing) ffname (str): Name of the gaff-like forcefield file (default: gaff2) Returns: ExtraAmberParameters: Parameters for the molecule; this object can be used to create forcefield parameters for other systems that contain this molecule """ # Check that there's only 1 residue, give it a name assert mol.num_residues == 1 if mol.residues[0].resname is None: mol.residues[0].resname = 'UNL' print 'Assigned residue name "UNL" to %s' % mol resname = mol.residues[0].resname # check that atoms have unique names if len(set(atom.name for atom in mol.atoms)) != mol.num_atoms: raise ValueError( 'This molecule does not have uniquely named atoms, cannot assign FF' ) if charges == 'am1-bcc' and 'am1-bcc' not in mol.properties: calc_am1_bcc_charges(mol) elif charges == 'gasteiger' and 'gasteiger' not in mol.properties: calc_gasteiger_charges(mol) if charges == 'zero': charge_array = [0.0 for atom in mol.atoms] elif isinstance(charges, basestring): charge_array = u.array( [mol.properties[charges][atom] for atom in mol.atoms]) if not charge_array.dimensionless: # implicitly convert floats to fundamental charge units charge_array = charge_array.to(u.q_e).magnitude else: charge_array = [charges[atom] for atom in mol.atoms] inputs = { 'mol.mol2': mol.write(format='mol2'), 'mol.charges': '\n'.join(map(str, charge_array)) } cmds = [ 'antechamber -i mol.mol2 -fi mol2 -o mol_charged.mol2 ' ' -fo mol2 -c rc -cf mol.charges -rn %s' % resname, 'parmchk -i mol_charged.mol2 -f mol2 -o mol.frcmod', 'tleap -f leap.in', 'sed -e "s/tempresname/%s/g" mol_rename.lib > mol.lib' % resname ] inputs['leap.in'] = '\n'.join([ "source leaprc.%s" % ffname, "tempresname = loadmol2 mol_charged.mol2", "fmod = loadamberparams mol.frcmod", "check tempresname", "saveoff tempresname mol_rename.lib", "saveamberparm tempresname mol.prmtop mol.inpcrd", "quit\n" ]) def finish_job(j): param = ExtraAmberParameters(j.get_output('mol.lib'), j.get_output('mol.frcmod'), j) tempmol = mdt.assign_forcefield(mol, parameters=param) mol.ff = tempmol.ff return param job = pyccc.Job(image=mdt.compute.get_image_path(IMAGE), command=' && '.join(cmds), inputs=inputs, when_finished=finish_job, name="GAFF assignment: %s" % mol.name) return mdt.compute.run_job(job, _return_result=True, **kwargs)
arr[3] = 5.0 with pytest.raises(units.DimensionalityError): arr[:] = np.arange(5, 10) * units.fs arr[2:3] = np.arange(5, 6) * units.ureg.angstrom / units.ureg.fs np.testing.assert_allclose(arr[2:3].magnitude, np.arange(5, 6) / 10.0) arr[:] = np.arange(10, 15) * units.ureg.micrometers / units.ureg.picoseconds np.testing.assert_allclose(arr.magnitude, np.arange(10, 15)) @pytest.mark.parametrize( ['a1', 'a2'], ((np.ones(3), np.ones(3) * 10 / 10), (np.ones(3), u.array(np.ones(3))), (np.ones(3) * u.nm, 10.0 * np.ones(3) * u.angstrom), (np.ones( (3, 3)) * u.nm / u.angstrom, np.ones(3) * 10)), ids="numpy-numpy numpy-dimensionless nm-angstrom nm/angstrom-numpy".split( )) def test_array_almost_equal_returns_true(a1, a2): assert u.arrays_almost_equal(a1, a2) assert u.arrays_almost_equal(a2, a1) @pytest.mark.parametrize( ['a1', 'a2'], ((np.ones(3), np.ones(3) * 10 / 10 * u.eV), (np.ones(3) * u.nm, 10.0 * np.ones(3) * u.dalton), (u.array(np.ones(3)), np.ones(3) * u.kcalpermol)), ids="numpy-ev nm-dalton dimensionless-kcalpermole".split()) def test_array_almost_equal_raises_dimensionality_error(a1, a2):
def parameterize(mol, charges='esp', ffname='gaff2', **kwargs): """Parameterize ``mol``, typically using GAFF parameters. This will both assign a forcefield to the molecule (at ``mol.ff``) and produce the parameters so that they can be used in other systems (e.g., so that this molecule can be simulated embedded in a larger protein) Note: 'am1-bcc' and 'gasteiger' partial charges will be automatically computed if necessary. Other charge types must be precomputed. Args: mol (moldesign.Molecule): charges (str or dict): what partial charges to use? Can be a dict (``{atom:charge}``) OR a string, in which case charges will be read from ``mol.properties.[charges name]``; typical values will be 'esp', 'mulliken', 'am1-bcc', etc. Use 'zero' to set all charges to 0 (for QM/MM and testing) ffname (str): Name of the gaff-like forcefield file (default: gaff2) Returns: ExtraAmberParameters: Parameters for the molecule; this object can be used to create forcefield parameters for other systems that contain this molecule """ # Check that there's only 1 residue, give it a name assert mol.num_residues == 1 if mol.residues[0].resname is None: mol.residues[0].resname = 'UNL' print 'Assigned residue name "UNL" to %s' % mol resname = mol.residues[0].resname # check that atoms have unique names if len(set(atom.name for atom in mol.atoms)) != mol.num_atoms: raise ValueError('This molecule does not have uniquely named atoms, cannot assign FF') if charges == 'am1-bcc' and 'am1-bcc' not in mol.properties: calc_am1_bcc_charges(mol) elif charges == 'gasteiger' and 'gasteiger' not in mol.properties: calc_gasteiger_charges(mol) if charges == 'zero': charge_array = [0.0 for atom in mol.atoms] elif isinstance(charges, basestring): charge_array = u.array([mol.properties[charges][atom] for atom in mol.atoms]) if not charge_array.dimensionless: # implicitly convert floats to fundamental charge units charge_array = charge_array.to(u.q_e).magnitude else: charge_array = [charges[atom] for atom in mol.atoms] inputs = {'mol.mol2': mol.write(format='mol2'), 'mol.charges': '\n'.join(map(str, charge_array))} cmds = ['antechamber -i mol.mol2 -fi mol2 -o mol_charged.mol2 ' ' -fo mol2 -c rc -cf mol.charges -rn %s' % resname, 'parmchk -i mol_charged.mol2 -f mol2 -o mol.frcmod', 'tleap -f leap.in', 'sed -e "s/tempresname/%s/g" mol_rename.lib > mol.lib' % resname] inputs['leap.in'] = '\n'.join(["source leaprc.%s" % ffname, "tempresname = loadmol2 mol_charged.mol2", "fmod = loadamberparams mol.frcmod", "check tempresname", "saveoff tempresname mol_rename.lib", "saveamberparm tempresname mol.prmtop mol.inpcrd", "quit\n"]) def finish_job(j): param = ExtraAmberParameters(j.get_output('mol.lib'), j.get_output('mol.frcmod'), j) tempmol = mdt.assign_forcefield(mol, parameters=param) mol.ff = tempmol.ff return param job = pyccc.Job(image=mdt.compute.get_image_path(IMAGE), command=' && '.join(cmds), inputs=inputs, when_finished=finish_job, name="GAFF assignment: %s" % mol.name) return mdt.compute.run_job(job, _return_result=True, **kwargs)