Esempio n. 1
0
def place_ligands(atom_dict, lig_info, sites, buffer, fixed_loc, cap):
    """
    Adds the requested amount of ligands at random sites, returns updated
    atom_dict.
    """
    # Sorry for the length of this function, should probably be broken up in
    # multiple functions.
    ligand_type = lig_info["ligand_type"]
    n_ligands = lig_info["n_ligands"]
    extension = lig_info["extension"]
    fail_bool = False
    id = max(atom_dict) + 1
    original_ligand = ligand_type
    j = 0
    tried = []
    # Print which ligands are being placed
    if extension and extension[0] is not False:
        ex_string = ''
        for x in extension:
            if x is not False:
                ex_string += x[:-4] + ' + '
        ex_string = ex_string[:-3]
        print("\nPlacing " + ligand_type[:-4] + " + " + ex_string)
    else:
        print("\nPlacing " + ligand_type[:-4])

    # Add ligands until done
    while j < n_ligands:
        # preventive in case of rounding errors
        if len(sites) == 0:
            break
        extra_rotation = 0
        ligand_type = original_ligand

        # Randomly choose site, get relevant info, don't pick site that has already been tried
        sites_list = list(sites).copy()
        remaining_sites = [x for x in sites_list if x not in tried]

        if len(remaining_sites) == 0:
            print("\nUnable to place more ligands of type " +
                  str(original_ligand) + ". Placed " + str(j) + " out of " +
                  str(n_ligands) + " requested ligands.\n")
            retry = hf.y2true(input("Try again (y), or continue (n)?: "))
            if retry:
                print("Trying again...")
                return 1
            break
        # Use predetermined sites if required, "loc" variables refer to
        # connection site and its primary atom
        if fixed_loc and ligand_type != cap:
            if len(lig_info["loc_info"]) == 0:
                break
            loc_id = random.choice(list(lig_info["loc_info"]))
            loc_vec = lig_info["loc_info"][loc_id]["loc_vec"]
            loc_sites = copy.deepcopy(sites[loc_id]['sites_xyz'])
            random_rotation = lig_info["loc_info"][loc_id]["rotation"]
            del lig_info["loc_info"][loc_id]
        # Use random sites
        else:
            loc_id = random.choice(remaining_sites)
            loc_sites = sites[loc_id]['sites_xyz'].copy()
            loc_vec = random.choice(loc_sites)
            random_rotation = random.random() * 2 * math.pi
        loc_primary_xyz = sites[loc_id]['primary_xyz']
        tried_loc_vec = []
        lig = prep_ligand_file(atom_dict, ligand_type, extension, cap)
        stop = False

        # Loop over every site connected to chosen atom
        while True:
            # Make sure you don't try same loc_vec twice
            for vec in tried_loc_vec:
                if hf.distance_checker(vec, loc_vec) < 0.001:
                    stop = True
            if stop:
                break
            # Get correct rotation for ligand relative to site
            axis = np.cross(loc_vec, [0, 0, 1])
            # Prevent dividing by 0 when vectors are already lined up
            if np.linalg.norm(axis) == 0:
                axis = [1, 0, 0]
                angle = 0
            else:
                angle = -hf.angle_checker(loc_vec, [0, 0, 1])
            rot_mat = hf.rotation_matrix(axis, angle)

            # Place ligand if possible, otherwise rotate
            while True:
                xyz_list = []
                temp_atom_dict = {}
                broken = False
                min_dist_lig = math.inf
                initial_length = hf.check_bond_len(
                    bond_len_dict, lig[hf.base_atom(lig)]["element"],
                    atom_dict[loc_id]["element"])
                for atom, lig_val in lig.items():
                    lig_coor = copy.deepcopy(lig_val["coor"])
                    lig_coor[2] += initial_length
                    # Rotate every atom in ligand according to random_rotation
                    rotated_atom = np.dot(
                        hf.rotation_matrix([0, 0, 1],
                                           random_rotation + extra_rotation),
                        lig_coor)
                    # Rotate in right direction (with respect to crystal)
                    new_v = np.dot(rot_mat, rotated_atom)
                    atom_xyz = [
                        c1 + c2 for c1, c2 in zip(new_v, loc_primary_xyz)
                    ]
                    atom_element = lig[atom]['element']
                    temp_atom_dict[id] = {
                        "coor": atom_xyz,
                        "element": atom_element,
                        "type": "ligand",
                        "ligand_type": ligand_type,
                        "loc_id": loc_id,
                        "loc_vec": loc_vec,
                        "rotation": random_rotation + extra_rotation
                    }
                    xyz_list.append(atom_xyz)
                    id += 1
                    # Check for overlap
                    if ligand_type != cap:
                        for test_atom, values in atom_dict.items():
                            space = hf.check_bond_len(
                                bond_len_dict, atom_element,
                                values["element"]) + buffer
                            if test_atom != loc_id:
                                dist = hf.distance_checker(
                                    values["coor"], xyz_list[-1])
                                if dist < space:
                                    broken = True
                                    break
                    # Check for overlap when capping, use different rotation
                    if ligand_type == cap:
                        new_pos = new_v.copy()
                        changed = False
                        while True:
                            min_dist_lig = math.inf
                            for test_lig, values in atom_dict.items():
                                if values["type"] == "ligand":
                                    dist = hf.distance_checker(
                                        values["coor"], [
                                            c1 + c2 for c1, c2 in zip(
                                                new_pos, loc_primary_xyz)
                                        ])
                                    bond_len_loc = hf.check_bond_len(
                                        bond_len_dict, atom_element,
                                        values["element"])
                                    # Sorry for magic number
                                    if dist < bond_len_loc + 0.3:
                                        if dist < min_dist_lig:
                                            min_dist_lig = dist
                                            min_dist_lig_coor = values["coor"]
                                            changed = True
                            if min_dist_lig == math.inf:
                                if changed:
                                    xyz_list = [
                                        c1 + c2 for c1, c2 in zip(
                                            new_pos, loc_primary_xyz)
                                    ]
                                    temp_atom_dict[id - 1] = {
                                        "coor": xyz_list,
                                        "element": atom_element,
                                        "type": "ligand",
                                        "ligand_type": ligand_type,
                                        "loc_id": loc_id,
                                        "loc_vec": loc_vec
                                    }
                                break
                            # Rotate away from closest atom
                            vec_closest_element = [
                                c1 - c2 for c1, c2 in zip(
                                    min_dist_lig_coor, loc_primary_xyz)
                            ]
                            axis = np.cross(vec_closest_element, new_pos)
                            angle = 0.1
                            rot_mat = hf.rotation_matrix(axis, angle)
                            new_pos = np.dot(rot_mat, new_pos.copy())

                    if broken:
                        break
                # Check for ligand collisions except when last ligand is added
                if ligand_type != cap:
                    # Rotate if there is a collision
                    if broken and extra_rotation < math.pi * 2:
                        # Rotate less with fixed loc
                        if fixed_loc:
                            extra_rotation += 0.1
                        else:
                            extra_rotation += 0.2
                        continue
                    # break if rotated fully
                    elif extra_rotation > math.pi * 2:
                        extra_rotation = 0
                        tried.append(loc_id)
                        tried_loc_vec.append(loc_vec)
                        fail_bool = True
                        break
                    # Merge dicts if no collision
                    elif not broken:
                        atom_dict = {**atom_dict, **temp_atom_dict}
                        if j % 10 == 0:
                            print(str(j) + " ligands added")
                        if loc_id in sites:
                            del sites[loc_id]
                        tried_loc_vec = [loc_vec]
                        ligand_type = cap
                        lig = prep_ligand_file(atom_dict, cap, [False], cap)
                        j += 1
                        break
                else:
                    atom_dict = {**atom_dict, **temp_atom_dict}
                    if loc_id in sites:
                        del sites[loc_id]
                    tried_loc_vec.append(loc_vec)
                    break
            # Add final ligand type to remaining sites at chosen atom
            try:
                loc_vec = loc_sites[(loc_sites.index(loc_vec) + 1) %
                                    len(loc_sites)]
            except ValueError:
                min_dist = math.inf
                loc_index = 0
                for item in loc_sites:
                    distance = hf.distance_checker(loc_vec, item)
                    if distance < min_dist:
                        min_dist = distance
                        loc_index_min = loc_index
                    loc_index += 1
                loc_vec = loc_sites[(loc_index_min + 1) % len(loc_sites)]

    # Detect failure to place all requested ligands if replacing
    if fixed_loc and fail_bool:
        retry = hf.y2true(
            input(
                "Not all ligands were able to be put in the same spot, try again? y/n: "
            ))
        if retry:
            print("Trying again...")
            return 1
        else:
            cont = hf.y2true(input("Continue with fewer ligands? y/n: "))
        if not cont:
            sys.exit()

    return atom_dict
