From sciagent-skills
Runs cloud quantum chemistry calculations: geometry optimization, conformer generation, torsional scans, DFT/semiempirical minimization, properties (dipole, charges, orbitals) via Python SDK, no local QC needed.
npx claudepluginhub jaechang-hits/sciagent-skills --plugin sciagent-skillsThis skill uses the workspace's default tool permissions.
Rowan is a cloud quantum chemistry platform that exposes DFT and semiempirical calculations through a Python SDK (`rowan`). Submit calculations (geometry optimization, conformer generation, torsional scans, single-point energies) from Python scripts or Jupyter notebooks, and retrieve results — energies, geometries, partial charges, frontier orbital energies — without managing Gaussian, ORCA, or...
Automates cloud quantum chemistry via Python API: pKa prediction, geometry optimization, conformer search, docking (AutoDock Vina), AI cofolding (Chai-1/Boltz). No local setup.
Analyzes molecules using RDKit: parses SMILES/SDF, computes descriptors (MW, LogP, TPSA), fingerprints (Morgan, MACCS), Tanimoto similarity, SMARTS filtering, Lipinski checks, reactions, 2D/3D coords.
Simplifies RDKit for drug discovery with Pythonic API: SMILES parsing, standardization, descriptors, fingerprints, clustering, 3D conformers, parallel processing.
Share bugs, ideas, or general feedback.
Rowan is a cloud quantum chemistry platform that exposes DFT and semiempirical calculations through a Python SDK (rowan). Submit calculations (geometry optimization, conformer generation, torsional scans, single-point energies) from Python scripts or Jupyter notebooks, and retrieve results — energies, geometries, partial charges, frontier orbital energies — without managing Gaussian, ORCA, or Psi4 installations. Rowan handles job queuing, execution, and storage. A free tier is available for academic and exploratory use.
rowan (official Python SDK)ROWAN_API_KEY environment variable after account creationpip install rowan
# Set API key (add to .bashrc or .env)
export ROWAN_API_KEY="your_api_key_here"
import rowan
# Authenticate (uses ROWAN_API_KEY environment variable automatically)
client = rowan.RowanClient()
# Run geometry optimization of aspirin at GFN2-xTB level
job = client.compute(
smiles="CC(=O)Oc1ccccc1C(=O)O",
method="gfn2-xtb",
tasks=["optimize"],
)
print(f"Job ID: {job.id}, Status: {job.status}")
# Wait for completion and retrieve energy
result = client.wait(job.id)
print(f"Energy: {result.energy:.6f} Hartree")
print(f"Optimized geometry atoms: {len(result.geometry.atoms)}")
import rowan
import os
# Option 1: automatic (reads ROWAN_API_KEY env variable)
client = rowan.RowanClient()
# Option 2: explicit key
client = rowan.RowanClient(api_key=os.environ["ROWAN_API_KEY"])
print(f"Authenticated as: {client.user.email}")
print(f"Organization: {client.user.organization}")
Optimize a molecular geometry to the nearest local minimum.
import rowan
client = rowan.RowanClient()
# GFN2-xTB semiempirical (fast, good for conformer screening)
job_xtb = client.compute(
smiles="CCc1ccc(cc1)NC(=O)C", # paracetamol
method="gfn2-xtb",
tasks=["optimize"],
)
result_xtb = client.wait(job_xtb.id)
print(f"xTB optimized energy: {result_xtb.energy:.6f} Hartree")
print(f"Geometry: {len(result_xtb.geometry.atoms)} atoms")
# DFT optimization: B3LYP/6-31G* (accurate, slower)
job_dft = client.compute(
smiles="CCc1ccc(cc1)NC(=O)C",
method="b3lyp",
basis_set="6-31g*",
tasks=["optimize"],
solvent="water", # implicit solvent (SMD model)
)
result_dft = client.wait(job_dft.id)
print(f"B3LYP/6-31G* energy (water): {result_dft.energy:.6f} Hartree")
print(f"Dipole moment: {result_dft.dipole_moment:.3f} Debye")
Generate multiple 3D conformers and rank by energy.
import rowan
import pandas as pd
client = rowan.RowanClient()
# Generate and optimize 10 conformers at GFN2-xTB level
job = client.compute(
smiles="CC(C)CC1=CC=C(C=C1)C(C)C(=O)O", # ibuprofen
method="gfn2-xtb",
tasks=["conformers"],
n_conformers=10,
)
result = client.wait(job.id)
# Rank conformers by relative energy
conformers = result.conformers
df = pd.DataFrame([
{"conformer_id": i,
"energy_hartree": c.energy,
"rel_energy_kcal": (c.energy - min(c2.energy for c2 in conformers)) * 627.509}
for i, c in enumerate(conformers)
]).sort_values("rel_energy_kcal")
print(df.to_string(index=False))
print(f"\nLowest energy conformer ID: {df.iloc[0]['conformer_id']}")
Map energy as a function of a dihedral angle.
import rowan
import numpy as np
import matplotlib.pyplot as plt
client = rowan.RowanClient()
# Scan the C-C=C-C dihedral of butene
job = client.compute(
smiles="CC=CC", # but-2-ene
method="gfn2-xtb",
tasks=["torsion_scan"],
torsion_atoms=[0, 1, 2, 3], # atom indices defining dihedral
n_scan_points=36, # 36 points = 10-degree steps
)
result = client.wait(job.id)
angles = [point.angle for point in result.torsion_scan]
energies = [point.energy for point in result.torsion_scan]
rel_e = [(e - min(energies)) * 627.509 for e in energies] # convert to kcal/mol
fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(angles, rel_e, "o-", color="steelblue")
ax.set_xlabel("Dihedral angle (degrees)")
ax.set_ylabel("Relative energy (kcal/mol)")
ax.set_title("Torsional potential: but-2-ene C-C=C-C dihedral")
plt.tight_layout()
plt.savefig("torsion_scan.png", dpi=150)
print("Torsion scan saved -> torsion_scan.png")
Compute electronic properties at a fixed geometry (HOMO/LUMO, partial charges, ESP).
import rowan
client = rowan.RowanClient()
# Single-point DFT: get frontier orbital energies and partial charges
job = client.compute(
smiles="c1ccccc1N", # aniline
method="b3lyp",
basis_set="6-311+g**",
tasks=["single_point", "partial_charges", "orbitals"],
)
result = client.wait(job.id)
print(f"HOMO energy: {result.homo_energy:.4f} eV")
print(f"LUMO energy: {result.lumo_energy:.4f} eV")
print(f"HOMO-LUMO gap: {result.lumo_energy - result.homo_energy:.4f} eV")
print(f"Dipole moment: {result.dipole_moment:.3f} Debye")
# Partial charges (Mulliken)
for i, (atom, charge) in enumerate(zip(result.geometry.atoms, result.partial_charges)):
print(f" Atom {i} ({atom.symbol}): {charge:+.4f} e")
Submit multiple molecules concurrently.
import rowan
import pandas as pd
from concurrent.futures import ThreadPoolExecutor, as_completed
client = rowan.RowanClient()
smiles_list = [
("aspirin", "CC(=O)Oc1ccccc1C(=O)O"),
("caffeine", "Cn1cnc2c1c(=O)n(c(=O)n2C)C"),
("paracetamol", "CC(=O)Nc1ccc(O)cc1"),
("ibuprofen", "CC(C)Cc1ccc(cc1)C(C)C(=O)O"),
]
def submit_and_wait(name, smiles):
job = client.compute(smiles=smiles, method="gfn2-xtb", tasks=["optimize"])
result = client.wait(job.id)
return {"name": name, "smiles": smiles, "energy": result.energy}
results = []
with ThreadPoolExecutor(max_workers=4) as executor:
futures = {executor.submit(submit_and_wait, n, s): n for n, s in smiles_list}
for future in as_completed(futures):
results.append(future.result())
df = pd.DataFrame(results)
df.to_csv("batch_energies.csv", index=False)
print(df)
| Parameter | Module | Default | Range / Options | Effect |
|---|---|---|---|---|
method | compute | required | "gfn2-xtb", "b3lyp", "wb97x-d", "mp2" | QC method; xTB is fastest, DFT is accurate, MP2 for correlation |
basis_set | compute (DFT) | method-dependent | "6-31g*", "6-311+g**", "def2-svp", "def2-tzvp" | Basis set; larger = more accurate and slower |
tasks | compute | required | ["optimize"], ["single_point"], ["conformers"], ["torsion_scan"] | What calculation to run |
n_conformers | conformers task | 10 | 1–100 | Number of conformers to generate and optimize |
n_scan_points | torsion_scan | 36 | 12–72 | Number of dihedral scan points (360/n gives step size) |
solvent | compute | None | "water", "dmso", "methanol", etc. | Implicit SMD solvation model |
torsion_atoms | torsion_scan | required | list of 4 atom indices | Defines the dihedral angle for the scan |
Use GFN2-xTB for screening, DFT for final characterization: xTB is 100–1000x faster than DFT and adequate for conformer ranking and geometry exploration. Switch to B3LYP or wB97X-D for accurate energetics, HOMO/LUMO gaps, and publishable results.
Always optimize geometry before computing properties: Single-point calculations on unrelaxed geometries (e.g., from SMILES 3D embedding) give unreliable energies and electronic properties. Run tasks=["optimize"] first.
Set solvent for biologically relevant molecules: Gas-phase energies often differ dramatically from solution-phase for charged or highly polar molecules. Use solvent="water" for drug candidates evaluated in aqueous conditions.
Poll job status for long DFT calculations: Use client.wait() with a timeout or poll with client.get_job(job_id).status in long-running batch workflows to avoid blocking.
Export optimized geometries as XYZ for reuse: Save the optimized geometry to avoid re-running costly DFT jobs when different property calculations are needed on the same structure.
with open("optimized.xyz", "w") as f:
f.write(result.geometry.to_xyz())
Goal: Find the lowest-energy conformer of a drug candidate and compute its electronic properties.
import rowan
import pandas as pd
client = rowan.RowanClient()
smiles = "CC(C)Cc1ccc(cc1)C(C)C(=O)O" # ibuprofen
# Step 1: Generate and rank conformers at fast xTB level
conf_job = client.compute(
smiles=smiles, method="gfn2-xtb", tasks=["conformers"], n_conformers=20
)
conf_result = client.wait(conf_job.id)
lowest_conf = min(conf_result.conformers, key=lambda c: c.energy)
print(f"Lowest conformer energy: {lowest_conf.energy:.6f} Hartree")
# Step 2: Refine with DFT and compute properties
dft_job = client.compute(
xyz=lowest_conf.to_xyz(), # use optimized xTB geometry as DFT start
method="b3lyp",
basis_set="6-31g*",
tasks=["optimize", "partial_charges", "orbitals"],
solvent="water",
)
dft_result = client.wait(dft_job.id)
print(f"B3LYP/6-31G* energy (water): {dft_result.energy:.6f} Hartree")
print(f"HOMO-LUMO gap: {dft_result.lumo_energy - dft_result.homo_energy:.4f} eV")
print(f"Dipole moment: {dft_result.dipole_moment:.3f} Debye")
Goal: Compare the energies of two tautomers to determine which is more stable in water.
import rowan
client = rowan.RowanClient()
tautomers = {
"keto": "CC(=O)CC(=O)C", # acetylacetone keto form
"enol": "CC(=O)C=C(O)C", # acetylacetone enol form
}
energies = {}
for name, smiles in tautomers.items():
job = client.compute(
smiles=smiles,
method="b3lyp",
basis_set="6-311+g**",
tasks=["optimize"],
solvent="water",
)
result = client.wait(job.id)
energies[name] = result.energy
print(f"{name}: {result.energy:.6f} Hartree")
delta_e_hartree = energies["enol"] - energies["keto"]
delta_e_kcal = delta_e_hartree * 627.509
print(f"\nDelta E (enol - keto): {delta_e_kcal:.2f} kcal/mol")
print(f"More stable form: {'enol' if delta_e_kcal < 0 else 'keto'}")
result.energy — total electronic energy in Hartreeresult.geometry — optimized 3D geometry (atoms + coordinates)result.dipole_moment — scalar dipole moment in Debyeresult.homo_energy, result.lumo_energy — frontier orbital energies in eVresult.partial_charges — list of per-atom charges in elementary charge unitsresult.conformers — list of conformer objects with individual energiesresult.torsion_scan — list of (angle, energy) scan pointsWhen to use: Compare relative energies of two conformers or reaction intermediates in one call.
import rowan
client = rowan.Client()
smiles_list = ["CC(=O)O", "C(=O)(O)C"] # Acetic acid, two representations
jobs = []
for smi in smiles_list:
job = client.compute(
molecule=rowan.Molecule.from_smiles(smi),
theory_level="gfn2-xtb",
task="single_point",
)
jobs.append(job)
# Collect energies
for smi, job in zip(smiles_list, jobs):
result = client.wait(job)
print(f"{smi}: energy = {result.energy:.6f} Hartree")
When to use: Monitor a long-running optimization or conformer search without blocking.
import rowan, time
client = rowan.Client()
job = client.compute(
molecule=rowan.Molecule.from_smiles("c1ccccc1"),
theory_level="gfn2-xtb",
task="optimize",
)
print(f"Job ID: {job.id}")
# Poll every 10 seconds (non-blocking)
for _ in range(30):
status = client.status(job)
print(f"Status: {status}")
if status in ("completed", "failed"):
break
time.sleep(10)
result = client.get(job)
print(f"Final energy: {result.energy:.6f} Hartree")
| Problem | Cause | Solution |
|---|---|---|
AuthenticationError | API key not set or expired | Set ROWAN_API_KEY env variable; regenerate key at rowanquantum.com |
Job stays in queued status | High server load on free tier | Wait or upgrade to paid tier; free tier may queue during peak hours |
SCF not converged error | DFT self-consistent field failed | Try a smaller basis set; use method="gfn2-xtb" first to get a better starting geometry |
ValueError: invalid SMILES | Malformed SMILES string | Validate with Chem.MolFromSmiles(smiles) using RDKit before submission |
| High energy after optimization | Geometry stuck in local minimum | Generate multiple conformers with tasks=["conformers"] and pick the lowest |
Missing result.homo_energy | Orbital calculation not requested | Add "orbitals" to the tasks list |