diff --git a/build/lib/pycofbuilder/__init__.py b/build/lib/pycofbuilder/__init__.py index 41a33dea..b9c040ca 100644 --- a/build/lib/pycofbuilder/__init__.py +++ b/build/lib/pycofbuilder/__init__.py @@ -43,6 +43,6 @@ __author__ = "Felipe Lopes de Oliveira" __license__ = "MIT" -__version__ = '0.0.6' +__version__ = '0.0.8' __email__ = "felipe.lopes@nano.ufrj.br" __status__ = "Development" diff --git a/build/lib/pycofbuilder/data/__pycache__/__init__.cpython-311.pyc b/build/lib/pycofbuilder/data/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..a9b73b2c Binary files /dev/null and b/build/lib/pycofbuilder/data/__pycache__/__init__.cpython-311.pyc differ diff --git a/build/lib/pycofbuilder/data/__pycache__/topology.cpython-311.pyc b/build/lib/pycofbuilder/data/__pycache__/topology.cpython-311.pyc index 3a666390..95c8d283 100644 Binary files a/build/lib/pycofbuilder/data/__pycache__/topology.cpython-311.pyc and b/build/lib/pycofbuilder/data/__pycache__/topology.cpython-311.pyc differ diff --git a/build/lib/pycofbuilder/data/__pycache__/topology.cpython-39.pyc b/build/lib/pycofbuilder/data/__pycache__/topology.cpython-39.pyc new file mode 100644 index 00000000..29265257 Binary files /dev/null and b/build/lib/pycofbuilder/data/__pycache__/topology.cpython-39.pyc differ diff --git a/build/lib/pycofbuilder/framework.py b/build/lib/pycofbuilder/framework.py index 20376e51..958802de 100644 --- a/build/lib/pycofbuilder/framework.py +++ b/build/lib/pycofbuilder/framework.py @@ -33,11 +33,11 @@ rotation_matrix_from_vectors, unit_vector, angle, - get_framework_symm_text) + get_framework_symm_text, + get_bonds) # Import pycofbuilder io_tools -from pycofbuilder.io_tools import (save_json, - save_chemjson, +from pycofbuilder.io_tools import (save_chemjson, save_cif, save_xyz, save_turbomole, @@ -120,6 +120,7 @@ def __init__(self, name: str = None, **kwargs): self.symm_tol = kwargs.get('symm_tol', 0.1) self.angle_tol = kwargs.get('angle_tol', 0.5) self.dist_threshold = kwargs.get('dist_threshold', 0.8) + self.bond_threshold = kwargs.get('bond_threshold', 1.3) self.bb1_name = None self.bb2_name = None @@ -132,6 +133,7 @@ def __init__(self, name: str = None, **kwargs): self.atom_labels = [] self.cellMatrix = np.eye(3) self.cellParameters = np.array([1, 1, 1, 90, 90, 90]).astype(float) + self.bonds = [] self.lattice_sgs = None self.space_group = None @@ -368,9 +370,24 @@ def from_building_blocks(self, result = net_build_dict[net](bb1, bb2, stacking, **kwargs) + structure = Structure( + self.cellMatrix, + self.atom_types, + self.atom_pos, + coords_are_cartesian=True, + site_properties={'source': self.atom_labels} + ) + + self.bonds = get_bonds(structure, self.bond_threshold) + return result - def save(self, fmt: str = 'cif', supercell: list = [1, 1, 1], save_dir=None, primitive=False) -> None: + def save(self, + fmt: str = 'cif', + supercell: list = [1, 1, 1], + save_dir=None, + primitive=False, + save_bonds=True) -> None: ''' Save the structure in a specif file format. @@ -392,7 +409,6 @@ def save(self, fmt: str = 'cif', supercell: list = [1, 1, 1], save_dir=None, pri ''' save_dict = { - 'json': save_json, 'cjson': save_chemjson, 'cif': save_cif, 'xyz': save_xyz, @@ -422,6 +438,11 @@ def save(self, fmt: str = 'cif', supercell: list = [1, 1, 1], save_dir=None, pri final_structure = structure.make_supercell(supercell, in_place=False) + if save_bonds: + bonds = get_bonds(final_structure, self.bond_threshold) + else: + bonds = [] + structure_dict = final_structure.as_dict() cell = structure_dict['lattice']['matrix'] @@ -440,7 +461,8 @@ def save(self, fmt: str = 'cif', supercell: list = [1, 1, 1], save_dir=None, pri cell=cell, atom_types=atom_types, atom_labels=atom_labels, - atom_pos=atom_pos) + atom_pos=atom_pos, + bonds=bonds) # --------------- Net creation methods -------------------------- # diff --git a/build/lib/pycofbuilder/io_tools.py b/build/lib/pycofbuilder/io_tools.py index e0d79d2c..3cf4c8f0 100644 --- a/build/lib/pycofbuilder/io_tools.py +++ b/build/lib/pycofbuilder/io_tools.py @@ -237,28 +237,39 @@ def read_cif(path, file_name): return None -def save_xsf(path: str = None, - file_name: str = None, - cell: np.ndarray = np.eye(3), - atom_types: list = None, - atom_pos: list = None, - atom_labels: list = None, - frac_coords: bool = False): +def save_xsf(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + bond_orders: list = None, + frac_coords=False): """ Save a file in format `.xsf` on the `path`. Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.xsf` extention. + Name of the file. Does not neet to contain the extention. cell : numpy array Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom label. + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. """ file_name = file_name.split('.')[0] @@ -290,31 +301,39 @@ def save_xsf(path: str = None, xsf_file.close() -def save_pqr(path: str = None, - file_name: str = None, - cell: np.ndarray = np.eye(3), - atom_types: list = None, - atom_pos: list = None, - atom_labels: list = None, - partial_charges: list = None, - frac_coords: bool = False): +def save_pqr(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + bond_orders: list = None, + frac_coords=False): """ Save a file in format `.pqr` on the `path`. Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.pqr` extention. + Name of the file. Does not neet to contain the extention. cell : numpy array Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom partial charges. + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. - partial_charges: list + atom_charges : list List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. """ file_name = file_name.split('.')[0] @@ -337,7 +356,7 @@ def save_pqr(path: str = None, cell[4], cell[5])) - if partial_charges is None: + if atom_charges is None: atom_line = 'ATOM {:>4} {:>2} MOL A 0 {:>8.3f}{:>8.3f}{:>8.3f} {:>15}\n' for i in range(len(atom_pos)): pqr_file.write(atom_line.format(i + 1, @@ -354,34 +373,53 @@ def save_pqr(path: str = None, atom_pos[i][0], atom_pos[i][1], atom_pos[i][2], - partial_charges[i], + atom_charges[i], atom_types[i])) + if bonds and not bond_orders: + bond_orders = [1 for i in range(len(bonds))] + + if bonds: + for i in range(len(bonds)): + for j in range(bond_orders[i]): + pqr_file.write(f'CONECT {bonds[i][0] + 1:4} {bonds[i][1] + 1:4}\n') + pqr_file.close() -def save_pdb(path: str = None, - file_name: str = None, - cell: np.ndarray = np.eye(3), - atom_types: list = None, - atom_pos: list = None, - atom_labels: list = None, - frac_coords: bool = False): +def save_pdb(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + bond_orders: list = None, + frac_coords=False): """ Save a file in format `.pdb` on the `path`. Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.pdb` extention. + Name of the file. Does not neet to contain the extention. cell : numpy array Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom partial charges. + List of strings containing containg the N atom labels atom_pos : list - Nx3 array contaning the atoms coordinates in cartesian form. + Nx3 array contaning the atoms coordinates. + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. """ file_name = file_name.split('.')[0] @@ -414,37 +452,54 @@ def save_pdb(path: str = None, atom_pos[i][2], atom_types[i])) - pdb_file.close() + if bonds and not bond_orders: + bond_orders = [1 for i in range(len(bonds))] + + if bonds: + for i in range(len(bonds)): + for j in range(bond_orders[i]): + pdb_file.write(f'CONECT {bonds[i][0] + 1:4} {bonds[i][1] + 1:4}\n') + pdb_file.close() -def save_gjf(path: str = None, - file_name: str = None, - cell: list = None, - atom_types: list = None, - atom_pos: list = None, - atom_labels: list = None, - frac_coords: bool = False, - text: str = 'opt pm6'): +def save_gjf(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + bond_orders: list = None, + frac_coords=False, + header: str = 'opt pm6'): """ - Save a file in format `.gjf` on the `path`. + Save a file in format `.pqr` on the `path`. Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.gjf` extention. + Name of the file. Does not neet to contain the extention. cell : numpy array Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom partial charges. + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. - text : str + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. + header : str Parameters for Gaussian calculations. """ - if len(cell) == 6: cell = cellpar_to_cell(cell) @@ -457,7 +512,7 @@ def save_gjf(path: str = None, temp_file = open(os.path.join(path, file_name + '.gjf'), 'w') temp_file.write(f'%chk={file_name}.chk \n') - temp_file.write(f'# {text}\n') + temp_file.write(f'# {header}\n') temp_file.write('\n') temp_file.write(f'{file_name}\n') temp_file.write('\n') @@ -476,28 +531,39 @@ def save_gjf(path: str = None, temp_file.close() -def save_xyz(path: str = None, - file_name: str = None, - cell: np.ndarray = np.eye(3), - atom_types: list = None, - atom_pos: list = None, - atom_labels: list = None, - frac_coords: bool = False): +def save_xyz(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + bond_orders: list = None, + frac_coords=False): """ Save a file in format `.xyz` on the `path`. Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.xyz` extention. + Name of the file. Does not neet to contain the extention. + cell : numpy array + Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. atom_types : list List of strings containing containg the N atom types + atom_label : list + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. - cell : numpy array - Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. """ if len(cell) == 3: @@ -527,27 +593,39 @@ def save_xyz(path: str = None, temp_file.close() -def save_turbomole(path: str = None, - file_name: str = None, - cell: np.ndarray = np.eye(3), - atom_types: list = None, - atom_pos: list = None, - atom_labels: list = None, - frac_coords: bool = False): - """Save the structure in Turbomole .coord format +def save_turbomole(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + bond_orders: list = None, + frac_coords=False): + """ + Save the structure in Turbomole .coord format on the `path`. Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.coord` extention. + Name of the file. Does not neet to contain the extention. cell : numpy array Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom partial charges. + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. """ if cell.shape == (3, 3): @@ -575,29 +653,39 @@ def save_turbomole(path: str = None, temp_file.write('$end\n') -def save_vasp(path: str = None, - file_name: str = None, - cell: np.ndarray = np.eye(3), - atom_types: list = None, - atom_pos: list = None, - atom_labels: list = None, - frac_coords: bool = False): - """Save the structure in VASP .vasp format +def save_vasp(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + bond_orders: list = None, + frac_coords=False): + """ + Save the structure in VASP .vasp format on the `path`. Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.vasp` extention. + Name of the file. Does not neet to contain the extention. cell : numpy array Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom partial charges. + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. - coords_are_cartesian : bool - If True, the coordinates are in cartesian coordinates. + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. """ if cell.shape == 6: @@ -634,15 +722,19 @@ def save_vasp(path: str = None, atom_types[i])) -def save_qe(path: str = None, - file_name: str = None, - cell: np.ndarray = np.eye(3), - atom_types: list = None, - atom_pos: list = None, - atom_labels: list = None, - frac_coords: bool = False, - calc_type: str = 'scf'): - ''' +def save_qe(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + bond_orders: list = None, + frac_coords=False, + calc_type: str = 'scf', + kspacing: float = 0.3): + """ Save the structure in Quantum Espresso .pwscf format. The `input_dict` can be used to specify the input parameters for the @@ -657,20 +749,28 @@ def save_qe(path: str = None, Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.pwscf` extention. + Name of the file. Does not neet to contain the extention. cell : numpy array Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom labels. + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. frac_coords : bool If True, the coordinates are in fractional coordinates. calc_type : str - Type of pw.x calculation. Can be 'scf', 'nscf', 'bands', and 'vc-relax'. - ''' + Type of calculation. Can be 'scf', 'vc-relax', 'relax', 'md', 'vc-md', 'vc-tddft', 'tddft'. + kspacing : float + Kpoints spacing in 1/Angstrom. + """ if len(cell) == 6: cell_matrix = cellpar_to_cell(cell) @@ -721,7 +821,7 @@ def save_qe(path: str = None, # If the kpoints grid is not specified, calculate it automatically if 'k_points' not in input_dict.keys(): if 'kspacing' not in input_dict.keys(): - input_dict['kspacing'] = 0.3 + input_dict['kspacing'] = kspacing input_dict['kpoints'] = get_kgrid(cell_matrix, input_dict['kspacing']) with open(os.path.join(path, file_name + '.pwscf'), 'w') as f: @@ -809,68 +909,38 @@ def convert_cif_2_qe(out_path, file_name): k_dist=0.3) -def save_json(path, file_name, cell, atom_types, atom_pos, atom_labels, frac_coords=False): +def save_chemjson(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + frac_coords=False): """ Save a file in format `.json` on the `path`. Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.cif` extention. - atom_label : list - List of strings containing containg the N atom partial charges. - atom_pos : list - Nx3 array contaning the atoms coordinates. + Name of the file. Does not neet to contain the extention. cell : numpy array Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. - """ - - file_name = file_name.split('.')[0] - - cof_json = create_COF_json(file_name) - - if len(cell) == 3: - cell_par = cell_to_cellpar(np.array(cell)).tolist() - cell_matrix = np.array(cell).astype(float).tolist() - - if len(cell) == 6: - cell_par = np.array(cell).astype(float).tolist() - cell_matrix = cellpar_to_cell(cell_par).tolist() - - cof_json['system']['geo_opt'] = False - - cof_json['geometry']['cell_matrix'] = cell_matrix - cof_json['geometry']['cell_parameters'] = cell_par - cof_json['geometry']['atom_labels'] = list(atom_types) - cof_json['geometry']['atom_pos'] = list(atom_pos) - - write_json(path, file_name, cof_json) - - -def save_chemjson(path, - file_name, - cell, - atom_types, - atom_pos, - atom_labels, - frac_coords=False): - """ - Save a file in format `.json` on the `path`. - - Parameters - ---------- - path : str - Path to the file. - file_name : str - Name of the file. Does not neet to contain the `.cif` extention. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom partial charges. + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. - cell : numpy array - Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. """ file_name = file_name.split('.')[0] @@ -887,18 +957,20 @@ def save_chemjson(path, AtomTypes=atom_types, AtomPositions=atom_pos, AtomLabels=atom_labels, - CartesianPositions=not frac_coords) + CartesianPositions=not frac_coords, + BondIndexes=bonds) write_json(path, file_name, chemJSON) -def save_cif(path, - file_name, - cell, - atom_types, - atom_pos, - atom_labels=None, - partial_charges=False, +def save_cif(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, frac_coords=False): """ Save a file in format `.cif` on the `path`. @@ -906,15 +978,23 @@ def save_cif(path, Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.cif` extention. + Name of the file. Does not neet to contain the extention. + cell : numpy array + Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom partial charges. + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. - cell : numpy array - Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. """ file_name = file_name.split('.')[0] @@ -932,7 +1012,7 @@ def save_cif(path, _audit_creation_date {date.today().strftime("%Y-%d-%m")} _audit_creation_method pyCOFBuilder -_audit_author_name 'Felipe Lopes de Oliveira' +_audit_author_name '{os.getlogin()}' _chemical_name_common '{file_name}' _cell_length_a {a:>10.6f} @@ -956,7 +1036,7 @@ def save_cif(path, _atom_site_fract_z """ - if partial_charges is not False: + if atom_charges: cif_text += ' _atom_site_charge\n' if frac_coords is False: @@ -966,22 +1046,33 @@ def save_cif(path, for i in range(len(atom_pos)): u, v, w = atom_pos[i][0], atom_pos[i][1], atom_pos[i][2] - if partial_charges is not False: - cif_text += '{:<7} {} {:>15.9f} {:>15.9f} {:>15.9f} {:>10.5f}\n'.format( + if atom_charges: + atom_labels[i] = f"{atom_types[i]}{str(i + 1)}_{atom_labels[i]}" + cif_text += '{:<15} {} {:>15.9f} {:>15.9f} {:>15.9f} {:>10.5f}\n'.format( f"{atom_types[i]}{str(i + 1)}_{atom_labels[i]}", atom_types[i], u, v, w, - partial_charges[i]) + atom_charges[i]) else: - cif_text += '{:<7} {} {:>15.9f} {:>15.9f} {:>15.9f}\n'.format( + atom_labels[i] = f"{atom_types[i]}{str(i + 1)}_{atom_labels[i]}" + cif_text += '{:<15} {} {:>15.9f} {:>15.9f} {:>15.9f}\n'.format( f"{atom_types[i]}{str(i + 1)}_{atom_labels[i]}", atom_types[i], u, v, w) + if bonds: + cif_text += '\nloop_\n' + cif_text += '_geom_bond_atom_site_label_1\n' + cif_text += '_geom_bond_atom_site_label_2\n' + cif_text += '_geom_bond_distance\n' + + for bond in bonds: + cif_text += f'{atom_labels[bond[0]]:10} {atom_labels[bond[1]]:10} {bond[2]:.5f}\n' + # Write cif_text to file cif_file = open(os.path.join(path, file_name + '.cif'), 'w') cif_file.write(cif_text) @@ -1189,10 +1280,10 @@ def create_structure_CJSON(StructureName: str, CellParameters: list = None, CellMatrix: list = None, AtomTypes: list = None, + AtomLabels: list = [], AtomPositions: list = None, CartesianPositions: bool = False, - AtomLabels: list = [], - Bonds: list = [], + BondIndexes: list = [], BondOrders: list = [], PartialCharges: dict = {}, ) -> dict: @@ -1208,11 +1299,20 @@ def create_structure_CJSON(StructureName: str, List with the cell parameters. CellMatrix : list List with the cell matrix. Optional + AtomTypes : list + List with the atom types. AtomLabels : list List with the atom labels. AtomPositions : list List with the atom positions. - + CartesianPositions : bool + If True, the coordinates are in cartesian coordinates. + BondIndexes : list + List with the bonds indexes and bond length. + BondOrders : list + List with the bond orders. + PartialCharges : dict + Dictionary with the partial charges. """ chemJSON = create_empty_CJSON() @@ -1252,10 +1352,16 @@ def create_structure_CJSON(StructureName: str, CartPosition = np.array([np.dot(V_cart, atom) for atom in AtomPositions]).flatten().tolist() chemJSON['atoms']['coords']['3d'] = CartPosition - chemJSON['atoms']['PartialCharges'] = PartialCharges + if PartialCharges != {}: + chemJSON['atoms']['PartialCharges'] = PartialCharges + + bond_indexes = [[i[0], i[1]] for i in BondIndexes] + bond_indexes = [item for row in bond_indexes for item in row] + + bond_orders = BondOrders if BondOrders != [] else [1] * len(bond_indexes) - chemJSON['bonds']['connections']['index'] = Bonds - chemJSON['bonds']['order'] = BondOrders + chemJSON['bonds']['connections']['index'] = bond_indexes + chemJSON['bonds']['order'] = bond_orders return chemJSON diff --git a/build/lib/pycofbuilder/tools.py b/build/lib/pycofbuilder/tools.py index 14f74f17..95563552 100644 --- a/build/lib/pycofbuilder/tools.py +++ b/build/lib/pycofbuilder/tools.py @@ -1004,3 +1004,65 @@ def cell_to_ibrav(cell): 'celldm(6)': np.cos(np.deg2rad(gamma))} return celldm + + +def is_bonded(atom1: str, atom2: str, dist: float, cutoff: float = 1.3): + """ + Determine if two atoms are bonded based on the distance between them. + Two atoms are considered bonded if the distance between them is less than + the sum of their covalent radii multiplied by a cutoff factor. + + Parameters + ---------- + atom1 : str + Label of the first atom + atom2 : str + Label of the second atom + dist : float + Distance between the two atoms + cutoff : float + Cutoff factor for the covalent radii. Default: 1.3 + """ + + periodic_table = elements_dict(property='covalent_radius') + + # Get the covalent radii of the two atoms + cr_1 = periodic_table[atom1] + cr_2 = periodic_table[atom2] + + # Calculate max bond distance + max_bond_distance = (cr_1 + cr_2) * cutoff + + if dist < 0.6: + print('Distance between atoms is less than 0.6 Å. Check if the structure is correct.') + + # Check if the distance is less than the cutoff + if 0.6 < dist <= max_bond_distance: + return True + else: + return False + + +def get_bonds(structure, cutoff=1.3): + """ + Get the bonded atoms in a structure based on the distance between them. + + Parameters + ---------- + structure : pymatgen.Structure + Structure object of pymatgen + cutoff : float + Cutoff factor for the covalent radii. Default: 1.3 Å + """ + + atom_types = [i.as_dict()['species'][0]['element'] for i in structure.sites] + + # Get bonded atoms + center_indices, points_indies, _, bond_distances = structure.get_neighbor_list(5) + bonded_atoms = np.array([center_indices, points_indies, bond_distances]).T + + bonded_atoms = [i for i in bonded_atoms if is_bonded(atom_types[int(i[0])], atom_types[int(i[1])], i[2], cutoff)] + + bonded_atoms = [[int(i[0]), int(i[1]), i[2]] for i in bonded_atoms] + + return bonded_atoms diff --git a/dist/pycofbuilder-0.0.7.6.tar.gz b/dist/pycofbuilder-0.0.7.6.tar.gz deleted file mode 100644 index aae2e51d..00000000 Binary files a/dist/pycofbuilder-0.0.7.6.tar.gz and /dev/null differ diff --git a/dist/pycofbuilder-0.0.7.6-py3-none-any.whl b/dist/pycofbuilder-0.0.8-py3-none-any.whl similarity index 57% rename from dist/pycofbuilder-0.0.7.6-py3-none-any.whl rename to dist/pycofbuilder-0.0.8-py3-none-any.whl index b0612149..e5be3fa9 100644 Binary files a/dist/pycofbuilder-0.0.7.6-py3-none-any.whl and b/dist/pycofbuilder-0.0.8-py3-none-any.whl differ diff --git a/dist/pycofbuilder-0.0.8.tar.gz b/dist/pycofbuilder-0.0.8.tar.gz new file mode 100644 index 00000000..0a095566 Binary files /dev/null and b/dist/pycofbuilder-0.0.8.tar.gz differ diff --git a/pyproject.toml b/pyproject.toml index 21bc3627..1ef003c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ [project] name = "pycofbuilder" -version = '0.0.7.6' +version = '0.0.8' authors = [ { name="Felipe Lopes", email="felipe.lopes@nano.ufrj.br" }, ] diff --git a/setup.py b/setup.py index 8006fe8c..0cc663a5 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ license = f.read() -VERSION = '0.0.7.6' +VERSION = '0.0.8' DESCRIPTION = 'A package for Covalent Organic Frameworks sturcture assembly.' setup( diff --git a/src/pycofbuilder.egg-info/PKG-INFO b/src/pycofbuilder.egg-info/PKG-INFO index 72fda503..46994799 100644 --- a/src/pycofbuilder.egg-info/PKG-INFO +++ b/src/pycofbuilder.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: pycofbuilder -Version: 0.0.7.6 +Version: 0.0.8 Summary: A package for Covalent Organic Frameworks sturcture creation based on the reticular approach. Home-page: https://github.com/lipelopesoliveira/pyCOFBuilder Author: Felipe Lopes Oliveira diff --git a/src/pycofbuilder.egg-info/SOURCES.txt b/src/pycofbuilder.egg-info/SOURCES.txt index e7897012..f1ce56f6 100644 --- a/src/pycofbuilder.egg-info/SOURCES.txt +++ b/src/pycofbuilder.egg-info/SOURCES.txt @@ -19,7 +19,9 @@ src/pycofbuilder.egg-info/top_level.txt src/pycofbuilder/data/__init__.py src/pycofbuilder/data/periodic_table.json src/pycofbuilder/data/topology.py +src/pycofbuilder/data/__pycache__/__init__.cpython-311.pyc src/pycofbuilder/data/__pycache__/topology.cpython-311.pyc +src/pycofbuilder/data/__pycache__/topology.cpython-39.pyc src/pycofbuilder/data/conector/BOH2.cjson src/pycofbuilder/data/conector/Br.cjson src/pycofbuilder/data/conector/CH2CN.cjson diff --git a/src/pycofbuilder/__init__.py b/src/pycofbuilder/__init__.py index 41a33dea..b9c040ca 100644 --- a/src/pycofbuilder/__init__.py +++ b/src/pycofbuilder/__init__.py @@ -43,6 +43,6 @@ __author__ = "Felipe Lopes de Oliveira" __license__ = "MIT" -__version__ = '0.0.6' +__version__ = '0.0.8' __email__ = "felipe.lopes@nano.ufrj.br" __status__ = "Development" diff --git a/src/pycofbuilder/framework.py b/src/pycofbuilder/framework.py index 20376e51..958802de 100644 --- a/src/pycofbuilder/framework.py +++ b/src/pycofbuilder/framework.py @@ -33,11 +33,11 @@ rotation_matrix_from_vectors, unit_vector, angle, - get_framework_symm_text) + get_framework_symm_text, + get_bonds) # Import pycofbuilder io_tools -from pycofbuilder.io_tools import (save_json, - save_chemjson, +from pycofbuilder.io_tools import (save_chemjson, save_cif, save_xyz, save_turbomole, @@ -120,6 +120,7 @@ def __init__(self, name: str = None, **kwargs): self.symm_tol = kwargs.get('symm_tol', 0.1) self.angle_tol = kwargs.get('angle_tol', 0.5) self.dist_threshold = kwargs.get('dist_threshold', 0.8) + self.bond_threshold = kwargs.get('bond_threshold', 1.3) self.bb1_name = None self.bb2_name = None @@ -132,6 +133,7 @@ def __init__(self, name: str = None, **kwargs): self.atom_labels = [] self.cellMatrix = np.eye(3) self.cellParameters = np.array([1, 1, 1, 90, 90, 90]).astype(float) + self.bonds = [] self.lattice_sgs = None self.space_group = None @@ -368,9 +370,24 @@ def from_building_blocks(self, result = net_build_dict[net](bb1, bb2, stacking, **kwargs) + structure = Structure( + self.cellMatrix, + self.atom_types, + self.atom_pos, + coords_are_cartesian=True, + site_properties={'source': self.atom_labels} + ) + + self.bonds = get_bonds(structure, self.bond_threshold) + return result - def save(self, fmt: str = 'cif', supercell: list = [1, 1, 1], save_dir=None, primitive=False) -> None: + def save(self, + fmt: str = 'cif', + supercell: list = [1, 1, 1], + save_dir=None, + primitive=False, + save_bonds=True) -> None: ''' Save the structure in a specif file format. @@ -392,7 +409,6 @@ def save(self, fmt: str = 'cif', supercell: list = [1, 1, 1], save_dir=None, pri ''' save_dict = { - 'json': save_json, 'cjson': save_chemjson, 'cif': save_cif, 'xyz': save_xyz, @@ -422,6 +438,11 @@ def save(self, fmt: str = 'cif', supercell: list = [1, 1, 1], save_dir=None, pri final_structure = structure.make_supercell(supercell, in_place=False) + if save_bonds: + bonds = get_bonds(final_structure, self.bond_threshold) + else: + bonds = [] + structure_dict = final_structure.as_dict() cell = structure_dict['lattice']['matrix'] @@ -440,7 +461,8 @@ def save(self, fmt: str = 'cif', supercell: list = [1, 1, 1], save_dir=None, pri cell=cell, atom_types=atom_types, atom_labels=atom_labels, - atom_pos=atom_pos) + atom_pos=atom_pos, + bonds=bonds) # --------------- Net creation methods -------------------------- # diff --git a/src/pycofbuilder/io_tools.py b/src/pycofbuilder/io_tools.py index e0d79d2c..3cf4c8f0 100644 --- a/src/pycofbuilder/io_tools.py +++ b/src/pycofbuilder/io_tools.py @@ -237,28 +237,39 @@ def read_cif(path, file_name): return None -def save_xsf(path: str = None, - file_name: str = None, - cell: np.ndarray = np.eye(3), - atom_types: list = None, - atom_pos: list = None, - atom_labels: list = None, - frac_coords: bool = False): +def save_xsf(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + bond_orders: list = None, + frac_coords=False): """ Save a file in format `.xsf` on the `path`. Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.xsf` extention. + Name of the file. Does not neet to contain the extention. cell : numpy array Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom label. + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. """ file_name = file_name.split('.')[0] @@ -290,31 +301,39 @@ def save_xsf(path: str = None, xsf_file.close() -def save_pqr(path: str = None, - file_name: str = None, - cell: np.ndarray = np.eye(3), - atom_types: list = None, - atom_pos: list = None, - atom_labels: list = None, - partial_charges: list = None, - frac_coords: bool = False): +def save_pqr(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + bond_orders: list = None, + frac_coords=False): """ Save a file in format `.pqr` on the `path`. Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.pqr` extention. + Name of the file. Does not neet to contain the extention. cell : numpy array Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom partial charges. + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. - partial_charges: list + atom_charges : list List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. """ file_name = file_name.split('.')[0] @@ -337,7 +356,7 @@ def save_pqr(path: str = None, cell[4], cell[5])) - if partial_charges is None: + if atom_charges is None: atom_line = 'ATOM {:>4} {:>2} MOL A 0 {:>8.3f}{:>8.3f}{:>8.3f} {:>15}\n' for i in range(len(atom_pos)): pqr_file.write(atom_line.format(i + 1, @@ -354,34 +373,53 @@ def save_pqr(path: str = None, atom_pos[i][0], atom_pos[i][1], atom_pos[i][2], - partial_charges[i], + atom_charges[i], atom_types[i])) + if bonds and not bond_orders: + bond_orders = [1 for i in range(len(bonds))] + + if bonds: + for i in range(len(bonds)): + for j in range(bond_orders[i]): + pqr_file.write(f'CONECT {bonds[i][0] + 1:4} {bonds[i][1] + 1:4}\n') + pqr_file.close() -def save_pdb(path: str = None, - file_name: str = None, - cell: np.ndarray = np.eye(3), - atom_types: list = None, - atom_pos: list = None, - atom_labels: list = None, - frac_coords: bool = False): +def save_pdb(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + bond_orders: list = None, + frac_coords=False): """ Save a file in format `.pdb` on the `path`. Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.pdb` extention. + Name of the file. Does not neet to contain the extention. cell : numpy array Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom partial charges. + List of strings containing containg the N atom labels atom_pos : list - Nx3 array contaning the atoms coordinates in cartesian form. + Nx3 array contaning the atoms coordinates. + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. """ file_name = file_name.split('.')[0] @@ -414,37 +452,54 @@ def save_pdb(path: str = None, atom_pos[i][2], atom_types[i])) - pdb_file.close() + if bonds and not bond_orders: + bond_orders = [1 for i in range(len(bonds))] + + if bonds: + for i in range(len(bonds)): + for j in range(bond_orders[i]): + pdb_file.write(f'CONECT {bonds[i][0] + 1:4} {bonds[i][1] + 1:4}\n') + pdb_file.close() -def save_gjf(path: str = None, - file_name: str = None, - cell: list = None, - atom_types: list = None, - atom_pos: list = None, - atom_labels: list = None, - frac_coords: bool = False, - text: str = 'opt pm6'): +def save_gjf(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + bond_orders: list = None, + frac_coords=False, + header: str = 'opt pm6'): """ - Save a file in format `.gjf` on the `path`. + Save a file in format `.pqr` on the `path`. Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.gjf` extention. + Name of the file. Does not neet to contain the extention. cell : numpy array Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom partial charges. + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. - text : str + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. + header : str Parameters for Gaussian calculations. """ - if len(cell) == 6: cell = cellpar_to_cell(cell) @@ -457,7 +512,7 @@ def save_gjf(path: str = None, temp_file = open(os.path.join(path, file_name + '.gjf'), 'w') temp_file.write(f'%chk={file_name}.chk \n') - temp_file.write(f'# {text}\n') + temp_file.write(f'# {header}\n') temp_file.write('\n') temp_file.write(f'{file_name}\n') temp_file.write('\n') @@ -476,28 +531,39 @@ def save_gjf(path: str = None, temp_file.close() -def save_xyz(path: str = None, - file_name: str = None, - cell: np.ndarray = np.eye(3), - atom_types: list = None, - atom_pos: list = None, - atom_labels: list = None, - frac_coords: bool = False): +def save_xyz(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + bond_orders: list = None, + frac_coords=False): """ Save a file in format `.xyz` on the `path`. Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.xyz` extention. + Name of the file. Does not neet to contain the extention. + cell : numpy array + Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. atom_types : list List of strings containing containg the N atom types + atom_label : list + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. - cell : numpy array - Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. """ if len(cell) == 3: @@ -527,27 +593,39 @@ def save_xyz(path: str = None, temp_file.close() -def save_turbomole(path: str = None, - file_name: str = None, - cell: np.ndarray = np.eye(3), - atom_types: list = None, - atom_pos: list = None, - atom_labels: list = None, - frac_coords: bool = False): - """Save the structure in Turbomole .coord format +def save_turbomole(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + bond_orders: list = None, + frac_coords=False): + """ + Save the structure in Turbomole .coord format on the `path`. Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.coord` extention. + Name of the file. Does not neet to contain the extention. cell : numpy array Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom partial charges. + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. """ if cell.shape == (3, 3): @@ -575,29 +653,39 @@ def save_turbomole(path: str = None, temp_file.write('$end\n') -def save_vasp(path: str = None, - file_name: str = None, - cell: np.ndarray = np.eye(3), - atom_types: list = None, - atom_pos: list = None, - atom_labels: list = None, - frac_coords: bool = False): - """Save the structure in VASP .vasp format +def save_vasp(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + bond_orders: list = None, + frac_coords=False): + """ + Save the structure in VASP .vasp format on the `path`. Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.vasp` extention. + Name of the file. Does not neet to contain the extention. cell : numpy array Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom partial charges. + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. - coords_are_cartesian : bool - If True, the coordinates are in cartesian coordinates. + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. """ if cell.shape == 6: @@ -634,15 +722,19 @@ def save_vasp(path: str = None, atom_types[i])) -def save_qe(path: str = None, - file_name: str = None, - cell: np.ndarray = np.eye(3), - atom_types: list = None, - atom_pos: list = None, - atom_labels: list = None, - frac_coords: bool = False, - calc_type: str = 'scf'): - ''' +def save_qe(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + bond_orders: list = None, + frac_coords=False, + calc_type: str = 'scf', + kspacing: float = 0.3): + """ Save the structure in Quantum Espresso .pwscf format. The `input_dict` can be used to specify the input parameters for the @@ -657,20 +749,28 @@ def save_qe(path: str = None, Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.pwscf` extention. + Name of the file. Does not neet to contain the extention. cell : numpy array Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom labels. + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. frac_coords : bool If True, the coordinates are in fractional coordinates. calc_type : str - Type of pw.x calculation. Can be 'scf', 'nscf', 'bands', and 'vc-relax'. - ''' + Type of calculation. Can be 'scf', 'vc-relax', 'relax', 'md', 'vc-md', 'vc-tddft', 'tddft'. + kspacing : float + Kpoints spacing in 1/Angstrom. + """ if len(cell) == 6: cell_matrix = cellpar_to_cell(cell) @@ -721,7 +821,7 @@ def save_qe(path: str = None, # If the kpoints grid is not specified, calculate it automatically if 'k_points' not in input_dict.keys(): if 'kspacing' not in input_dict.keys(): - input_dict['kspacing'] = 0.3 + input_dict['kspacing'] = kspacing input_dict['kpoints'] = get_kgrid(cell_matrix, input_dict['kspacing']) with open(os.path.join(path, file_name + '.pwscf'), 'w') as f: @@ -809,68 +909,38 @@ def convert_cif_2_qe(out_path, file_name): k_dist=0.3) -def save_json(path, file_name, cell, atom_types, atom_pos, atom_labels, frac_coords=False): +def save_chemjson(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, + frac_coords=False): """ Save a file in format `.json` on the `path`. Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.cif` extention. - atom_label : list - List of strings containing containg the N atom partial charges. - atom_pos : list - Nx3 array contaning the atoms coordinates. + Name of the file. Does not neet to contain the extention. cell : numpy array Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. - """ - - file_name = file_name.split('.')[0] - - cof_json = create_COF_json(file_name) - - if len(cell) == 3: - cell_par = cell_to_cellpar(np.array(cell)).tolist() - cell_matrix = np.array(cell).astype(float).tolist() - - if len(cell) == 6: - cell_par = np.array(cell).astype(float).tolist() - cell_matrix = cellpar_to_cell(cell_par).tolist() - - cof_json['system']['geo_opt'] = False - - cof_json['geometry']['cell_matrix'] = cell_matrix - cof_json['geometry']['cell_parameters'] = cell_par - cof_json['geometry']['atom_labels'] = list(atom_types) - cof_json['geometry']['atom_pos'] = list(atom_pos) - - write_json(path, file_name, cof_json) - - -def save_chemjson(path, - file_name, - cell, - atom_types, - atom_pos, - atom_labels, - frac_coords=False): - """ - Save a file in format `.json` on the `path`. - - Parameters - ---------- - path : str - Path to the file. - file_name : str - Name of the file. Does not neet to contain the `.cif` extention. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom partial charges. + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. - cell : numpy array - Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. """ file_name = file_name.split('.')[0] @@ -887,18 +957,20 @@ def save_chemjson(path, AtomTypes=atom_types, AtomPositions=atom_pos, AtomLabels=atom_labels, - CartesianPositions=not frac_coords) + CartesianPositions=not frac_coords, + BondIndexes=bonds) write_json(path, file_name, chemJSON) -def save_cif(path, - file_name, - cell, - atom_types, - atom_pos, - atom_labels=None, - partial_charges=False, +def save_cif(path: str, + file_name: str, + cell: list, + atom_types: list, + atom_labels: list, + atom_pos: list, + atom_charges: list = None, + bonds: list = None, frac_coords=False): """ Save a file in format `.cif` on the `path`. @@ -906,15 +978,23 @@ def save_cif(path, Parameters ---------- path : str - Path to the file. + Path to the save the file. file_name : str - Name of the file. Does not neet to contain the `.cif` extention. + Name of the file. Does not neet to contain the extention. + cell : numpy array + Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_types : list + List of strings containing containg the N atom types atom_label : list - List of strings containing containg the N atom partial charges. + List of strings containing containg the N atom labels atom_pos : list Nx3 array contaning the atoms coordinates. - cell : numpy array - Can be a 3x3 array contaning the cell vectors or a list with the 6 cell parameters. + atom_charges : list + List of strings containing containg the N atom partial charges. + bonds : list + List of lists containing the index of the bonded atoms and the bond length. + frac_coords : bool + If True, the coordinates are in fractional coordinates. """ file_name = file_name.split('.')[0] @@ -932,7 +1012,7 @@ def save_cif(path, _audit_creation_date {date.today().strftime("%Y-%d-%m")} _audit_creation_method pyCOFBuilder -_audit_author_name 'Felipe Lopes de Oliveira' +_audit_author_name '{os.getlogin()}' _chemical_name_common '{file_name}' _cell_length_a {a:>10.6f} @@ -956,7 +1036,7 @@ def save_cif(path, _atom_site_fract_z """ - if partial_charges is not False: + if atom_charges: cif_text += ' _atom_site_charge\n' if frac_coords is False: @@ -966,22 +1046,33 @@ def save_cif(path, for i in range(len(atom_pos)): u, v, w = atom_pos[i][0], atom_pos[i][1], atom_pos[i][2] - if partial_charges is not False: - cif_text += '{:<7} {} {:>15.9f} {:>15.9f} {:>15.9f} {:>10.5f}\n'.format( + if atom_charges: + atom_labels[i] = f"{atom_types[i]}{str(i + 1)}_{atom_labels[i]}" + cif_text += '{:<15} {} {:>15.9f} {:>15.9f} {:>15.9f} {:>10.5f}\n'.format( f"{atom_types[i]}{str(i + 1)}_{atom_labels[i]}", atom_types[i], u, v, w, - partial_charges[i]) + atom_charges[i]) else: - cif_text += '{:<7} {} {:>15.9f} {:>15.9f} {:>15.9f}\n'.format( + atom_labels[i] = f"{atom_types[i]}{str(i + 1)}_{atom_labels[i]}" + cif_text += '{:<15} {} {:>15.9f} {:>15.9f} {:>15.9f}\n'.format( f"{atom_types[i]}{str(i + 1)}_{atom_labels[i]}", atom_types[i], u, v, w) + if bonds: + cif_text += '\nloop_\n' + cif_text += '_geom_bond_atom_site_label_1\n' + cif_text += '_geom_bond_atom_site_label_2\n' + cif_text += '_geom_bond_distance\n' + + for bond in bonds: + cif_text += f'{atom_labels[bond[0]]:10} {atom_labels[bond[1]]:10} {bond[2]:.5f}\n' + # Write cif_text to file cif_file = open(os.path.join(path, file_name + '.cif'), 'w') cif_file.write(cif_text) @@ -1189,10 +1280,10 @@ def create_structure_CJSON(StructureName: str, CellParameters: list = None, CellMatrix: list = None, AtomTypes: list = None, + AtomLabels: list = [], AtomPositions: list = None, CartesianPositions: bool = False, - AtomLabels: list = [], - Bonds: list = [], + BondIndexes: list = [], BondOrders: list = [], PartialCharges: dict = {}, ) -> dict: @@ -1208,11 +1299,20 @@ def create_structure_CJSON(StructureName: str, List with the cell parameters. CellMatrix : list List with the cell matrix. Optional + AtomTypes : list + List with the atom types. AtomLabels : list List with the atom labels. AtomPositions : list List with the atom positions. - + CartesianPositions : bool + If True, the coordinates are in cartesian coordinates. + BondIndexes : list + List with the bonds indexes and bond length. + BondOrders : list + List with the bond orders. + PartialCharges : dict + Dictionary with the partial charges. """ chemJSON = create_empty_CJSON() @@ -1252,10 +1352,16 @@ def create_structure_CJSON(StructureName: str, CartPosition = np.array([np.dot(V_cart, atom) for atom in AtomPositions]).flatten().tolist() chemJSON['atoms']['coords']['3d'] = CartPosition - chemJSON['atoms']['PartialCharges'] = PartialCharges + if PartialCharges != {}: + chemJSON['atoms']['PartialCharges'] = PartialCharges + + bond_indexes = [[i[0], i[1]] for i in BondIndexes] + bond_indexes = [item for row in bond_indexes for item in row] + + bond_orders = BondOrders if BondOrders != [] else [1] * len(bond_indexes) - chemJSON['bonds']['connections']['index'] = Bonds - chemJSON['bonds']['order'] = BondOrders + chemJSON['bonds']['connections']['index'] = bond_indexes + chemJSON['bonds']['order'] = bond_orders return chemJSON diff --git a/src/pycofbuilder/tools.py b/src/pycofbuilder/tools.py index 14f74f17..95563552 100644 --- a/src/pycofbuilder/tools.py +++ b/src/pycofbuilder/tools.py @@ -1004,3 +1004,65 @@ def cell_to_ibrav(cell): 'celldm(6)': np.cos(np.deg2rad(gamma))} return celldm + + +def is_bonded(atom1: str, atom2: str, dist: float, cutoff: float = 1.3): + """ + Determine if two atoms are bonded based on the distance between them. + Two atoms are considered bonded if the distance between them is less than + the sum of their covalent radii multiplied by a cutoff factor. + + Parameters + ---------- + atom1 : str + Label of the first atom + atom2 : str + Label of the second atom + dist : float + Distance between the two atoms + cutoff : float + Cutoff factor for the covalent radii. Default: 1.3 + """ + + periodic_table = elements_dict(property='covalent_radius') + + # Get the covalent radii of the two atoms + cr_1 = periodic_table[atom1] + cr_2 = periodic_table[atom2] + + # Calculate max bond distance + max_bond_distance = (cr_1 + cr_2) * cutoff + + if dist < 0.6: + print('Distance between atoms is less than 0.6 Å. Check if the structure is correct.') + + # Check if the distance is less than the cutoff + if 0.6 < dist <= max_bond_distance: + return True + else: + return False + + +def get_bonds(structure, cutoff=1.3): + """ + Get the bonded atoms in a structure based on the distance between them. + + Parameters + ---------- + structure : pymatgen.Structure + Structure object of pymatgen + cutoff : float + Cutoff factor for the covalent radii. Default: 1.3 Å + """ + + atom_types = [i.as_dict()['species'][0]['element'] for i in structure.sites] + + # Get bonded atoms + center_indices, points_indies, _, bond_distances = structure.get_neighbor_list(5) + bonded_atoms = np.array([center_indices, points_indies, bond_distances]).T + + bonded_atoms = [i for i in bonded_atoms if is_bonded(atom_types[int(i[0])], atom_types[int(i[1])], i[2], cutoff)] + + bonded_atoms = [[int(i[0]), int(i[1]), i[2]] for i in bonded_atoms] + + return bonded_atoms