Esempio n. 2
0
def replace_ligands(atom_dict, lig_dict, cap, sites, buffer, input_rep,
                    filename, foldername):
    """
    Takes in an atom dict with ligands and replaces the ligands by the
    requested replacements, saves it to a new quantum dot and returns the
    new atom dict.
    """
    replacement_list = []
    rep_ext_list = []
    loc_dict = {cap[:-4]: []}
    rep_dict = copy.deepcopy(lig_dict)
    lig_dict_inv = {}
    # Store the replacements and replacee's in the right way
    for ligand, lig_val in lig_dict.items():
        if lig_val["ligand_type"] != cap:
            # Use existing input
            if input_rep:
                replacement_list = [input_rep[0]]
                rep_ext_list = [input_rep[1]]
                rep_dict[ligand]["extension"] = rep_ext_list[-1]
            # Get new input
            else:
                replacement_list.append(
                    input("Replace " + lig_val["ligand_type"] + " with: ") +
                    ".xyz")
                extend = hf.y2true(input("Extend ligand? y/n: "))
                if extend:
                    rep_ext_list.append([input("Extend with: ") + ".xyz"])
                    rep_dict[ligand]["extension"] = rep_ext_list[-1]
                else:
                    rep_ext_list.append([False])
                    rep_dict[ligand]["extension"] = [False]
            # Store info in dict
            loc_dict[ligand] = {
                "loc_id": [],
                "replacement": replacement_list[-1]
            }
            rep_dict[ligand]["ligand_type"] = replacement_list[-1]
            rep_dict[ligand]["loc_info"] = {}
            # Reverses key and value
            lig_dict_inv[lig_val["ligand_type"]] = ligand

    # Get info on all atoms that need to be deleted, and get the relevant info
    # of the replacee's
    atom_del_list = []
    for atom, at_values in atom_dict.items():
        if at_values["type"] == 'ligand':
            atom_del_list.append(atom)
            if at_values["ligand_type"] != cap:
                rep_dict[lig_dict_inv[at_values["ligand_type"]]]["loc_info"][
                    at_values["loc_id"]] = {
                        "loc_vec": at_values["loc_vec"],
                        "rotation": at_values["rotation"]
                    }

    rep_dict_copy = copy.deepcopy(rep_dict)
    atom_dict_copy = copy.deepcopy(atom_dict)
    sites_copy = copy.deepcopy(sites)
    done = False
    # Actually delete and replace
    while not done:
        for item in atom_del_list:
            del atom_dict[item]
        for lig, values in rep_dict.items():
            atom_dict = place_ligands(atom_dict, values, sites, buffer, True,
                                      cap)
            if atom_dict == 1:
                atom_dict = copy.deepcopy(atom_dict_copy)
                sites = copy.deepcopy(sites_copy)
                rep_dict = copy.deepcopy(rep_dict_copy)
                break
            else:
                done = True
    hf.dict2file(atom_dict, filename, foldername)

    return atom_dict, rep_dict
