# -*- coding: utf-8 -*-
# Created by Felipe Lopes de Oliveira
# Distributed under the terms of the MIT License.
"""
This module contains tools for input and output file manipulation used by pyCOFBuilder.
"""
import os
from datetime import date
import numpy as np
from pymatgen.io.cif import CifParser
import simplejson
from pycofbuilder.tools import (elements_dict,
cell_to_cellpar,
cellpar_to_cell,
get_fractional_to_cartesian_matrix,
get_cartesian_to_fractional_matrix,
get_kgrid,
formula_from_atom_list,
smiles_to_xsmiles,
cell_to_ibrav)
[docs]
def save_csv(path, file_name, data, delimiter=',', head=False):
"""
Saves a file in format `.csv`.
Parameters
----------
path : str
Path to the file.
file_name : str
Name of the `csv` file. Does not neet to contain the `.csv` extention.
data : list
Data to be saved.
delimiter: str
Delimiter of the columns. `,` is the default.
head : str
Names of the columns.
"""
# Remove the extention if exists
file_name = file_name.split('.')[0]
file_name = os.path.join(path, file_name + '.csv')
file_temp = open(file_name, 'w')
if head is not False:
file_temp.write(head)
for i in range(len(data)):
file_temp.write(delimiter.join([str(j) for j in data[i]]) + '\n')
file_temp.close()
[docs]
def read_xyz(path, file_name):
"""
Reads a file in format `.xyz` from the `path` given and returns
a list containg the N atom labels and a Nx3 array contaning
the atoms coordinates.
Parameters
----------
path : str
Path to the file.
file_name : str
Name of the `xyz` file. Does not neet to contain the `.xyz` extention.
Returns
-------
atom_labels : list
List of strings containing containg the N atom labels.
atom_pos : numpy array
Nx3 array contaning the atoms coordinates
"""
# Remove the extention if exists
file_name = file_name.split('.')[0]
if os.path.exists(os.path.join(path, file_name + '.xyz')):
temp_file = open(os.path.join(path, file_name + '.xyz'), 'r').readlines()
atoms = [i.split() for i in temp_file[2:]]
atom_labels = [i[0] for i in atoms if len(i) > 1]
atom_pos = np.array([[float(i[1]), float(i[2]), float(i[3])] for i in atoms if len(i) > 1])
return atom_labels, atom_pos
else:
print(f'File {file_name} not found!')
return None
[docs]
def read_pdb(path, file_name):
"""
Reads a file in format `.pdb` from the `path` given and returns
a list containg the N atom labels and a Nx3 array contaning
the atoms coordinates.
Parameters
----------
path : str
Path to the file.
file_name : str
Name of the `pdb` file. Does not neet to contain the `.pdb` extention.
Returns
-------
atom_labels : list
List of strings containing containg the N atom labels.
atom_pos : numpy array
Nx3 array contaning the atoms coordinates
"""
# Remove the extention if exists
file_name = file_name.split('.')[0]
if not os.path.exists(os.path.join(path, file_name + '.pdb')):
raise FileNotFoundError(f'File {file_name} not found!')
temp_file = open(os.path.join(path, file_name + '.pdb'), 'r').read().splitlines()
cellParameters = np.array([i.split()[1:] for i in temp_file if 'CRYST1' in i][0]).astype(float)
AtomTypes = [i.split()[2] for i in temp_file if 'ATOM' in i]
CartPos = np.array([i.split()[4:7] for i in temp_file if 'ATOM' in i]).astype(float)
return cellParameters, AtomTypes, CartPos
[docs]
def read_gjf(path, file_name):
"""
Reads a file in format `.gjf` from the `path` given and returns
a list containg the N atom labels and a Nx3 array contaning
the atoms coordinates.
Parameters
----------
path : str
Path to the file.
file_name : str
Name of the `gjf` file. Does not neet to contain the `.gjf` extention.
Returns
-------
atom_labels : list
List of strings containing containg the N atom labels.
atom_pos : numpy array
Nx3 array contaning the atoms coordinates
"""
# Remove the extention if exists
file_name = file_name.split('.')[0]
if os.path.exists(os.path.join(path, file_name + '.gjf')):
temp_file = open(os.path.join(path, file_name + '.gjf'), 'r').readlines()
temp_file = [i.split() for i in temp_file if i != '\n']
atoms = [i for i in temp_file if i[0] in elements_dict()]
atom_labels = [i[0] for i in atoms]
atom_pos = np.array([[float(i[1]), float(i[2]), float(i[3])] for i in atoms])
return atom_labels, atom_pos
else:
print(f'File {file_name} not found!')
return None
[docs]
def read_cif(path, file_name):
"""
Reads a file in format `.cif` from the `path` given and returns
a list containg the N atom labels and a Nx3 array contaning
the atoms coordinates.
Parameters
----------
path : str
Path to the file.
file_name : str
Name of the `cif` file. Does not neet to contain the `.cif` extention.
Returns
-------
cell : numpy array
3x3 array contaning the cell vectors.
atom_labels : list
List of strings containing containg the N atom labels.
atom_pos : numpy array
Nx3 array contaning the atoms coordinates
charges : list
List of strings containing containg the N atom partial charges.
"""
# Remove the extention if exists
file_name = file_name.split('.')[0]
if os.path.exists(os.path.join(path, file_name + '.cif')):
temp_file = open(os.path.join(path, file_name + '.cif'), 'r').readlines()
cell = []
atom_label = []
atom_pos = []
charges = []
has_charges = False
for i in temp_file:
if 'cell_length_a' in i:
cell += [float(i.split()[-1])]
if 'cell_length_b' in i:
cell += [float(i.split()[-1])]
if 'cell_length_c' in i:
cell += [float(i.split()[-1])]
if 'cell_angle_alpha' in i:
cell += [float(i.split()[-1])]
if '_cell_angle_beta' in i:
cell += [float(i.split()[-1])]
if '_cell_angle_gamma' in i:
cell += [float(i.split()[-1])]
if '_atom_site_charge' in i:
has_charges = True
for i in temp_file:
line = i.split()
if len(line) > 1 and line[0] in elements_dict().keys():
atom_label += [line[0]]
atom_pos += [[float(j) for j in line[2:5]]]
if has_charges:
charges += [float(line[-1])]
cell = cellpar_to_cell(cell)
return cell, atom_label, atom_pos, charges
else:
print(f'File {file_name} not found!')
return None
[docs]
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 save the file.
file_name : str
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.
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]
if len(cell) == 6:
cell = cellpar_to_cell(cell)
if frac_coords:
# Convert to fractional coordinates
frac_matrix = get_fractional_to_cartesian_matrix(*cell_to_cellpar(cell))
atom_pos = [np.dot(frac_matrix, [i[0], i[1], i[2]]) for i in atom_pos]
xsf_file = open(os.path.join(path, file_name + '.xsf'), 'w')
xsf_file.write(' CRYSTAL\n')
xsf_file.write(' PRIMVEC\n')
for i in range(len(cell)):
xsf_file.write(f' {cell[i][0]:>15.9f} {cell[i][1]:>15.9f} {cell[i][2]:>15.9f}\n')
xsf_file.write(' PRIMCOORD\n')
xsf_file.write(f' {len(atom_pos)} 1\n')
for i in range(len(atom_pos)):
xsf_file.write('{:3s} {:>15.9f} {:>15.9f} {:>15.9f}\n'.format(atom_types[i],
atom_pos[i][0],
atom_pos[i][1],
atom_pos[i][2]))
xsf_file.close()
[docs]
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 save the file.
file_name : str
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.
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]
if len(cell) == 3:
cell = cell_to_cellpar(cell)
if frac_coords:
# Convert to fractional coordinates
frac_matrix = get_fractional_to_cartesian_matrix(*cell)
atom_pos = [np.dot(frac_matrix, [i[0], i[1], i[2]]) for i in atom_pos]
pqr_file = open(os.path.join(path, file_name + '.pqr'), 'w')
pqr_file.write(f'TITLE {file_name} \n')
pqr_file.write('REMARK 4\n')
pqr_file.write('CRYST1{:>9.3f}{:>9.3f}{:>9.3f}{:>7.2f}{:>7.2f}{:>7.2f} P1\n'.format(cell[0],
cell[1],
cell[2],
cell[3],
cell[4],
cell[5]))
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,
atom_types[i],
atom_pos[i][0],
atom_pos[i][1],
atom_pos[i][2],
atom_types[i]))
else:
atom_line = 'ATOM {:>4} {:>2} MOL A 0 {:>8.3f}{:>8.3f}{:>8.3f}{:>8.5f} {:>15}\n'
for i in range(len(atom_pos)):
pqr_file.write(atom_line.format(i + 1,
atom_types[i],
atom_pos[i][0],
atom_pos[i][1],
atom_pos[i][2],
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()
[docs]
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 save the file.
file_name : str
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.
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]
if len(cell) == 3:
cell = cell_to_cellpar(cell)
if frac_coords:
# Convert to fractional coordinates
frac_matrix = get_fractional_to_cartesian_matrix(*cell)
atom_pos = [np.dot(frac_matrix, [i[0], i[1], i[2]]) for i in atom_pos]
pdb_file = open(os.path.join(path, file_name + '.pdb'), 'w')
pdb_file.write(f'TITLE {file_name} \n')
pdb_file.write('REMARK pyCOFBuilder\n')
pdb_file.write('CRYST1{:>9.3f}{:>9.3f}{:>9.3f}{:>7.2f}{:>7.2f}{:>7.2f} P1\n'.format(cell[0],
cell[1],
cell[2],
cell[3],
cell[4],
cell[5]))
atom_line = 'ATOM {:>4} {:>2} MOL {:>13.3f}{:>8.3f}{:>8.3f} 1.00 0.00 {:>11}\n'
for i in range(len(atom_pos)):
pdb_file.write(atom_line.format(i+1,
atom_types[i],
atom_pos[i][0],
atom_pos[i][1],
atom_pos[i][2],
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]):
pdb_file.write(f'CONECT {bonds[i][0] + 1:4} {bonds[i][1] + 1:4}\n')
pdb_file.close()
[docs]
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 `.pqr` on the `path`.
Parameters
----------
path : str
Path to the save the file.
file_name : str
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.
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)
if frac_coords:
# Convert to fractional coordinates
frac_matrix = get_fractional_to_cartesian_matrix(*cell_to_cellpar(cell))
atom_pos = [np.dot(frac_matrix, [i[0], i[1], i[2]]) for i in atom_pos]
file_name = file_name.split('.')[0]
temp_file = open(os.path.join(path, file_name + '.gjf'), 'w')
temp_file.write(f'%chk={file_name}.chk \n')
temp_file.write(f'# {header}\n')
temp_file.write('\n')
temp_file.write(f'{file_name}\n')
temp_file.write('\n')
temp_file.write('0 1 \n')
for i in range(len(atom_types)):
temp_file.write('{:<5s}{:>15.7f}{:>15.7f}{:>15.7f}\n'.format(atom_types[i],
atom_pos[i][0],
atom_pos[i][1],
atom_pos[i][2]))
if cell is not None:
for i in range(len(cell)):
temp_file.write('Tv {:>15.7f}{:>15.7f}{:>15.7f}\n'.format(*cell[i]))
temp_file.write('\n\n')
temp_file.close()
[docs]
def save_xyz(path: str,
file_name: str,
atom_types: list,
atom_pos: list,
atom_labels: list = None,
cell: list = None,
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 save the file.
file_name : str
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.
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:
cell = cell_to_cellpar(cell) if len(cell) == 3 else cell
if frac_coords:
# Convert to fractional coordinates
frac_matrix = get_fractional_to_cartesian_matrix(cell)
atom_pos = [np.dot(frac_matrix, [i[0], i[1], i[2]]) for i in atom_pos]
file_name = file_name.split('.')[0]
temp_file = open(os.path.join(path, file_name + '.xyz'), 'w')
temp_file.write(f'{len(atom_types)}\n')
if cell is None:
temp_file.write(f'{file_name}\n')
else:
temp_file.write(f'{cell[0]} {cell[1]} {cell[2]} {cell[3]} {cell[4]} {cell[5]}\n')
for i in range(len(atom_types)):
temp_file.write('{:<5s}{:>15.7f}{:>15.7f}{:>15.7f}\n'.format(atom_types[i],
atom_pos[i][0],
atom_pos[i][1],
atom_pos[i][2]))
temp_file.close()
[docs]
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 save the file.
file_name : str
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.
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 np.array(cell).shape == (3, 3):
cell = cell_to_cellpar(cell)
if frac_coords:
# Convert to fractional coordinates
frac_matrix = get_fractional_to_cartesian_matrix(*cell)
atom_pos = [np.dot(frac_matrix, [i[0], i[1], i[2]]) for i in atom_pos]
with open(os.path.join(path, file_name + '.coord'), 'w') as temp_file:
temp_file.write('$coord angs\n')
for i in range(len(atom_types)):
temp_file.write('{:>15.7f}{:>15.7f}{:>15.7f} {:<5s}\n'.format(atom_pos[i][0],
atom_pos[i][1],
atom_pos[i][2],
atom_types[i]))
temp_file.write('$periodic 3\n')
temp_file.write('$cell\n')
temp_file.write('{} {} {} {} {} {}\n'.format(*cell))
temp_file.write('$opt\n')
temp_file.write(' engine=inertial\n')
temp_file.write('$end\n')
[docs]
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 save the file.
file_name : str
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.
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 np.array(cell).shape == 6:
cell = cellpar_to_cell(cell)
unique_atoms = []
for i in atom_types:
if i not in unique_atoms:
unique_atoms.append(i)
composition_dict = {i: atom_types.count(i) for i in unique_atoms}
with open(os.path.join(path, file_name + '.vasp'), 'w') as temp_file:
temp_file.write(f'{file_name}\n')
temp_file.write('1.0\n')
for i in range(3):
temp_file.write('{:>15.7f}{:>15.7f}{:>15.7f}\n'.format(cell[i][0],
cell[i][1],
cell[i][2]))
temp_file.write(' '.join(composition_dict.keys()) + '\n')
temp_file.write(' '.join([str(i) for i in composition_dict.values()]) + '\n')
if frac_coords:
temp_file.write('Direct\n')
else:
temp_file.write('Cartesian\n')
for i in range(len(atom_types)):
temp_file.write('{:>15.7f}{:>15.7f}{:>15.7f} {:<5s}\n'.format(atom_pos[i][0],
atom_pos[i][1],
atom_pos[i][2],
atom_types[i]))
[docs]
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
QuantumESPRESSO calculation.
This dictionary must contain the keys: `control`, `system`, `electrons`, and `ions`.
Each of these keys must contain a dictionary with the corresponding input parameters.
This dictionary can contain the kpoints item, with the kpoints grid as a list of 3 integers.
Additionally, it can contain the kspacing item, with the kpoints spacing as a float. In this
case the kpoints grid will be calculated automatically. By default, the kspacing is set to 0.3.
Parameters
----------
path : str
Path to the save the file.
file_name : str
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.
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 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)
else:
cell_matrix = cell
ibrav_dict = cell_to_ibrav(cell_matrix)
input_dict = {}
input_dict['control'] = {
'prefix': f"'{file_name}'",
'calculation': f"{calc_type}",
'restart_mode': "'from_scratch'",
'wf_collect': '.true.',
'pseudo_dir': "'$PSEUDO_DIR'",
'outdir': "'$SCRATCH_DIR'",
'verbosity': "'high'",
'tstress': '.true.',
'tprnfor': '.true.',
'etot_conv_thr': '1.0d-5',
'forc_conv_thr': '1.0d-6',
'nstep': 1000}
input_dict['system'] = {
'nat': len(atom_types),
'ntyp': len(set(atom_types)),
'ecutwfc': 40,
'ecutrho': 360,
'vdw_corr': "'grimme-d3'",
'occupations': "'smearing'",
**ibrav_dict}
input_dict['electrons'] = {
'conv_thr': 1.0e-9,
'electron_maxstep': 100,
'mixing_beta': 0.3}
if calc_type == 'vc-relax':
input_dict['ions'] = {
'ion_dynamics': "'bfgs'"}
input_dict['cell'] = {
'cell_dynamics': "'bfgs'",
'cell_dofree': "'all'"}
# 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'] = kspacing
input_dict['kpoints'] = get_kgrid(cell_matrix, input_dict['kspacing'])
with open(os.path.join(path, file_name + '.pwscf'), 'w') as f:
f.write('&CONTROL\n')
for key in input_dict['control']:
f.write(f" {key} = {input_dict['control'][key]}\n")
f.write('/\n\n')
f.write('&SYSTEM\n')
for key in input_dict['system']:
f.write(f" {key} = {input_dict['system'][key]}\n")
f.write('/\n\n')
f.write('&ELECTRONS\n')
for key in input_dict['electrons']:
f.write(f" {key} = {input_dict['electrons'][key]}\n")
f.write('/\n\n')
if calc_type == 'vc-relax':
f.write('&IONS\n')
for key in input_dict['ions']:
f.write(f" {key} = {input_dict['ions'][key]}\n")
f.write('/\n\n')
f.write('&CELL\n')
for key in input_dict['cell']:
f.write(f" {key} = {input_dict['cell'][key]}\n")
f.write('/\n\n')
f.write('ATOMIC_SPECIES\n')
for atom in set(atom_types):
f.write(f" {atom} {elements_dict()[atom]:>9.5f} {atom}.PSEUDO.UPF\n")
f.write('\n')
# f.write('CELL_PARAMETERS (angstrom) \n')
# for v in cell_matrix:
# f.write(f'{v[0]:>15.9f} {v[1]:>15.9f} {v[2]:>15.9f}\n')
# f.write('\n')
if frac_coords:
coords_type = 'crystal'
else:
coords_type = 'angstrom'
f.write(f'ATOMIC_POSITIONS ({coords_type})\n')
for i, atom in enumerate(atom_pos):
f.write('{:<5s}{:>15.9f}{:>15.9f}{:>15.9f} ! {:5}\n'.format(atom_types[i],
atom[0],
atom[1],
atom[2],
atom_labels[i]))
f.write('\n')
f.write('K_POINTS automatic\n')
f.write(' {} {} {} 1 1 1\n'.format(*input_dict['kpoints']))
[docs]
def convert_cif_2_qe(out_path, file_name):
"""
Convert a cif file to a Quantum Espresso input file
Parameters
----------
out_path : str
Path to the file.
file_name : str
Name of the file. Does not neet to contain the `.cif` extention.
"""
cell, atom_labels, atom_pos, _ = read_cif(out_path, file_name, has_charges=False)
print(cell, atom_labels, atom_pos)
save_qe(out_path,
file_name,
cell,
atom_labels,
atom_pos,
coords_are_cartesian=True,
supercell=False,
angs=False,
ecut=40,
erho=360,
k_dist=0.3)
[docs]
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 save the file.
file_name : str
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.
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]
if len(cell) == 6:
CellParameters = cell
CellMatrix = None
if len(cell) == 3:
CellParameters = None
CellMatrix = cell
chemJSON = create_structure_CJSON(StructureName=file_name.split('.')[0],
CellParameters=CellParameters,
CellMatrix=CellMatrix,
AtomTypes=atom_types,
AtomPositions=atom_pos,
AtomLabels=atom_labels,
CartesianPositions=not frac_coords,
BondIndexes=bonds)
write_json(path, file_name, chemJSON)
[docs]
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`.
Parameters
----------
path : str
Path to the save the file.
file_name : str
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.
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]
if len(cell) == 3:
a, b, c, alpha, beta, gamma = cell_to_cellpar(cell)
if len(cell) == 6:
a, b, c, alpha, beta, gamma = cell
if atom_labels is None:
atom_labels = [''] * len(atom_types)
cif_text = f"""\
data_{file_name}
_audit_creation_date {date.today().strftime("%Y-%d-%m")}
_audit_creation_method pyCOFBuilder
_audit_author_name '{os.getlogin()}'
_chemical_name_common '{file_name}'
_cell_length_a {a:>10.6f}
_cell_length_b {b:>10.6f}
_cell_length_c {c:>10.6f}
_cell_angle_alpha {alpha:>6.2f}
_cell_angle_beta {beta:>6.2f}
_cell_angle_gamma {gamma:>6.2f}
_space_group_name_H-M_alt 'P 1'
_space_group_IT_number 1
loop_
_symmetry_equiv_pos_as_xyz
'x, y, z'
loop_
_atom_site_label
_atom_site_type_symbol
_atom_site_fract_x
_atom_site_fract_y
_atom_site_fract_z
"""
if atom_charges:
cif_text += ' _atom_site_charge\n'
if frac_coords is False:
# Convert to fractional coordinates
frac_matrix = get_cartesian_to_fractional_matrix(a, b, c, alpha, beta, gamma)
atom_pos = [np.dot(frac_matrix, [i[0], i[1], i[2]]) for i in atom_pos]
for i in range(len(atom_pos)):
u, v, w = atom_pos[i][0], atom_pos[i][1], atom_pos[i][2]
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,
atom_charges[i])
else:
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)
cif_file.close()
[docs]
def convert_json_2_cif(origin_path, file_name, destiny_path, charge_type='None'):
"""
Convert a file in format `.json` to `.cif`.
Parameters
----------
origin_path : str
Path to the '.json' file.
file_name : str
Name of the file. Does not neet to contain the `.json` extention.
destiny_path : str
path where the `.cif` file will be saved.
"""
framework_JSON = read_json(origin_path, file_name)
cell = framework_JSON['geometry']['cell_matrix']
atom_labels = framework_JSON['geometry']['atom_labels']
atom_pos = framework_JSON['geometry']['atom_pos']
if charge_type + '_charges' in list(framework_JSON['system'].keys()):
partial_charges = framework_JSON['geometry'][charge_type + '_charges']
else:
partial_charges = False
save_cif(destiny_path,
file_name,
cell,
atom_labels,
atom_pos,
partial_charges,
frac_coords=False)
[docs]
def convert_gjf_2_xyz(path, file_name):
file_name = file_name.split('.')[0]
atom_labels, atom_pos = read_gjf(path, file_name + '.gjf')
save_xyz(path, file_name + '.xyz', atom_labels, atom_pos)
[docs]
def convert_xyz_2_gjf(path, file_name):
file_name = file_name.split('.')[0]
atom_labels, atom_pos = read_xyz(path, file_name + '.xyz')
save_gjf(path=path,
file_name=file_name + '.gjf',
atom_types=atom_labels,
atom_pos=atom_pos,
cell=[10, 10, 10, 90, 90, 90])
[docs]
def convert_cif_2_xyz(path, file_name, supercell=[1, 1, 1]):
file_name = file_name.split('.')[0]
structure = CifParser(os.path.join(path, file_name + '.cif')).get_structures(primitive=True)[0]
structure.make_supercell([[supercell[0], 0, 0], [0, supercell[1], 0], [0, 0, supercell[2]]])
dict_sctructure = structure.as_dict()
a, b, c = dict_sctructure['lattice']['a']
b = dict_sctructure['lattice']['b']
c = dict_sctructure['lattice']['c']
alpha = round(dict_sctructure['lattice']['alpha'])
beta = round(dict_sctructure['lattice']['beta'])
gamma = round(dict_sctructure['lattice']['gamma'])
atom_labels = [i['label'] for i in dict_sctructure['sites']]
atom_pos = [i['xyz'] for i in dict_sctructure['sites']]
temp_file = open(os.path.join(path, file_name + '.xyz'), 'w')
temp_file.write(f'{len(atom_labels)} \n')
temp_file.write(f'{a} {b} {c} {alpha} {beta} {gamma}\n')
for i in range(len(atom_labels)):
temp_file.write('{:<5s}{:>15.7f}{:>15.7f}{:>15.7f}\n'.format(atom_labels[i],
atom_pos[i][0],
atom_pos[i][1],
atom_pos[i][2]))
temp_file.close()
[docs]
def write_json(path, name, COF_json):
name = name.split('.')[0]
if os.path.exists(path) is not True:
os.mkdir(path)
save_path = os.path.join(path, name + '.cjson')
with open(save_path, 'w', encoding='utf-8') as f:
simplejson.dump(COF_json,
f,
ensure_ascii=False,
separators=(',', ':'),
indent=2,
ignore_nan=True)
[docs]
def read_json(path, name):
cof_path = os.path.join(path, name + '.json')
with open(cof_path, 'r') as r:
json_object = simplejson.loads(r.read())
return json_object
[docs]
def create_COF_json(name) -> dict:
"""
Create a empty dictionary with the COF information.
"""
system_info = 'Informations about the system.'
geometry_info = 'Informations about the geometry.'
optimization_info = 'Information about the optimization process.'
adsorption_info = 'Information about the adsorption simulation experiments on RASPA2'
textural_info = 'Information about the textural properties'
spectrum_info = 'Information about spectra simulation.'
experimental_info = 'Experimental data DRX, FTIR, ssNMR, UV-VIS...'
COF_json = {'system': {'description': system_info,
'name': name,
'geo_opt': True,
'execution_times_seconds': {}},
'geometry': {'description': geometry_info},
'optimization': {'description': optimization_info},
'adsorption': {'description': adsorption_info},
'textural': {'description': textural_info},
'spectrum': {'description': spectrum_info},
'experimental': {'description': experimental_info}
}
return COF_json
[docs]
def create_empty_CJSON() -> dict:
"""
Create a dictionary with the structure information to be saved using the
chemical JSON format.
"""
chemJSON = {
"chemicalJson": 1,
"name": "",
"formula": "",
"unitCell": {
"a": 0.0,
"b": 0.0,
"c": 0.0,
"alpha": 0.0,
"beta": 0.0,
"gamma": 0.0,
"cellVectors": []
},
"atoms": {
"elements": {
"number": [],
"type": []
},
"coords": {
"3d": [],
"3dFractional": []
},
"formalCharges": [],
"labels": []
},
"bonds": {
"connections": {
"index": []
},
"order": []
},
"PartialCharges": {},
"properties": {
"molecularMass": 0,
"totalCharge": 0,
"spinMultiplicity": 1,
"totalEnergy": 0,
"bandGap": 0,
},
"spectra": {},
"vibrations": {},
"metadata": {},
}
return chemJSON
[docs]
def create_structure_CJSON(StructureName: str,
CellParameters: list = None,
CellMatrix: list = None,
AtomTypes: list = None,
AtomLabels: list = [],
AtomPositions: list = None,
CartesianPositions: bool = False,
BondIndexes: list = [],
BondOrders: list = [],
PartialCharges: dict = {},
) -> dict:
"""
Creates a dictionary with the structure information to be saved using the
chemical JSON format.
Parameters
----------
StructureName : str
Name of the structure.
CellParameters : list
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()
chemJSON['name'] = StructureName
chemJSON['formula'] = formula_from_atom_list(AtomTypes)
if CellParameters is not None:
CellMatrix = cellpar_to_cell(CellParameters)
else:
CellParameters = cell_to_cellpar(CellMatrix)
CellMatrix = np.array(CellMatrix)
chemJSON['unitCell']['a'] = CellParameters[0]
chemJSON['unitCell']['b'] = CellParameters[1]
chemJSON['unitCell']['c'] = CellParameters[2]
chemJSON['unitCell']['alpha'] = CellParameters[3]
chemJSON['unitCell']['beta'] = CellParameters[4]
chemJSON['unitCell']['gamma'] = CellParameters[5]
chemJSON['unitCell']['cellVectors'] = CellMatrix.flatten().tolist()
AtomNumbers = [elements_dict(property="atomic_number")[i] for i in AtomTypes]
chemJSON['atoms']['elements']['number'] = AtomNumbers
chemJSON['atoms']['elements']['type'] = AtomTypes
chemJSON['atoms']['elements']['labels'] = AtomLabels
if CartesianPositions:
chemJSON['atoms']['coords']['3d'] = np.array(AtomPositions).flatten().tolist()
V_frac = get_cartesian_to_fractional_matrix(*CellParameters)
FracPosition = np.array([np.dot(V_frac, atom) for atom in AtomPositions]).flatten().tolist()
chemJSON['atoms']['coords']['3dFractional'] = FracPosition
else:
chemJSON['atoms']['coords']['3dFractional'] = np.array(AtomPositions).flatten().tolist()
V_cart = get_fractional_to_cartesian_matrix(*CellParameters)
CartPosition = np.array([np.dot(V_cart, atom) for atom in AtomPositions]).flatten().tolist()
chemJSON['atoms']['coords']['3d'] = CartPosition
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'] = bond_indexes
chemJSON['bonds']['order'] = bond_orders
return chemJSON
[docs]
def generate_mol_dict(path, file_name, name, code, smiles):
xsmiles, xsmiles_label, composition = smiles_to_xsmiles(smiles)
if file_name.endswith('gjf'):
atom_types, atom_pos = read_gjf(path, file_name)
elif file_name.endswith('xyz'):
atom_types, atom_pos = read_xyz(path, file_name)
mol_dict = {
"name": name,
"smiles": smiles,
"code": code,
"xsmiles": xsmiles,
"xsmiles_label": xsmiles_label,
"formula": composition,
"atoms": {
"elements": {"elementType": atom_types},
"coords": {"3d": atom_pos.tolist()}
}
}
print(mol_dict)
write_json(path, file_name.split('.')[0], mol_dict)