miniconda, RDkit,vina, tkdm, OpenBabel, Autodock-vina y estereoisómeros R/S.

These four commands download the latest Linux installer (for your chosen architecture), rename it to a shorter file name, perform a silent install, and then delete the installer:

mkdir -p ~/miniconda3
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/miniconda3/miniconda.sh
bash ~/miniconda3/miniconda.sh -b -u -p ~/miniconda3
rm ~/miniconda3/miniconda.sh

After installing, close and reopen your terminal application or refresh it by running the following command:

source ~/miniconda3/bin/activate

To initialize conda on all available shells, run the following command:

conda init --all

Desde linea de comandos de un cluster Linux, en el que no soy administrador, he instalado miniconda y con "source ~/miniconda3/bin/activate" consigo que aparezca (base), pero al salir de la sesion y entrar de nuevo en ella tengo que repetir esta sentencia, ¿cómo consigo que siempre me aparezca (base) sin usar source "source ~/miniconda3/bin/activate" cada vez que abro mi sesion por SSH?

Edita el archivo de configuración del shell y añade la al final del archivo la línea inferior siguiente:

nano ~/.bashrc
source ~/miniconda3/bin/activate

Crear un entorno virtual llamado "RD-openbabel"

Open Babel 3.1.1 (o versiones más recientes) requiere al menos Python 3.7 para instalar y utilizar la biblioteca. Puedes crear un nuevo entorno en Miniconda con una versión compatible de Python, como Python 3.8 o superior:

conda create -n RD-openbabel python=3.8
conda activate RD-openbabel
conda install -c conda-forge openbabel

¿cómo verifico que tengo instalado openbabel?

obabel -V
Open Babel 3.1.0 -- Nov 30 2023 -- 20:44:30

Instalar RDKit, tqdm, y AutoDock Vina 1.2.5 en mi nuevo entorno de Miniconda llamado "RD-openbabel".

conda install -c conda-forge rdkit tqdm
conda install -c conda-forge vina=1.2.5

Después de instalar, puedes verificar que todo funciona correctamente:

python -c "import rdkit; print(rdkit.__version__)"
python -c "import tqdm; print(tqdm.__version__)"
vina --version
obabel -V
conda env list
conda list

Script para generar todos estereoisómeros posibles de una molécula con carbonos quirales.

