Exemple #1
0
    def __init__(self, host, rootfolder):
        self.host = host
        self.rootfolder = rootfolder
        self.host_folder = path.join(rootfolder, host)
        self.na_folder = path.join(self.host_folder, self.type_na)
        self.input_folder = path.join(self.na_folder, 'input')

        self.spring_obj = Spring(self.rootfolder, self.host, self.type_na, self.n_bp)
        self.df_all_k = self.spring_obj.read_k_b0_pairtype_df_given_cutoff(self.cutoff)

        self.crd = path.join(self.input_folder, '{0}.nohydrogen.avg.crd'.format(self.type_na))
        self.npt4_crd = path.join(self.input_folder, '{0}.nohydrogen.crd'.format(self.type_na))
        self.u = MDAnalysis.Universe(self.crd, self.crd)
        self.map, self.inverse_map, self.residues_map, self.atomid_map,\
        self.atomid_map_inverse, self.atomname_map, self.strandid_map,\
        self.resid_map, self.mass_map = self.__build_map()

        self.node_list = None
        self.d_idx = None
        self.n_node = None
        self.adjacency_mat = None
        self.degree_mat = None
        self.laplacian_mat = None

        self.w = None  # Eigenvalue array
        self.v = None  # Eigenvector matrix, the i-th column is the i-th eigenvector
        self.strand1_array = list() # 0: STRAND1, 1: STRAND2
        self.strand2_array = list() #
        self.strand1_benchmark = None
        self.strand2_benchmark = None
Exemple #2
0
 def __initialize_df(self):
     d_temp = dict()
     for host in self.hosts:
         spring_obj = Spring(self.rootfolder, host, self.type_na, self.n_bp)
         df0 = spring_obj.read_k_b0_pairtype_df_given_cutoff(self.cutoff)
         d_temp[host] = k_b0_util.get_central_bps_df(df0)
     return d_temp
Exemple #3
0
 def __read_df_st(self):
     criteria = 1e-3
     spring_obj = Spring(self.rootfolder, self.host, self.type_na,
                         self.n_bp)
     df = spring_obj.read_k_b0_pairtype_df_given_cutoff(self.cutoff)
     df1 = get_df_by_filter_st(df, 'st')
     mask = df1['k'] > criteria
     return df1[mask]
