def calculate_dipoles(self): """ Returns ------- """ local_indices = np.array([(a.index, a.residue.atom(0).index) for a in self.structure.top.atoms], dtype='int32') local_displacements = md.compute_displacements(self.structure, local_indices, periodic=True) molecule_indices = np.array([(a.residue.atom(0).index, 0) for a in self.structure.top.atoms], dtype='int32') molecule_displacements = md.compute_displacements(self.structure, molecule_indices, periodic=True) xyz = local_displacements + molecule_displacements conf_dipoles = {} for conf in self.conformer_atom_data: conf_at_indices = np.asarray( [l[0] for l in self.conformer_atom_data[conf]]) conf_charges = np.asarray( [l[1] for l in self.conformer_atom_data[conf]]) conf_xyz = xyz[0, conf_at_indices, :] moments = conf_xyz.transpose(1, 0).dot(conf_charges) moments *= 10.0 conf_dipoles[conf] = moments return conf_dipoles
def dipole_moments(traj, charges): """Calculate the dipole moments of each frame in a trajectory. Parameters ---------- traj : Trajectory An mdtraj trajectory. charges : np.ndarray, shape=(n_atoms), dtype=float Charges of each atom in the trajectory, expressed in units of the elementary charge constant. Returns ------- moments : np.ndarray, shape=(n_frames, 3), dtype=float Dipole moments of trajectory, units of nm * elementary charge. Notes ----- This code works by first calculating displacements relative to the first atom in each residue (e.g. local frame). Then, this is added to the PBC-corrected displacement between the first atom in the two molecules. This total displacement is then used as to calculate the box dipole moment. """ local_indices = np.array([(a.index, a.residue.atom(0).index) for a in traj.top.atoms], dtype='int32') local_displacements = md.compute_displacements(traj, local_indices, periodic=True) molecule_indices = np.array([(a.residue.atom(0).index, 0) for a in traj.top.atoms], dtype='int32') molecule_displacements = md.compute_displacements(traj, molecule_indices, periodic=True) xyz = local_displacements + molecule_displacements moments = xyz.transpose(0, 2, 1).dot(charges) return moments
def dipole_moments_md(traj, charges): local_indices = np.array([(a.index, a.residue.atom(0).index) for a in traj.top.atoms], dtype='int32') local_displacements = md.compute_displacements(traj, local_indices, periodic=False) molecule_indices = np.array([(a.residue.atom(0).index, 0) for a in traj.top.atoms], dtype='int32') molecule_displacements = md.compute_displacements(traj, molecule_indices, periodic=False) xyz = local_displacements + molecule_displacements moments = xyz.transpose(0, 2, 1).dot(charges) return moments
def _run_amber_traj(traj, ext_ref): # Test triclinic case where simple approach in Tuckerman text does not # always work distopt = md.compute_distances(traj, [[0, 9999]], opt=True) distslw = md.compute_distances(traj, [[0, 9999]], opt=False) dispopt = md.compute_displacements(traj, [[0, 9999]], opt=True) dispslw = md.compute_displacements(traj, [[0, 9999]], opt=False) eq(distopt, distslw, decimal=5) eq(dispopt, dispslw, decimal=5) assert_allclose(distopt.flatten(), ext_ref, atol=2e-5) # Make sure distances from displacements are the same eq(np.sqrt((dispopt.squeeze()**2).sum(axis=1)), distopt.squeeze()) eq(np.sqrt((dispslw.squeeze()**2).sum(axis=1)), distslw.squeeze()) eq(dispopt, dispslw, decimal=5)
def _run_amber_traj(trajname, ext_ref): # Test triclinic case where simple approach in Tuckerman text does not # always work traj = md.load(get_fn(trajname), top=get_fn("test.parm7")) distopt = md.compute_distances(traj, [[0, 9999]], opt=True) distslw = md.compute_distances(traj, [[0, 9999]], opt=False) dispopt = md.compute_displacements(traj, [[0, 9999]], opt=True) dispslw = md.compute_displacements(traj, [[0, 9999]], opt=False) eq(distopt, distslw, decimal=5) eq(dispopt, dispslw, decimal=5) assert_allclose(distopt.flatten(), ext_ref, atol=2e-5) # Make sure distances from displacements are the same eq(np.sqrt((dispopt.squeeze() ** 2).sum(axis=1)), distopt.squeeze()) eq(np.sqrt((dispslw.squeeze() ** 2).sum(axis=1)), distslw.squeeze()) eq(dispopt, dispslw, decimal=5)
def dangling(frame, dictionary, sign, dist, distOSN, idx, idxOSN, normals): pair_ox = np.asarray(list(itertools.product(idx, idxOSN))) # remove pairs of same index pair_ox = pair_ox[np.std(pair_ox, axis=1) != 0] dist_ox = md.compute_distances(frame, pair_ox).reshape(idx.size, -1) h1o = np.c_[idx, idx + 1] h2o = np.c_[idx, idx + 2] # compute H-O vectors vec_h1o = md.compute_displacements(frame, h1o).reshape(-1, 3) vec_h2o = md.compute_displacements(frame, h2o).reshape(-1, 3) cosine_h1o = np.einsum('ij,ij->i', vec_h1o, normals) / np.linalg.norm( vec_h1o, axis=1) cosine_h2o = np.einsum('ij,ij->i', vec_h2o, normals) / np.linalg.norm( vec_h2o, axis=1) angle_h1o = np.arccos(np.clip(cosine_h1o, -1, 1)) / np.pi * 180 angle_h2o = np.arccos(np.clip(cosine_h2o, -1, 1)) / np.pi * 180 h1ox = np.c_[np.asarray(pair_ox)[:, 0] + 1, np.asarray(pair_ox)[:, 0], np.asarray(pair_ox)[:, 1]] h2ox = np.c_[np.asarray(pair_ox)[:, 0] + 2, np.asarray(pair_ox)[:, 0], np.asarray(pair_ox)[:, 1]] # compute H-O...O angles angle_h1ox = md.compute_angles(frame, h1ox).reshape(idx.size, -1) / np.pi * 180 angle_h2ox = md.compute_angles(frame, h2ox).reshape(idx.size, -1) / np.pi * 180 # selection of dangling OH based on R-beta definition (DOI: 10.1021/acs.jctc.7b00566) Rc = dist_ox <= .35 beta1 = angle_h1ox <= 50 beta2 = angle_h2ox <= 50 angles_ho = np.append(angle_h1o[np.sum(beta1 * Rc, axis=1) == 0], angle_h2o[np.sum(beta2 * Rc, axis=1) == 0]) dictionary['ndang'] = np.append(dictionary['ndang'], angles_ho.size) dictionary['all'] = np.append(dictionary['all'], idx.size) hist, _ = np.histogram(angles_ho, bins=np.arange(0, 181, 2), density=False) dictionary['theta'] += hist
def test_dipole_moments(): traj = md.load(get_fn("tip3p_300K_1ATM.xtc"), top=get_fn("tip3p_300K_1ATM.pdb")) charges = np.tile(tip3p_charges, traj.n_residues) moments0 = md.geometry.dipole_moments(traj, charges) # Now we screw up the molecule wholeness referencing all distances relative to atom zero. atom_indices = np.array([np.zeros(traj.n_atoms), np.arange(traj.n_atoms)], dtype='int32').T # E.g. [[0, 0], [0, 1], [0, 2], [0, 3]]... xyz = md.compute_displacements(traj, atom_indices, periodic=True) # Define coordinates relative to atom 0, PBC corrected. traj.xyz = xyz moments1 = md.geometry.dipole_moments(traj, charges) eq(moments0, moments1, decimal=4)
def test_static_dielectric(): traj = md.load(get_fn("tip3p_300K_1ATM.xtc"), top=get_fn("tip3p_300K_1ATM.pdb")) charges = np.tile(tip3p_charges, traj.n_residues) epsilon0 = md.geometry.static_dielectric(traj, charges, temperature) atom_indices = np.array([np.zeros(traj.n_atoms), np.arange(traj.n_atoms)], dtype='int32').T # E.g. [[0, 0], [0, 1], [0, 2], [0, 3]]... xyz = md.compute_displacements(traj, atom_indices, periodic=True) # Define coordinates relative to atom 0, PBC corrected. traj.xyz = xyz epsilon1 = md.geometry.static_dielectric(traj, charges, temperature) eq(epsilon0, epsilon1, decimal=3) reference = 87.1818 # From gromacs, see above comment assert abs((epsilon1 - reference) / reference) < 1E-3, "Dielectric tolerance not met!"
def cosine(frame, dist, atom, normals, dictionary, sign, edges, toM, layers): selection_string = 'name ' + dictionary['pair'][ 0] + ' or name ' + dictionary['pair'][1] pair = np.array(frame.top.select(selection_string)).reshape(-1, 2) vec = md.compute_displacements(frame, pair).reshape(-1, 3) cosine = np.einsum('ij,ij->i', vec, normals) / np.linalg.norm(vec, axis=1) if atom == 'C2': cosine = -cosine hist, _ = np.histogram(dist, bins=edges, weights=cosine, density=False) dictionary['cosine'] += hist * toM angle = np.arccos(np.clip(cosine, -1, 1)) / np.pi * 180 for i in range(len(layers) - 1): mask = np.logical_and(dist > layers[i], dist < layers[i + 1]) hist, _ = np.histogram(angle[mask], bins=np.arange(0, 181, 2), density=False) dictionary['theta'][i] += hist
def calc_solute_solvent_displacements(self, traj, solute_name, solute_center_of_mass, solvent_name, solvent_center_of_mass, periodic = True): ''' This function calculates the displacements between solute and solvent center of masses using md.traj's function of displacement. First, the first atoms of the solute and solvent are found. Then, we copy the trajectory, change the coordinates to match center of masses of solute and solvent, then calculate displacements between solute and solvent. INPUTS: traj: trajectory from md.traj solute_name: name of the solute as a string solute_center_of_mass: numpy array containing the solute center of mass solvent_name: name of the solvent as a string solvent_center_of_mass: numpy array containing solvent center of mass OUTPUTS: displacements: displacements as a time frame x number of displacemeny numpy float ''' print("--- CALCULATING SOLUTE(%s) and SOLVENT(%s) DISPLACEMENTS"%(solute_name, solvent_name)) ## COPYING TRAJECTORY copied_traj=traj[:] ## FINDING FIRST ATOM INDEX FOR ALL SOLUTE AND SOLVENT Solute_first_atoms = self.find_first_atom_index(traj = traj, residue_name = solute_name ) Solvent_first_atoms = self.find_first_atom_index(traj = traj, residue_name = solvent_name ) ## CHANGING POSITION OF THE SOLUTE copied_traj.xyz[:, Solute_first_atoms] = solute_center_of_mass[:] if self.map_type == 'COM': ## CHANGING POSITIONS OF SOLVENT copied_traj.xyz[:, Solvent_first_atoms] = solvent_center_of_mass[:] ## CREATING ATOM PAIRS BETWEEN SOLUTE AND SOLVENT COM atom_pairs = [ [Solute_first_atoms[0], x] for x in Solvent_first_atoms] else: ## ALL OTHER OPTIONS #self.map_type == 'allatom' or self.map_type == 'allatomwithsoluteoxygen' or self.map_type == '3channel_oxy': ## FINDING ALL ATOMS num_solvent, solvent_index = calc_tools.find_total_atoms(traj, solvent_name) ## CREATING ATOM PAIRS BETWEEN SOLUTE AND EACH SOLVENT atom_pairs = [ [Solute_first_atoms[0], x] for x in solvent_index] ## FINDING THE DISPLACEMENTS USING MD TRAJ -- Periodic is true displacements = md.compute_displacements( traj=copied_traj, atom_pairs = atom_pairs, periodic = periodic) return displacements
def test_dipole_moments(get_fn): traj = md.load(get_fn("tip3p_300K_1ATM.xtc"), top=get_fn("tip3p_300K_1ATM.pdb")) charges = np.tile(tip3p_charges, traj.n_residues) moments0 = md.geometry.dipole_moments(traj, charges) # Now we screw up the molecule wholeness referencing all distances relative to atom zero. # E.g. [[0, 0], [0, 1], [0, 2], [0, 3]]... atom_indices = np.array([np.zeros(traj.n_atoms), np.arange(traj.n_atoms)], dtype='int32').T # Define coordinates relative to atom 0, PBC corrected. xyz = md.compute_displacements(traj, atom_indices, periodic=True) traj.xyz = xyz moments1 = md.geometry.dipole_moments(traj, charges) eq(moments0, moments1, decimal=4)
def test_static_dielectric(get_fn): traj = md.load(get_fn("tip3p_300K_1ATM.xtc"), top=get_fn("tip3p_300K_1ATM.pdb")) charges = np.tile(tip3p_charges, traj.n_residues) epsilon0 = md.geometry.static_dielectric(traj, charges, temperature) # E.g. [[0, 0], [0, 1], [0, 2], [0, 3]]... atom_indices = np.array([np.zeros(traj.n_atoms), np.arange(traj.n_atoms)], dtype='int32').T # Define coordinates relative to atom 0, PBC corrected. xyz = md.compute_displacements(traj, atom_indices, periodic=True) traj.xyz = xyz epsilon1 = md.geometry.static_dielectric(traj, charges, temperature) eq(epsilon0, epsilon1, decimal=3) reference = 87.1818 # From gromacs, see above comment assert abs((epsilon1 - reference) / reference) < 1E-3, "Dielectric tolerance not met!"
def __optimizer(self, coords, centers): """Perform the minimization scheme Parameters ---------- coords : 3*N array(float) Coordinates of all solvate molecules centers : array(int) Containing all metal centers which has bound solvate in the same order as `coords` Raises ------ ValueError If one or more coordinates are missing centers Returns ------- coords Optimized oxygen coordinates vectors Optimized M-O vectors """ self.__numOfOH = len(self.__hydCenters) self.__numOfOH2 = len(self.__watCenters) vectors = np.empty([0, 3], dtype=float) cutoff = 6.0 M = self.topol.trj.xyz[0][centers] * 10 if (len(M) != len(coords)): raise ValueError("Internal error: some solvate molecules were\ not assigned to a center.") # Drops duplicates centerCoordination = np.array(self.topol.bondgraph.\ loc[self.topol.bondgraph['j'].\ isin(centers)]['j'].value_counts()) missing = len(centers) - len(centerCoordination) # Readd duplicates if (missing > 0): centerCoordination = np.append(centerCoordination,\ centerCoordination[\ len(centerCoordination)-missing:]) centerNeighbours = np.empty([0, 3], dtype=float) centerNumNeighbours = np.empty([0], dtype=int) # Get neighbours to each center self.__verboseprint("Calculating guesses for the optimizer:") i = 0 self.__j = len(centers) for center in centers: neighbours = md.compute_neighbors(self.topol.trj, cutoff/10.0,\ np.array([center])) centerNumNeighbours = np.hstack((centerNumNeighbours,\ len(neighbours[0]))) centerArray = np.repeat(center, len(neighbours[0])) dispArray = np.vstack((neighbours, centerArray)) dispArray = np.transpose(dispArray) dispVectors = md.compute_displacements(self.topol.trj, dispArray,\ periodic = True)[0] neighbourCoords = -dispVectors*10 +\ self.topol.trj.xyz[0][center]*10 centerNeighbours = np.vstack((centerNeighbours, -dispVectors*10 +\ self.topol.trj.xyz[0][center]*10)) res = minimize(potential.potential, coords[i], args = (np.asarray([center]), self.topol,\ np.asarray(np.vstack((neighbourCoords,\ np.delete(coords, i, axis=0))), dtype=float),\ np.asarray([len(neighbours[0])+\ len(coords)-1]),\ self.boxVectors, np.append(self.__dMOH,\ self.__dMOH2), np.asarray(np.hstack((self.__numOfOH,\ self.__numOfOH2)))), jac = potential.potential_jac, callback=self.__show_progress_prep, method = self.solver, options=self.prepOptions) coords[i] = res.x i += 1 self.__i += 1 self.__verboseprint("\n") self.__i = 0 self.__centers = centers self.__centerNeighbours = centerNeighbours self.__centerNumNeighbours = centerNumNeighbours # Timings for objective function start = timer() potential.potential(coords.flatten(), centers, self.topol,\ centerNeighbours, centerNumNeighbours,\ self.boxVectors, np.append(self.__dMOH,\ self.__dMOH2), np.asarray(np.hstack((self.__numOfOH,\ self.__numOfOH2)))) end = timer() self.__potTime = end - start self.__verboseprint("Potential takes: " + str(end-start) +\ " seconds to calculate") start = timer() potential.potential_jac(coords.flatten(), centers, self.topol,\ centerNeighbours, centerNumNeighbours,\ self.boxVectors, np.append(self.__dMOH,\ self.__dMOH2), np.asarray(np.hstack((self.__numOfOH,\ self.__numOfOH2)))) end = timer() self.__jacPotTime = end - start self.__verboseprint("Potential Jacobian takes: " + str(end-start) +\ " seconds to calculate\n") self.__verboseprint("Minimizing potential with " +\ str(len(coords.flatten())) + " free variables...") # Run minimization self.__start = timer() res = minimize(potential.potential, coords, args = (centers, self.topol, centerNeighbours,\ centerNumNeighbours, self.boxVectors, np.append(self.__dMOH,\ self.__dMOH2), np.asarray(np.hstack((self.__numOfOH,\ self.__numOfOH2)))), jac = potential.potential_jac, method = self.solver, callback = self.__show_progress, options=self.optOptions) self.__verboseprint("\n") if (res.success): if (not self.silent): self.__verboseputs.success("Successfully minimized"+\ " potential!\n") else: self.__verboseputs.warning("Optimization failed... Try with"+\ " different optimizer using the"+\ " -solver flag or increasing max"+\ " iterations with -maxiter\n") if (self.optLog): file = open('minimization.log', 'w') file.write("Iteration:\tFunction value:\n") for ind, line in enumerate(self.__optimizationLog): file.write(str(ind + 1) + " \t" + str(line) + "\n") print("\n") print("Output from Minimizer:") file.write(str(res) + "\n") file.close() coords = np.reshape(res.x, (-1, 3)) # Since minimization returns flat # array. # Recalculate directional vectors from optimized coordinates i = 0 for coord in coords: vectors = np.vstack((vectors, (coord-M[i])/\ np.linalg.norm(coord-M[i]))) i += 1 return coords, vectors
def get_pair_displacements(trajectory, topology, substrate_name, substrate_number, atom_types): ## IDENTIFY ATOMS # Substrate filter print('Finding \'interesting\' atoms...') print('\t[1]: Not in the substrate') print('\t[2]: Not part of the backbone') print('\t[3]: Not in water (HOH)') print('\t[4]: Are on the list of atom types') substrate = [ atom.index for atom in topology.atoms if (atom.residue.name == substrate_name and atom.residue.index == substrate_number) ] # Atoms of interest filter - atom type in list and not part of the backbone interesting_atoms = [ atom.index for atom in topology.atoms if (atom.name in atom_types and atom.residue.name != substrate_name and not atom.is_backbone and atom.residue.name != 'HOH') ] print('\t\t\t\t\tSuccess!') print('\t# of \'interesting\' atoms:', end=" ") print(np.shape(interesting_atoms)) # Neighbors - selects everyone nearby frame by frame print('Finding substrate neighbours...', end=" ") substrate_neighbours = md.compute_neighbors(trajectory, 0.5, substrate, interesting_atoms) print('\tSuccess!') # Hydrogen atom types print('Finding hydrogens...', end=" ") h_atom_types = [ 'h1', 'h2', 'h3', 'h4', 'h5', 'ha', 'hc', 'hn', 'ho', 'hp', 'hs', 'HW', 'hx', 'H', 'h1', 'h2', 'H3', 'h4', 'h5', 'ha', 'HA', 'hc', 'HK', 'hn', 'ho', 'hp', 'HP', 'hs', 'HT', 'hw', 'HW', 'hx', 'HZ', 'HH3' ] upper_h_atom_types = [atom_type.upper() for atom_type in h_atom_types] print('\t\t\tSuccess!') ## CALCULATE # Unique neighbors - makes a 1D array of all the unique neighbour atoms from all frames print('Eliminating duplicate atoms...', end=" ") unique_neighbor_atoms = [] unique_pairs = [] for frame in substrate_neighbours: for atom in frame: if atom not in unique_neighbor_atoms: unique_neighbor_atoms.append(atom) unique_neighbor_atoms.sort() print('\t\tSuccess!') print('\t# of unique atoms:', end=" ") print(np.shape(unique_neighbor_atoms)) # Distance between everyone print('Combining atoms into pairs...', end=" ") pairs = topology.select_pairs(substrate, unique_neighbor_atoms) print('\t\tSuccess!') print('\t# of atom pairs:', end=" ") print(np.shape(pairs)) print('Eliminating duplicate pairs...', end=" ") for pair in pairs: atom1 = topology.atom(pair[0]) atom2 = topology.atom(pair[1]) # combine atom names - if both names appear in h_bond_types, it's an H-H pair and can be excluded atom_name_tuple = [atom1.name.upper(), atom2.name.upper()] h_h_pair = all(atom in upper_h_atom_types for atom in atom_name_tuple) # filter pairs in the same reside and H-H if atom1.residue != atom2.residue and not h_h_pair: unique_pairs.append(pair) print('\t\tSuccess!') # Who's touching? print('Selecting pairs with nearby atoms...', end=" ") relevant_pairs = [] for pair in unique_pairs: relevant_pair = filter_pair(trajectory, pair, relevant_pairs) if relevant_pair is not False: relevant_pairs.append(relevant_pair) print('\tSuccess!') print('\t# of \'relevant\' atom pairs:', end=" ") print(np.shape(relevant_pairs)) # Calculate displacement (vector) print('Calculating displacement vectors...', end=" ") relevant_displacements = md.compute_displacements(trajectory, relevant_pairs) print('\tSuccess!') print('\tDisplacement array:', end=" ") print(np.shape(relevant_displacements)) return (relevant_displacements)
def calc_dipole(namesolv, topology, traj, bond_definitions, forcefield, bulk=False, mol_pdb=None): """ This script calculates the distribution of molecular dipole moments. It calculates distributions both including and omitting polarization. Parameters ---------- namesolv: string The name of the molecule to calculate the dipole for. topology: string Filename of the topology file for the entire trajectory. traj: string Filename of the trajectory. bond_definitions: Filename of the forcefield residue definitions. forcefield: Filename of the forcefield definitions. bulk: Whether to calculate the molecule dipole as an average over all like residues in the bulk. mol_pdb: If the full system topology contains multiple forcefield files (or is complex for some othe reason), it may be easier just to specify a single pdb file for the molecule whose dipole is being calculated. In this case, give the full system topology in the usual argument, and give a supplemental pdb filename using this argument to calculate charges on the single molecule. This is necessary because the force field files are only used for determining charges on the molecule, but after charges are determined the forcefield files are no longer used. """ temperature = 300 # use OpenMM library to get charges # here we load pdb without drude oscillators if mol_pdb is not None: pdb = PDBFile(mol_pdb) else: pdb = PDBFile(topology) pdb.topology.loadBondDefinitions(bond_definitions) pdb.topology.createStandardBonds() modeller = Modeller(pdb.topology, pdb.positions) forcefield = ForceField(forcefield) modeller.addExtraParticles(forcefield) system = forcefield.createSystem(modeller.topology, nonbondedCutoff=1.4 * nanometer, constraints=None, rigidWater=True) nbondedForce = [ f for f in [system.getForce(i) for i in range(system.getNumForces())] if type(f) == NonbondedForce ][0] # this is not important, this is just to create openmm object that we can use to access topology integ_md = DrudeLangevinIntegrator(temperature, 1 / picosecond, 1 * kelvin, 1 / picosecond, 0.001 * picoseconds) platform = Platform.getPlatformByName('CPU') simmd = Simulation(modeller.topology, system, integ_md, platform) ##### # # construct charge array for solvent molecule # also construct charge array for static charges, # so that we can separately compute static and induced dipole moments # #### charges = [] chargesnopol = [] for res in simmd.topology.residues(): if res.name == namesolv: for i in range(len(res._atoms)): index = res._atoms[i].index (q, sig, eps) = nbondedForce.getParticleParameters(index) charges.append(q._value) if 'D' in res._atoms[i].name: # no charge on drude without polarization chargesnopol.append(0.0) else: # to get non-polarization charges, # look for matching drude particle typeA = res._atoms[i].name typeD = "D" + typeA indexD = -1 for j in range(len(res._atoms)): if typeD == res._atoms[j].name: indexD = res._atoms[j].index break if indexD >= 0: (qD, sig, eps) = nbondedForce.getParticleParameters(indexD) qstatic = q._value + qD._value else: qstatic = q._value chargesnopol.append(qstatic) break systemTopology = mdtraj.load(topology).topology # get indices solvent molecules solvent = [] for res in systemTopology.residues: if res.name == namesolv: solvent.append(list(map(lambda a: a.index, res._atoms))) if not bulk: break center_of_charge_atoms = [] for res in systemTopology.residues: if res.name == namesolv: for atom in res._atoms: atom_name = {"BF4": "B", "TMA": "N"}[namesolv] if atom.name == atom_name: center_of_charge_atoms.append(atom.index) break if not bulk: break # convert from e*nm to Debye conv = 1.0 / 0.0208194 framestart = 0 frameend = framestart + 1 muhist = [] munopolhist = [] for i in range(framestart, frameend): frame = mdtraj.load_frame(traj, i, top=topology) # calculate dipole of each solvent molecule for j in range(len(solvent)): # make a list of all atom pairs with the charge-carrying center res_center = center_of_charge_atoms[j] atom_pairs = [[res_center, index] for index in solvent[j]] disp = compute_displacements(frame, atom_pairs, periodic=True) mu = np.array([0.0, 0.0, 0.0]) munopol = np.array([0.0, 0.0, 0.0]) for k in range(len(solvent[j])): munopol += chargesnopol[k] * disp[0][k] mu += charges[k] * disp[0][k] muhist.append(la.norm(mu) * conv) munopolhist.append(la.norm(munopol) * conv) hist1 = np.histogram(muhist, 50, density=True) hist2 = np.histogram(munopolhist, 50, density=True) print("dipole distribution with polarization") for i in range(len(hist1[0])): print(hist1[1][i], hist1[0][i]) print("dipole distribution without polarization") for i in range(len(hist2[0])): print(hist2[1][i], hist2[0][i])
def __calculate_pair_vectors(self, coordination,\ OH_frac, OH2_frac, center): """Calculate coordinates for solvate on for 4-coordinated atoms Notes ----- Calculates the sum of vectors from oxygen to 4-coordinated metal atoms. These vectors are used as first guesses of the positions for the hydration molecules. Returns ------- vectors : ndarray(float) Directional vectors for each solvate coord : ndarray(float) Coordinates for oxygen atom in each solvate centers : ndarray(int) List of each center that each solvate belongs to """ centerIndices, neighbourgraph = self.__get_center_neighbours(\ coordination, center) if (len(centerIndices) > 0): centers = np.empty([0], dtype=int) self.__verboseprint(("Found {} centers that are Nmax - 2 = {}-fold" " coordinated\n".format(len(centerIndices),\ coordination))) vectors = np.empty([0, 3], dtype=float) randIndices = random.sample(range(0, len(centerIndices)), int((OH_frac+OH2_frac)*\ float(len(centerIndices)))) indices = centerIndices[randIndices] for center in indices: points = np.empty([0, 3], dtype=float) tempVectors = np.empty([0, 3], dtype=float) pairVectors = np.empty([0, 3], dtype=float) sumVec = np.array([0, 0, 0], dtype=float) points = np.vstack( (points, self.topol.trj.xyz[0][center] * 10)) # Get neighbours neighbours = np.array(neighbourgraph[center]) centerArray = np.repeat(center, len(neighbours)) # Compute displacement vectors dispArray = np.vstack((neighbours, centerArray)) dispArray = np.transpose(dispArray) dispVectors = md.compute_displacements(self.topol.trj,\ dispArray,\ periodic = True)[0] for vector in dispVectors: if (np.linalg.norm(vector) < 0.1): puts.warning("Directional vector less than 0.1,"+\ " will not hydrate atom with index: " +\ str(center) + ". Please verify structure"+\ " before using it.\n") continue vector = vector / np.linalg.norm(vector) tempVectors = np.vstack((tempVectors, vector)) sumVec += vector # Sum M-O vectors sumVec = sumVec / np.linalg.norm(sumVec) for vector in tempVectors: tempVector = sumVec - vector pairVectors = np.vstack((pairVectors, tempVector)) #Sort vectors with increasing norm pairVectors = sorted(pairVectors, key = lambda x: np.sqrt(x[0]**2 + x[1]**2\ + x[2]**2)) # Choose the two vectors with smallest norm pairVec1 = pairVectors[0] / np.linalg.norm(pairVectors[0]) pairVec2 = pairVectors[1] / np.linalg.norm(pairVectors[1]) # If there are only 2 neighbours, rotate Pi/2 around # directional vector to maximize distance to neighbours if (len(tempVectors) == 2): axis = sumVec angle = np.pi / 2 pairVec1 = np.dot(mac.rotate_around_vec(axis, angle),\ pairVec1) pairVec2 = np.dot(mac.rotate_around_vec(axis, angle),\ pairVec2) # Rotate the vectors towards each other # (away from center of bulk) crossProd = np.cross(pairVec1, pairVec2) dotProdVec1 = np.dot(sumVec, pairVec1) dotProdVec2 = np.dot(sumVec, pairVec2) if (dotProdVec1 < 0): pairVec1 = np.dot(mac.rotate_around_vec(crossProd,\ np.pi/7), pairVec1) pairVec2 = np.dot(mac.rotate_around_vec(crossProd,\ -np.pi/7), pairVec2) else: angle = -np.pi / 7 pairVec1 = np.dot(mac.rotate_around_vec(crossProd,\ np.pi/7), pairVec1) pairVec2 = np.dot(mac.rotate_around_vec(crossProd,\ -np.pi/7), pairVec2) vectors = np.vstack((vectors, pairVec1)) vectors = np.vstack((vectors, pairVec2)) centers = np.append(centers, center) centers = np.append(centers, center) return vectors, centers else: return (np.empty([0, 3], dtype=float), np.empty([0], dtype=int))
def __calculate_vectors(self, coordination,\ OH_frac, OH2_frac, center): """Calculate coordinates for solvate on for 5-coordinated atoms Notes ----- See calculate_pair_vectors """ vectors = np.empty([0, 3], dtype=float) vec = np.array([0, 0, 0]) tempNeighbour = np.array([0, 0, 0]) #Get indices for metal centers with coordination Nmax - 1 centerIndices, neighbourgraph = self.__get_center_neighbours(\ coordination, center) if (len(centerIndices) > 0): # If there are any Nmax-1 centers centers = np.empty([0], dtype=int) self.__verboseprint(("Found {} centers that are Nmax - 1 = {}-fold" " coordinated.\n".format(len(centerIndices),\ coordination))) #Calculate only for user specified fraction randIndices = random.sample(range(0, len(centerIndices)),\ int((OH2_frac + OH_frac)*\ float(len(centerIndices)))) indices = centerIndices[randIndices] #Calculate M-O vectors for center in indices: vec = [0, 0, 0] neighbours = np.array(neighbourgraph[center]) centerArray = np.repeat(center, len(neighbours)) dispArray = np.vstack((neighbours, centerArray)) dispArray = np.transpose(dispArray) dispVectors = md.compute_displacements(self.topol.trj,\ dispArray,\ periodic = True)[0] #for neighbour in neighbourgraph[center]: for vector in dispVectors: vector = vector / np.linalg.norm(vector) vec = vec + vector #If norm of directional vector too small if (np.linalg.norm(vec) < 0.1): puts.warning("Directional vector almost 0, will " +\ "not hydrate at atom with index: " +\ str(center + 1)) print("Probably due to symmetric center...\n") else: centers = np.append(centers, center) vec = vec / np.linalg.norm(vec) vectors = np.vstack((vectors, vec)) return vectors, centers else: # Else return empty arrays return (np.empty([0, 3], dtype=float), np.empty([0], dtype=int))