Necesitas tener instalado en tu entorno virtual RDkit y OpenBabel. Puedes ejecutar este script en Windows o en Linux simplemente comentando (#) la ruta que no quieras usar.

from rdkit import Chem
from rdkit.Chem import AllChem
from rdkit.Chem.EnumerateStereoisomers import EnumerateStereoisomers, StereoEnumerationOptions
import os

# Define input and output directories
input_directory = r'/home/jant/runQuiral/01/'  # Update with your path
output_directory = os.path.join(input_directory, 'isomers')
os.makedirs(output_directory, exist_ok=True)

# Function to clear stereochemistry assignments
def clear_stereochemistry(mol):
    """Removes CIP codes and resets chiral centers to unspecified."""
    for atom in mol.GetAtoms():
        if atom.HasProp('_CIPCode'):  # Clear CIP codes
            atom.ClearProp('_CIPCode')
        atom.SetChiralTag(Chem.rdchem.ChiralType.CHI_UNSPECIFIED)

# Function to generate stereoisomers and minimize their energy
def process_molecules(input_directory, output_directory):
    """Processes all SDF files in the input directory, generating stereoisomers."""
    for file_name in os.listdir(input_directory):
        if file_name.endswith('.sdf'):
            input_file_path = os.path.join(input_directory, file_name)
            output_file_path = os.path.join(output_directory, file_name.replace('.sdf', '_isomers.sdf'))
            
            # Skip molecules that have already been processed
            if os.path.exists(output_file_path) and os.path.getsize(output_file_path) > 0:
                print(f"Skipping {file_name}: isomers file already exists.")
                continue
            
            suppl = Chem.SDMolSupplier(input_file_path)
            molecules = [mol for mol in suppl if mol is not None]
            
            if not molecules:
                print(f"No valid molecules found in {file_name}. Skipping.")
                continue
            
            writer = Chem.SDWriter(output_file_path)
            
            for mol in molecules:
                chiral_centers = Chem.FindMolChiralCenters(mol, includeUnassigned=True)
                num_centers = len(chiral_centers)
                
                if num_centers > 4:
                    print(f"Skipping {Chem.MolToSmiles(mol)}: more than 4 chiral centers.")
                    open(output_file_path, 'w').close()  # Create an empty file
                    continue
                
                # Work on a copy to avoid modifying the original molecule
                mol_copy = Chem.Mol(mol)
                clear_stereochemistry(mol_copy)
                
                # Generate stereoisomers
                opts = StereoEnumerationOptions(tryEmbedding=True)
                isomers = tuple(EnumerateStereoisomers(mol_copy, options=opts))
                num_isomers = len(isomers)
                print(f"Found {num_isomers} isomers for {Chem.MolToSmiles(mol)}")
                
                # Print SMILES of isomers
                print("Isomer SMILES:")
                for smi in sorted(Chem.MolToSmiles(x, isomericSmiles=True) for x in isomers):
                    print(f"  {smi}")
                
                # Get molecule base name
                base_name = mol.GetProp("_Name") if mol.HasProp("_Name") else file_name.replace('.sdf', '')
                
                for idx, isomer in enumerate(isomers, start=1):
                    isomer_name = f"{base_name}_{idx}"
                    isomer.SetProp("_Name", isomer_name)
                    
                    isomer = Chem.AddHs(isomer)  # Add hydrogen atoms
                    try:
                        AllChem.EmbedMolecule(isomer, AllChem.ETKDG())  # Generate 3D coordinates
                        AllChem.MMFFOptimizeMolecule(isomer, mmffVariant='MMFF94s')  # Energy minimization
                    except Exception as e:
                        print(f"Energy minimization failed for {isomer_name}: {e}")
                        continue
                    
                    writer.write(isomer)
            
            writer.close()
            print(f"Isomers for {file_name} saved to {output_file_path}")

# Run the function
process_molecules(input_directory, output_directory)
print(f"All isomers have been generated and saved in {output_directory}")

Si tienes varios miles de ficheros *.sdf y para trabajar en paralelo, divídelos en 8 grupos, en este ejemplo:

import os
import shutil
from math import ceil

# Quiero dividir 960,000 ficheros *.sdf en 8 grupos para que sea más fácil trabajar con ellos.
# Nombre del script: 05-distribuir_sdfs_8_grupos.py
# Ejecución: Windows, fecha creacción de esta versión: 2025-01-28
# Ruta base
ruta_base = r"D:\aaa"

# Cambiar aquí para ajustar el número de grupos (8 en este caso)
num_grupos = 8
carpetas_destino = [os.path.join(ruta_base, f"{i:02}") for i in range(1, num_grupos + 1)]

# Crear carpetas de destino si no existen
for carpeta in carpetas_destino:
    os.makedirs(carpeta, exist_ok=True)

# Listar los archivos .sdf en la ruta base
archivos_sdf = [f for f in os.listdir(ruta_base) if f.endswith(".sdf")]

# Calcular el número de archivos por grupo
n_archivos = len(archivos_sdf)
if n_archivos == 0:
    print("No se encontraron archivos .sdf en la ruta especificada.")
else:
    # Cambiar aquí para ajustar el número de grupos (8 en este caso)
    archivos_por_grupo = ceil(n_archivos / num_grupos)

    # Distribuir y copiar archivos
    for i, archivo in enumerate(archivos_sdf):
        grupo = i // archivos_por_grupo  # Determinar a qué grupo pertenece
        destino = carpetas_destino[grupo]  # Carpeta de destino correspondiente
        origen = os.path.join(ruta_base, archivo)
        shutil.copy(origen, destino)  # Copiar archivo
        print(f"Archivo {archivo} copiado a {destino}")

    print(f"Se distribuyeron {n_archivos} archivos .sdf en {num_grupos} grupos.")





Última modificación: