Пример #1
0
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')
Пример #2
0
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()
Пример #3
0
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!')
    '''
Пример #4
0
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)))
Пример #5
0
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)
Пример #6
0
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)))
Пример #7
0
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):]))
Пример #8
0
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
Пример #9
0
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')
Пример #10
0
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')
Пример #11
0
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')
Пример #12
0
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')
Пример #13
0
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)))
Пример #14
0
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')
Пример #15
0
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!')
    '''
Пример #16
0
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)))
Пример #17
0
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)
Пример #18
0
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')
Пример #19
0
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')