def set_dihedral_index(dihedral_ind, proper=True): dihed = tuple(dihedral_ind) check_in = proper_dihedral_inds if proper else improper_dihedral_inds # Check if this dihedral is already present if (dihed in check_in) or (dihed[::-1] in check_in): return # Assure that the angles are below 175° (3.054326 rad) if not dihedral_valid(coords3d, dihedral_ind, deg_thresh=max_deg): log_dihed_skip(dihedral_ind) return if proper: proper_dihedral_inds.append(dihed) else: improper_dihedral_inds.append(dihed)
def update_internals( new_coords3d, old_internals, primitives, dihedral_inds, check_dihedrals=False, logger=None, ): prim_internals = eval_primitives(new_coords3d, primitives) new_internals = [prim_int.val for prim_int in prim_internals] internal_diffs = np.array(new_internals) - old_internals dihedrals = [prim_internals[i] for i in dihedral_inds] dihedral_num = len(dihedrals) dihedral_diffs = internal_diffs[-dihedral_num:] # Find differences that are shifted by 2*pi shifted_by_2pi = np.abs(np.abs(dihedral_diffs) - 2 * np.pi) < np.pi / 2 new_dihedrals = np.array([dihed.val for dihed in dihedrals]) if any(shifted_by_2pi): new_dihedrals[shifted_by_2pi] -= ( 2 * np.pi * np.sign(dihedral_diffs[shifted_by_2pi])) # Update values for dihed, new_val in zip(dihedrals, new_dihedrals): dihed.val = new_val # See if dihedrals became invalid (collinear atoms) if check_dihedrals: are_valid = [ dihedral_valid(new_coords3d, prim.inds) for prim in dihedrals ] try: first_dihedral = dihedral_inds[0] except IndexError: first_dihedral = 0 invalid_inds = [ i + first_dihedral for i, is_valid in enumerate(are_valid) if not is_valid ] if len(invalid_inds) > 0: invalid_prims = [primitives[i] for i in invalid_inds] log(logger, "Dihedral(s) became invalid! Need new internal coordinates!") raise NeedNewInternalsException(new_coords3d, invalid_inds=invalid_inds, invalid_prims=invalid_prims) return prim_internals
def get_dihedral_inds(coords3d, bond_inds, bend_inds, max_deg, logger=None): max_rad = np.deg2rad(max_deg) bond_dict = dict() for from_, to_ in bond_inds: bond_dict.setdefault(from_, list()).append(to_) bond_dict.setdefault(to_, list()).append(from_) proper_dihedral_inds = list() improper_candidates = list() improper_dihedral_inds = list() def log_dihed_skip(inds): log( logger, f"Skipping generation of dihedral {inds} " "as some of the the atoms are (close too) linear.", ) def set_dihedral_index(dihedral_ind, proper=True): dihed = tuple(dihedral_ind) check_in = proper_dihedral_inds if proper else improper_dihedral_inds # Check if this dihedral is already present if (dihed in check_in) or (dihed[::-1] in check_in): return # Assure that the angles are below 175° (3.054326 rad) if not dihedral_valid(coords3d, dihedral_ind, deg_thresh=max_deg): log_dihed_skip(dihedral_ind) return if proper: proper_dihedral_inds.append(dihed) else: improper_dihedral_inds.append(dihed) for bond, bend in it.product(bond_inds, bend_inds): # print("bond", bond, "bend", bend) central = bend[1] bend_set = set(bend) bond_set = set(bond) # Check if the two sets share one common atom. If not continue. intersect = bend_set & bond_set # print("intersect", intersect) if len(intersect) != 1: continue # if bond == frozenset((0, 11)) and bend == (0, 3, 4): # import pdb; pdb.set_trace() # pass # TODO: check collinearity of bond and bend. # When the common atom between bond and bend is a terminal, and not a central atom # in the bend we create a proper dihedral. Improper dihedrals are only created # when no proper dihedrals have been found. if central not in bond_set: # The new terminal atom in the dihedral is the one, that doesn' intersect. terminal = tuple(bond_set - intersect)[0] intersecting_atom = tuple(intersect)[0] bend_terminal = tuple(bend_set - {central} - intersect)[0] bend_rad = Bend._calculate(coords3d, bend) # Bend atoms are nearly collinear. Check if we can skip the central bend atom # and use an atom that is conneced to the terminal atom of the bend or bond. if bend_rad >= max_rad: bend_terminal_bonds = set(bond_dict[bend_terminal]) - bend_set bond_terminal_bonds = set(bond_dict[terminal]) - bond_set set_dihedrals = [ (terminal, intersecting_atom, bend_terminal, betb) for betb in bend_terminal_bonds ] + [(bend_terminal, intersecting_atom, terminal, botb) for botb in bond_terminal_bonds] # Hardcoded for now ... look ahead to next shell of atoms if not any([ dihedral_valid(coords3d, inds, deg_thresh=max_deg) for inds in set_dihedrals ]): set_dihedrals = [] for betb in bend_terminal_bonds: bend_terminal_bonds_v2 = set( bond_dict[betb]) - bend_set - bond_set set_dihedrals = [(terminal, intersecting_atom, betb, betb_v2) for betb_v2 in bend_terminal_bonds_v2] for botb in bond_terminal_bonds: bond_terminal_bonds_v2 = set( bond_dict[botb]) - bend_set - bond_set set_dihedrals = [(bend_terminal, intersecting_atom, botb, botb_v2) for botb_v2 in bond_terminal_bonds_v2] elif intersecting_atom == bend[0]: set_dihedrals = [[terminal] + list(bend)] else: set_dihedrals = [list(bend) + [terminal]] [set_dihedral_index(dihed) for dihed in set_dihedrals] # If the common atom is the central atom we try to form an out # of plane bend / improper torsion. They may be created later on. else: fourth_atom = list(bond_set - intersect) dihedral_ind = list(bend) + fourth_atom # This way dihedrals may be generated that contain linear # atoms and these would be undefinied. So we check for this. if dihedral_valid(coords3d, dihedral_ind, deg_thresh=max_deg): improper_candidates.append(dihedral_ind) else: log_dihed_skip(dihedral_ind) # Now try to create the remaining improper dihedrals. if (len(coords3d) >= 4) and (len(proper_dihedral_inds) == 0): log( logger, "Could not define any proper dihedrals! Generating improper dihedrals!", ) for improp in improper_candidates: set_dihedral_index(improp, proper=False) log( logger, "Permutational symmetry not considerd in generation of " "improper dihedrals.", ) return proper_dihedral_inds, improper_dihedral_inds