Exemple #4
0
class GraphAgent:
    type_na = 'bdna+bdna'
    n_bp = 21
    cutoff = 4.7

    def __init__(self, host, rootfolder):
        self.host = host
        self.rootfolder = rootfolder
        self.host_folder = path.join(rootfolder, host)
        self.na_folder = path.join(self.host_folder, self.type_na)
        self.input_folder = path.join(self.na_folder, 'input')

        self.spring_obj = Spring(self.rootfolder, self.host, self.type_na,
                                 self.n_bp)
        self.df_all_k = self.spring_obj.read_k_b0_pairtype_df_given_cutoff(
            self.cutoff)

        self.crd = path.join(self.input_folder,
                             '{0}.nohydrogen.avg.crd'.format(self.type_na))
        self.npt4_crd = path.join(self.input_folder,
                                  '{0}.nohydrogen.crd'.format(self.type_na))
        self.u = MDAnalysis.Universe(self.crd, self.crd)
        self.map, self.inverse_map, self.residues_map, self.atomid_map,\
        self.atomid_map_inverse, self.atomname_map, self.strandid_map,\
        self.resid_map, self.mass_map = self.__build_map()

        self.node_list = None
        self.d_idx = None
        self.n_node = None
        self.adjacency_mat = None
        self.degree_mat = None
        self.laplacian_mat = None

        self.w = None  # Eigenvalue array
        self.v = None  # Eigenvector matrix, the i-th column is the i-th eigenvector
        self.strand1_array = list()  # 0: STRAND1, 1: STRAND2
        self.strand2_array = list()  #
        self.strand1_benchmark = None
        self.strand2_benchmark = None

        self.d_seq = {
            'STRAND1': sequences[host]['guide'],
            'STRAND2': sequences[host]['target']
        }

    def build_node_list(self):
        node_list = list()
        d_idx = dict()
        idx = 0
        for cgname, atomname in self.atomname_map.items():
            atom_type = pairtype.d_atomcgtype[atomname]
            if atom_type == 'B':
                node_list.append(cgname)
                d_idx[cgname] = idx
                idx += 1
        self.node_list = node_list
        self.d_idx = d_idx
        self.n_node = len(self.node_list)
        print(f"Thare are {self.n_node} nodes.")

    def initialize_three_mat(self):
        self.adjacency_mat = np.zeros((self.n_node, self.n_node))
        self.degree_mat = np.zeros((self.n_node, self.n_node))
        self.laplacian_mat = np.zeros((self.n_node, self.n_node))
        print('Initialize adjacency, degree and Laplacian matrices... Done.')

    def build_degree_from_adjacency(self):
        for idx in range(self.n_node):
            self.degree_mat[idx, idx] = self.adjacency_mat[idx, :].sum()

    def build_laplacian_by_adjacency_degree(self):
        self.laplacian_mat = self.degree_mat + self.adjacency_mat
        print("Finish the setup for Laplaican matrix.")

    def get_networkx_graph(self, df, key='k'):
        # key: 'k', 'b0'
        node1_list = df['Atomid_i'].tolist()
        node2_list = df['Atomid_j'].tolist()
        weight_list = df[key].tolist()
        edges_list = [(node1, node2, {
            'weight': weight
        }) for node1, node2, weight in zip(node1_list, node2_list, weight_list)
                      ]
        G = nx.Graph()
        G.add_nodes_from(self.get_node_list_by_id())
        G.add_edges_from(edges_list)
        return G

    def get_node_list_by_id(self):
        return [self.atomid_map[name] for name in self.node_list]

    def get_networkx_d_pos(self, radius, dist_bw_base, dist_bw_strand):
        d_atcg = {
            'A': {
                'STRAND1': ADE_Base,
                'STRAND2': ADE_Right_Base
            },
            'T': {
                'STRAND1': THY_Base,
                'STRAND2': THY_Right_Base
            },
            'C': {
                'STRAND1': CYT_Base,
                'STRAND2': CYT_Right_Base
            },
            'G': {
                'STRAND1': GUA_Base,
                'STRAND2': GUA_Right_Base
            }
        }
        d_strandid_resid = self.get_d_strandid_resid()
        d_pos = dict()
        x_move = 0
        y_move = 0
        for strand_id in ['STRAND1', 'STRAND2']:
            for resid in range(1, self.n_bp + 1):
                resname = self.d_seq[strand_id][resid - 1]
                nucleobase = d_atcg[resname][strand_id](radius)
                nucleobase.translate_xy(x_move, y_move)
                for name in d_strandid_resid[strand_id][resid]:
                    atomid = self.atomid_map[name]
                    atomname = self.atomname_map[name]
                    d_pos[atomid] = nucleobase.d_nodes[atomname]
                if strand_id == 'STRAND1' and (resid != self.n_bp):
                    y_move += dist_bw_base
                elif (strand_id == 'STRAND1') and (resid == self.n_bp):
                    y_move -= 0
                else:
                    y_move -= dist_bw_base
            x_move -= dist_bw_strand
        return d_pos

    def get_d_strandid_resid(self):
        d_strandid_resid = self.initialize_d_strandid_resid()
        for name in self.node_list:
            strandid = self.strandid_map[name]
            resid = self.resid_map[name]
            d_strandid_resid[strandid][resid].append(name)
        return d_strandid_resid

    def initialize_d_strandid_resid(self):
        d_strandid_resid = dict()
        for strand_id in ['STRAND1', 'STRAND2']:
            d_strandid_resid[strand_id] = dict()
            for resid in range(1, self.n_bp + 1):
                d_strandid_resid[strand_id][resid] = list()
        return d_strandid_resid

    def get_D_by_atomname_strandid(self, sele_name, sele_strandid):
        sele_resid_list = list(range(4, 19))
        sele_idx_list = list()
        for idx, name in enumerate(self.node_list):
            if (self.atomname_map[name] == sele_name) and (
                    self.strandid_map[name]
                    == sele_strandid) and (self.resid_map[name]
                                           in sele_resid_list):
                sele_idx_list.append(idx)
        sele_D = np.zeros((self.n_node, self.n_node))
        for idx in sele_idx_list:
            sele_D[idx, idx] = self.degree_mat[idx, idx]
        return sele_D

    def get_D_by_atomname_strandid_resname(self, sele_name, sele_strandid,
                                           sele_resname):
        sele_resid_list = self.get_sele_resid_list_by_resname(
            sele_resname, sele_strandid)
        sele_idx_list = list()
        for idx, name in enumerate(self.node_list):
            if (self.atomname_map[name] == sele_name) and (
                    self.strandid_map[name]
                    == sele_strandid) and (self.resid_map[name]
                                           in sele_resid_list):
                sele_idx_list.append(idx)
        sele_D = np.zeros((self.n_node, self.n_node))
        for idx in sele_idx_list:
            sele_D[idx, idx] = self.degree_mat[idx, idx]
        return sele_D

    def get_sele_resid_list_by_resname(self, resname, strandid):
        sele_resid_list = list()
        central_resids = list(range(4, 19))
        #central_resids = list(range(1, 22))
        for idx, nt_name in enumerate(self.d_seq[strandid]):
            resid = idx + 1
            if (resid in central_resids) and (nt_name == resname):
                sele_resid_list.append(resid)
        return sele_resid_list

    def get_A_by_atomname1_atomname2(self, atomname_i, atomname_j,
                                     sele_strandid):
        sele_idx_list = list()
        for resid_i in range(4, 18):
            resid_j = resid_i + 1
            idx_i = self.d_idx[self.map[
                self.get_key_by_atomname_resid_strandid(
                    atomname_i, resid_i, sele_strandid)]]
            idx_j = self.d_idx[self.map[
                self.get_key_by_atomname_resid_strandid(
                    atomname_j, resid_j, sele_strandid)]]
            sele_idx_list.append((idx_i, idx_j))
        sele_A = np.zeros((self.n_node, self.n_node))
        for idx_i, idx_j in sele_idx_list:
            sele_A[idx_i, idx_j] = self.adjacency_mat[idx_i, idx_j]
        i_lower = np.tril_indices(self.n_node, -1)
        sele_A[i_lower] = sele_A.transpose()[
            i_lower]  # make the matrix symmetric
        return sele_A

    def get_A_by_atomname1_atomname2_by_resnames(self, atomname_i, atomname_j,
                                                 resname_i, resname_j,
                                                 sele_strandid):
        sele_idx_list = list()
        resid_i_list, resid_j_list = self.get_resid_i_resid_j_list(
            resname_i, resname_j, sele_strandid)
        for resid_i, resid_j in zip(resid_i_list, resid_j_list):
            idx_i = self.d_idx[self.map[
                self.get_key_by_atomname_resid_strandid(
                    atomname_i, resid_i, sele_strandid)]]
            idx_j = self.d_idx[self.map[
                self.get_key_by_atomname_resid_strandid(
                    atomname_j, resid_j, sele_strandid)]]
            sele_idx_list.append((idx_i, idx_j))
        sele_A = np.zeros((self.n_node, self.n_node))
        for idx_i, idx_j in sele_idx_list:
            sele_A[idx_i, idx_j] = self.adjacency_mat[idx_i, idx_j]
        i_lower = np.tril_indices(self.n_node, -1)
        sele_A[i_lower] = sele_A.transpose()[
            i_lower]  # make the matrix symmetric
        return sele_A

    def get_resid_i_resid_j_list(self, resname_i, resname_j, sele_strandid):
        seq = self.d_seq[sele_strandid]
        central_resids = range(4, 19)
        resid_i_list = list()
        resid_j_list = list()
        for resid in central_resids:
            if (seq[resid - 1] == resname_i) and (seq[resid] == resname_j):
                resid_i_list.append(resid)
                resid_j_list.append(resid + 1)
        return resid_i_list, resid_j_list

    def get_atomidpairs_atomname1_atomname2(self, atomname_i, atomname_j,
                                            sele_strandid):
        atomidpairs = list()
        for resid_i in range(4, 18):
            resid_j = resid_i + 1
            idx_i = self.atomid_map[self.map[
                self.get_key_by_atomname_resid_strandid(
                    atomname_i, resid_i, sele_strandid)]]
            idx_j = self.atomid_map[self.map[
                self.get_key_by_atomname_resid_strandid(
                    atomname_j, resid_j, sele_strandid)]]
            atomidpairs.append((idx_i, idx_j))
        return atomidpairs

    def get_key_by_atomname_resid_strandid(self, atomname, resid, strandid):
        return f'segid {strandid} and resid {resid} and name {atomname}'

    def get_filter_by_atomname_strandid(self, sele_name, sele_strandid):
        sele_resid_list = list(range(4, 19))
        sele_idx_list = list()
        for idx, name in enumerate(self.node_list):
            if (self.atomname_map[name] == sele_name) and (
                    self.strandid_map[name]
                    == sele_strandid) and (self.resid_map[name]
                                           in sele_resid_list):
                sele_idx_list.append(idx)

        y = np.zeros(self.n_node)
        y[sele_idx_list] = 1
        return y / np.linalg.norm(y)

    def get_filter_by_atomname_for_YR(self, sele_name, sele_resname,
                                      sele_strandid):
        sele_resid_list = list(range(4, 19))
        sele_idx_list = list()
        for idx, name in enumerate(self.node_list):
            resid = self.resid_map[name]
            if resid not in sele_resid_list:
                continue
            strandid = self.strandid_map[name]
            if strandid != sele_strandid:
                continue
            resname = self.d_seq[strandid][resid - 1]
            if resname != sele_resname:
                continue
            if self.atomname_map[name] == sele_name:
                sele_idx_list.append(idx)
        y = np.zeros(self.n_node)
        y[sele_idx_list] = 1
        return y / np.linalg.norm(y)

    def eigen_decompose(self):
        w, v = np.linalg.eig(self.laplacian_mat)
        idx = w.argsort()[::-1]  # sort from big to small
        self.w = w[idx]
        self.v = v[:, idx]

    def get_eigenvalue_by_id(self, sele_id):
        return self.w[sele_id - 1]

    def get_eigenvector_by_id(self, sele_id):
        return self.v[:, sele_id - 1]

    def get_qtAq(self, sele_id):
        eigvector_sele = self.get_eigenvector_by_id(sele_id)
        return np.dot(eigvector_sele.T,
                      np.dot(self.adjacency_mat, eigvector_sele))

    def get_qtDq(self, sele_id):
        eigvector_sele = self.get_eigenvector_by_id(sele_id)
        return np.dot(eigvector_sele.T, np.dot(self.degree_mat,
                                               eigvector_sele))

    def get_qtMq(self, sele_id, M):
        ### M is customized matrix
        eigvector_sele = self.get_eigenvector_by_id(sele_id)
        return np.dot(eigvector_sele.T, np.dot(M, eigvector_sele))

    def vmd_show_crd(self):
        print(f'vmd -cor {self.npt4_crd}')

    def copy_nohydrogen_crd(self):
        allsys_root = '/home/yizaochen/codes/dna_rna/all_systems'
        srt = path.join(allsys_root, self.host, self.type_na, 'input',
                        'heavyatoms', f'{self.type_na}.nohydrogen.crd')
        dst = self.npt4_crd
        copyfile(srt, dst)
        print(f'cp {srt} {dst}')

    def decide_eigenvector_strand(self, eigv_id):
        eigv = self.get_eigenvector_by_id(eigv_id)
        dot_product = np.dot(eigv, self.strand1_benchmark)
        if np.isclose(dot_product, 0.):
            return True  #'STRAND2'
        else:
            return False  #'STRAND1'

    def set_strand_array(self):
        for eigv_id in range(1, self.n_node + 1):
            if self.decide_eigenvector_strand(eigv_id):
                self.strand2_array.append(eigv_id)
            else:
                self.strand1_array.append(eigv_id)
        print(f'Total number of nodes: {self.n_node}')
        print(
            f'There are {len(self.strand1_array)} eigenvectors belonging to STRAND1.'
        )
        print(
            f'There are {len(self.strand2_array)} eigenvectors belonging to STRAND2.'
        )
        print(
            f'Sum of two strands: {len(self.strand1_array)+len(self.strand2_array)}'
        )

    def get_lambda_by_strand(self, strandid):
        if strandid == 'STRAND1':
            return [
                self.get_eigenvalue_by_id(eigv_id)
                for eigv_id in self.strand1_array
            ]
        else:
            return [
                self.get_eigenvalue_by_id(eigv_id)
                for eigv_id in self.strand2_array
            ]

    def get_eigvector_by_strand(self, strandid, sele_id):
        if strandid == 'STRAND1':
            real_eigv_id = self.strand1_array[sele_id]
        else:
            real_eigv_id = self.strand2_array[sele_id]
        return self.get_eigenvector_by_id(
            real_eigv_id), self.get_eigenvalue_by_id(real_eigv_id)

    def set_adjacency_by_df(self, df_sele):
        idx_i_list = self.__get_idx_list(df_sele['Atomid_i'])
        idx_j_list = self.__get_idx_list(df_sele['Atomid_j'])
        k_list = df_sele['k'].tolist()
        for idx_i, idx_j, k in zip(idx_i_list, idx_j_list, k_list):
            self.adjacency_mat[idx_i, idx_j] = k

    def set_adjacency_by_d(self, d_sele):
        idx_i_list = self.__get_idx_list(d_sele['Atomid_i'])
        idx_j_list = self.__get_idx_list(d_sele['Atomid_j'])
        k_list = d_sele['k']
        for idx_i, idx_j, k in zip(idx_i_list, idx_j_list, k_list):
            self.adjacency_mat[idx_i, idx_j] = k

    def make_adjacency_symmetry(self):
        i_lower = np.tril_indices(self.n_node, -1)
        self.adjacency_mat[i_lower] = self.adjacency_mat.transpose()[
            i_lower]  # make the matrix symmetric

    def write_show_nodes_tcl(self, tcl_out, colorid=0, vdw_radius=1.0):
        serials_str = self.__get_serial_nodes()
        f = open(tcl_out, 'w')
        f.write('display resize 362 954\n\n')
        f.write('mol color ColorID 6\n')
        f.write('mol representation Lines 3.000\n')
        f.write('mol selection all\n')
        f.write('mol material Opaque\n')
        f.write('mol addrep 0\n')
        f.write(f'mol color ColorID {colorid}\n')
        f.write(f'mol representation VDW {vdw_radius:.3f} 12.000\n')
        f.write(f'mol selection serial {serials_str}\n')
        f.write('mol material Opaque\n')
        f.write('mol addrep 0\n')
        f.write(f'mol color ColorID 7\n')
        f.write(f'mol representation VDW 0.300 12.000\n')
        f.write(f'mol selection serial 6 7 8 9\n')
        f.write('mol material Opaque\n')
        f.write('mol addrep 0\n')
        f.close()
        print(f'Write tcl to {tcl_out}')
        print(f'source {tcl_out}')

    def process_lines_for_edges_tcl(self, lines, df_sele, radius=0.05):
        u_npt4 = MDAnalysis.Universe(self.npt4_crd, self.npt4_crd)
        for atomid1, atomid2 in zip(df_sele['Atomid_i'], df_sele['Atomid_j']):
            line = self.__get_draw_edge_line(u_npt4.atoms.positions,
                                             atomid1 - 1, atomid2 - 1, radius)
            lines.append(line)
        return lines

    def write_lines_to_tcl_out(self, lines, tcl_out):
        f = open(tcl_out, 'w')
        for line in lines:
            f.write(line)
        f.close()
        print(f'Write tcl to {tcl_out}')
        print(f'source {tcl_out}')

    def __get_idx_list(self, df_column):
        cgname_list = [self.atomid_map_inverse[atomid] for atomid in df_column]
        return [self.d_idx[cgname] for cgname in cgname_list]

    def __get_serial_nodes(self):
        serials_list = [
            str(self.atomid_map[cgname]) for cgname in self.d_idx.keys()
        ]
        return ' '.join(serials_list)

    def __get_draw_edge_line(self, positions, atomid1, atomid2, radius):
        str_0 = 'graphics 0 cylinder {'
        str_1 = f'{positions[atomid1,0]:.3f} {positions[atomid1,1]:.3f} {positions[atomid1,2]:.3f}'
        str_2 = '} {'
        str_3 = f'{positions[atomid2,0]:.3f} {positions[atomid2,1]:.3f} {positions[atomid2,2]:.3f}'
        str_4 = '} '
        str_5 = f'radius {radius:.2f}\n'
        return str_0 + str_1 + str_2 + str_3 + str_4 + str_5

    def __build_map(self):
        d1 = dict()  # key: selction, value: cgname
        d2 = dict()  # key: cgname,   value: selection
        d3 = dict()
        d4 = dict()  # key: cgname, value: atomid
        d5 = dict()  # key: atomid, value: cgname
        d6 = dict()  # key: cgname, value: atomname
        d7 = dict()  # key: cgname, value: strand_id
        d8 = dict()  # key: cgname, value: resid
        d9 = dict()  # key: cgname, value: mass
        atomid = 1
        segid1 = self.u.select_atoms("segid STRAND1")
        d3['STRAND1'] = dict()
        for i, atom in enumerate(segid1):
            cgname = 'A{0}'.format(i + 1)
            selection = self.__get_selection(atom)
            d1[selection] = cgname
            d2[cgname] = selection
            if atom.resid not in d3['STRAND1']:
                d3['STRAND1'][atom.resid] = list()
            d3['STRAND1'][atom.resid].append(cgname)
            d4[cgname] = atomid
            d5[atomid] = cgname
            d6[cgname] = atom.name
            d7[cgname] = 'STRAND1'
            d8[cgname] = atom.resid
            d9[cgname] = atom.mass
            atomid += 1
        segid2 = self.u.select_atoms("segid STRAND2")
        d3['STRAND2'] = dict()
        for i, atom in enumerate(segid2):
            cgname = 'B{0}'.format(i + 1)
            selection = self.__get_selection(atom)
            d1[selection] = cgname
            d2[cgname] = selection
            if atom.resid not in d3['STRAND2']:
                d3['STRAND2'][atom.resid] = list()
            d3['STRAND2'][atom.resid].append(cgname)
            d4[cgname] = atomid
            d5[atomid] = cgname
            d6[cgname] = atom.name
            d7[cgname] = 'STRAND2'
            d8[cgname] = atom.resid
            d9[cgname] = atom.mass
            atomid += 1
        return d1, d2, d3, d4, d5, d6, d7, d8, d9

    def __get_selection(self, atom):
        return 'segid {0} and resid {1} and name {2}'.format(
            atom.segid, atom.resid, atom.name)
