def functionalize_structure_with_linkers(structure_path, linker_path, fnlinkers, output_dir=Path()): linker = Atoms.from_cml(Path(linker_path)) structure = Atoms.from_cif(structure_path) output_dir = Path(output_dir) assign_pair_params_to_structure(structure) for fnlinker_path in fnlinkers: print("reading %s" % fnlinker_path) fnlinker = Atoms.from_lammps_data(open(fnlinker_path, "r"), use_comment_for_type_labels=True) try: new_structure = replace_pattern_in_structure( structure, linker, fnlinker) with open( output_dir.joinpath(Path(fnlinker_path).stem + ".lmpdat"), "w") as fd: new_structure.to_lammps_data(fd) except Exception as e: print("ERROR! ", e.args)
def check_cml_files(paths, linker, num_linkers, position_check_indices=[]): """ checks if more coordinates were changed in functionalized linker than just the newly added functional group atoms """ for fnlinker_path in paths: fnlinker = Atoms.from_cml(fnlinker_path) match_pairs = find_unchanged_atom_pairs(linker, fnlinker) if len(match_pairs) == (len(linker) - num_linkers): marker = "" else: marker = "*" bad_indices = set( np.argwhere(fnlinker.positions[0:len(linker), :] != linker.positions[0:len(linker), :])[:, 0]) marker += ".".join([str(i + 1) for i in bad_indices]) if (fnlinker[position_check_indices].positions != linker[position_check_indices].positions).any(): position_marker = "P" else: position_marker = "" print("%s%s %s: unchanged %d/%d" % (marker, position_marker, fnlinker_path, len(match_pairs), len(fnlinker)))
linker.positions[0:len(linker), :])[:, 0]) marker += ".".join([str(i + 1) for i in bad_indices]) if (fnlinker[position_check_indices].positions != linker[position_check_indices].positions).any(): position_marker = "P" else: position_marker = "" print("%s%s %s: unchanged %d/%d" % (marker, position_marker, fnlinker_path, len(match_pairs), len(fnlinker))) # check UIO-66 linkers linker_path = Path("linkers-cml/uio66.cml") linker = Atoms.from_cml(linker_path) all_linkers = list(Path("linkers-cml").glob("uio66-*.cml")) double_linkers = list(Path("linkers-cml").glob("uio66-*-2.cml")) non_double_linkers = set(all_linkers) - set(double_linkers) print("\nUIO-66 two linkers") check_cml_files(double_linkers, linker, 2, (0, 14)) print("\nUIO-66 one linker (and more than two linkers)") check_cml_files(non_double_linkers, linker, 1, (0, 14)) # check UIO-67 linkers linker_path = Path("linkers-cml/uio67.cml") linker = Atoms.from_cml(linker_path) all_linkers = list(Path("linkers-cml").glob("uio67-*.cml")) double_linkers = list(Path("linkers-cml").glob("uio67-*-2.cml")) non_double_linkers = set(all_linkers) - set(double_linkers)
""" convert all linkers to cif files, so our lammps-data file can be compared to the pete-boyd code You will need to run ``` gsed -i -e 's/_atom_site_Cartn_/_atom_site_/g' *.cif ``` afterwards so that the cif has the labels that Lammps-interface requires ```fish for x in ../linkers-cif/*.cif lammps-interface --molecule-ff=UFF $x end ``` """ from pathlib import Path import numpy as np from mofun import Atoms out_path = Path("linkers-cif") for fnlinker_path in Path("linkers-cml").glob("*.cml"): print("reading %s" % fnlinker_path) fnlinker = Atoms.from_cml(fnlinker_path) fnlinker.cell = cell = 50 * np.identity(3) fnlinker.to_ase().write(out_path.joinpath(fnlinker_path.stem + ".cif"))
def cml2lmpdat_typed_parameterized_for_new_atoms(fnlinker_path, linker_path=None, outpath="-"): uff_rules = { "H": [ ("H_b", dict(n=2)), ("H_", {}) ], "N": [ ("N_R", dict(n=3, aromatic=True)), ("N_1", dict(neighbors=("N","N"))) ], "O": [("O_1", dict(n=1))], "C": [("C_R", dict(n=3, aromatic=True))] } bond_order_rules = [({'N_1'}, 2), ({'N_1', 'N_2'}, 2)] linker = None if linker_path is not None: linker = Atoms.from_cml(linker_path) fnlinker = Atoms.from_cml(fnlinker_path) # assign uff atom types using mofun.rough_uff g = nx.Graph() g.add_edges_from(fnlinker.bonds) uff_types = ruff.assign_uff_atom_types(g, fnlinker.elements, override_rules=uff_rules) fnlinker.retype_atoms_from_uff_types(uff_types) # calculate all possible many-body terms fnlinker.calc_angles() fnlinker.calc_dihedrals() if linker is not None: # remove any dihedrals, angles and bonds that are unchanged from the original linker, # as we are only going to relax the new functional group, leaving everything else fixed. print("Num dihedrals, angles, bonds: %d, %d, %d" % (len(fnlinker.dihedrals), len(fnlinker.angles), len(fnlinker.bonds))) match_pairs = find_unchanged_atom_pairs(linker, fnlinker) unchanged_atom_indices = set() if len(match_pairs) > 0: unchanged_atom_indices = set(list(zip(*match_pairs))[1]) # assign atoms to molecules where 0 is original linker, 1 is for new functional group atoms fnlinker.atom_groups = [0 if i in unchanged_atom_indices else 1 for i in range(len(fnlinker))] fnlinker.bonds = delete_if_all_in_set(fnlinker.bonds, unchanged_atom_indices) fnlinker.angles = delete_if_all_in_set(fnlinker.angles, unchanged_atom_indices) # calculate potential parameters and assign type #s to linker fnlinker.pair_params = ['%10.6f %10.6f # %s' % (*ruff.pair_params(a1), a1) for a1 in fnlinker.atom_type_labels] bond_types = [order_types([uff_types[b1], uff_types[b2]]) for b1, b2 in fnlinker.bonds] unique_bond_types = list(dict.fromkeys(bond_types).keys()) fnlinker.bond_types = [unique_bond_types.index(bt) for bt in bond_types] bond_params = [(*ruff.bond_params(a1, a2, bond_order_rules=bond_order_rules), "%s %s" % (a1, a2)) for (a1, a2) in unique_bond_types] fnlinker.bond_type_params = ['%10.6f %10.6f # %s' % params for params in bond_params] angle_types = [order_types([uff_types[a] for a in atoms]) for atoms in fnlinker.angles] unique_angle_types = list(dict.fromkeys(angle_types).keys()) fnlinker.angle_types = [unique_angle_types.index(a) for a in angle_types] angle_params = [(*ruff.angle_params(*a_ids, bond_order_rules=bond_order_rules), "%s %s %s" % a_ids) for a_ids in unique_angle_types] fnlinker.angle_type_params = [angle2lammpsdat(a) for a in angle_params] num_dihedrals_per_bond = Counter([order_types([a2, a3]) for _, a2, a3, _ in fnlinker.dihedrals]) if linker is not None: fnlinker.dihedrals = delete_if_all_in_set(fnlinker.dihedrals, unchanged_atom_indices) dihedral_types = [(*order_types([uff_types[a] for a in atoms]), num_dihedrals_per_bond[order_types([atoms[1], atoms[2]])]) for atoms in fnlinker.dihedrals] unique_dihedral_types = list(dict.fromkeys(dihedral_types).keys()) dihedral_params = [ruff.dihedral_params(*a_ids, bond_order_rules=bond_order_rules) for a_ids in unique_dihedral_types] # delete any dihedrals when the params come back None (i.e. for *_1) for i in reversed(range(len(dihedral_params))): if dihedral_params[i] is None: none_dihedral = unique_dihedral_types[i] print(len(fnlinker.dihedrals), len(dihedral_types), len(unique_dihedral_types), len(dihedral_params)) fnlinker.dihedrals = [d for j, d in enumerate(fnlinker.dihedrals) if dihedral_types[j] != none_dihedral] dihedral_types = [d for d in dihedral_types if d != none_dihedral] del(unique_dihedral_types[i]) del(dihedral_params[i]) print(len(fnlinker.dihedrals), len(dihedral_types), len(unique_dihedral_types), len(dihedral_params)) # assign dihedral types fnlinker.dihedral_types = [unique_dihedral_types.index(a) for a in dihedral_types] dihedral_params = [(*ruff.dihedral_params(*a_ids, bond_order_rules=bond_order_rules), "%s %s %s %s M=%d" % a_ids) for a_ids in unique_dihedral_types] fnlinker.dihedral_type_params = ['%s %10.6f %d %d # %s' % params for params in dihedral_params] print("Num dihedrals, angles, bonds: %d, %d, %d" % (len(fnlinker.dihedrals), len(fnlinker.angles), len(fnlinker.bonds))) # output lammps-data file with open(outpath, "w") as f: fnlinker.to_lammps_data(f)