Skip to content

Working with Compounds

The Compound class is the primary entry point for PHOENIX.

Creating Compounds

From SMILES

The standard way to create a compound is from a SMILES string:

from phoenix import Compound

# Simple molecules
ethanol = Compound.from_smiles("CCO")
acetone = Compound.from_smiles("CC(=O)C")

# Aromatic compounds
benzene = Compound.from_smiles("c1ccccc1")
nitrobenzene = Compound.from_smiles("c1ccccc1[N+](=O)[O-]")

# Complex structures
tnt = Compound.from_smiles("Cc1c([N+](=O)[O-])cc([N+](=O)[O-])cc1[N+](=O)[O-]")

With Phase Specification

Specify the phase for more accurate thermodynamic calculations:

# Gas phase (default)
water_gas = Compound.from_smiles("O", phase="g")

# Liquid phase
water_liquid = Compound.from_smiles("O", phase="l")

# Solid phase
water_solid = Compound.from_smiles("O", phase="s")

Basic Properties

Molecular Information

compound = Compound.from_smiles("CCO")

# Molecular formula (Hill notation)
print(compound.formula)          # C2H6O

# Molecular weight in g/mol
print(compound.molecular_weight)  # 46.07

# Elemental composition
print(compound.composition)       # {'C': 2, 'H': 6, 'O': 1}

# SMILES representations
print(compound.original_smiles)   # CCO (as provided)
print(compound.canonical_smiles)  # CCO (canonicalized)

# InChIKey for database lookup
print(compound.inchikey)          # LFQSCWFLJHTTHZ-UHFFFAOYSA-N

Atom Count

compound = Compound.from_smiles("Cc1ccccc1")  # Toluene

# Total atoms including hydrogens
print(compound.num_atoms)  # 15 (C7H8)

Thermodynamic Properties

Enthalpy of Formation

compound = Compound.from_smiles("CCO")

# Access as property (at 298.15 K)
hf = compound.enthalpy_of_formation
print(f"ΔHf° = {hf.value:.1f} {hf.unit}")

# Access the raw float value
print(f"ΔHf° = {compound.delta_hf_kJ_mol:.1f} kJ/mol")

# At different temperatures
hf_400 = compound.enthalpy_of_formation(T=400)
hf_500 = compound.enthalpy_of_formation(T=500)

Entropy

compound = Compound.from_smiles("CCO")

# Standard entropy
s = compound.entropy
print(f"S° = {s.value:.1f} {s.unit}")  # J/(mol·K)

# At different temperature
s_500 = compound.entropy(T=500)

Heat Capacity

compound = Compound.from_smiles("CCO")

# Heat capacity at 298.15 K
cp = compound.heat_capacity()
print(f"Cp = {cp.value:.1f} {cp.unit}")

# At elevated temperature
cp_500 = compound.heat_capacity(temperature_K=500)

Thermodynamic State

Get all properties at once using thermo_at():

compound = Compound.from_smiles("CCO")

# All properties at 500 K
state = compound.thermo_at(T=500)

print(f"H = {state.H.value:.1f} kJ/mol")
print(f"S = {state.S.value:.1f} J/(mol·K)")
print(f"Cp = {state.Cp.value:.1f} J/(mol·K)")
print(f"G = {state.G.value:.1f} kJ/mol")

Hazard Properties

Oxygen Balance

compound = Compound.from_smiles("c1ccccc1[N+](=O)[O-]")

# Oxygen balance as percentage
ob = compound.oxygen_balance
print(f"OB% = {ob:.1f}%")  # Negative = oxygen deficient

Maximum Decomposition

compound = Compound.from_smiles("c1ccccc1[N+](=O)[O-]")

# Calculate decomposition
decomp = compound.max_decomposition()

print(f"ΔHd = {decomp.delta_hd_kJ_mol:.1f} kJ/mol")
print(f"ΔHd = {decomp.delta_hd_cal_g:.1f} cal/g")

# Decomposition products
for product, moles in decomp.products.items():
    if moles > 0.01:
        print(f"  {product}: {moles:.2f} mol")

Full Hazard Evaluation

compound = Compound.from_smiles("c1ccccc1[N+](=O)[O-]")

# Complete hazard assessment
result = compound.evaluate_hazard()

print(f"Hazard Class: {result.hazard_class}")  # HIGH, MEDIUM, or LOW
print(f"Triggered Criteria: {result.triggered_criteria}")
print(f"Functional Group Alerts: {result.functional_group_alerts}")

Warnings and Validation

Automatic Validation

PHOENIX validates compounds during creation:

from phoenix import (
    Compound,
    InvalidSmilesError,
    UnsupportedElementError,
    UnsupportedStructureError,
)

# Invalid SMILES raises InvalidSmilesError
try:
    Compound.from_smiles("not-a-smiles")
except InvalidSmilesError as e:
    print(f"Invalid SMILES: {e}")

# Unsupported elements raise UnsupportedElementError
try:
    Compound.from_smiles("[Fe]")  # Iron not supported
except UnsupportedElementError as e:
    print(f"Unsupported: {e.elements}")

# Charged species raise UnsupportedStructureError
try:
    Compound.from_smiles("[NH4+]")  # Charged species
except UnsupportedStructureError as e:
    print(f"Unsupported structure: {e.reason}")

Large Molecule Warnings

PHOENIX warns for molecules with more than 100 atoms:

import warnings

# Large molecules generate warnings
large_molecule = Compound.from_smiles("C" * 50)  # Very large
# UserWarning: Large molecule (150 atoms). Benson GA accuracy may be reduced...

Accessing Warnings

compound = Compound.from_smiles("C" * 50)

# Check for warnings generated during creation
for warning in compound.warnings:
    print(f"Warning: {warning}")

Supported Elements

PHOENIX supports organic compounds containing:

Element Symbol
Carbon C
Hydrogen H
Nitrogen N
Oxygen O
Sulfur S
Phosphorus P
Fluorine F
Chlorine Cl
Bromine Br

Elements not supported: metals, iodine, silicon, boron, etc.

RDKit Integration

Access the underlying RDKit molecule for advanced operations:

from rdkit import Chem
from rdkit.Chem import Descriptors

compound = Compound.from_smiles("CCO")

# Get RDKit Mol object
rdmol = compound.rdmol

# Use RDKit functions
logp = Descriptors.MolLogP(rdmol)
hbd = Descriptors.NumHDonors(rdmol)

print(f"LogP: {logp:.2f}")
print(f"H-bond donors: {hbd}")

String Representations

compound = Compound.from_smiles("c1ccccc1")

# repr shows canonical SMILES
print(repr(compound))  # Compound('c1ccccc1')

# str shows formula and MW
print(str(compound))   # C6H6 (MW=78.11)

Equality and Hashing

Compounds are compared by canonical SMILES:

# Same molecule, different SMILES
c1 = Compound.from_smiles("CCO")
c2 = Compound.from_smiles("OCC")

print(c1 == c2)  # True (same canonical SMILES)
print(hash(c1) == hash(c2))  # True

# Use in sets and dicts
compounds = {c1, c2}
print(len(compounds))  # 1 (deduplicated)

Next Steps