Esempio n. 3
0
def single_qd(atom_dict, sites, foldername):
    """
    Create a single quantum dot and allow for replacement of ligands for a new
    one.
    """
    filename = input("Save QD as: ")
    i = 0
    lig_list = []
    extension_list = []
    coverage_list = []
    n_ligands_list = []
    lig_dict = {}
    n_sites = len(sites)
    # Get all ligands
    while True:
        ligand_file = input(
            "Filename of ligand to be added, or type files to see available ligands: "
        ) + ".xyz"
        # Print available ligands
        if ligand_file[:-4] == "files":
            hf.print_lig()
            ligand_file = input("Filename of ligand to be added: ") + ".xyz"
        lig_list.append(ligand_file)
        extend = hf.y2true(
            input(
                "Extend ligand? Note: this replaces the last atom in the ligand file. y/n: "
            ))
        if extend:
            extension_list.append([input("Extend with: ") + ".xyz"])
            while True:
                extra_ext = input(
                    "Further extension to extension, or type n: ")
                if extra_ext != 'n':
                    extension_list[-1].append(extra_ext + ".xyz")
                else:
                    break
        else:
            extension_list.append([False])
        coverage = float(input("Coverage (fraction): "))
        coverage_list.append(coverage)
        n_ligands_list.append(round(coverage * n_sites))
        lig_dict[i] = {
            "ligand_type": ligand_file,
            "extension": extension_list[-1],
            "coverage": coverage,
            "n_ligands": round(coverage * n_sites)
        }
        i += 1
        more = hf.y2true(input("Add another type? y/n: "))
        if not more:
            cap = input("Cap remaining sites with (single atom): ") + ".xyz"
            lig_dict[i] = {
                "ligand_type": cap,
                "extension": [False],
                "coverage": 1,
                "n_ligands": n_sites
            }
            break

    buffer = float(
        input(
            "Buffer (Angstrom) (distance between ligands will be at least buffer + bonding length): "
        ))
    sites_copy = copy.deepcopy(sites)
    atom_dict_copy = copy.deepcopy(atom_dict)
    # Place ligands
    while True:
        stopped = False
        for ligand, values in lig_dict.items():
            atom_dict = place_ligands(atom_dict, values, sites, buffer, False,
                                      cap)
            if atom_dict == 1:
                atom_dict = copy.deepcopy(atom_dict_copy)
                sites = copy.deepcopy(sites_copy)
                stopped = True
                break
        if not stopped:
            break
    hf.dict2file(atom_dict, filename, foldername)

    # Replace ligands
    while True:
        replacement_ligands = hf.y2true(
            input("Replace ligands to create new quantum dot? y/n: "))
        if replacement_ligands:
            sites = sites_copy.copy()
            filename = input("Write to file: ")
            buffer = float(input("Buffer: "))
            replaced = replace_ligands(atom_dict, lig_dict, cap, sites, buffer,
                                       False, filename, foldername)
            atom_dict = replaced[0]
            lig_dict = replaced[1]
        else:
            break
