MolPort database v9.


El siguiente script de Python lee todos los ficheros SDF que hemos descargado desde MolPort 2026-11-05 en nuestra ruta /home/jant.encinar/MolPortv9/SDF_original/ y genera en la ruta /home/jant.encinar/MolPortv9/sdf-500_v9/ ficheros SDF con 500 moléculas filtradas usando los criterios siguientes: 1) que exista una sola molécula (elimina fragmentos, sales, iones, etc), 2) peso molecular entre 400 y 750 Da, 3) compuestos con 2 o menos carbonos quirales, 4) extrae el nombre de MolPort del campo "PUBCHEM_EXT_DATASOURCE_REGID" y lo coloca en cada molecula seleccionada, 5) genera moléculas 3D y añade Hidrógenos.


import os
import glob
import time
import multiprocessing as mp
from rdkit import Chem
from rdkit.Chem import AllChem, Descriptors
from rdkit.Chem.EnumerateStereoisomers import EnumerateStereoisomers, StereoEnumerationOptions
from rdkit import RDLogger

# --- 1. PATH DEFINITIONS (Edit these paths for your Linux server) ---
INPUT_DIR = "/home/jant.encinar/MolPortv9/SDF_original"
OUTPUT_DIR = "/home/jant.encinar/MolPortv9/sdf-500_v9"
CPUS_A_USAR = 32
# -------------------------------------------------------------------

def procesar_archivo(filepath):
    """
    Funcion ejecutada en paralelo por cada nucleo.
    Procesa un archivo SDF individual aplicando los filtros y generando estereoisomeros 3D.
    """
    # Desactivar advertencias de RDKit en procesos hijos
    RDLogger.DisableLog('rdApp.*')
    
    filename = os.path.basename(filepath)
    base_name = os.path.splitext(filename)[0]
    
    supplier = Chem.ForwardSDMolSupplier(filepath, sanitize=True)
    stereo_opts = StereoEnumerationOptions(tryEmbedding=True, maxIsomers=4)
    
    file_counter = 1
    mol_counter_in_file = 0
    isomers_saved = 0
    mols_evaluated = 0
    
    def get_new_writer(counter):
        out_path = os.path.join(OUTPUT_DIR, f"MPv9_{base_name}_{counter:04d}.sdf")
        return Chem.SDWriter(out_path)
        
    writer = get_new_writer(file_counter)
    
    for mol in supplier:
        mols_evaluated += 1
        if mol is None: 
            continue
            
        try:
            # --- 1. FILTRO: ESTRUCTURA UNICA (Descartar mezclas, sales, agua, iones) ---
            if len(Chem.GetMolFrags(mol)) > 1:
                continue
                
            # --- 2. FILTRO: PESO MOLECULAR (400 a 750 Da) ---
            mw = Descriptors.MolWt(mol)
            if not (400 <= mw <= 750):
                continue
                
            # --- 3. FILTRO: CARBONOS QUIRALES (Entre 1 y 2 carbonos quirales) ---
            chiral_centers = Chem.FindMolChiralCenters(mol, includeUnassigned=True)
            chiral_carbons = [c for c in chiral_centers if mol.GetAtomWithIdx(c[0]).GetSymbol() == 'C']
            num_chiral = len(chiral_carbons)
            
            if num_chiral < 1 or num_chiral > 2:
                continue

            # Obtencion de ID de PubChem si existe
            base_id = mol.GetProp("PUBCHEM_EXT_DATASOURCE_REGID") if mol.HasProp("PUBCHEM_EXT_DATASOURCE_REGID") else f"MOL_{mols_evaluated}"
            
            # Copia de los campos originales (, etc.)
            props_originales = {prop: mol.GetProp(prop) for prop in mol.GetPropNames()}

            # --- 4. GENERACION DE ESTEREOISOMEROS (Hasta 2^2 = 4) Y 3D ---
            isomers = list(EnumerateStereoisomers(mol, options=stereo_opts))
            
            for idx, isomer in enumerate(isomers):
                isomer_name = f"{base_id}_{idx+1}"
                isomer_h = Chem.AddHs(isomer)
                
                # Reasignar campos de PubChem al nuevo objeto 3D
                for k, v in props_originales.items():
                    isomer_h.SetProp(k, v)
                
                # Generacion de coordenadas 3D
                res = AllChem.EmbedMolecule(isomer_h, randomSeed=42)
                if res != 0:
                    res = AllChem.EmbedMolecule(isomer_h, useRandomCoords=True, randomSeed=42)
                    if res != 0:
                        continue
                    
                # Optimizacion con MMFF
                try:
                    AllChem.MMFFOptimizeMolecule(isomer_h, maxIters=200)
                except Exception:
                    pass

                # Asignacion de etiquetas R/S
                try:
                    Chem.AssignStereochemistry(isomer_h, force=True, cleanIt=True)
                    centros_rs = Chem.FindMolChiralCenters(isomer_h)
                    if centros_rs:
                        texto_quiral = " | ".join([f"C{i}: {rs}" for i, rs in centros_rs])
                        isomer_h.SetProp("QUIRALIDAD", texto_quiral)
                    else:
                        isomer_h.SetProp("QUIRALIDAD", "No defined centers")
                except Exception:
                    pass

                isomer_h.SetProp("_Name", isomer_name)
                
                # Escritura en el SDF de salida
                try:
                    writer.write(isomer_h)
                    mol_counter_in_file += 1
                    isomers_saved += 1
                except Exception:
                    continue
                    
                if mol_counter_in_file == 500:
                    writer.close()
                    file_counter += 1
                    writer = get_new_writer(file_counter)
                    mol_counter_in_file = 0
                    
        except Exception:
            continue
            
    if writer is not None:
        writer.close()
        
    # Eliminar archivo si quedo vacio
    if mol_counter_in_file == 0:
        last_file = os.path.join(OUTPUT_DIR, f"MPv9_{base_name}_{file_counter:04d}.sdf")
        if os.path.exists(last_file):
            os.remove(last_file)

    return mols_evaluated, isomers_saved, filename


