def calc_avg_tilt_angle(traj_filename, top_filename, output_filename, ndx_filename, n_chains): """Calculate the avg. tilt angle of both monolayers in a two monolayer system Returns the average tilt angle of each monolayer at each frame of a trajectory in a dual monolayer system. Parameters ---------- traj_filename : str Name of trajectory file top_filename : str Name of topology file output_filename : str Name of output file ndx_filename : str Name of Gromacs .ndx file which specifies atom groups n_chains : int Number of chains per monolayer """ groups = read_ndx(ndx_filename) bottom_chains = [id - 1 for id in groups['bottom_chains']] bottom_chains = [ chain.tolist() for chain in np.array_split(bottom_chains, n_chains) ] top_chains = [id - 1 for id in groups['top_chains']] top_chains = [ chain.tolist() for chain in np.array_split(top_chains, n_chains) ] traj = md.load(traj_filename, top=top_filename) tilts = [] tilts_err = [] for monolayer in [bottom_chains, top_chains]: monolayer_tilt = [] for i, chain in enumerate(monolayer): chain_slice = traj.atom_slice(chain) directors = _compute_director(chain_slice) monolayer_tilt.append([ _tilt_angle(director, [0.0, 0.0, 1.0]) for director in directors ]) monolayer_tilt = np.asarray(monolayer_tilt) monolayer_tilt = np.transpose(monolayer_tilt) tilts_by_frame = np.mean(monolayer_tilt, axis=1) tilts_err_by_frame = np.std(monolayer_tilt, axis=1) tilts.append(tilts_by_frame) tilts_err.append(tilts_err_by_frame) tilts = np.asarray(tilts) tilts = np.transpose(tilts) np.savetxt(output_filename, np.column_stack((traj.time, tilts)), header='Time\tBottom\tTop')
def calc_hexagonal_order_freud(traj_filename, top_filename, output_filename, ndx_filename, n_chains): from freud.box import Box from freud.order import HexOrderParameter topology = md.load(top_filename).topology atoms = np.array(list(topology.atoms)) atom_names = [atom.name for atom in atoms] groups = read_ndx(ndx_filename) bottom_chains = np.array(groups['bottom_chains']) - 1 top_chains = np.array(groups['top_chains']) - 1 bottom_chains = np.array_split(bottom_chains, n_chains) top_chains = np.array_split(top_chains, n_chains) bottom_termini = np.array(groups['bottom_termini']) - 1 top_termini = np.array(groups['top_termini']) - 1 bottom_termini = np.array_split(bottom_termini, n_chains) top_termini = np.array_split(top_termini, n_chains) traj = md.load(traj_filename, top=top_filename) for i, group in enumerate( [bottom_chains, top_chains, bottom_termini, top_termini]): group_COM = [] for chain in group: chain_slice = traj.atom_slice(chain) chain_com = md.compute_center_of_mass(chain_slice) for com in chain_com: for j in range(2): if com[j] < 0: com[j] += traj.unitcell_lengths[0, j] elif com[j] > traj.unitcell_lengths[0, j]: com[j] -= traj.unitcell_lengths[0, j] group_COM.append(chain_com) group_COM = np.array(group_COM).transpose(1, 0, 2) rmax = 0.75 box = Box(Lx=traj.unitcell_lengths[0, 0], Ly=traj.unitcell_lengths[0, 1], Lz=traj.unitcell_lengths[0, 2]) order_parameter = [] for xyz in group_COM: median = np.median(xyz[:, 2]) if i == 2: xyz = np.array( [point for point in xyz if point[2] > median - 0.5]) elif i == 3: xyz = np.array( [point for point in xyz if point[2] < median + 0.5]) hex_order = HexOrderParameter(rmax, 6) hex_order.compute(box, xyz.astype(np.float32)) order_parameter.append(abs(hex_order.getPsi()).mean()) print(np.mean(order_parameter[int(len(order_parameter) / 2):])) import pdb pdb.set_trace()
def interfacial_fraction(traj_filename, top_filename, output_filename, ndx_filename, n_chains): """Estimate the fraction of chains where termini are at the interface Some description of what is returned Parameters ---------- traj_filename : str Name of trajectory file top_filename : str Name of topology file output_filename : str Name of output file ndx_filename : str Name of Gromacs .ndx file which specifies atom groups n_chains : int Number of chains per monolayer """ traj = md.load(traj_filename, top=top_filename) interfacial_frac_bottom = [] interfacial_frac_top = [] groups = read_ndx(ndx_filename) for group in ['bottom_termini', 'top_termini']: termini = np.array(ndx[group]) - 1 termini = np.array_split(termini, n_chains) xyz = [] for chain in termini: chain_slice = traj.atom_slice(chain) chain_com = md.compute_center_of_mass(chain_slice) xyz.append(chain_com[0]) termini_z = np.array([pos[2] for pos in bottom_xyz]) if group == 'bottom_termini': cut = np.median(termini_z) - 0.5 termini_xy = np.array([ coords[:2] for i, coords in enumerate(xyz) if termini_z[i] > cut ]) interfacial_frac_bottom.append(len(termini_xy) / 100) else: cut = np.median(termini_z) + 0.5 termini_xy = np.array([ coords[:2] for i, coords in enumerate(xyz) if termini_z[i] < cut ]) interfacial_frac_top.append(len(termini_xy) / 100) print('Under construction!') '''
def calc_com_2Dmsd(traj_filename, top_filename, output_filetag, ndx_filename, chainlength): topology = md.load(top_filename).topology atoms = np.array(list(topology.atoms)) atom_names = [atom.name for atom in atoms] groups = read_ndx(ndx_filename) bottom_chains = [ id - 1 for id in groups['bottom_chains'] if atom_names[id - 1] == 'C' ] n_chains = int(len(bottom_chains) / chainlength) bottom_chains = [ chain.tolist() for chain in np.array_split(bottom_chains, n_chains) ] top_chains = [ id - 1 for id in groups['top_chains'] if atom_names[id - 1] == 'C' ] top_chains = [ chain.tolist() for chain in np.array_split(top_chains, n_chains) ] traj = md.load(traj_filename, top=top_filename) locations = [] for i, chain in enumerate(bottom_chains): chain_slice = traj.atom_slice(chain) com = md.compute_center_of_mass(chain_slice) for com_frame in com: for j in range(2): if com_frame[j] < 0: com_frame[j] += traj.unitcell_lengths[0, j] elif com_frame[j] > traj.unitcell_lengths[0, j]: com_frame[j] -= traj.unitcell_lengths[0, j] locations.append(com) locations = np.array(locations).transpose(1, 0, 2) com_traj = _create_com_traj(locations, traj.time, traj.unitcell_lengths, traj.unitcell_angles) msd = [] for frame in com_traj: msd_frame = 0 for i, atom in enumerate(com_traj.top.atoms): disp = frame.xyz[0, i, :2] - com_traj.xyz[0, i, :2] for j, dim in enumerate(disp): if dim > com_traj.unitcell_lengths[0, j] / 2: dim -= traj.unitcell_lengths[0, j] elif dim < -com_traj.unitcell_lengths[0, j] / 2: dim += traj.unitcell_lengths[0, j] msd_frame += np.linalg.norm(dim)**2 msd_frame /= com_traj.top.n_atoms msd.append(msd_frame) np.savetxt(output_filetag + '.txt', np.column_stack((traj.time, msd)))
def calc_friction(trr_filename, output_filename, ndx_filename): import MDAnalysis as mda groups = read_ndx(ndx_filename) bottom = groups['bottom'] bottom = np.array(bottom) bottom -= 1 fric = [] trr = mda.coordinates.TRR.TRRReader(trr_filename) for frame in trr: forces_on_bottom = frame.forces[bottom] fric.append([frame.time, np.sum(forces_on_bottom[:, 0]) * 0.0166]) np.savetxt(output_filename, fric)
def calc_interdigitation(traj_filename, top_filename, output_filename, ndx_filename, bin_size=0.025): groups = read_ndx(ndx_filename) bottom_monolayer = [id - 1 for id in groups['bottom_chains']] top_monolayer = [id - 1 for id in groups['top_chains']] traj = md.load(traj_filename, top=top_filename) top_z = traj.xyz[:, top_monolayer, 2] bottom_z = traj.xyz[:, bottom_monolayer, 2] masses = np.array([atom.element.mass for atom in traj.top.atoms]) top_masses = masses[top_monolayer] bottom_masses = masses[bottom_monolayer] top_max = np.max(top_z, axis=1) bottom_min = np.min(bottom_z, axis=1) bins = [ int((zmax - zmin) / bin_size) for zmax, zmin in zip(top_max, bottom_min) ] interdigitation = [] for i, (top_frame_z, bottom_frame_z) in enumerate(zip(top_z, bottom_z)): hist_top, bin_edges = np.histogram(top_frame_z, bins=bins[i], range=(bottom_min[i], top_max[i]), normed=False, weights=top_masses) hist_bottom, bin_edges = np.histogram(bottom_frame_z, bins=bins[i], range=(bottom_min[i], top_max[i]), normed=False, weights=bottom_masses) hist_overlap = [] bin_middles = [(bin_edges[j + 1] + edge) / 2 for j, edge in enumerate(bin_edges[:-1])] for count_top, count_bottom in zip(hist_top, hist_bottom): count_prod = count_top * count_bottom count_sum = count_top + count_bottom overlap = 0 if count_sum != 0: overlap = (4 * count_prod) / (count_sum**2) hist_overlap.append(overlap) hist_overlap = np.asarray(hist_overlap) lam = simps(hist_overlap, bin_middles) interdigitation.append(lam) np.savetxt(output_filename, np.column_stack((traj.time, interdigitation)))
def calc_hexagonal_order_freud(traj_filename, top_filename, output_filename, ndx_filename, chainlength): from freud.box import Box from freud.order import HexOrderParameter topology = md.load(top_filename).topology atoms = np.array(list(topology.atoms)) atom_names = [atom.name for atom in atoms] groups = read_ndx(ndx_filename) bottom_chains = [ id - 1 for id in groups['bottom_chains'] if atom_names[id - 1] == 'C' ] n_chains = int(len(bottom_chains) / chainlength) bottom_chains = np.array_split(bottom_chains, n_chains) top_chains = [ id - 1 for id in groups['top_chains'] if atom_names[id - 1] == 'C' ] top_chains = np.array_split(top_chains, n_chains) traj = md.load(traj_filename, top=top_filename) for i, group in enumerate([bottom_chains, top_chains]): group_COM = [] for chain in group: chain_slice = traj.atom_slice(chain) chain_com = md.compute_center_of_mass(chain_slice) for com in chain_com: for j in range(2): if com[j] < 0: com[j] += traj.unitcell_lengths[0, j] elif com[j] > traj.unitcell_lengths[0, j]: com[j] -= traj.unitcell_lengths[0, j] group_COM.append(chain_com) group_COM = np.array(group_COM).transpose(1, 0, 2) rmax = 0.75 box = Box(Lx=traj.unitcell_lengths[0, 0], Ly=traj.unitcell_lengths[0, 1], Lz=traj.unitcell_lengths[0, 2]) order_parameter = [] for xyz in group_COM: hex_order = HexOrderParameter(rmax, 6) hex_order.compute(box, xyz.astype(np.float32)) order_parameter.append(abs(hex_order.getPsi()).mean()) print(np.mean(order_parameter[int(len(order_parameter) / 2):]))
def _gather_chains(top_filename, ndx_filename, n_chains, C_only=False): """Helper function to gather chain indices into separate arrays Parameters ---------- top_filename : str Name of topology file (typically GRO format) ndx_filename : str Name of the GROMACS index file to read group information from n_chains : int Number of monolayer chains per surface C_only : bool, optional, default=False Only include carbon atoms in the calculation Returns ------- list of lists Chain indices in the bottom monolayer list of lists Chain indices in the top monolayer """ topo = md.load(top_filename).topology atoms = np.array([atom.name for atom in topo.atoms]) groups = read_ndx(ndx_filename) if C_only: bottom_chains = [ id for id in groups['bottom_chains'] if atoms[id] == 'C' ] top_chains = [id for id in groups['top_chains'] if atoms[id] == 'C'] else: bottom_chains = [id for id in groups['bottom_chains']] top_chains = [id for id in groups['top_chains']] bottom_chains = [ chain.tolist() for chain in np.array_split(bottom_chains, n_chains) ] top_chains = [ chain.tolist() for chain in np.array_split(top_chains, n_chains) ] return bottom_chains, top_chains
def calc_nematic_order(traj_filename, top_filename, output_filename, ndx_filename, n_chains): """Calculate the nematic order of both monolayers in a two monolayer system Returns the nematic order of each monolayer at each frame of a trajectory in a dual monolayer system. Parameters ---------- traj_filename : str Name of trajectory file top_filename : str Name of topology file output_filename : str Name of output file ndx_filename : str Name of Gromacs .ndx file which specifies atom groups n_chains : int Number of chains per monolayer """ groups = read_ndx(ndx_filename) bottom_chains = [id - 1 for id in groups['bottom_chains']] bottom_chains = [ chain.tolist() for chain in np.array_split(bottom_chains, n_chains) ] top_chains = [id - 1 for id in groups['top_chains']] top_chains = [ chain.tolist() for chain in np.array_split(top_chains, n_chains) ] traj = md.load(traj_filename, top=top_filename) S2_bottom = md.compute_nematic_order(traj, indices=bottom_chains) S2_top = md.compute_nematic_order(traj, indices=top_chains) np.savetxt(output_filename, np.column_stack((traj.time, S2_bottom, S2_top)), header='Time\tBottom\tTop')
def count_hydrogen_bonds(traj_filename, top_filename, ndx_filename, mol2_filename, top_group, bottom_group, output_filename): """Count the number of inter- or intra-monolayer hydrogen bonds The number of inter- and intra-monolayer hydrogen bonds are determined for each frame in a trajectory using the Wernet-Nilsson method implemented in MDTraj and are output to a file with a user-specified filename. Parameters ---------- traj_filename : str Name of trajectory file (typically XTC format) top_filename : str Name of topology file (typically GRO format) ndx_filename : str Name of the GROMACS index file to read group information from mol2_filename : str Name of mol2 file used to read bond information top_group : str Tag for index group (read from NDX file) for indices considered as part of the top monolayer bottom_group : str Tag for index group (read from NDX file) for indices considered as part of the bottom monolayer output_filename : str Name of file to output results to """ topo = md.load(top_filename).topology atoms = list(topo.atoms) groups = read_ndx(ndx_filename) bottom_monolayer = np.array(groups[bottom_group]) top_monolayer = np.array(groups[top_group]) monolayers = np.hstack((bottom_monolayer, top_monolayer)) h_bonds_top = [] h_bonds_bottom = [] h_bonds_interface = [] time_traj = [] for traj_chunk in md.iterload(traj_filename, top=mol2_filename, chunk=10): for i, atom in enumerate(traj_chunk.top.atoms): atom.element = atoms[i].element h_bonds_total = md.wernet_nilsson(traj_chunk) for frame in h_bonds_total: h_bonds_top_frame = [] h_bonds_bottom_frame = [] h_bonds_interface_frame = [] for bond in frame: if all(atom_id in top_monolayer for atom_id in bond): h_bonds_top_frame.append(tuple(bond)) elif all(atom_id in bottom_monolayer for atom_id in bond): h_bonds_bottom_frame.append(tuple(bond)) elif all(atom_id in monolayers for atom_id in bond): h_bonds_interface_frame.append(tuple(bond)) h_bonds_top.append(len(h_bonds_top_frame)) h_bonds_bottom.append(len(h_bonds_bottom_frame)) h_bonds_interface.append(len(h_bonds_interface_frame)) time_traj.append(traj_chunk.time) time_traj = np.concatenate(time_traj).ravel() h_bonds_top = np.asarray(h_bonds_top) h_bonds_bottom = np.asarray(h_bonds_bottom) h_bonds_interface = np.asarray(h_bonds_interface) np.savetxt(output_filename, np.column_stack((time_traj, h_bonds_interface, h_bonds_top, h_bonds_bottom)), header='Time\tInter-\tIntra-top\tIntra-bottom')
def count_hydrogen_bonds(traj_filename, top_filename, output_filename, mol2_filename, ndx_filename, top_group, bottom_group): """Count the number of inter- or intra-monolayer hydrogen bonds The number of inter- and intra-monolayer hydrogen bonds is determined for each frame in a trajectory using the Wernet-Nilsson method implemented in MDTraj. Parameters ---------- traj_filename : str Name of trajectory file top_filename : str Name of topology file output_filename : str Name of output file mol2_filename : str Name of mol2 file used to read bond information ndx_filename : str Name of Gromacs .ndx file which specifies atom groups top_group : str Only atom indices from this group (read from a .ndx file) will be considered as part of the top monolayer. bottom_group : str Only atom indices from this group (read from a .ndx file) will be considered as part of the bottom monolayer. """ top = md.load(top_filename).topology top_atoms = [atom for atom in top.atoms] groups = read_ndx(ndx_filename) top_monolayer = np.array(groups[top_group]) - 1 bottom_monolayer = np.array(groups[bottom_group]) - 1 monolayers = np.hstack((bottom_monolayer, top_monolayer)) h_bonds_top = [] h_bonds_bottom = [] h_bonds_interface = [] time_traj = [] for traj_chunk in md.iterload(traj_filename, top=mol2_filename, chunk=10): for i, atom in enumerate(traj_chunk.top.atoms): atom.element = top_atoms[i].element h_bonds_total = md.wernet_nilsson(traj_chunk) for frame in h_bonds_total: h_bonds_top_frame = [ tuple(bond) for bond in frame if all(atom_id in top_monolayer for atom_id in bond) ] h_bonds_bottom_frame = [ tuple(bond) for bond in frame if all(atom_id in bottom_monolayer for atom_id in bond) ] h_bonds_interface_frame = [ tuple(bond) for bond in frame if (all(atom_id in monolayers for atom_id in bond) and tuple(bond) not in h_bonds_top_frame and tuple(bond) not in h_bonds_bottom_frame) ] h_bonds_top.append(len(h_bonds_top_frame)) h_bonds_bottom.append(len(h_bonds_bottom_frame)) h_bonds_interface.append(len(h_bonds_interface_frame)) time_traj.append(traj_chunk.time) time_traj = np.concatenate(time_traj).ravel() h_bonds_top = np.asarray(h_bonds_top) h_bonds_bottom = np.asarray(h_bonds_bottom) h_bonds_interface = np.asarray(h_bonds_interface) np.savetxt(output_filename, np.column_stack((time_traj, h_bonds_interface, h_bonds_top, h_bonds_bottom)), header='Time\tInter-\tIntra-top\tIntra-bottom')
def calc_gauche_defects(traj_filename, top_filename, output_filename, ndx_filename, chainlength): """Calculate the roughness of a monolayer Parameters ---------- traj_filename : str Name of (unwrapped) trajectory file top_filename : str Name of topology file output_filename : str Name of output file """ topology = md.load(top_filename).topology atoms = np.array(list(topology.atoms)) atom_names = [atom.name for atom in atoms] groups = read_ndx(ndx_filename) bottom_chains = [ id for id in groups['bottom_chains'] if atom_names[id - 1] == 'C' ] n_chains = int(len(bottom_chains) / chainlength) bottom_chains = [ chain.tolist() for chain in np.array_split(bottom_chains, n_chains) ] top_chains = [ id for id in groups['top_chains'] if atom_names[id - 1] == 'C' ] top_chains = [ chain.tolist() for chain in np.array_split(top_chains, n_chains) ] bottom_chemisorbed = [ id for id in groups['bottom_chemisorbed'] if atom_names[id - 1] == 'C' ] n_chemisorbed = int(len(bottom_chemisorbed) / chainlength) bottom_chemisorbed = [ chain.tolist() for chain in np.array_split(bottom_chemisorbed, n_chemisorbed) ] top_chemisorbed = [ id for id in groups['top_chemisorbed'] if atom_names[id - 1] == 'C' ] top_chemisorbed = [ chain.tolist() for chain in np.array_split(top_chemisorbed, n_chemisorbed) ] n_crosslinked = n_chains - n_chemisorbed if n_crosslinked > 0: bottom_crosslinked = [ id for id in groups['bottom_crosslinked'] if atom_names[id - 1] == 'C' ] bottom_crosslinked = [ chain.tolist() for chain in np.array_split(bottom_crosslinked, n_crosslinked) ] top_crosslinked = [ id for id in groups['top_crosslinked'] if atom_names[id - 1] == 'C' ] top_crosslinked = [ chain.tolist() for chain in np.array_split(top_crosslinked, n_crosslinked) ] if n_crosslinked > 0: groups = [ bottom_chains, top_chains, bottom_chemisorbed, top_chemisorbed, bottom_crosslinked, top_crosslinked ] dihedral_groups = [[], [], [], [], [], []] else: groups = [ bottom_chains, top_chains, bottom_chemisorbed, top_chemisorbed ] dihedral_groups = [[], [], [], []] for i, group in enumerate(groups): for chain in group: chain_dihedrals = [chain[j:j + 4] for j in range(len(chain) - 3)] for dihedral in chain_dihedrals: dihedral_groups[i].append(dihedral) traj = md.load(traj_filename, top=top_filename) gauche_defects = [] for dihedrals in dihedral_groups: dihedral_angles = md.compute_dihedrals(traj, dihedrals, periodic=False) gauche_defects_group = [] for angles in dihedral_angles: gauche_defects_group.append(_count_gauche(angles)) gauche_defects.append(gauche_defects_group) gauche_mean_chains = np.mean([gauche_defects[0], gauche_defects[1]], axis=0) gauche_mean_chemisorbed = np.mean([gauche_defects[2], gauche_defects[3]], axis=0) gauche_defects.insert(2, gauche_mean_chains) gauche_defects.insert(5, gauche_mean_chemisorbed) if n_crosslinked > 0: gauche_mean_crosslinked = np.mean( [gauche_defects[6], gauche_defects[7]], axis=0) gauche_defects.append(gauche_mean_crosslinked) gauche_defects = np.asarray(gauche_defects) gauche_defects = np.transpose(gauche_defects) if n_crosslinked > 0: np.savetxt(output_filename, np.column_stack((traj.time, gauche_defects)), header='Time\tBottom\tTop\tAll-mean\tBottom-chemisorbed\t' + 'Top-chemisorbed\tChemisorbed-mean\tBottom-crosslinked\t' + 'Top-crosslinked\tCrosslinked-mean') else: np.savetxt(output_filename, np.column_stack((traj.time, gauche_defects)), header='Time\tBottom\tTop\tAll-mean\tBottom-chemisorbed\t' + 'Top-chemisorbed\tChemisorbed-mean')
def calc_interdigitation(traj_filename, top_filename, ndx_filename, output_filename, bin_size=0.025): """Calculates the interdigitation between two monolayer films This calculation utilizes the procedure of Das et al. (see Ref) to calculate an overlap parameter between the two monolayers as a function of their mass densities at a particular z-location (normal to the surface). Integrating over this parameter from the bottom surface to the top surface yields the interdigitation in distance units. Parameters ---------- traj_filename : str Name of trajectory file (typically XTC format) top_filename : str Name of topology file (typically GRO format) ndx_filename : str Name of the GROMACS index file to read group information from output_filename : str Name of file to output results to bin-size : float Size of bins in the z-dimension (normal to the surface) to collect mass densities. This controls the fidelity of the interdigitation calculation. References ---------- .. [1] Das, C., Noro, M.G., Olmsted, P.D., "Simulation studies of stratum corneum lipid mixtures." (2009) Biophys. J. 97, 1941-1951 """ groups = read_ndx(ndx_filename) bottom_monolayer = groups['bottom_chains'] top_monolayer = groups['top_chains'] traj = md.load(traj_filename, top=top_filename) atoms = np.array(list(traj.top.atoms)) bottom_masses = np.array( [atom.element.mass for atom in atoms[bottom_monolayer]]) top_masses = np.array([atom.element.mass for atom in atoms[top_monolayer]]) bounds = (np.min(traj.xyz[:, bottom_monolayer, 2]), np.max(traj.xyz[:, top_monolayer, 2])) bins = int(round((bounds[1] - bounds[0]) / bin_size)) bin_size = (bounds[1] - bounds[0]) / bins interdigitation = [] for xyz in traj.xyz: hist_bottom, _ = np.histogram(xyz[bottom_monolayer, 2], bins=bins, range=bounds, normed=False, weights=bottom_masses) hist_top, bin_edges = np.histogram(xyz[top_monolayer, 2], bins=bins, range=bounds, normed=False, weights=top_masses) bin_centers = bin_edges[1:] - bin_size / 2 rho_overlap = [] for count_top, count_bottom in zip(hist_top, hist_bottom): count_prod = count_top * count_bottom count_sum = count_top + count_bottom overlap = 0 if count_sum != 0: overlap = (4 * count_prod) / (count_sum**2) rho_overlap.append(overlap) interdigitation.append(simps(rho_overlap, x=bin_centers)) np.savetxt(output_filename, np.column_stack((traj.time, interdigitation)))
def calc_avg_tilt_angle(traj_filename, top_filename, output_filename, ndx_filename, chainlength): """Calculate the average tilt angle of a monolayer Returns the average tilt angle at each frame of a trajectory, averaged between the top and bottom monolayers in a dual monolayer system. Parameters ---------- traj_filename : str Name of trajectory file top_filename : str Name of topology file output_filename : str Name of output file ndx_filename : str Name of Gromacs .ndx file which specifies atom groups chainlength : int Number of carbons per chain """ topology = md.load(top_filename).topology atoms = np.array(list(topology.atoms)) atom_names = [atom.name for atom in atoms] groups = read_ndx(ndx_filename) bottom_chains = [ id - 1 for id in groups['bottom_chains'] if atom_names[id - 1] == 'C' ] n_chains = int(len(bottom_chains) / chainlength) bottom_chains = [ chain.tolist() for chain in np.array_split(bottom_chains, n_chains) ] top_chains = [ id - 1 for id in groups['top_chains'] if atom_names[id - 1] == 'C' ] top_chains = [ chain.tolist() for chain in np.array_split(top_chains, n_chains) ] bottom_chemisorbed = [ id - 1 for id in groups['bottom_chemisorbed'] if atom_names[id - 1] == 'C' ] n_chemisorbed = int(len(bottom_chemisorbed) / chainlength) bottom_chemisorbed = [ chain.tolist() for chain in np.array_split(bottom_chemisorbed, n_chemisorbed) ] top_chemisorbed = [ id - 1 for id in groups['top_chemisorbed'] if atom_names[id - 1] == 'C' ] top_chemisorbed = [ chain.tolist() for chain in np.array_split(top_chemisorbed, n_chemisorbed) ] n_crosslinked = n_chains - n_chemisorbed if n_crosslinked > 0: bottom_crosslinked = [ id - 1 for id in groups['bottom_crosslinked'] if atom_names[id - 1] == 'C' ] bottom_crosslinked = [ chain.tolist() for chain in np.array_split(bottom_crosslinked, n_crosslinked) ] top_crosslinked = [ id - 1 for id in groups['top_crosslinked'] if atom_names[id - 1] == 'C' ] top_crosslinked = [ chain.tolist() for chain in np.array_split(top_crosslinked, n_crosslinked) ] traj = md.load(traj_filename, top=top_filename) if n_crosslinked > 0: tilt_groups = [ bottom_chains, top_chains, bottom_chemisorbed, top_chemisorbed, bottom_crosslinked, top_crosslinked ] else: tilt_groups = [ bottom_chains, top_chains, bottom_chemisorbed, top_chemisorbed ] tilts = [] tilts_err = [] for group in tilt_groups: group_tilt_angles = [] for i, chain in enumerate(group): chain_slice = traj.atom_slice(chain) directors = _compute_director(chain_slice) group_tilt_angles.append([ _tilt_angle(director, [0.0, 0.0, 1.0]) for director in directors ]) group_tilt_angles = np.asarray(group_tilt_angles) group_tilt_angles = np.transpose(group_tilt_angles) tilts_by_frame = np.mean(group_tilt_angles, axis=1) tilts_err_by_frame = np.std(group_tilt_angles, axis=1) tilts.append(tilts_by_frame) tilts_err.append(tilts_err_by_frame) tilts_mean_chains = np.mean([tilts[0], tilts[1]], axis=0) tilts_mean_chemisorbed = np.mean([tilts[2], tilts[3]], axis=0) tilts.insert(2, tilts_mean_chains) tilts.insert(5, tilts_mean_chemisorbed) if n_crosslinked > 0: tilts_mean_crosslinked = np.mean([tilts[6], tilts[7]], axis=0) tilts.append(tilts_mean_crosslinked) tilts = np.asarray(tilts) tilts = np.transpose(tilts) if n_crosslinked > 0: np.savetxt(output_filename, np.column_stack((traj.time, tilts)), header='Time\tBottom\tTop\tAll-mean\tBottom-chemisorbed\t' + 'Top-chemisorbed\tChemisorbed-mean\tBottom-crosslinked\t' + 'Top-crosslinked\tCrosslinked-mean') else: np.savetxt(output_filename, np.column_stack((traj.time, tilts)), header='Time\tBottom\tTop\tAll-mean\tBottom-chemisorbed\t' + 'Top-chemisorbed\tChemisorbed-mean')
def voronoi_termini(traj_filename, top_filename, output_filename, ndx_filename, n_chains): """Perform a Voronoi tessellation to estimate coordination number and area Some description of what is returned Parameters ---------- traj_filename : str Name of trajectory file top_filename : str Name of topology file output_filename : str Name of output file ndx_filename : str Name of Gromacs .ndx file which specifies atom groups n_chains : int Number of chains per monolayer """ traj = md.load(traj_filename, top=top_filename) x_length.unitcell_lengths[0, 0] y_length.unitcell_lengths[0, 1] coordination_number = [] coordination_err = [] interfacial_frac = [] groups = read_ndx(ndx_filename) for group in ['bottom_termini', 'top_termini']: termini = np.array(ndx[group]) - 1 termini = np.array_split(termini, n_chains) xyz = [] for chain in termini: chain_slice = traj.atom_slice(chain) chain_com = md.compute_center_of_mass(chain_slice) xyz.append(chain_com[0]) termini_z = np.array([pos[2] for pos in bottom_xyz]) if group == 'bottom_termini': cut = np.median(termini_z) - 0.5 termini_xy = np.array([ coords[:2] for i, coords in enumerate(xyz) if termini_z[i] > cut ]) else: cut = np.median(termini_z) + 0.5 termini_xy = np.array([ coords[:2] for i, coords in enumerate(xyz) if termini_z[i] < cut ]) termini_xy = [arr.tolist() for arr in termini_xy] cells = pyvoro.compute_2d_voronoi(points=termini_xy, limits=[[0., x_length], [0., y_length]], dispersion=2, periodic=[False, False]) coordination_number.append( np.mean([float(len(cell['adjacency'])) for cell in cells])) coordination_err.append( np.std([float(len(cell['adjacency'])) for cell in cells])) print('Under construction!') '''
def calc_OCF(traj_filename, top_filename, output_filetag, ndx_filename, chainlength): """Calculate the orientational correlation function of monolayer chains Parameters ---------- traj_filename : str Name of (unwrapped) trajectory file top_filename : str Name of topology file output_filename : str Name of output file """ topology = md.load(top_filename).topology atoms = np.array(list(topology.atoms)) atom_names = [atom.name for atom in atoms] groups = read_ndx(ndx_filename) bottom_chains = [ id - 1 for id in groups['bottom_chains'] if atom_names[id - 1] == 'C' ] n_chains = int(len(bottom_chains) / chainlength) bottom_chains = [ chain.tolist() for chain in np.array_split(bottom_chains, n_chains) ] top_chains = [ id - 1 for id in groups['top_chains'] if atom_names[id - 1] == 'C' ] top_chains = [ chain.tolist() for chain in np.array_split(top_chains, n_chains) ] traj = md.load(traj_filename, top=top_filename) directors = [] locations = [] for i, chain in enumerate(bottom_chains): chain_slice = traj.atom_slice(chain) chain_director = _compute_director(chain_slice) for director in chain_director: if director[-1] < 0: director *= -1. directors.append(chain_director) com = md.compute_center_of_mass(chain_slice) for com_frame in com: for j in range(2): if com_frame[j] < 0: com_frame[j] += traj.unitcell_lengths[0, j] elif com_frame[j] > traj.unitcell_lengths[0, j]: com_frame[j] -= traj.unitcell_lengths[0, j] locations.append(com) directors = np.array(directors).transpose(1, 0, 2) film_directors = np.mean(directors, axis=1) film_directors /= np.linalg.norm(film_directors, axis=1).reshape(len(film_directors), 1) locations = np.array(locations).transpose(1, 0, 2) com_traj = _create_com_traj(locations, traj.time, traj.unitcell_lengths, traj.unitcell_angles) pairs = com_traj.top.select_pairs('all', 'all') distances = md.compute_distances(com_traj, pairs, periodic=True) cof = [] cof_6 = [] for i, frame in enumerate(directors): frame_angles = np.array( [_angle(film_directors[i], director) for director in frame]) dist_hist = np.histogram(distances[i], bins=np.arange(0.25, 2.5, 0.1)) correlations = np.cos( np.array([ 2. * (frame_angles[pairs[j, 0]] - frame_angles[pairs[j, 1]]) for j, dist in enumerate(distances[i]) ])) correlation_hist = np.histogram(distances[i], bins=np.arange(0.25, 2.5, 0.1), weights=correlations) correlations_6 = np.cos( np.array([ 6. * (frame_angles[pairs[j, 0]] - frame_angles[pairs[j, 1]]) for j, dist in enumerate(distances[i]) ])) correlation_hist_6 = np.histogram(distances[i], bins=np.arange(0.25, 2.5, 0.1), weights=correlations_6) cof.append(correlation_hist[0] / dist_hist[0]) cof_6.append(correlation_hist_6[0] / dist_hist[0]) cof = np.array(cof) cof = np.mean(cof, axis=0) cof_6 = np.array(cof_6) cof_6 = np.mean(cof_6, axis=0) r_vals = np.arange(0.25, 2.5, 0.1) r_vals = np.array([(val + r_vals[i + 1]) / 2 for i, val in enumerate(r_vals[:-1])]) np.savetxt(output_filetag + '2.txt', np.vstack((r_vals, cof))) np.savetxt(output_filetag + '6.txt', np.vstack((r_vals, cof_6)))
def calc_gauche_position(traj_filename, top_filename, output_filename, ndx_filename, chainlength): """Calculate the roughness of a monolayer Parameters ---------- traj_filename : str Name of (unwrapped) trajectory file top_filename : str Name of topology file output_filename : str Name of output file """ topology = md.load(top_filename).topology atoms = np.array(list(topology.atoms)) atom_names = [atom.name for atom in atoms] groups = read_ndx(ndx_filename) bottom_chains = [ id for id in groups['bottom_chains'] if atom_names[id - 1] == 'C' ] n_chains = int(len(bottom_chains) / chainlength) bottom_chains = [ chain.tolist() for chain in np.array_split(bottom_chains, n_chains) ] top_chains = [ id for id in groups['top_chains'] if atom_names[id - 1] == 'C' ] top_chains = [ chain.tolist() for chain in np.array_split(top_chains, n_chains) ] bottom_chemisorbed = [ id for id in groups['bottom_chemisorbed'] if atom_names[id - 1] == 'C' ] n_chemisorbed = int(len(bottom_chemisorbed) / chainlength) bottom_chemisorbed = [ chain.tolist() for chain in np.array_split(bottom_chemisorbed, n_chemisorbed) ] top_chemisorbed = [ id for id in groups['top_chemisorbed'] if atom_names[id - 1] == 'C' ] top_chemisorbed = [ chain.tolist() for chain in np.array_split(top_chemisorbed, n_chemisorbed) ] n_crosslinked = n_chains - n_chemisorbed if n_crosslinked > 0: bottom_crosslinked = [ id for id in groups['bottom_crosslinked'] if atom_names[id - 1] == 'C' ] bottom_crosslinked = [ chain.tolist() for chain in np.array_split(bottom_crosslinked, n_crosslinked) ] top_crosslinked = [ id for id in groups['top_crosslinked'] if atom_names[id - 1] == 'C' ] top_crosslinked = [ chain.tolist() for chain in np.array_split(top_crosslinked, n_crosslinked) ] if n_crosslinked > 0: groups = [ bottom_chains, top_chains, bottom_chemisorbed, top_chemisorbed, bottom_crosslinked, top_crosslinked ] else: groups = [ bottom_chains, top_chains, bottom_chemisorbed, top_chemisorbed ] dihedral_groups = [] for group in groups: group_dihedrals = [[] for i in range(chainlength - 3)] for chain in group: chain_dihedrals = [chain[i:i + 4] for i in range(len(chain) - 3)] for i, dihedral in enumerate(chain_dihedrals): group_dihedrals[i].append(dihedral) dihedral_groups.append(group_dihedrals) traj = md.load(traj_filename, top=top_filename) gauche_defects = [] for group in dihedral_groups: gauche_defects_group = [] for position in group: dihedral_angles = md.compute_dihedrals(traj, position, periodic=False) gauche_defects_position = [] for angles in dihedral_angles: gauche_defects_position.append(_count_gauche(angles)) gauche_defects_group.append(gauche_defects_position) gauche_defects.append(gauche_defects_group) gauche_sum_chains = np.sum([gauche_defects[0], gauche_defects[1]], axis=0) gauche_sum_chemisorbed = np.sum([gauche_defects[2], gauche_defects[3]], axis=0) gauche_defects.insert(2, gauche_sum_chains) gauche_defects.insert(5, gauche_sum_chemisorbed) if n_crosslinked > 0: gauche_sum_crosslinked = np.sum([gauche_defects[6], gauche_defects[7]], axis=0) gauche_defects.append(gauche_sum_crosslinked) filename_modifiers = [ 'bottom', 'top', 'all', 'bottom-chemisorbed', 'top-chemisorbed', 'chemisorbed', 'bottom-crosslinked', 'top-crosslinked', 'crosslinked' ] else: filename_modifiers = [ 'bottom', 'top', 'all', 'bottom-chemisorbed', 'top-chemisorbed', 'chemisorbed' ] gauche_defects = np.asarray(gauche_defects) for i, group in enumerate(gauche_defects): header = 'Time\t' + '\t'.join(str(num) for num in range(len(group))) new_filename = output_filename.split('.')[0] + \ '-{}.'.format(filename_modifiers[i]) + output_filename.split('.')[1] np.savetxt(new_filename, np.column_stack((traj.time, np.transpose(group))), header=header)
def calc_monolayer_roughness(traj_filename, top_filename, ndx_filename, output_filename, bins=5, heavy_only=False): """Calculate the surface roughness of the monolayer film Parameters ---------- traj_filename : str Name of trajectory file (typically XTC format) top_filename : str Name of topology file (typically GRO format) ndx_filename : str Name of the GROMACS index file to read group information from output_filename : str Name of file to output results to bins : int, optional, default=5 Number of bins per surface dimension to pixelate the surface heavy_only : bool, optional, default=False Exclude hydrogens from the calculation """ topo = md.load(top_filename).topology atoms = list(topo.atoms) groups = read_ndx(ndx_filename) if heavy_only: bottom_monolayer = np.array( [id for id in groups['bottom'] if atoms[id].name != 'H']) top_monolayer = np.array( [id for id in groups['top'] if atoms[id].name != 'H']) else: bottom_monolayer = np.array(groups['bottom']) top_monolayer = np.array(groups['top']) traj = md.load(traj_filename, top=top_filename) xy_plane = [[0.0, traj.unitcell_lengths[0, 0]], [0.0, traj.unitcell_lengths[0, 1]]] roughness = [] for monolayer, statistic in zip([bottom_monolayer, top_monolayer], [_film_level_bottom, _film_level_top]): roughness_monolayer = [] for frame in traj.xyz: monolayer_surface = binned_statistic_2d(x=frame[monolayer, 0], y=frame[monolayer, 1], values=frame[monolayer, 2], statistic=statistic, bins=bins, range=xy_plane) roughness_monolayer.append(np.std(monolayer_surface[0])) roughness.append(roughness_monolayer) roughness = np.transpose(roughness) np.savetxt(output_filename, np.column_stack((traj.time, roughness)), header='Time\tBottom\tTop')
def calc_nematic_order(traj_filename, top_filename, output_filename, ndx_filename, chainlength): """Calculate the nematic order of a monolayer Returns the average nematic order at each frame of a trajectory between the top and bottom monolayers in a dual monolayer system. Parameters ---------- traj_filename : str Name of trajectory file top_filename : str Name of topology file output_filename : str Name of output file ndx_filename : str Name of Gromacs .ndx file which specifies atom groups chainlength : int Number of carbons per chain """ topology = md.load(top_filename).topology atoms = np.array(list(topology.atoms)) atom_names = [atom.name for atom in atoms] groups = read_ndx(ndx_filename) bottom_chains = [ id - 1 for id in groups['bottom_chains'] if atom_names[id - 1] == 'C' ] n_chains = int(len(bottom_chains) / chainlength) bottom_chains = [ chain.tolist() for chain in np.array_split(bottom_chains, n_chains) ] top_chains = [ id - 1 for id in groups['top_chains'] if atom_names[id - 1] == 'C' ] top_chains = [ chain.tolist() for chain in np.array_split(top_chains, n_chains) ] bottom_chemisorbed = [ id - 1 for id in groups['bottom_chemisorbed'] if atom_names[id - 1] == 'C' ] n_chemisorbed = int(len(bottom_chemisorbed) / chainlength) bottom_chemisorbed = [ chain.tolist() for chain in np.array_split(bottom_chemisorbed, n_chemisorbed) ] top_chemisorbed = [ id - 1 for id in groups['top_chemisorbed'] if atom_names[id - 1] == 'C' ] top_chemisorbed = [ chain.tolist() for chain in np.array_split(top_chemisorbed, n_chemisorbed) ] n_crosslinked = n_chains - n_chemisorbed if n_crosslinked > 0: bottom_crosslinked = [ id - 1 for id in groups['bottom_crosslinked'] if atom_names[id - 1] == 'C' ] bottom_crosslinked = [ chain.tolist() for chain in np.array_split(bottom_crosslinked, n_crosslinked) ] top_crosslinked = [ id - 1 for id in groups['top_crosslinked'] if atom_names[id - 1] == 'C' ] top_crosslinked = [ chain.tolist() for chain in np.array_split(top_crosslinked, n_crosslinked) ] traj = md.load(traj_filename, top=top_filename) S2_bottom = md.compute_nematic_order(traj, indices=bottom_chains) S2_top = md.compute_nematic_order(traj, indices=top_chains) S2_bottom_chemisorbed = md.compute_nematic_order( traj, indices=bottom_chemisorbed) S2_top_chemisorbed = md.compute_nematic_order(traj, indices=top_chemisorbed) if n_crosslinked > 0: S2_bottom_crosslinked = md.compute_nematic_order( traj, indices=bottom_crosslinked) S2_top_crosslinked = md.compute_nematic_order(traj, indices=top_crosslinked) S2_mean_crosslinked = np.mean( [S2_bottom_crosslinked, S2_top_crosslinked], axis=0) S2_mean_chains = np.mean([S2_bottom, S2_top], axis=0) S2_mean_chemisorbed = np.mean([S2_bottom_chemisorbed, S2_top_chemisorbed], axis=0) if n_crosslinked > 0: np.savetxt(output_filename, np.column_stack( (traj.time, S2_bottom, S2_top, S2_mean_chains, S2_bottom_chemisorbed, S2_top_chemisorbed, S2_mean_chemisorbed, S2_bottom_crosslinked, S2_top_crosslinked, S2_mean_crosslinked)), header='Time\tBottom\tTop\tAll-mean\t' + 'Bottom-chemisorbed\tTop-chemisorbed\tChemisorbed-mean\t' + 'Bottom-crosslinked\tTop-crosslinked\tCrosslinked-mean') else: np.savetxt(output_filename, np.column_stack((traj.time, S2_bottom, S2_top, S2_mean_chains, S2_bottom_chemisorbed, S2_top_chemisorbed, S2_mean_chemisorbed)), header='Time\tBottom\tTop\tAll-mean\t' + 'Bottom-chemisorbed\tTop-chemisorbed\tChemisorbed-mean')