Esempio n. 4
0
def series_lig(atom_dict, sites, foldername):
    """
    Obtains the lig_dict containing the values of the ligands to be added to
    the quantum dot. Here this is done by combining all 'bases' and extensions
    with eachother. e.g. when you put in C10H21 and C9H19 as bases, and COOH
    and NH2 as extensions you'll get C10H21 + COOH, C10H21 + NH2, C9H19 + COOH
    C9H19 + NH2
    """
    base_list = []
    ext_list = []
    lig_dict = {}
    n_sites = len(sites)
    sites_copy = copy.deepcopy(sites)
    # Obtain bases of ligands
    while True:
        base = input(
            "Add ligand to base list, or type 'files' to show available ligands, or type 'done': "
        )
        if base == 'files':
            hf.print_lig()
        if base == 'done':
            break
        base_list.append(base + ".xyz")
    # Obtain extensions
    while True:
        ext = input(
            "Add extension to extension list, or type 'none', or 'done': ")
        if ext == 'done':
            break
        if ext == 'none':
            ext_list.append([False])
        # Extend extensions
        else:
            ext_list.append([ext + ".xyz"])
            extra_ext = input("Further extension to extension, or type n: ")
            if extra_ext != 'n':
                ext_list[-1].append(extra_ext + ".xyz")

    coverage = float(input("Coverage (fraction): "))
    cap = input("Cap remaining sites with (single atom): ") + ".xyz"
    sec_buffer = float(
        input(
            "Buffer size (Angstrom)? Distance between ligands will be at least bonding length + buffer: "
        ))
    buffer = float(
        input(
            "Initial buffer (Buffer for first QD. Helps create space for later QD's): "
        ))
    atom_dict_copy = copy.deepcopy(atom_dict)
    i = 0

    # Create all quantum dots
    for base in base_list:
        print(base)
        for extension in ext_list:
            filename = base[:-4]
            for item in extension:
                if item is not False:
                    filename += "+" + item[:-4]
            # Create first quantum dot
            if i == 0:
                lig_dict[0] = {
                    "ligand_type": base,
                    "extension": extension,
                    "coverage": coverage,
                    "n_ligands": round(coverage * n_sites)
                }

                lig_dict[1] = {
                    "ligand_type": cap,
                    "extension": [False],
                    "coverage": 1,
                    "n_ligands": n_sites
                }
                while True:
                    stopped = False
                    for ligand, values in lig_dict.items():
                        atom_dict = place_ligands(atom_dict, values, sites,
                                                  buffer, False, cap)
                        # Detect failure to place all ligands
                        if atom_dict == 1:
                            atom_dict = copy.deepcopy(atom_dict_copy)
                            sites = copy.deepcopy(sites_copy)
                            stopped = True
                            break
                    if not stopped:
                        i += 1
                        buffer = sec_buffer
                        hf.dict2file(atom_dict, filename, foldername)
                        break
            # Replace ligands
            else:
                sites = sites_copy.copy()
                input_rep = [base, extension]
                replaced = replace_ligands(atom_dict, lig_dict, cap, sites,
                                           buffer, input_rep, filename,
                                           foldername)
                atom_dict = replaced[0]
                lig_dict = replaced[1]
    # Allow for more replacements
    while True:
        replacement_ligands = hf.y2true(
            input("Replace ligands to create new quantum dot? y/n: "))
        if replacement_ligands:
            sites = sites_copy.copy()
            filename = input("Write to file: ")
            buffer = float(input("Buffer: "))
            replaced = replace_ligands(atom_dict, lig_dict, cap, sites, buffer,
                                       False, filename, foldername)
            atom_dict = replaced[0]
            lig_dict = replaced[1]
        else:
            break