Exemple #5
0
class GraphAgent:
    type_na = 'bdna+bdna'
    n_bp = 21
    cutoff = 4.7

    def __init__(self, host, rootfolder):
        self.host = host
        self.rootfolder = rootfolder
        self.host_folder = path.join(rootfolder, host)
        self.na_folder = path.join(self.host_folder, self.type_na)
        self.input_folder = path.join(self.na_folder, 'input')

        self.spring_obj = Spring(self.rootfolder, self.host, self.type_na, self.n_bp)
        self.df_all_k = self.spring_obj.read_k_b0_pairtype_df_given_cutoff(self.cutoff)

        self.crd = path.join(self.input_folder, '{0}.nohydrogen.avg.crd'.format(self.type_na))
        self.npt4_crd = path.join(self.input_folder, '{0}.nohydrogen.crd'.format(self.type_na))
        self.u = MDAnalysis.Universe(self.crd, self.crd)
        self.map, self.inverse_map, self.residues_map, self.atomid_map,\
        self.atomid_map_inverse, self.atomname_map, self.strandid_map,\
        self.resid_map, self.mass_map = self.__build_map()

        self.node_list = None
        self.d_idx = None
        self.n_node = None
        self.adjacency_mat = None
        self.degree_mat = None
        self.laplacian_mat = None

        self.w = None  # Eigenvalue array
        self.v = None  # Eigenvector matrix, the i-th column is the i-th eigenvector
        self.strand1_array = list() # 0: STRAND1, 1: STRAND2
        self.strand2_array = list() #
        self.strand1_benchmark = None
        self.strand2_benchmark = None
        
    def build_node_list(self):
        node_list = list()
        d_idx = dict()
        idx = 0
        for cgname, atomname in self.atomname_map.items():
            atom_type = pairtype.d_atomcgtype[atomname]
            if atom_type == 'B':
                node_list.append(cgname)
                d_idx[cgname] = idx
                idx += 1
        self.node_list = node_list
        self.d_idx = d_idx
        self.n_node = len(self.node_list)
        print(f"Thare are {self.n_node} nodes.")

    def initialize_three_mat(self):
        self.adjacency_mat = np.zeros((self.n_node, self.n_node))
        self.degree_mat = np.zeros((self.n_node, self.n_node))
        self.laplacian_mat = np.zeros((self.n_node, self.n_node))
        print('Initialize adjacency, degree and Laplacian matrices... Done.')

    def build_degree_from_adjacency(self):
        for idx in range(self.n_node):
            self.degree_mat[idx, idx] = self.adjacency_mat[idx, :].sum()

    def build_laplacian_by_adjacency_degree(self):
        self.laplacian_mat = self.degree_mat + self.adjacency_mat
        print("Finish the setup for Laplaican matrix.")

    def eigen_decompose(self):
        w, v = np.linalg.eig(self.laplacian_mat)
        idx = w.argsort()[::-1] # sort from big to small
        self.w = w[idx]
        self.v = v[:, idx]

    def get_eigenvalue_by_id(self, sele_id):
        return self.w[sele_id-1]

    def get_eigenvector_by_id(self, sele_id):
        return self.v[:,sele_id-1]

    def vmd_show_crd(self):
        print(f'vmd -cor {self.npt4_crd}')

    def copy_nohydrogen_crd(self):
        allsys_root = '/home/yizaochen/codes/dna_rna/all_systems'
        srt = path.join(allsys_root, self.host, self.type_na, 'input', 'heavyatoms', f'{self.type_na}.nohydrogen.crd')
        dst = self.npt4_crd
        copyfile(srt, dst)
        print(f'cp {srt} {dst}')

    def decide_eigenvector_strand(self, eigv_id):
        eigv = self.get_eigenvector_by_id(eigv_id)
        dot_product = np.dot(eigv, self.strand1_benchmark)
        if np.isclose(dot_product, 0.):
            return True #'STRAND2'
        else:
            return False #'STRAND1'

    def set_strand_array(self):
        for eigv_id in range(1, self.n_node+1):
            if self.decide_eigenvector_strand(eigv_id):
                self.strand2_array.append(eigv_id)
            else:
                self.strand1_array.append(eigv_id)
        print(f'Total number of nodes: {self.n_node}')
        print(f'There are {len(self.strand1_array)} eigenvectors belonging to STRAND1.')
        print(f'There are {len(self.strand2_array)} eigenvectors belonging to STRAND2.')
        print(f'Sum of two strands: {len(self.strand1_array)+len(self.strand2_array)}')

    def get_lambda_by_strand(self, strandid):
        if strandid == 'STRAND1':
            return [self.get_eigenvalue_by_id(eigv_id) for eigv_id in self.strand1_array]
        else:
            return [self.get_eigenvalue_by_id(eigv_id) for eigv_id in self.strand2_array]

    def get_eigvector_by_strand(self, strandid, sele_id):
        if strandid == 'STRAND1':
            real_eigv_id = self.strand1_array[sele_id]
        else:
            real_eigv_id = self.strand2_array[sele_id]
        return self.get_eigenvector_by_id(real_eigv_id), self.get_eigenvalue_by_id(real_eigv_id)

    def set_adjacency_by_df(self, df_sele):
        idx_i_list = self.__get_idx_list(df_sele['Atomid_i'])
        idx_j_list = self.__get_idx_list(df_sele['Atomid_j'])
        k_list = df_sele['k'].tolist()
        for idx_i, idx_j, k in zip(idx_i_list, idx_j_list, k_list):
            self.adjacency_mat[idx_i, idx_j] = k

    def set_adjacency_by_d(self, d_sele):
        idx_i_list = self.__get_idx_list(d_sele['Atomid_i'])
        idx_j_list = self.__get_idx_list(d_sele['Atomid_j'])
        k_list = d_sele['k']
        for idx_i, idx_j, k in zip(idx_i_list, idx_j_list, k_list):
            self.adjacency_mat[idx_i, idx_j] = k
    
    def make_adjacency_symmetry(self):
        i_lower = np.tril_indices(self.n_node, -1)
        self.adjacency_mat[i_lower] = self.adjacency_mat.transpose()[i_lower]  # make the matrix symmetric

    def write_show_nodes_tcl(self, tcl_out, colorid=0, vdw_radius=1.0):
        serials_str = self.__get_serial_nodes()
        f = open(tcl_out, 'w')
        f.write('display resize 362 954\n\n')
        f.write('mol color ColorID 6\n')
        f.write('mol representation Lines 3.000\n')
        f.write('mol selection all\n')
        f.write('mol material Opaque\n')
        f.write('mol addrep 0\n')
        f.write(f'mol color ColorID {colorid}\n')
        f.write(f'mol representation VDW {vdw_radius:.3f} 12.000\n')
        f.write(f'mol selection serial {serials_str}\n')
        f.write('mol material Opaque\n')
        f.write('mol addrep 0\n')
        f.write(f'mol color ColorID 7\n')
        f.write(f'mol representation VDW 0.300 12.000\n')
        f.write(f'mol selection serial 6 7 8 9\n')
        f.write('mol material Opaque\n')
        f.write('mol addrep 0\n')
        f.close()
        print(f'Write tcl to {tcl_out}')
        print(f'source {tcl_out}')

    def process_lines_for_edges_tcl(self, lines, df_sele, radius=0.05):
        u_npt4 = MDAnalysis.Universe(self.npt4_crd, self.npt4_crd)       
        for atomid1, atomid2 in zip(df_sele['Atomid_i'], df_sele['Atomid_j']):
            line = self.__get_draw_edge_line(u_npt4.atoms.positions, atomid1-1, atomid2-1, radius)
            lines.append(line)
        return lines

    def write_lines_to_tcl_out(self, lines, tcl_out):
        f = open(tcl_out, 'w')        
        for line in lines:
            f.write(line)
        f.close()
        print(f'Write tcl to {tcl_out}')
        print(f'source {tcl_out}')
        
    def __get_idx_list(self, df_column):
        cgname_list = [self.atomid_map_inverse[atomid] for atomid in df_column]
        return [self.d_idx[cgname] for cgname in cgname_list]

    def __get_serial_nodes(self):
        serials_list = [str(self.atomid_map[cgname]) for cgname in self.d_idx.keys()]
        return ' '.join(serials_list)

    def __get_draw_edge_line(self, positions, atomid1, atomid2, radius):
        str_0 = 'graphics 0 cylinder {'
        str_1 = f'{positions[atomid1,0]:.3f} {positions[atomid1,1]:.3f} {positions[atomid1,2]:.3f}'
        str_2 = '} {'
        str_3 = f'{positions[atomid2,0]:.3f} {positions[atomid2,1]:.3f} {positions[atomid2,2]:.3f}'
        str_4 = '} '
        str_5 = f'radius {radius:.2f}\n'
        return str_0 + str_1 + str_2 + str_3 + str_4 + str_5
       
    def __build_map(self):
        d1 = dict()  # key: selction, value: cgname
        d2 = dict()  # key: cgname,   value: selection
        d3 = dict()
        d4 = dict()  # key: cgname, value: atomid
        d5 = dict()  # key: atomid, value: cgname
        d6 = dict()  # key: cgname, value: atomname
        d7 = dict()  # key: cgname, value: strand_id
        d8 = dict()  # key: cgname, value: resid
        d9 = dict()  # key: cgname, value: mass
        atomid = 1
        segid1 = self.u.select_atoms("segid STRAND1")
        d3['STRAND1'] = dict()
        for i, atom in enumerate(segid1):
            cgname = 'A{0}'.format(i+1)
            selection = self.__get_selection(atom)
            d1[selection] = cgname
            d2[cgname] = selection
            if atom.resid not in d3['STRAND1']:
                d3['STRAND1'][atom.resid] = list()
            d3['STRAND1'][atom.resid].append(cgname)
            d4[cgname] = atomid
            d5[atomid] = cgname
            d6[cgname] = atom.name
            d7[cgname] = 'STRAND1'
            d8[cgname] = atom.resid
            d9[cgname] = atom.mass
            atomid += 1
        segid2 = self.u.select_atoms("segid STRAND2")
        d3['STRAND2'] = dict()
        for i, atom in enumerate(segid2):
            cgname = 'B{0}'.format(i+1)
            selection = self.__get_selection(atom)
            d1[selection] = cgname
            d2[cgname] = selection
            if atom.resid not in d3['STRAND2']:
                d3['STRAND2'][atom.resid] = list()
            d3['STRAND2'][atom.resid].append(cgname)
            d4[cgname] = atomid
            d5[atomid] = cgname
            d6[cgname] = atom.name
            d7[cgname] = 'STRAND2'
            d8[cgname] = atom.resid
            d9[cgname] = atom.mass
            atomid += 1
        return d1, d2, d3, d4, d5, d6, d7, d8, d9
        
    def __get_selection(self, atom):
        return 'segid {0} and resid {1} and name {2}'.format(atom.segid, atom.resid, atom.name)