def assign_torsion_types(cgmodel, torsion_list):
    """Internal function for assigning torsion types"""
    
    torsion_types = [] # List of torsion types for each torsion in torsion_list
    torsion_array = np.zeros((len(torsion_list),4))
    
    # Relevant torsion types are added to a dictionary as they are discovered 
    torsion_dict = {}
    
    # Create an inverse dictionary for getting torsion string name from integer type
    inv_torsion_dict = {}
    
    # Counter for number of torsion types found:
    i_torsion_type = 0  
    
    for i in range(len(torsion_list)):
        torsion_array[i,0] = torsion_list[i][0]
        torsion_array[i,1] = torsion_list[i][1]
        torsion_array[i,2] = torsion_list[i][2]
        torsion_array[i,3] = torsion_list[i][3]
        
        particle_types = [
            CGModel.get_particle_type_name(cgmodel,torsion_list[i][0]),
            CGModel.get_particle_type_name(cgmodel,torsion_list[i][1]),
            CGModel.get_particle_type_name(cgmodel,torsion_list[i][2]),
            CGModel.get_particle_type_name(cgmodel,torsion_list[i][3])
        ]
        
        string_name = ""
        reverse_string_name = ""
        for particle in particle_types:
            string_name += f"{particle}_"
        string_name = string_name[:-1]
        for particle in reversed(particle_types):
            reverse_string_name += f"{particle}_"
        reverse_string_name = reverse_string_name[:-1]
            
        if (string_name in torsion_dict.keys()) == False:
            # New torsion type found, add to torsion dictionary
            i_torsion_type += 1
            torsion_dict[string_name] = i_torsion_type
            torsion_dict[reverse_string_name] = i_torsion_type
            # For inverse dict we will use only the forward name based on first encounter
            inv_torsion_dict[str(i_torsion_type)] = string_name
            
            # print(f"adding new torsion type {i_torsion_type}: {string_name} to dictionary")
            # print(f"adding reverse version {i_torsion_type}: {reverse_string_name} to dictionary")
            
            
        torsion_types.append(torsion_dict[string_name])
                        
    # Sort torsions by type into separate sub arrays for mdtraj compute_dihedrals
    torsion_sub_arrays = {}
    for i in range(i_torsion_type):
        torsion_sub_arrays[str(i+1)] = np.zeros((torsion_types.count(i+1),4))
    
    # Counter vector for all angle types
    n_i = np.zeros((i_torsion_type,1), dtype=int) 
    
    for i in range(len(torsion_list)):
        torsion_sub_arrays[str(torsion_types[i])][n_i[torsion_types[i]-1],:] = torsion_array[i,:]
        n_i[torsion_types[i]-1] += 1
        
    return torsion_types, torsion_array, torsion_sub_arrays, n_i, i_torsion_type, torsion_dict, inv_torsion_dict
def assign_bond_types(cgmodel, bond_list):
    """Internal function for assigning bond types"""

    bond_types = []

    bond_array = np.zeros((len(bond_list), 2))

    # Relevant bond types are added to a dictionary as they are discovered
    bond_dict = {}

    # Create an inverse dictionary for getting bond string name from integer type
    inv_bond_dict = {}

    # Counter for number of bond types found:
    i_bond_type = 0

    # Assign bond types:

    for i in range(len(bond_list)):
        bond_array[i, 0] = bond_list[i][0]
        bond_array[i, 1] = bond_list[i][1]

        particle_types = [
            CGModel.get_particle_type_name(cgmodel, bond_list[i][0]),
            CGModel.get_particle_type_name(cgmodel, bond_list[i][1])
        ]

        string_name = ""
        reverse_string_name = ""
        for particle in particle_types:
            string_name += f"{particle}_"
        string_name = string_name[:-1]
        for particle in reversed(particle_types):
            reverse_string_name += f"{particle}_"
        reverse_string_name = reverse_string_name[:-1]

        if (string_name in bond_dict.keys()) == False:
            # New bond type found, add to bond dictionary
            i_bond_type += 1
            bond_dict[string_name] = i_bond_type
            bond_dict[reverse_string_name] = i_bond_type
            # For inverse dict we will use only the forward name based on first encounter
            inv_bond_dict[str(i_bond_type)] = string_name
            # print(f"adding new bond type {i_bond_type}: {string_name} to dictionary")
            # print(f"adding reverse version {i_bond_type}: {reverse_string_name} to dictionary")

        bond_types.append(bond_dict[string_name])

    # Sort bonds by type into separate sub arrays for mdtraj compute_distances
    bond_sub_arrays = {}
    for i in range(i_bond_type):
        bond_sub_arrays[str(i + 1)] = np.zeros((bond_types.count(i + 1), 2))

    # Counter vector for all bond types
    n_i = np.zeros((i_bond_type, 1), dtype=int)

    for i in range(len(bond_list)):
        bond_sub_arrays[str(bond_types[i])][n_i[bond_types[i] -
                                                1], :] = bond_array[i, :]
        n_i[bond_types[i] - 1] += 1

    return bond_types, bond_array, bond_sub_arrays, n_i, i_bond_type, bond_dict, inv_bond_dict