name: rowan description: Rowan est une plateforme de workflow en chimie computationnelle et modélisation moléculaire native du cloud avec une API Python. À utiliser pour les prédictions de pKa et macropKa, les ensembles de conformères et tautomères, l'amarrage et l'amarrage d'analogues, le corepliement protéine-ligand, la génération d'ASM, la dynamique moléculaire, la perméabilité, les workflows de descripteurs, et les tâches associées de modélisation de petites molécules ou de protéines. Idéale pour le criblage batch programmatique, les pipelines de chimie multi-étapes, et les workflows qui nécessiteraient autrement de maintenir une infrastructure HPC/GPU locale. license: Propriétaire (clé API requise) compatibility: Python 3.12+, clé API requise tags: [scientific-skills, rowan, api, cheminformatics, workflow, devops, python, simulation] metadata: skill-author: Rowan Science trigger-keywords: ["prédiction pKa", "amarrage moléculaire", "recherche de conformères", "workflow chimie", "découverte de médicaments", "SMILES", "structure protéique", "modélisation moléculaire batch", "chimie cloud"] -------|----------------|-------------------|-------| | Descripteurs | <1 min | 0,5–2 | Léger, bon pour le tri | | pKa (transition simple) | 2–5 min | 2–5 | Dépend de la taille de la molécule | | MacropKa (pH 0–14) | 5–15 min | 5–15 | Échantillonnage plus large, coût plus élevé | | Recherche de conformères | 3–10 min | 3–10 | La qualité de l'ensemble importe | | Recherche de tautomères | 2–5 min | 2–5 | Systèmes hétérocycliques | | Amarrage (ligand simple) | 5–20 min | 5–20 | Dépend de la taille de la poche, affinage | | Amarrage d'analogues (10–50 ligands) | 30–120 min | 30–100+ | Cadre de référence partagé | | Génération d'ASM | 5–30 min | 5–30 | Dépend de la longueur de la séquence | | Corepliement protéine-ligand | 15–60 min | 20–50+ | Prédiction de structure IA, très gourmande en GPU |
Démarrage rapide
uv pip install rowan-python
import rowan
rowan.api_key = "your_api_key_here" # ou définir la variable d'env ROWAN_API_KEY
# Soumettre un workflow de descripteurs — complète en moins d'une minute
wf = rowan.submit_descriptors_workflow("CC(=O)Oc1ccccc1C(=O)O", name="aspirin")
result = wf.result()
print(result.descriptors['MW']) # 180.16
print(result.descriptors['SLogP']) # 1.19
print(result.descriptors['TPSA']) # 59.44
Si cela s'affiche sans erreur, vous êtes correctement configuré.
Installation
uv pip install rowan-python
# ou : pip install rowan-python
Gestion des utilisateurs et webhooks
Authentification
Définissez une clé API via une variable d'environnement (recommandé) :
export ROWAN_API_KEY="your_api_key_here"
Ou définissez-la directement en Python :
import rowan
rowan.api_key = "your_api_key_here"
Vérifiez l'authentification :
import rowan
user = rowan.whoami() # Renvoie les infos utilisateur si authentifié
print(f"User: {user.email}")
print(f"Credits available: {user.credits_available_string}")
Gestion des secrets de webhook
Pour la vérification de la signature des webhooks, gérez les secrets via votre compte utilisateur :
import rowan
# Récupérez votre secret de webhook actuel (renvoie None s'il n'existe pas)
secret = rowan.get_webhook_secret()
if secret is None:
secret = rowan.create_webhook_secret()
print(f"Secret key: {secret.secret}")
# Renouvelez votre secret (invalide l'ancien, crée un nouveau)
# À utiliser régulièrement pour la sécurité
new_secret = rowan.rotate_webhook_secret()
print(f"New secret created (old secret disabled): {new_secret.secret}")
# Vérifiez les signatures de webhooks entrants
is_valid = rowan.verify_webhook_secret(
request_body=b"...", # Corps brut de la requête (bytes)
signature="X-Rowan-Signature", # Depuis l'en-tête de requête
secret=secret.secret
)
Formats d'entrée de molécules
Rowan accepte les molécules aux formats suivants :
- SMILES (préféré) :
"CCO","c1ccccc1O" - Motifs SMARTS (pour certains workflows) : sous-ensemble SMARTS pour la correspondance de sous-structure
- InChI (si pris en charge dans votre version API) :
"InChI=1S/C2H6O/c1-2-3/h3H,2H2,1H3"
L'API validera l'entrée et lèvera une rowan.ValidationError si une molécule ne peut pas être parsée. Utilisez toujours des SMILES canonicalisés pour la reproductibilité.
Conseil : Utilisez RDKit pour valider les SMILES avant la soumission :
from rdkit import Chem
smiles = "CCO"
mol = Chem.MolFromSmiles(smiles)
if mol is None:
raise ValueError(f"Invalid SMILES: {smiles}")
Schéma d'utilisation principal
La plupart des tâches Rowan suivent le même schéma en trois étapes :
- Soumettre un workflow
- Attendre la fin (avec streaming optionnel)
- Récupérer les résultats typés avec des propriétés pratiques
import rowan
# 1. Soumettre — utiliser la fonction de workflow spécifique (pas la générique submit_workflow)
workflow = rowan.submit_descriptors_workflow(
"CC(=O)Oc1ccccc1C(=O)O",
name="aspirin descriptors",
)
# 2. & 3. Attendre et récupérer
result = workflow.result() # Bloque jusqu'à la fin (par défaut : wait=True, poll_interval=5)
print(result.data) # Dict brut
print(result.descriptors['MW']) # 180.16 — utiliser result.descriptors dict, pas result.molecular_weight
Pour les workflows à longue durée d'exécution, utilisez le streaming :
for partial in workflow.stream_result(poll_interval=5):
print(f"Progress: {partial.complete}%")
print(partial.data)
result() vs. stream_result()
| Motif | À utiliser quand | Durée |
|---|---|---|
result() |
Vous pouvez attendre le résultat complet | <5 min généralement |
stream_result() |
Vous voulez un retour de progression ou avez besoin de résultats partiels précoces | >5 min, ou utilisation interactive |
Directive : Utilisez result() pour les descripteurs, pKa. Utilisez stream_result() pour la recherche de conformères, l'amarrage, le corepliement.
Travail avec les résultats
L'API de Rowan inclut des objets de résultats de workflow typés avec des propriétés pratiques.
Utiliser les propriétés typées et .data
Les résultats ont deux schémas d'accès :
- Propriétés pratiques (recommandé en premier) :
result.descriptors,result.best_pose,result.conformer_energies - Fallback brut :
result.data— dictionnaire brut depuis l'API
Exemple :
result = rowan.submit_descriptors_workflow(
"CCO",
name="ethanol",
).result()
# Propriété pratique (renvoie dict de tous les descripteurs) :
print(result.descriptors['MW']) # 46.042
print(result.descriptors['SLogP']) # -0.001
print(result.descriptors['TPSA']) # 57.96
# Fallback de données brutes (descripteurs imbriqués sous la clé 'descriptors') :
print(result.data['descriptors'])
# {'MW': 46.042, 'SLogP': -0.001, 'TPSA': 57.96, 'nHBDon': 1.0, 'nHBAcc': 1.0, ...}
Remarque : DescriptorsResult n'a pas de propriété molecular_weight. Les clés de descripteur utilisent des noms courts (MW, SLogP, nHBDon) et non des noms verbeux.
Invalidation de cache
Certaines propriétés de résultat sont chargées tardivement (par ex., géométries de conformères, structures protéiques). Pour rafraîchir :
result.clear_cache()
new_structures = result.conformer_molecules # Récupérées à nouveau
Projets, dossiers et organisation
Pour les campagnes non triviales, utilisez les projets et dossiers pour garder le travail organisé.
Projets
import rowan
# Créer un projet
project = rowan.create_project(name="CDK2 lead optimization")
rowan.set_project("CDK2 lead optimization")
# Tous les workflows suivants vont dans ce projet
wf = rowan.submit_descriptors_workflow("CCO", name="test compound")
# Récupérer plus tard
project = rowan.retrieve_project("CDK2 lead optimization")
workflows = rowan.list_workflows(project=project, size=50)
Dossiers
# Créer une structure de dossiers hiérarchique
folder = rowan.create_folder(name="docking/batch_1/screening")
wf = rowan.submit_docking_workflow(
# ... paramètres d'amarrage ...
folder=folder,
name="compound_001",
)
# Lister les workflows dans un dossier
results = rowan.list_workflows(folder=folder)
Arbres de décision pour les workflows
pKa vs. MacropKa
Utilisez le pKa microscopique quand :
- Vous avez besoin du pKa d'un seul groupe ionisable
- Vous vous intéressez aux transitions acide-base et à la thermodynamique de protonation
- La molécule a un ou deux sites ionisables
- La rapidité est critique (plus rapide, moins de crédits)
Utilisez macropKa quand :
- Vous avez besoin du comportement dépendant du pH sur une plage physiologiquement pertinente (par ex., 0–14)
- Vous voulez une charge agrégée et des populations d'état de protonation sur le pH
- La molécule a plusieurs groupes ionisables avec protonation couplée
- Vous avez besoin de propriétés en aval comme la solubilité aqueuse à différents pH
Exemple de décision :
Phénol (pKa ~10) : Utiliser pKa microscopique
Amine (pKa ~9–10) : Utiliser pKa microscopique
Médicament multi-ionisable (N, O, groupe acide) : Utiliser macropKa
Évaluation ADME sur pH du tractus GI : Utiliser macropKa
Recherche de conformères vs. recherche de tautomères
Utilisez la recherche de conformères quand :
- Une seule forme tautomère est connue
- Vous avez besoin d'un ensemble 3D divers pour l'amarrage, la MD ou l'analyse SAR
- Les liaisons rotatives dominent l'espace chimique
Utilisez la recherche de tautomères quand :
- L'équilibre tautomère est incertain (par ex., hétérocycles, systèmes céto-énol)
- Vous avez besoin de modéliser tous les isomères de protonation pertinents
- Les calculs en aval (amarrage, pKa) dépendent de la forme tautomère
Workflow combiné :
# Étape 1 : Trouver le meilleur tautomère
taut_wf = rowan.submit_tautomer_search_workflow(
initial_molecule="O=c1[nH]ccnc1",
name="imidazole tautomers",
)
best_taut = taut_wf.result().best_tautomer
# Étape 2 : Générer des conformères à partir du meilleur tautomère
conf_wf = rowan.submit_conformer_search_workflow(
initial_molecule=best_taut,
name="imidazole conformers",
)
Amarrage vs. amarrage d'analogues vs. corepliement
| Workflow | À utiliser quand | Entrée | Sortie |
|---|---|---|---|
| Amarrage | Ligand simple, poche connue | Protéine + SMILES + coords poche | Pose, score, dG |
| Amarrage d'analogues | 5–100+ composés apparentés | Protéine + liste SMILES + ligand référence | Toutes les poses, alignées sur référence |
| Corepliement protéine-ligand | Séquence + ligand, pas de structure cristalline | Séquence protéine + SMILES | Complexe lié prédit par ML |
Catégories de workflows courantes
1. Descripteurs
Un point d'entrée léger pour le tri batch, l'analyse SAR ou les scripts exploratoires.
wf = rowan.submit_descriptors_workflow(
"CC(=O)Oc1ccccc1C(=O)O", # arg positionnel, accepte chaîne SMILES
name="aspirin descriptors",
)
result = wf.result()
print(result.descriptors['MW']) # 180.16
print(result.descriptors['SLogP']) # 1.19
print(result.descriptors['TPSA']) # 59.44
print(result.data['descriptors'])
# {'MW': 180.16, 'SLogP': 1.19, 'TPSA': 59.44, 'nHBDon': 1.0, 'nHBAcc': 4.0, ...}
Clés de descripteur courantes :
| Clé | Description | Plage médicament typique |
|---|---|---|
MW |
Poids moléculaire (Da) | <500 (Lipinski) |
SLogP |
LogP calculé (lipophilie) | -2 à +5 |
TPSA |
Surface polaire topologique (Ų) | <140 pour biodisponibilité orale |
nHBDon |
Nombre de donneurs de liaison H | ≤5 (Lipinski) |
nHBAcc |
Nombre d'accepteurs de liaison H | ≤10 (Lipinski) |
nRot |
Nombre de liaisons rotatives | <10 pour médicaments oraux |
nRing |
Nombre de cycles | — |
nHeavyAtom |
Nombre d'atomes lourds | — |
FilterItLogS |
Solubilité aqueuse estimée (LogS) | >-4 préféré |
Lipinski |
Passage Ro5 Lipinski (1.0) ou échec (0.0) | — |
Le résultat contient des centaines de descripteurs moléculaires supplémentaires (BCUT, GETAWAY, WHIM, etc.) ; accédez à n'importe lequel via result.descriptors['key'].
2. pKa microscopique
Pour la thermodynamique d'état de protonation et le comportement acide/base d'une structure spécifique.
Deux méthodes sont disponibles :
| Méthode | Entrée | Rapidité | Couvre | À utiliser quand |
|---|---|---|---|---|
chemprop_nevolianis2025 |
Chaîne SMILES | Rapide | Déprotonation uniquement (bases conjuguées anioniques) | Groupes acides uniquement ; criblage rapide |
starling |
Chaîne SMILES | Rapide | Acide + base (protonation/déprotonation complète) | La plupart des molécules ressemblant à des médicaments ; méthode SMILES préférée |
aimnet2_wagen2024 (par défaut) |
Objet molécule 3D | Plus lent, plus haute précision | Acide + base | Vous avez déjà une structure 3D (par ex. d'une recherche de conformères) |
# Chemin rapide : entrée SMILES avec couverture acide+base complète (utiliser starling quand disponible)
wf = rowan.submit_pka_workflow(
initial_molecule="c1ccccc1O", # SMILES phénol ; param est initial_molecule, pas initial_smiles
method="starling", # méthode SMILES rapide, couvre acide+base ; chemprop_nevolianis2025 est déprotonation uniquement
name="phenol pKa",
)
result = wf.result()
print(result.strongest_acid) # 9.81 (pKa du site le plus acide)
print(result.conjugate_bases) # liste de {pka, smiles, atom_index, ...} par site déprotonatable
3. MacropKa
Pour le comportement de protonation dépendant du pH sur une plage.
wf = rowan.submit_macropka_workflow(
initial_smiles="CN1CCN(CC1)C2=NC=NC3=CC=CC=C32", # imidazole
min_pH=0,
max_pH=14,
min_charge=-2, # par défaut
max_charge=2, # par défaut
compute_aqueous_solubility=True, # par défaut
name="imidazole macropKa",
)
result = wf.result()
print(result.pka_values) # liste des valeurs pKa
print(result.logd_by_ph) # dict de {pH: logD}
print(result.aqueous_solubility_by_ph) # dict de {pH: solubilité}
print(result.isoelectric_point) # point isoélectrique
print(result.data)
# {'pKa_values': [...], 'logD_by_pH': {...}, 'aqueous_solubility_by_pH': {...}, ...}
4. Recherche de conformères
Pour la génération d'ensemble 3D quand la qualité de l'ensemble importe.
wf = rowan.submit_conformer_search_workflow(
initial_molecule="CCOC(=O)N1CCC(CC1)Oc1ncnc2ccccc12",
num_conformers=50, # Optionnel : surcharger la valeur par défaut
name="conformer search",
)
result = wf.result()
print(result.conformer_energies) # [0.0, 1.2, 2.5, ...]
print(result.conformer_molecules) # Liste de molécules 3D
print(result.best_conformer) # Conformère d'énergie minimale
5. Recherche de tautomères
Pour les hétérocycles et les systèmes où l'état tautomère affecte la modélisation en aval.
wf = rowan.submit_tautomer_search_workflow(
initial_molecule="O=c1[nH]ccnc1", # ou tautomère céto
name="imidazolone tautomers",
)
result = wf.result()
print(result.best_tautomer) # Chaîne SMILES la plus stable
print(result.tautomers) # Liste des SMILES tautomères
print(result.molecules) # Liste des objets molécule
6. Amarrage
Pour l'amarrage protéine-ligand avec affinage de pose optionnel et génération de conformères.
# Télécharger protéine une fois, réutiliser dans plusieurs workflows
protein = rowan.upload_protein(
name="CDK2",
file_path="cdk2.pdb",
)
# Définir la poche de liaison
pocket = {
"center": [10.5, 24.2, 31.8],
"size": [18.0, 18.0, 18.0],
}
# Soumettre l'amarrage
wf = rowan.submit_docking_workflow(
protein=protein,
pocket=pocket,
initial_molecule="CCNc1ncc(c(Nc2ccc(F)cc2)n1)-c1cccnc1",
do_pose_refinement=True,
do_conformer_search=True,
name="lead docking",
)
result = wf.result()
print(result.scores) # Scores d'amarrage (kcal/mol)
print(result.best_pose) # Objet Mol avec coordonnées 3D
print(result.data) # Dict résultat brut
Conseils de préparation protéique :
- Les fichiers PDB doivent être relativement propres (supprimer l'eau/hétéroatomes sauf si intentionné)
- Utiliser le même objet protéine sur une série d'amarrage pour la cohérence
- Si vous avez un ID PDB, utiliser
rowan.create_protein_from_pdb_id()à la place
7. Amarrage d'analogues
Pour placer une série de composés dans un contexte de liaison partagé.
# Série d'analogues (par ex., campagne SAR)
analogues = [
"CCNc1ncc(c(Nc2ccc(F)cc2)n1)-c1cccnc1", # référence
"CCNc1ncc(c(Nc2ccc(Cl)cc2)n1)-c1cccnc1", # chloro
"CCNc1ncc(c(Nc2ccc(OC)cc2)n1)-c1cccnc1", # méthoxy
"CCNc1ncc(c(Nc2cc(C)c(F)cc2)n1)-c1cccnc1", # méthyl, fluoro
]
wf = rowan.submit_analogue_docking_workflow(
analogues=analogues,
initial_molecule=analogues[0], # Ligand référence
protein=protein,
pocket=pocket,
name="SAR series docking",
)
result = wf.result()
print(result.analogue_scores) # Liste de scores pour chaque analogue
print(result.best_poses) # Liste de poses
8. Génération d'ASM
Pour l'alignement de séquences multiples (utile pour le corepliement en aval).
wf = rowan.submit_msa_workflow(
initial_protein_sequences=[
"MENFQKVEKIGEGTYGVVYKARNKLTGEVVALKKIRLDTETEGVP"
],
output_formats=["colabfold", "chai", "boltz"],
name="target MSA",
)
result = wf.result()
result.download_files() # Télécharge les alignements sur disque
9. Corepliement protéine-ligand
Pour la prédiction de complexe lié basée sur l'IA quand aucune structure cristalline n'est disponible.
wf = rowan.submit_protein_cofolding_workflow(
initial_protein_sequences=[
"MENFQKVEKIGEGTYGVVYKARNKLTGEVVALKKIRLDTETEGVP"
],
initial_smiles_list=[
"CCNc1ncc(c(Nc2ccc(F)cc2)n1)-c1cccnc1"
],
name="protein-ligand cofolding",
)
result = wf.result()
print(result.predictions) # Liste des structures prédites
print(result.messages) # Métadonnées du modèle/avertissements
predicted_structure = result.get_predicted_structure()
predicted_structure.write("predicted_complex.pdb")
Tous les types de workflows pris en charge
Tous les workflows suivent le même schéma soumettre → attendre → récupérer et supportent les webhooks et l'organisation projet/dossier.
Workflows de modélisation moléculaire principaux
| Workflow | Fonction | À utiliser quand |
|---|---|---|
| Descripteurs | submit_descriptors_workflow |
Tri en première passe : MW, LogP, TPSA, HBA/HBD, filtre Lipinski |
| pKa | submit_pka_workflow |
Groupe ionisable simple ; besoin de thermodynamique de protonation |
| MacropKa | submit_macropka_workflow |
Médicaments multi-ionisables ; charge dépendante du pH/LogD/solubilité |
| Recherche de conformères | submit_conformer_search_workflow |
Ensemble 3D pour amarrage, MD ou SAR ; tautomère connu |
| Recherche de tautomères | submit_tautomer_search_workflow |
Hétérocycles, céto-énol ; forme tautomère incertaine |
| Solubilité | submit_solubility_workflow |
Prédiction de solubilité aqueuse ou spécifique au solvant |
| Perméabilité membranaire | submit_membrane_permeability_workflow |
Caco-2, PAMPA, BBB, perméabilité plasmatique |
| ADMET | submit_admet_workflow |
Balayage large de ressemblance aux médicaments et propriétés ADMET |
Workflows de conception basée sur la structure
| Workflow | Fonction | À utiliser quand |
|---|---|---|
| Amarrage | submit_docking_workflow |
Ligand simple, poche de liaison connue |
| Amarrage d'analogues | submit_analogue_docking_workflow |
Série SAR (5–100+ composés) dans une poche partagée |
| Amarrage batch | submit_batch_docking_workflow |
Criblage rapide de la bibliothèque ; grands ensembles de composés |
| MD protéique | submit_protein_md_workflow |
Dynamique à long terme ; échantillonnage conformationnel |
| MD analyse de pose | submit_pose_analysis_md_workflow |
Affinage MD d'une pose d'amarrage |
| Corepliement protéique | submit_protein_cofolding_workflow |
Pas de structure cristalline ; complexe lié prédit par IA |
| Conception de liant protéique | submit_protein_binder_design_workflow |
Génération de liant de novo contre une cible protéique |
Chimie computationnelle avancée
| Workflow | Fonction | À utiliser quand |
|---|---|---|
| Calcul basique | submit_basic_calculation_workflow |
Optimisation géométrique QM/ML ou énergie ponctuelle |
| Propriétés électroniques | submit_electronic_properties_workflow |
Dipôle, charges partielles, HOMO-LUMO, ESP |
| BDE | submit_bde_workflow |
Énergies de dissociation de liaison ; prédiction de point mou métabolique |
| Potentiel de redox | submit_redox_potential_workflow |
Potentiels d'oxydation/réduction |
| États de spin | submit_spin_states_workflow |
Ordre d'énergie des états de spin pour organométalliques/radicaux |
| Tension | submit_strain_workflow |
Tension conformationnelle relative au minimum global |
| Scan | submit_scan_workflow |
Scans PES ; profils de torsion |
| Optimisation multi-étapes | submit_multistage_opt_workflow |
Optimisation progressive sur les niveaux de théorie |
Chimie de réaction
| Workflow | Fonction | À utiliser quand |
|---|---|---|
| Recherche TS à double extrémité | submit_double_ended_ts_search_workflow |
État de transition entre deux structures connues |
| IRC | submit_irc_workflow |
Confirmer la connectivité TS ; coordonnée de réaction intrinsèque |
Propriétés avancées
| Workflow | Fonction | À utiliser quand |
|---|---|---|
| RMN | submit_nmr_workflow |
Décalages chimiques 1H/13C prédits pour vérification de structure |
| Mobilité ionique | submit_ion_mobility_workflow |
Section transversale de collision (CCS) pour développement de méthode MS |
| Force de liaison hydrogène | submit_hydrogen_bond_basicity_workflow |
Force de donneur/accepteur de liaison H pour formulation/solubilité |
| Fukui | submit_fukui_workflow |
Indices de réactivité de site pour attaque électrophile/nucléophile |
| Décomposition d'énergie d'interaction | submit_interaction_energy_decomposition_workflow |
Analyse d'interaction au niveau des fragments |
Énergie libre de liaison
| Workflow | Fonction | À utiliser quand |
|---|---|---|
| RBFE/FEP | submit_relative_binding_free_energy_perturbation_workflow |
ΔΔG relatif pour série congénère |
| Graphe RBFE | submit_rbfe_graph_workflow |
Construire et optimiser un réseau de perturbation RBFE |
Biologie séquentielle et structurale
| Workflow | Fonction | À utiliser quand |
|---|---|---|
| ASM | submit_msa_workflow |
Alignement de séquences multiples pour corepliement (ColabFold, Chai, Boltz) |
| Conformères dépendants du solvant | submit_solvent_dependent_conformers_workflow |
Ensembles de conformères conscients de la solvatation |
Soumission et récupération batch
Pour les bibliothèques ou séries d'analogues, soumettez dans une boucle en utilisant la fonction de workflow spécifique. Les fonctions génériques rowan.batch_submit_workflow() et rowan.submit_workflow() retournent actuellement des erreurs 422 de l'API — utilisez les fonctions nommées (submit_descriptors_workflow, submit_pka_workflow, etc.) à la place.
Soumettre un batch
smileses = ["CCO", "CC(=O)O", "c1ccccc1O"]
names = ["ethanol", "acetic acid", "phenol"]
workflows = [
rowan.submit_descriptors_workflow(smi, name=name)
for smi, name in zip(smileses, names)
]
print(f"Submitted {len(workflows)} workflows")
Vérifier le statut batch
statuses = rowan.batch_poll_status([wf.uuid for wf in workflows])
# Retourne les comptages agrégés — pas par UUID :
# {'queued': 0, 'running': 1, 'complete': 2, 'failed': 0, 'total': 3, ...}
if statuses["complete"] == statuses["total"]:
print("All workflows done")
elif statuses["failed"] > 0:
print(f"{statuses['failed']} workflows failed")
Récupérer et collecter les résultats
results = []
for wf in workflows:
try:
result = wf.result()
results.append(result.data)
except rowan.WorkflowError as e:
print(f"Workflow {wf.uuid} failed: {e}")
# Optionnellement agréger dans DataFrame
import pandas as pd
df = pd.DataFrame(results)
Motif non-bloquant / vérifier en arrière-plan
Pour les workflows à longue durée d'exécution où vous ne voulez pas garder un processus ouvert, soumettez les workflows, enregistrez leurs UUID, et vérifiez plus tard dans un processus séparé.
Session 1 — soumettre et enregistrer les UUID :
import rowan, json
rowan.api_key = "..."
smileses = ["CCO", "CC(=O)O", "c1ccccc1O"]
workflows = [
rowan.submit_descriptors_workflow(smi, name=f"compound_{i}")
for i, smi in enumerate(smileses)
]
# Enregistrer les UUID sur disque (ou dans une base de données)
uuids = [wf.uuid for wf in workflows]
with open("workflow_uuids.json", "w") as f:
json.dump(uuids, f)
print("Submitted. Check back later.")
Session 2 — vérifier le statut et collecter les résultats quand prêt :
import rowan, json
rowan.api_key = "..."
with open("workflow_uuids.json") as f:
uuids = json.load(f)
results = []
for uuid in uuids:
wf = rowan.retrieve_workflow(uuid)
if wf.done():
result = wf.result(wait=False)
results.append({"uuid": uuid, "data": result.data})
else:
print(f"{uuid}: still running ({wf.status})")
print(f"Collected {len(results)} completed results")
Webhooks et workflows asynchrones
Pour les campagnes à longue durée d'exécution ou quand vous ne voulez pas garder un processus actif, utilisez les webhooks pour notifier votre serveur quand les workflows se terminent.
Configurer les webhooks
Chaque fonction de soumission de workflow accepte un paramètre webhook_url :
wf = rowan.submit_docking_workflow(
protein=protein,
pocket=pocket,
initial_molecule="CCO",
webhook_url="https://myserver.com/rowan_callback",
name="docking with webhook",
)
print(f"Workflow submitted. Result will be POSTed to webhook when complete.")
Les URLs de webhook peuvent être passées à n'importe quelle fonction de workflow spécifique (submit_docking_workflow(), submit_pka_workflow(), submit_descriptors_workflow(), etc.).
Authentification webhook avec secrets
Rowan supporte la vérification de signature de webhook pour s'assurer que les requêtes sont authentiques. Vous devrez :
- Créer ou récupérer un secret webhook :
import rowan
# Créer un nouveau secret webhook
secret = rowan.create_webhook_secret()
print(f"Your webhook secret: {secret.secret}")
# Ou récupérer un secret existant
secret = rowan.get_webhook_secret()
# Renouveler votre secret (invalide l'ancien, crée un nouveau)
new_secret = rowan.rotate_webhook_secret()
- Vérifier les requêtes webhook entrantes :
import rowan
import hmac
import json
def verify_webhook(request_body: bytes, signature: str, secret: str) -> bool:
"""Vérifier la signature HMAC-SHA256 d'une requête webhook."""
return rowan.verify_webhook_secret(request_body, signature, secret)
Charge utile webhook et signature
Quand un workflow se termine, Rowan fait un POST d'une charge utile JSON à votre URL de webhook avec l'en-tête :
X-Rowan-Signature: <signature HMAC-SHA256>
Le corps de la requête contient le résultat complet du workflow :
{
"workflow_uuid": "wf_12345abc",
"workflow_type": "docking",
"workflow_name": "lead docking",
"status": "COMPLETED_OK",
"created_at": "2025-04-01T12:00:00Z",
"completed_at": "2025-04-01T12:15:30Z",
"data": {
"scores": [-8.2, -8.0, -7.9],
"best_pose": {...},
"metadata": {...}
}
}
Exemple de gestionnaire webhook avec vérification de signature (FastAPI)
from fastapi import FastAPI, Request, HTTPException
import rowan
import json
app = FastAPI()
_ws = rowan.get_webhook_secret() or rowan.create_webhook_secret()
webhook_secret = _ws.secret
@app.post("/rowan_callback")
async def handle_rowan_webhook(request: Request):
# Récupérer le corps de la requête et la signature
body = await request.body()
signature = request.headers.get("X-Rowan-Signature")
if not signature:
raise HTTPException(status_code=400, detail="Missing X-Rowan-Signature header")
# Vérifier la signature
if not rowan.verify_webhook_secret(body, signature, webhook_secret):
raise HTTPException(status_code=401, detail="Invalid webhook signature")
# Parser et traiter
payload = json.loads(body)
wf_uuid = payload["workflow_uuid"]
status = payload["status"]
if status == "COMPLETED_OK":
print(f"Workflow {wf_uuid} succeeded!")
result_data = payload["data"]
# Traiter le résultat, mettre à jour la base de données, déclencher le workflow suivant, etc.
elif status == "FAILED":
print(f"Workflow {wf_uuid} failed!")
# Gérer l'échec
# Répondre rapidement pour éviter les retentatives
return {"status": "received"}
Meilleures pratiques des webhooks
- Toujours vérifier les signatures en utilisant
rowan.verify_webhook_secret()pour s'assurer que les requêtes viennent de Rowan - Répondre rapidement (< 5 secondes) ; déplacer le traitement lourd vers des tâches async ou des travaux de fond
- Implémenter l'idempotence : les workflows peuvent réessayer ; gérer les charges utiles dupliquées avec élégance en utilisant
workflow_uuid - Enregistrer tous les événements pour le débogage et l'audit
- Utiliser pour les campagnes longues : les webhooks brillent avec 50+ workflows ; pour les petits travaux, le polling avec
result()est plus simple - Renouveler les secrets régulièrement en utilisant
rowan.rotate_webhook_secret()pour la sécurité - Retourner le statut 2xx pour confirmer la réception ; Rowan peut réessayer sur les erreurs 5xx
Utilitaires protéiques
Télécharger des protéines
# Depuis un fichier PDB local
protein = rowan.upload_protein(
name="egfr_kinase_domain",
file_path="egfr_kinase.pdb",
)
# Depuis la base de données PDB
protein_from_pdb = rowan.create_protein_from_pdb_id(
name="CDK2 (1M17)",
code="1M17",
)
# Récupérer une protéine téléchargée précédemment
protein = rowan.retrieve_protein("protein-uuid")
# Lister toutes les protéines
my_proteins = rowan.list_proteins()
Conseils de préparation protéique
- Format de fichier : PDB, mmCIF (Rowan détecte automatiquement)
- Molécules d'eau : Rowan garde généralement l'eau pertinente ; supprimer l'eau en vrac avant si désiré
- Hétéroatomes : Les cofacteurs, ions et ligands liés sont généralement conservés ; supprimer les hétéroatomes indésirables avant téléchargement
- Protéines multi-chaînes : Entièrement supportées
- Résolution : Fonctionne avec les structures RMN, les modèles d'homologie et la cryo-EM ; la qualité importe pour les prédictions en aval
- Validation : Rowan valide la syntaxe PDB ; les fichiers gravement mal formés peuvent être rejetés
Exemple de bout en bout : campagne d'optimisation du composé-tête
Cet exemple démontre un workflow réaliste pour optimiser un composé-tête :
import rowan
import pandas as pd
# 1. Créer un projet et un dossier pour l'organisation
project = rowan.create_project(name="CDK2 Hit Optimization")
rowan.set_project("CDK2 Hit Optimization")
folder = rowan.create_folder(name="round_1_tautomers_and_pka")
# 2. Charger le composé-tête et les analogues
hit = "CCNc1ncc(c(Nc2ccc(F)cc2)n1)-c1cccnc1" # Composé-tête connu
analogues = [
"CCNc1ncc(c(Nc2ccccc2)n1)-c1cccnc1", # Retirer F
"CCNc1ncc(c(Nc2ccc(Cl)cc2)n1)-c1cccnc1", # Cl au lieu de F
"CCC(C)Nc1ncc(c(Nc2ccc(F)cc2)n1)-c1cccnc1", # Propyle au lieu d'éthyle
]
# 3. Déterminer les meilleurs tautomères (par sécurité)
print("Searching tautomeric forms...")
taut_workflows = [
rowan.submit_tautomer_search_workflow(
smi, name=f"analog_{i}", folder=folder,
)
for i, smi in enumerate(analogues)
]
best_tautomers = []
for wf in taut_workflows:
result = wf.result()
best_tautomers.append(result.best_tautomer)
# 4. Prédire pKa et propriétés basiques pour tous les analogues
print("Predicting pKa and properties...")
pka_workflows = [
rowan.submit_pka_workflow(
smi, method="chemprop_nevolianis2025", name=f"pka_{i}", folder=folder,
)
for i, smi in enumerate(best_tautomers)
]
descriptor_workflows = [
rowan.submit_descriptors_workflow(smi, name=f"desc_{i}", folder=folder)
for i, smi in enumerate(best_tautomers)
]
# 5. Collecter les résultats
pka_results = []
for wf in pka_workflows:
try:
result = wf.result()
pka_results.append({
"compound": wf.name,
"pka": result.strongest_acid, # pKa du site acide le plus fort
"uuid": wf.uuid,
})
except rowan.WorkflowError as e:
print(f"pKa prediction failed for {wf.name}: {e}")
descriptor_results = []
for wf in descriptor_workflows:
try:
result = wf.result()
desc = result.descriptors
descriptor_results.append({
"compound": wf.name,
"mw": desc.get("MW"),
"logp": desc.get("SLogP"),
"hba": desc.get("nHBAcc"),
"hbd": desc.get("nHBDon"),
"uuid": wf.uuid,
})
except rowan.WorkflowError as e:
print(f"Descriptor calculation failed for {wf.name}: {e}")
# 6. Fusionner et résumer
df_pka = pd.DataFrame(pka_results)
df_desc = pd.DataFrame(descriptor_results)
df = df_pka.merge(df_desc, on="compound", how="outer")
print("\n=== Preliminary SAR ===")
print(df.to_string())
# 7. Sélectionner le composé prometteur pour l'amarrage
# Les noms de composé sont "pka_0", "pka_1", etc. — extraire l'index pour retrouver SMILES
top_idx = int(df.loc[df["pka"].idxmin(), "compound"].split("_")[1])
top_smiles = best_tautomers[top_idx]
print(f"\nProceeding with docking: {top_smiles}")
# 8. Campagne d'amarrage
protein = rowan.create_protein_from_pdb_id(name="CDK2_1CKP", code="1CKP")
pocket = {"center": [10.5, 24.2, 31.8], "size": [18.0, 18.0, 18.0]}
docking_wf = rowan.submit_docking_workflow(
protein=protein,
pocket=pocket,
initial_molecule=top_smiles,
do_pose_refinement=True,
name=f"docking_{top_compound}",
)
dock_result = docking_wf.result()
print(f"\nDocking score: {dock_result.scores[0]:.2f} kcal/mol")
print(f"Best pose saved to: best_pose.pdb")
dock_result.best_pose.write("best_pose.pdb")
Gestion des erreurs et dépannage
Erreurs courantes et solutions
import rowan
# Erreur 1 : SMILES invalide
try:
wf = rowan.submit_descriptors_workflow("CCCC(CC", name="bad smiles") # Invalide
except rowan.ValidationError as e:
print(f"Invalid SMILES: {e}")
# Solution : utiliser RDKit pour valider avant la soumission
from rdkit import Chem
smi = Chem.MolToSmiles(Chem.MolFromSmiles(smi))
# Erreur 2 : Clé API non définie
try:
wf = rowan.submit_descriptors_workflow("CCO")
except rowan.AuthenticationError:
print("API key not found. Set ROWAN_API_KEY env var or call rowan.api_key = '...'")
# Erreur 3 : Crédits insuffisants
try:
wf = rowan.submit_protein_cofolding_workflow(...)
except rowan.InsufficientCreditsError as e:
print(f"Not enough credits: {e}. Purchase more or reduce job size.")
# Erreur 4 : Workflow échoué (molécule mauvaise, etc.)
try:
wf = rowan.submit_docking_workflow(...)
result = wf.result()
except rowan.WorkflowError as e:
print(f"Workflow failed: {e}")
# Vérifier wf.status pour les détails
print(f"Status: {wf.status}")
# Erreur 5 : Workflow pas encore complété — vérifier manuellement
result = wf.result(wait=True, poll_interval=5) # attend et vérifie toutes les 5s
# Ou vérifier le statut sans bloquer :
if not wf.done():
print("Workflow still running. Call wf.result() again later.")
Conseils de débogage
- Vérifier le statut du workflow :
wf.status, vérifierwf.done(), ou appelerwf.get_status() - Inspecter le résultat brut :
result.dataau lieu des propriétés pratiques - Relancer le workflow échoué : Enregistrer les UUID et réessayer avec
rowan.retrieve_workflow(uuid) - Valider les molécules au préalable : Utiliser RDKit ou Chemaxon avant la soumission batch
Motifs d'utilisation recommandés
- Préférer les workflows natifs de Rowan aux assemblages bas niveau quand ils existent
- Utiliser les projets et dossiers pour toute campagne non triviale (>5 workflows)
- Utiliser
result()pour bloquer jusqu'à l'achèvement (par défaut :wait=True, poll_interval=5) - Utiliser les propriétés de résultat typés en premier, revenir à
.datapour les champs non mappés - Utiliser la soumission batch pour les bibliothèques de composés ou les séries d'analogues
- Enchaîner les workflows pour les campagnes de chimie multi-étapes :
pKa → macropKa → perméabilité(évaluation ADME)recherche tautomère → amarrage → MD analyse de pose(affinage de pose)génération ASM → corepliement protéine-ligand(prédiction de structure IA)
- Utiliser les webhooks pour les campagnes à longue durée d'exécution (>50 workflows) ou les pipelines asynchrones
- Utiliser le streaming pour un retour d'information interactif sur les recherches de conformères/amarrage grandes
Résumé
Utilisez Rowan quand votre workflow nécessite une exécution en cloud pour les tâches de conception moléculaire, particulièrement quand vous voulez une API unifiée et une gestion de résultats cohérente dans la modélisation de petites molécules, les protéines, l'amarrage, la prédiction ADME, et la génération de structure ML.
Rowan est une plateforme de workflow de conception moléculaire, pas simplement un moteur de chimie à distance. Elle gère la mise à l'échelle d'infrastructure, la persistance des résultats, et l'orchestration de pipelines multi-étapes pour que vous vous concentriez sur la science.