def compute_files(cg_xyz, cg_top, t, molecule_name, element_names): # Create Trajectory object and save to .dcd file cg_traj = md.Trajectory(cg_xyz, cg_top, time=None, unitcell_lengths=t.unitcell_lengths, unitcell_angles=t.unitcell_angles) ## need better file naming convention and rule guideline cg_traj.save_dcd('cg_traj_{0:s}.dcd'.format( molecule_name)) ## need check statements to prevent file overwrite ## rename old/new files accordingly # Create rdfs file from pairs ## might need for loop if more elements (later implementation) ## need some way to recognize pairs of units - not just carbon elements if expanded pairs = cg_traj.top.select_pairs( selection1='name {0:s}'.format(element_names[0]), ## Check element selection2='name {0:s}'.format(element_names[0])) ## Check element r, g_r = md.compute_rdf(cg_traj, pairs=pairs, r_range=(0, 1.2), bin_width=0.005) ## identify end of range with data pairs ## maybe something with read-file function - compare data values next to each other ## See where data drop-off occurs and plot respectively ## maybe use slope - negative less than some number, set as cutoff ## record cutoff point somewhere for debugging purposes np.savetxt('rdfs_aa.txt', np.transpose( [r, g_r])) # need check statements to prevent file overwrite plot_output(r, g_r, molecule_name)
def test_rdf_periodic(): """ Test if the periodic argument gives the correct results""" test_file = TEST_FILE stack = load_structure(test_file) # calculate oxygen RDF for water oxygen = stack[:, stack.atom_name == 'OW'] interval = np.array([0, 10]) n_bins = 100 bins, g_r = rdf(oxygen[:, 0].coord, oxygen[:, 1:], interval=interval, bins=n_bins, periodic=True) # Compare with MDTraj import mdtraj traj = mdtraj.load(TEST_FILE) ow = [a.index for a in traj.topology.atoms if a.name == 'O'] pairs = itertools.product([ow[0]], ow[1:]) mdt_bins, mdt_g_r = mdtraj.compute_rdf(traj, list(pairs), r_range=interval / 10, n_bins=n_bins, periodic=True) assert np.allclose(bins, mdt_bins * 10) assert np.allclose(g_r, mdt_g_r, rtol=0.0001)
def compute_current_rdf(self, state, r_range, n_bins, smooth=True, max_frames=1e3): """ """ pairs = self.states[state]['pair_indices'] # TODO: More elegant way to handle units. # See https://github.com/ctk3b/msibi/issues/2 g_r_all = None first_frame = 0 max_frames = int(max_frames) for last_frame in range(max_frames, state.traj.n_frames + max_frames, max_frames): r, g_r = md.compute_rdf(state.traj[first_frame:last_frame], pairs, r_range= r_range, n_bins=n_bins) if g_r_all is None: g_r_all = np.zeros_like(g_r) g_r_all += g_r * len(state.traj[first_frame:last_frame]) / state.traj.n_frames first_frame = last_frame #r *= 10.0 rdf = np.vstack((r, g_r)).T self.states[state]['current_rdf'] = rdf if smooth: current_rdf = self.states[state]['current_rdf'] current_rdf[:, 1] = savitzky_golay(current_rdf[:, 1], 9, 2, deriv=0, rate=1) for row in current_rdf: row[1] = np.maximum(row[1], 0) # Compute fitness function comparing the two RDFs. f_fit = calc_similarity(rdf[:, 1], self.states[state]['target_rdf'][:, 1]) self.states[state]['f_fit'].append(f_fit)
def test_rdf(): """ General test to reproduce oxygen RDF for a box of water""" test_file = TEST_FILE stack = load_structure(test_file) # calculate oxygen RDF for water oxygen = stack[:, stack.atom_name == 'OW'] interval = np.array([0, 10]) n_bins = 100 bins, g_r = rdf(oxygen[:, 0].coord, oxygen, interval=interval, bins=n_bins, periodic=False) # Compare with MDTraj import mdtraj traj = mdtraj.load(TEST_FILE) ow = [a.index for a in traj.topology.atoms if a.name == 'O'] pairs = itertools.product([ow[0]], ow) mdt_bins, mdt_g_r = mdtraj.compute_rdf(traj, list(pairs), r_range=interval / 10, n_bins=n_bins, periodic=False) assert np.allclose(bins, mdt_bins * 10) assert np.allclose(g_r, mdt_g_r, rtol=0.0001)
def compute_rdf_from_partial(trj, r_range=None): compositions = dict() form_factors = dict() rdfs = dict() L = np.min(trj.unitcell_lengths) top = trj.topology elements = set([a.element for a in top.atoms]) denom = 0 for elem in elements: compositions[elem.symbol] = len(top.select('element {}'.format(elem.symbol)))/trj.n_atoms form_factors[elem.symbol] = elem.atomic_number denom += compositions[elem.symbol] * form_factors[elem.symbol] for i, (elem1, elem2) in enumerate(it.product(elements, repeat=2)): e1 = elem1.symbol e2 = elem2.symbol x_a = compositions[e1] x_b = compositions[e2] f_a = form_factors[e1] f_b = form_factors[e2] try: g_r = rdfs['{0}{1}'.format(e1, e2)] except KeyError: pairs = top.select_pairs(selection1='element {}'.format(e1), selection2='element {}'.format(e2)) if r_range == None: r, g_r = md.compute_rdf(trj, pairs=pairs, r_range=(0, L / 2)) else: r, g_r = md.compute_rdf(trj, pairs=pairs, r_range=r_range) rdfs['{0}{1}'.format(e1, e2)] = g_r if i == 0: total = g_r * (x_a*x_b*f_a*f_b) / denom**2 else: total += g_r * (x_a*x_b*f_a*f_b) / denom**2 return r, total
def compute_files(cg_xyz, cg_top, t, molecule_name, element_names): """Compute the trajectory and rdf files Parameters ---------- cg_xyz : ????? Coarse-grained xyz coordinates of all the beads cg_top : mdTraj.Topology Coarse-grained topology object for all the beads t : mdTraj.Trajectory (???? unsure if needed) Initial trajectory object generated from structure and trajectory files molecule_name : str Name of the molecule(s) in the system element_names : (???) (???) """ # Create Trajectory object and save to .dcd file cg_traj = md.Trajectory(cg_xyz, cg_top, time=None, unitcell_lengths=t.unitcell_lengths, unitcell_angles=t.unitcell_angles) ## need better file naming convention and rule guideline filepath = os.path.join(os.getcwd(), 'data/cg_traj_{0:s}.dcd'.format(molecule_name)) cg_traj.save_dcd( filepath) ## need check statements to prevent file overwrite ## rename old/new files accordingly # Create rdfs file from pairs ## might need for loop if more elements (later implementation) ## need some way to recognize pairs of units - not just carbon elements if expanded pairs = cg_traj.top.select_pairs( selection1='name {0:s}'.format(element_names[0]), ## Check element selection2='name {0:s}'.format(element_names[0])) ## Check element r, g_r = md.compute_rdf(cg_traj, pairs=pairs, r_range=(0, 1.2), bin_width=0.005) ## identify end of range with data pairs ## maybe something with read-file function - compare data values next to each other ## See where data drop-off occurs and plot respectively ## maybe use slope - negative less than some number, set as cutoff ## record cutoff point somewhere for debugging purposes filepath = os.path.join(os.getcwd(), 'data/rdfs_aa.txt') np.savetxt(filepath, np.transpose( [r, g_r])) # need check statements to prevent file overwrite print("Saved rdfs to file") plot_output(r, g_r, molecule_name)
def compute_current_rdf(self, state, r_range, n_bins, smooth=True, max_frames=50, verbose=False): """ """ pairs = self.states[state]["pair_indices"] # TODO: More elegant way to handle units. # See https://github.com/ctk3b/msibi/issues/2 g_r_all = None first_frame = 0 max_frames = int(max_frames) for last_frame in range(max_frames, state.traj.n_frames + max_frames, max_frames): r, g_r = md.compute_rdf( state.traj[first_frame:last_frame], pairs, r_range=r_range / 10, n_bins=n_bins, ) if g_r_all is None: g_r_all = np.zeros_like(g_r) g_r_all += (g_r * len(state.traj[first_frame:last_frame]) / state.traj.n_frames) first_frame = last_frame r *= 10 rdf = np.vstack((r, g_r_all)).T self.states[state]["current_rdf"] = rdf if smooth: current_rdf = self.states[state]["current_rdf"] current_rdf[:, 1] = savitzky_golay(current_rdf[:, 1], 9, 2, deriv=0, rate=1) for row in current_rdf: row[1] = np.maximum(row[1], 0) if verbose: # pragma: no cover plt.title(f"RDF smoothing for {state.name}") plt.plot(r, g_r, label="unsmoothed") plt.plot(r, current_rdf[:, 1], label="smoothed") plt.legend() plt.show() # Compute fitness function comparing the two RDFs. f_fit = calc_similarity(rdf[:, 1], self.states[state]["target_rdf"][:, 1]) self.states[state]["f_fit"].append(f_fit)
def rdf_by_frame(trj, **kwargs): """Helper function that computes rdf frame-wise and returns the average rdf over all frames. Can be useful for large systems in which a distance array of size n_frames * n_atoms (used internally in md.compute_rdf) takes requires too much memory. """ g_r = None for frame in trj: r, g_r_frame = md.compute_rdf(frame, **kwargs) if g_r is None: g_r = np.zeros_like(g_r_frame) g_r += g_r_frame g_r /= len(trj) return r, g_r
def test_compare_rdf_t_at_0(get_fn, periodic, opt): # Compute g(r, t) at t = 0 and compare to g(r) frames = 2 traj = md.load(get_fn('tip3p_300K_1ATM.xtc'), top=get_fn('tip3p_300K_1ATM.pdb')) pairs = traj.top.select_pairs('name O', 'name O') times = list() for j in range(frames): times.append([j, j]) r_t, g_r_t = md.compute_rdf_t(traj, pairs, times, self_correlation=False, periodic=periodic, opt=opt) mean_g_r_t = np.mean(g_r_t, axis=0) r, g_r = md.compute_rdf(traj[:frames], pairs, periodic=periodic, opt=periodic) assert eq(r_t, r) assert eq(mean_g_r_t, g_r, decimal=1)
def compute_current_rdf(self, state, r_range, n_bins, smooth=True, max_frames=1e3): """ """ pairs = self.states[state]['pair_indices'] # TODO: More elegant way to handle units. # See https://github.com/ctk3b/msibi/issues/2 g_r_all = None first_frame = 0 max_frames = int(max_frames) for last_frame in range(max_frames, state.traj.n_frames + max_frames, max_frames): r, g_r = md.compute_rdf(state.traj[first_frame:last_frame], pairs, r_range=r_range / 10, n_bins=n_bins) if g_r_all is None: g_r_all = np.zeros_like(g_r) g_r_all += g_r * len( state.traj[first_frame:last_frame]) / state.traj.n_frames first_frame = last_frame r *= 10 rdf = np.vstack((r, g_r_all)).T self.states[state]['current_rdf'] = rdf if smooth: current_rdf = self.states[state]['current_rdf'] current_rdf[:, 1] = savitzky_golay(current_rdf[:, 1], 9, 2, deriv=0, rate=1) for row in current_rdf: row[1] = np.maximum(row[1], 0) # Compute fitness function comparing the two RDFs. f_fit = calc_similarity(rdf[:, 1], self.states[state]['target_rdf'][:, 1]) self.states[state]['f_fit'].append(f_fit)
def run_rdf(job): print('Loading trj {}'.format(job)) if os.path.exists(os.path.join(job.workspace(), 'com.gro')): top_file = os.path.join(job.workspace(), 'com.gro') trj_file = os.path.join(job.workspace(), 'sample_com.xtc') trj = md.load(trj_file, top=top_file, stride=10) selections = dict() selections['cation'] = trj.topology.select('name li') selections['anion'] = trj.topology.select('resname {}'.format(job.statepoint()['anion'])) selections['acn'] = trj.topology.select('resname ch3cn') selections['chlor'] = trj.topology.select('resname chlor') selections['all'] = trj.topology.select('all') combos = [('cation', 'anion'), ('cation','cation'), ('anion','anion'), ('acn','anion'), ('acn','cation'), ('chlor','anion'), ('chlor','cation'), ('acn', 'chlor')] for combo in combos: fig, ax = plt.subplots() print('running rdf between {0} ({1}) and\t{2} ({3})\t...'.format(combo[0], len(selections[combo[0]]), combo[1], len(selections[combo[1]]))) r, g_r = md.compute_rdf(trj, pairs=trj.topology.select_pairs(selections[combo[0]], selections[combo[1]]), r_range=((0.0, 2.0))) data = np.vstack([r, g_r]) np.savetxt('txt_files/{}-{}-{}-rdf-{}-{}.txt'.format(job.sp.chlor_conc, job.sp.acn_conc, job.sp.il_conc, combo[0], combo[1]), np.transpose(np.vstack([r, g_r])), header='# r (nm)\tg(r)') ax.plot(r, g_r) plt.xlabel('r (nm)') plt.ylabel('G(r)') plt.savefig(os.path.join(job.workspace(), f'rdf-{combo[0]}-{combo[1]}.pdf')) print(' ... done\n')
def compute_rdf(self, atomtype_i, atomtype_j, output, bin_width=0.01, exclude_up_to=3, r_range=[0,2]): """ Compute RDF between pair of atoms, save to text Parameters --------- atomtype_i : str First atomtype atomtype_j : str Second atomtype output : str Filename exclude_up_to : int Exclude up to this many bonded terms for RDF calculation i.e., exclude_up_to=2 means 1-2 and 1-3 bonds are excluded from RDF Notes ----- Be VERY aware of the exclusion terms for computing RDFs """ """ Mine pairs = traj.topology.select_pairs(selection1='name {}'.format(atomtype_i), selection2='name {}'.format(atomtype_j)) (first, second) = mdtraj.compute_rdf(traj, pairs, [0, 2], bin_width=0.01 ) np.savetxt('{}.txt'.format(output), np.column_stack([first,second])) """ pairs = self.traj.topology.select_pairs("name '{0}'".format(atomtype_i), "name '{0}'".format(atomtype_j)) if exclude_up_to is not None: to_delete = find_1_n_exclusions(self.traj.topology, pairs, exclude_up_to) pairs = np.delete(pairs, to_delete, axis=0) (first, second) = mdtraj.compute_rdf(self.traj, pairs, r_range=r_range, bin_width=bin_width) np.savetxt('{}.txt'.format(output), np.column_stack([first,second]))
def run_rdf(job): print('Loading trj {}'.format(job)) if os.path.exists(os.path.join(job.workspace(), 'com.gro')): top_file = os.path.join(job.workspace(), 'com.gro') trj_file = os.path.join(job.workspace(), 'sample_com.xtc') trj = md.load(trj_file, top=top_file, stride=10) selections = dict() selections['cation'] = trj.topology.select('name {}'.format(job.statepoint()['cation'])) selections['anion'] = trj.topology.select('name {}'.format(job.statepoint()['anion'])) selections['ion'] = trj.topology.select('name {0} {1}'.format(job.statepoint()['cation'], job.statepoint()['anion'])) selections['solvent'] = trj.topology.select('not name {0} {1}'.format(job.statepoint()['cation'], job.statepoint()['anion'])) selections['all'] = trj.topology.select('all') combos = [('ion', 'ion'), ('cation', 'anion'), ('cation','cation'), ('anion','anion'), ('solvent','anion'), ('solvent','cation'), ('solvent', 'solvent')] for combo in combos: print('running rdf between {0} ({1}) and\t{2} ({3})\t...'.format(combo[0], len(selections[combo[0]]), combo[1], len(selections[combo[1]]))) r, g_r = md.compute_rdf(trj, pairs=trj.topology.select_pairs(selections[combo[0]], selections[combo[1]]), r_range=((0.0, 2.0))) data = np.vstack([r, g_r]) np.savetxt(os.path.join(job.workspace(), 'rdf-{}-{}.txt'.format(combo[0], combo[1])), np.transpose(np.vstack([r, g_r])), header='# r (nm)\tg(r)') print(' ... done\n')
#uses mdtraj to calculate rdf #only handles one frome, .pdb is a dummy file made with vmd from trj import mdtraj as md import numpy as np traj = md.load('freeze.lammpstrj', top = 'freeze.pdb') r, gofr = md.compute_rdf(traj) np.savetxt('rdf-mdtraj.txt', np.hstack((r, gofr)))
#!/usr/bin/env python2 # -*- coding: utf-8 -*- """ Created on Sun Feb 24 13:03:28 2019 @author: simon """ import matplotlib.pyplot as plt import mdtraj as md t = md.load_xyz('within.xyz', top='within.xyz') md.compute_rdf("within.xyz")
def structure_factor( trj, Q_range=(0.5, 50), n_points=1000, framewise_rdf=False, weighting_factor="fz", form="atomic", ): """Compute the structure factor through a fourier transform of the radial distribution function. The consdered trajectory must include valid elements. The computed structure factor is only valid for certain values of Q. The lowest value of Q that can sufficiently be described by a box of characteristic length `L` is `2 * pi / (L / 2)`. Parameters ---------- trj : mdtraj.Trajectory A trajectory for which the structure factor is to be computed. Q_range : list or np.ndarray, default=(0.5, 50) Minimum and maximum Values of the scattering vector, in `1/nm`, to be consdered. n_points : int, default=1000 framewise_rdf : boolean, default=False If True, computes the rdf frame-by-frame. This can be useful for managing memory in large systems. weighting_factor : string, optional, default='fz' Weighting factor for calculating the structure-factor, default is Faber-Ziman. See https://openscholarship.wustl.edu/etd/1358/ and http://isaacs.sourceforge.net/manual/page26_mn.html for details. form : string, optional, default='atomic' Method for determining form factors. If default, form factors are estimated from atomic numbers. If 'cromer-mann', form factors are determined from Cromer-Mann tables. Returns ------- Q : np.ndarray The values of the scattering vector, in `1/nm`, that was considered. S : np.ndarray The structure factor of the trajectory """ if weighting_factor not in ["fz", "al"]: raise ValueError( "Invalid weighting_factor `{}` is given." " The only weighting_factor currently supported is `fz`, and `al`." .format(weighting_factor)) rho = np.mean(trj.n_atoms / trj.unitcell_volumes) L = np.min(trj.unitcell_lengths) top = trj.topology elements = set([a.element for a in top.atoms]) compositions = dict() rdfs = dict() Q = np.logspace(np.log10(Q_range[0]), np.log10(Q_range[1]), num=n_points) S = np.zeros(shape=(len(Q))) for elem in elements: compositions[elem.symbol] = ( len(top.select("element {}".format(elem.symbol))) / trj.n_atoms) for i, q in enumerate(Q): num = 0 denom = 0 for elem in elements: denom += _get_normalize(method=weighting_factor, c=compositions[elem.symbol], f=get_form_factor(elem.symbol, q=q / 10, method=form)) for (elem1, elem2) in it.product(elements, repeat=2): e1 = elem1.symbol e2 = elem2.symbol f_a = get_form_factor(e1, q=q / 10, method=form) f_b = get_form_factor(e2, q=q / 10, method=form) x_a = compositions[e1] x_b = compositions[e2] try: g_r = rdfs["{0}{1}".format(e1, e2)] except KeyError: pairs = top.select_pairs( selection1="element {}".format(e1), selection2="element {}".format(e2), ) if framewise_rdf: r, g_r = rdf_by_frame(trj, pairs=pairs, r_range=(0, L / 2), bin_width=0.001) else: r, g_r = md.compute_rdf(trj, pairs=pairs, r_range=(0, L / 2), bin_width=0.001) rdfs["{0}{1}".format(e1, e2)] = g_r integral = simps(r**2 * (g_r - 1) * np.sin(q * r) / (q * r), r) coefficient = x_a * x_b * f_a * f_b pre_factor = 4 * np.pi * rho partial_sq = (integral * pre_factor) num += coefficient * (partial_sq) if weighting_factor == "fz": denom = denom**2 S[i] = num / denom return Q, S
def structure_factor_pair_test(trj_full, trj_sep, pair, Q_range=(0.5, 50), n_points=1000, framewise_rdf=False, method='fz'): """ trj_full is the trajectory with only IL, and trj_sep renamed the atoms in cation (using name of atom + 'ca') and renamed the atoms in anion (using name of atom + 'an'). """ rho = np.mean(trj_full.n_atoms / trj_full.unitcell_volumes) L = np.min(trj_full.unitcell_lengths) top_full = trj_full.topology top_sep = trj_sep.topology elements_i = set() elements_j = set() for a in top_sep.atoms: string = a.element.symbol if string[-2:] == pair[0]: elements_i.add((a.element)) if string[-2:] == pair[1]: elements_j.add((a.element)) Number_scale = dict() elements = set([a.element for a in top_full.atoms]) compositions = dict() form_factors = dict() rdfs = dict() Q = np.logspace(np.log10(Q_range[0]), np.log10(Q_range[1]), num=n_points) S = np.zeros(shape=(len(Q))) for elem in elements: compositions[elem.symbol] = len(top_full.select('element {}'.format(elem.symbol)))/trj_full.n_atoms form_factors[elem.symbol] = elem.atomic_number for i, q in enumerate(Q): num = 0 denom = 0 for elem in elements: denom += compositions[elem.symbol] * form_factors[elem.symbol] for (elem1, elem2) in it.product(elements, repeat=2): e1 = elem1.symbol e2 = elem2.symbol atomtype1 = e1[0] atomtype2 = e2[0] f_a = form_factors[e1] f_b = form_factors[e2] x_a = compositions[e1] x_b = compositions[e2] length_1 = len(top_sep.select('element {}'.format(e1 + pair[0]))) length_2 = len(top_sep.select('element {}'.format(e2 + pair[1]))) if (length_1 == 0) or (length_2 == 0): integral = 0 else: try: g_r = rdfs['{0}{1}'.format(e1, e2)] scale = Number_scale['{0}{1}'.format(e1, e2)] except KeyError: pairs = top_sep.select_pairs(selection1='element {}'.format(e1 + pair[0]), selection2='element {}'.format(e2 + pair[1])) if framewise_rdf: r, g_r = rdf_by_frame(trj_sep, pairs=pairs, r_range=(0, L / 2), bin_width=0.001) else: r, g_r = md.compute_rdf(trj_sep, pairs=pairs, r_range=(0, L / 2), bin_width=0.001) rdfs['{0}{1}'.format(e1, e2)] = g_r N_i = len(top_full.select('element {}'.format(e1))) N_j = len(top_full.select('element {}'.format(e2))) N_i_1 = len(top_sep.select('element {}'.format(e1+ pair[0]))) N_i_2 = len(top_sep.select('element {}'.format(e2+ pair[1]))) scale = N_i_1 * N_i_2 / (N_i * N_j) Number_scale['{0}{1}'.format(e1, e2)] = scale integral = simps(r ** 2 * (g_r - g_r[-1]) * np.sin(q * r) / (q * r), r) * scale # print(integral) if method == 'al': pre_factor = np.sqrt(x_a * x_b) * 4 * np.pi * rho partial_sq = (integral*pre_factor) num += (f_a*f_b) * (partial_sq+1) * np.sqrt(x_a*x_b) elif method == 'fz': pre_factor = 4 * np.pi * rho partial_sq = (integral*pre_factor) num += (x_a*f_a*x_b*f_b) * (partial_sq) S[i] = (num/(denom**2)) return Q, S
def structure_factor(trj, Q_range=(0.5, 50), n_points=1000, framewise_rdf=False, method='fz'): """Compute the structure factor. The consdered trajectory must include valid elements. The computed structure factor is only valid for certain values of Q. The lowest value of Q that can sufficiently be described by a box of characteristic length `L` is `2 * pi / (L / 2)`. Parameters ---------- trj : mdtraj.Trajectory A trajectory for which the structure factor is to be computed. Q_range : list or np.ndarray, default=(0.5, 50) Minimum and maximum Values of the scattering vector, in `1/nm`, to be consdered. n_points : int, default=1000 framewise_rdf : boolean, default=False If True, computes the rdf frame-by-frame. This can be useful for managing memory in large systems. method : string, optional, default='fz' Formalism for calculating the structure-factor, default is Faber-Ziman. Other option is the Faber-Ziman formalism. See https://openscholarship.wustl.edu/etd/1358/ and http://isaacs.sourceforge.net/manual/page26_mn.html for details. Returns ------- Q : np.ndarray The values of the scattering vector, in `1/nm`, that was considered. S : np.ndarray The structure factor of the trajectory """ rho = np.mean(trj.n_atoms / trj.unitcell_volumes) L = np.min(trj.unitcell_lengths) top = trj.topology elements = set([a.element for a in top.atoms]) compositions = dict() form_factors = dict() rdfs = dict() Q = np.logspace(np.log10(Q_range[0]), np.log10(Q_range[1]), num=n_points) S = np.zeros(shape=(len(Q))) for elem in elements: compositions[elem.symbol] = len(top.select('element {}'.format(elem.symbol)))/trj.n_atoms form_factors[elem.symbol] = elem.atomic_number for i, q in enumerate(Q): num = 0 denom = 0 for elem in elements: denom += compositions[elem.symbol] * form_factors[elem.symbol] for (elem1, elem2) in it.product(elements, repeat=2): e1 = elem1.symbol e2 = elem2.symbol f_a = form_factors[e1] f_b = form_factors[e2] x_a = compositions[e1] x_b = compositions[e2] try: g_r = rdfs['{0}{1}'.format(e1, e2)] except KeyError: pairs = top.select_pairs(selection1='element {}'.format(e1), selection2='element {}'.format(e2)) if framewise_rdf: r, g_r = rdf_by_frame(trj, pairs=pairs, r_range=(0, L / 2), bin_width=0.001) else: r, g_r = md.compute_rdf(trj, pairs=pairs, r_range=(0, L / 2), bin_width=0.001) rdfs['{0}{1}'.format(e1, e2)] = g_r integral = simps(r ** 2 * (g_r - 1) * np.sin(q * r) / (q * r), r) if method == 'al': pre_factor = np.sqrt(x_a * x_b) * 4 * np.pi * rho partial_sq = (integral*pre_factor) + int(e1==e2) num += (f_a*f_b) * (partial_sq+1) * np.sqrt(x_a*x_b) elif method == 'fz': pre_factor = 4 * np.pi * rho partial_sq = (integral*pre_factor) + 1 num += (x_a*f_a*x_b*f_b) * (partial_sq) S[i] = (num/(denom**2)) return Q, S
def structure_factor_pair(trj, pair, Q_range=(0.5, 50), n_points=1000, framewise_rdf=False, method='fz'): rho = np.mean(trj.n_atoms / trj.unitcell_volumes) L = np.min(trj.unitcell_lengths) top = trj.topology elements_i = set() elements_j = set() for a in top.atoms: string = a.element.symbol if string[-2:] == pair[0]: elements_i.add((a.element)) if string[-2:] == pair[1]: elements_j.add((a.element)) compositions_i = dict() compositions_j = dict() form_factors_i = dict() form_factors_j = dict() rdfs = dict() Q = np.logspace(np.log10(Q_range[0]), np.log10(Q_range[1]), num=n_points) S = np.zeros(shape=(len(Q))) for elem in elements_i: compositions_i[elem.symbol] = len(top.select('element {}'.format(elem.symbol)))/trj.n_atoms form_factors_i[elem.symbol] = elem.atomic_number for elem in elements_j: compositions_j[elem.symbol] = len(top.select('element {}'.format(elem.symbol)))/trj.n_atoms form_factors_j[elem.symbol] = elem.atomic_number for i, q in enumerate(Q): num = 0 denom = 0 for elem in elements_i: denom += compositions_i[elem.symbol] * form_factors_i[elem.symbol] for (elem1, elem2) in it.product(elements_i,elements_j): e1 = elem1.symbol e2 = elem2.symbol f_i = form_factors_i[e1] f_j = form_factors_j[e2] x_i = compositions_i[e1] x_j = compositions_j[e2] try: g_r = rdfs['{0}{1}'.format(e1, e2)] except KeyError: pairs = top.select_pairs(selection1='element {}'.format(e1), selection2='element {}'.format(e2)) if framewise_rdf: r, g_r = rdf_by_frame(trj, pairs=pairs, r_range=(0, L / 2), bin_width=0.001) else: r, g_r = md.compute_rdf(trj, pairs=pairs, r_range=(0, L / 2), bin_width=0.001) rdfs['{0}{1}'.format(e1, e2)] = g_r integral = simps(r ** 2 * (g_r - g_r[-1]) * np.sin(q * r) / (q * r), r) if method == 'al': pre_factor = np.sqrt(x_i * x_j) * 4 * np.pi * rho partial_sq = (integral*pre_factor) + int(e1==e2) num += (f_i*f_j) * (partial_sq + 1) * np.sqrt(x_i*x_j) elif method == 'fz': pre_factor = 4 * np.pi * rho partial_sq = (integral*pre_factor) + 1 num += (x_i*f_i*x_j*f_j) * (partial_sq) S[i] = (num/(denom**2)) return Q, S
def structure_factor(trj, Q_range=(0.5, 50), n_points=1000, framewise_rdf=False, weighting_factor='fz', isotopes={}, probe="neutron"): """Compute the structure factor through a fourier transform of the radial distribution function. The consdered trajectory must include valid elements. Atomic form factors are estimated by atomic number. The computed structure factor is only valid for certain values of Q. The lowest value of Q that can sufficiently be described by a box of characteristic length `L` is `2 * pi / (L / 2)`. Parameters ---------- trj : mdtraj.Trajectory A trajectory for which the structure factor is to be computed. Q_range : list or np.ndarray, default=(0.5, 50) Minimum and maximum Values of the scattering vector, in `1/nm`, to be consdered. n_points : int, default=1000 framewise_rdf : boolean, default=False If True, computes the rdf frame-by-frame. This can be useful for managing memory in large systems. weighting_factor : string, optional, default='fz' Weighting factor for calculating the structure-factor, default is Faber-Ziman. See https://openscholarship.wustl.edu/etd/1358/ and http://isaacs.sourceforge.net/manual/page26_mn.html for details. isotopes: dict, optional, default=None If the scattering experiment was run with specific isotopic compositions (i.e. an NDIS experiment), specify isotopic composition as follows: { element_1.symbol: { element_1.atomic_number_1: fraction, element_1.atomic_number_2: fraction, ... }, element_2.symbol: { ... }, ... } The sum over the fraction for each isotope for each element must be 1.0. An atomic number of -1 signifies no isotopic enrichment, at which point the average scattering length will be pulled. Returns ------- Q : np.ndarray The values of the scattering vector, in `1/nm`, that was considered. S : np.ndarray The structure factor of the trajectory """ if weighting_factor not in ['fz']: raise ValueError('Invalid weighting_factor `{}` is given.' ' The only weighting_factor currently supported is `fz`.'.format( weighting_factor)) rho = np.mean(trj.n_atoms / trj.unitcell_volumes) L = np.min(trj.unitcell_lengths) top = trj.topology elements = set([a.element for a in top.atoms]) compositions = dict() form_factors = dict() rdfs = dict() Q = np.logspace(np.log10(Q_range[0]), np.log10(Q_range[1]), num=n_points) S = np.zeros(shape=(len(Q))) for elem in elements: compositions[elem.symbol] = len(top.select('element {}'.format(elem.symbol)))/trj.n_atoms form_factors[elem.symbol] = get_form_factor(elem.atomic_number, isotopes.get(elem.atomic_number, {-1: 1.0}), probe=probe) for i, q in enumerate(Q): num = 0 denom = 0 for elem in elements: denom += compositions[elem.symbol] * form_factors[elem.symbol] for (elem1, elem2) in it.product(elements, repeat=2): e1 = elem1.symbol e2 = elem2.symbol f_a = form_factors[e1] f_b = form_factors[e2] x_a = compositions[e1] x_b = compositions[e2] try: g_r = rdfs['{0}{1}'.format(e1, e2)] except KeyError: pairs = top.select_pairs(selection1='element {}'.format(e1), selection2='element {}'.format(e2)) if framewise_rdf: r, g_r = rdf_by_frame(trj, pairs=pairs, r_range=(0, L / 2), bin_width=0.001) else: r, g_r = md.compute_rdf(trj, pairs=pairs, r_range=(0, L / 2), bin_width=0.001) rdfs['{0}{1}'.format(e1, e2)] = g_r integral = simps(r ** 2 * (g_r - 1) * np.sin(q * r) / (q * r), r) if weighting_factor == 'fz': pre_factor = 4 * np.pi * rho # It's an unrestricted double sum, so non-identical pairs need to be counted twice if e1 != e2: pre_factor *= 2.0 partial_sq = (integral*pre_factor) num += (x_a*f_a*x_b*f_b) * (partial_sq) # Faber-Ziman comes out in units of barn/sr/atom. 100 is to convert between fm^2 and barn. if weighting_factor == 'fz': S[i] = num/100.0 else: S[i] = (num/(denom**2)) return Q, S
bin_edges = np.linspace(0.0,1.0,n_bin) for this_frame in water_dist: n_ideal = 4.* np.pi*rho_id/3.*(bin_edges[1:]**3.0-bin_edges[:-1]**3.0) n_hist, _ = np.histogram(this_frame, bins = bin_edges,density=True) this_g2 = n_hist/n_ideal g2s.append(this_g2) g2s = np.array(g2s) bin_edges = 0.5*(bin_edges[1:]+bin_edges[:-1]) ave_g2 = np.mean(g2s,axis=0) g2_err = np.std(g2s,axis=0)/(ave_g2[-1]) ave_g2 = ave_g2/(ave_g2[-1]) r,grs = md.compute_rdf(traj, pairs = water_pairs,bin_width = 0.01) fig4 = plt.figure() plt.plot(r,grs,color='Green') plt.errorbar(bin_edges,ave_g2,yerr=g2_err) plt.xlabel('pairwise distances (nm)') plt.ylabel('normalized g(r)') plt.xlim(0,1.0) plt.ylim(0,3.0) plt.title('pair distribution function for simulated water') #plt.axvline(x = 1.0,ymin= 0,ymax = 1000,linestyle='--') fig4.savefig('/Users/shenglanqiao/Documents/GitHub/waterMD/output/norm_g_cubic_2nm.png') plt.close(fig4) # cut_off=np.where(mean_bin_edges >=1.0)[0][0] # intensity = np.absolute(fft(ave_g2[:cut_off]))**2.0
def structure_factor(trj, Q_range=(0.5, 50), n_points=1000, framewise_rdf=False): """Compute the structure factor. The consdered trajectory must include valid elements. The computed structure factor is only valid for certain values of Q. The lowest value of Q that can sufficiently be described by a box of characteristic length `L` is `2 * pi / (L / 2)`. Parameters ---------- trj : mdtraj.Trajectory A trajectory for which the structure factor is to be computed. Q_range : list or np.ndarray, default=(0.5, 50) Minimum and maximum Values of the scattering vector, in `1/nm`, to be consdered. n_points : int, default=1000 framewise_rdf : boolean, default=False If True, computes the rdf frame-by-frame. This can be useful for managing memory in large systems. Returns ------- Q : np.ndarray The values of the scattering vector, in `1/nm`, that was considered. S : np.ndarray The structure factor of the trajectory """ rho = np.mean(trj.n_atoms / trj.unitcell_volumes) L = np.min(trj.unitcell_lengths) top = trj.topology elements = set([a.element for a in top.atoms]) compositions = dict() form_factors = dict() rdfs = dict() Q = np.logspace(np.log10(Q_range[0]), np.log10(Q_range[1]), num=n_points) S = np.zeros(shape=(len(Q))) for elem in elements: compositions[elem.symbol] = len(top.select('element {}'.format(elem.symbol)))/trj.n_atoms form_factors[elem.symbol] = elem.atomic_number for i, q in enumerate(Q): num = 0 denom = 0 for elem in elements: denom += (compositions[elem.symbol] * form_factors[elem.symbol]) ** 2 for (elem1, elem2) in it.combinations_with_replacement(elements, 2): e1 = elem1.symbol e2 = elem2.symbol f_a = form_factors[e1] f_b = form_factors[e2] x_a = compositions[e1] x_b = compositions[e2] pre_factor = x_a * x_b * f_a * f_b * 4 * np.pi * rho try: g_r = rdfs['{0}{1}'.format(e1, e2)] except KeyError: pairs = top.select_pairs(selection1='element {}'.format(e1), selection2='element {}'.format(e2)) if framewise_rdf: r, g_r = rdf_by_frame(trj, pairs=pairs, r_range=(0, L / 2), bin_width=0.001) else: r, g_r = md.compute_rdf(trj, pairs=pairs, r_range=(0, L / 2), bin_width=0.001) rdfs['{0}{1}'.format(e1, e2)] = g_r integral = simps(r ** 2 * (g_r - 1) * np.sin(q * r) / (q * r), r) num += pre_factor * integral + int(e1 == e2) S[i] = num/denom return Q, S
import os import json import pdb import numpy as np import mdtraj curr_dir = os.getcwd() index = json.load(open('index.txt', 'r')) for comp in index.keys(): print(comp) os.chdir(os.path.join(curr_dir, comp)) all_rdfs = [] for chunk in mdtraj.iterload('npt_80-100ns.xtc', top='npt.gro', chunk=100): pairs = chunk.topology.select_pairs( 'resname DSPC and (name O22 or name O32)', 'resname HOH and name O') bins, rdf = mdtraj.compute_rdf(chunk, pairs, r_range=[0, 2]) all_rdfs.append(rdf) np.savetxt('ester-water_rdf.txt', np.column_stack( [bins, np.mean(all_rdfs, axis=0), np.std(all_rdfs, axis=0)]), header='r, g(r), error')
cg_top, time=None, unitcell_lengths=t.unitcell_lengths, unitcell_angles=t.unitcell_angles) cg_traj.save_dcd('cg_traj.dcd') # print(cg_traj) # print(cg_top) # print(cg_xyz) # ### Calculate RDF and save # In[ ]: pairs = cg_traj.top.select_pairs(selection1='name "carbon"', selection2='name "carbon"') # mdtraj.compute_rdf(traj, pairs=None, r_range=None, bin_width=0.005, n_bins=None, periodic=True, opt=True) r, g_r = md.compute_rdf(cg_traj, pairs=pairs, r_range=(0, 1.2), bin_width=0.005) np.savetxt('rdfs_aa.txt', np.transpose([r, g_r])) # In[ ]: fig, ax = plt.subplots() ax.plot(r, g_r) ax.set_xlabel("r") ax.set_ylabel("g(r)")