Esempio n. 5
0
def crystal_builder(a, atom_a, atom_b, diameter):
    """
    This function takes in the lattice constant, elements in the crystal and
    the diameter of the nanocrystal to be built. The unit cell is copied in
    all directions and cutoffs are made at the (111), (110) and (100) planes.
    ID numbers are assigned and neighbours are calculated.
    Then coordinates, elements, neighbours and atom id are stored in a dict.
    """
    n_unit = math.ceil(diameter)
    n_range = np.arange(-n_unit / 2.0 + 0.5, n_unit / 2.0)
    # Get unit cell
    uc = unit_cells.zns(a, atom_a, atom_b)
    all_atoms = []
    atom_dict = {}
    cut_off_distance = diameter * a / 2.0
    boundary_111 = 3.0 / math.sqrt(3) * cut_off_distance
    boundary_110 = 2.0 / math.sqrt(2) * cut_off_distance
    boundary_100 = cut_off_distance
    id = 0
    for x in n_range:
        for y in n_range:
            for z in n_range:
                # Place atoms one by one in correct spot
                for atom in uc.atom_xyz:
                    atom_element = atom[0]
                    atom_x = round(x * a + atom[1], 4)
                    atom_y = round(y * a + atom[2], 4)
                    atom_z = round(z * a + atom[3], 4)
                    atom_details = [atom_element, atom_x, atom_y, atom_z]

                    # Make sure not to put multiple atoms in same spot, and make cut-offs
                    if atom_details not in all_atoms and (
                            abs(atom_x) + abs(atom_y) + abs(atom_z) <=
                            boundary_111 and
                        (abs(atom_x) + abs(atom_y) <= boundary_110
                         and abs(atom_x) + abs(atom_z) <= boundary_110
                         and abs(atom_y) + abs(atom_z) <= boundary_110) and
                        (abs(atom_x) <= boundary_100
                         and abs(atom_y) <= boundary_100
                         and abs(atom_z) <= boundary_100)):
                        all_atoms.append(atom_details)
                        bound = hf.bond_checker(atom_details, atom_dict,
                                                bond_len_dict)
                        atom_dict[id] = {
                            "coor": [atom_x, atom_y, atom_z],
                            "element": atom_element,
                            "bound": bound,
                            "type": "crystal"
                        }
                        # Update bonds for already placed atoms
                        for item in bound:
                            atom_dict[item]["bound"].append(id)
                        id += 1
    # Remove all singly bound atoms, if wanted
    include_singles = hf.y2true(input("Include singly bound atoms? y/n: "))
    if not include_singles:
        while True:
            for test_id, values in atom_dict.items():
                stopped = False
                # Delete singly bonded atoms, update neighbour
                if len(values['bound']) == 1:
                    neighbour = values['bound'][0]
                    atom_dict[neighbour]['bound'].remove(test_id)
                    del dict[test_id]
                    stopped = True
                    break
            # Break if no singly bonded atoms remain
            if not stopped:
                break
    return atom_dict
Esempio n. 6
0
                        space = space + 0.25
                    if test_atom not in values["connected"]:
                        dist = hf.distance_checker(values2["coor"],
                                                   xyz_list[-1])
                        if dist < space + 0.25:
                            break
            atom_dict = {**atom_dict, **temp_atom_dict}
            break
    return atom_dict


if __name__ == "__main__":
    # Having a global dict with bonding lengths improves speed a lot
    global bond_len_dict
    bond_len_dict = hf.csv2dict("bonding_distances.csv")
    build = hf.y2true(
        input("Create new crystal (y) or use existing file (n)?: "))
    if build:
        a = float(input("Specify lattice constant (in Ångström): "))
        atom_a = input("Element for first element type: ")
        atom_b = input("Element for second element type: ")
        diameter = float(input("Diameter of quantum dot (in unit cells): "))
        atom_dict = crystal_builder(a, atom_a, atom_b, diameter)
    else:
        crystal_file = input(
            "Crystal file to use (don't write the file extension): ") + ".xyz"
        atom_dict = crystal_reader(crystal_file)

    foldername = input("Save in folder (or main): ")
    if foldername == "main":
        foldername = False
    sites = tetra_sites(atom_dict)