def main():
    if not os.path.exists(OUTPUT_DIR):
        os.makedirs(OUTPUT_DIR)

    print("Starting search in multiprocess mode (32 CPUs)...")
    start_time = time.time()
    
    sdf_files = glob.glob(os.path.join(INPUT_DIR, "*.sdf"))
    if not sdf_files:
        print(f"No files found in: {INPUT_DIR}")
        return

    print(f"Files found: {len(sdf_files)}")
    print("Background processing has started. Please wait...\n")
    
    total_mols = 0
    total_isomers = 0

    with mp.Pool(CPUS_A_USAR) as pool:
        for result in pool.imap_unordered(procesar_archivo, sdf_files):
            mols_eval, iso_saved, fname = result
            total_mols += mols_eval
            total_isomers += iso_saved
            print(f"File {fname} completed. (Added {iso_saved} isomers)")

    elapsed_time = time.time() - start_time
    print("-" * 50)
    print("PROCESS COMPLETED SUCCESSFULLY IN MULTIPROCESSING!")
    print(f"Initial molecules evaluated: {total_mols}")
    print(f"Stereoisomers (3D+H) saved: {total_isomers}")
    print(f"Total time: {elapsed_time/60:.2f} minutes.")


if __name__ == '__main__':
    main()




# Justo debajo tenemos el sbatch. Esto no es parte del script...
#!/bin/bash
#SBATCH --job-name=MP9
#SBATCH --qos=long
#SBATCH --cpus-per-task=32
#SBATCH --mem=64G
#SBATCH --time=2-00:00:00
#SBATCH --output=/home/jant.encinar/MolPortv9/error/MP9_salida-%j.out
#SBATCH --error=/home/jant.encinar/MolPortv9/error/MP9_error-%j.err

# Cargar el entorno de Python/Conda necesario
source /home/jant.encinar/anaconda3/etc/profile.d/conda.sh

# Nombre de mi entorno con RDkit instalado
conda activate vina

# Ejecutar el script
python3 /home/jant.encinar/MolPortv9/molport_v9.py

Ejecutado en Linux los resultados han sido los siguientes:

Starting search in multiprocess mode (32 CPUs)...
Files found: 11
Background processing has started. Please wait...

File iissc-003-500-000--003-999-999.sdf completed. (Added 63534 isomers)
File iissc-000-500-000--000-999-999.sdf completed. (Added 81005 isomers)
File iissc-001-500-000--001-999-999.sdf completed. (Added 100714 isomers)
File iissc-002-500-000--002-999-999.sdf completed. (Added 119277 isomers)
File iissc-003-000-000--003-499-999.sdf completed. (Added 119340 isomers)
File iissc-004-500-000--004-999-999.sdf completed. (Added 97044 isomers)
File iissc-001-000-000--001-499-999.sdf completed. (Added 130649 isomers)
File iissc-004-000-000--004-499-999.sdf completed. (Added 108595 isomers)
File iissc-000-000-000--000-499-999.sdf completed. (Added 210164 isomers)
File iissc-005-000-000--005-499-999.sdf completed. (Added 101994 isomers)
File iissc-002-000-000--002-499-999.sdf completed. (Added 221236 isomers)
--------------------------------------------------
PROCESS COMPLETED SUCCESSFULLY IN MULTIPROCESSING!
Initial molecules evaluated: 5406724
Stereoisomers (3D+H) saved: 1353552
Total time: 1404.34 minutes.

El acceso a la quimioteca en formato SDF (estrictamente con fines de investigación científica) está disponible en el siguiente enlace.