Skip to content

Compound API

Compound

A chemical compound parsed from SMILES with lazy property evaluation.

The Compound class provides: - SMILES parsing and canonicalization via RDKit - Input validation (element support, charge, radical checks) - Elemental composition extraction - Molecular weight and formula calculation - Thermodynamic property estimation (via phoenix.thermo) - Hazard evaluation (via phoenix.hazard)

Parameters

rdmol : rdkit.Chem.Mol RDKit molecule object (use from_smiles() for construction)

Examples

compound = Compound.from_smiles("CCO") compound.formula 'C2H6O' compound.molecular_weight 46.07 compound.composition

rdmol property

rdmol: Mol

Access the underlying RDKit Mol object.

original_smiles property

original_smiles: str | None

Original SMILES string used to create this compound.

canonical_smiles cached property

canonical_smiles: str

Canonical SMILES representation.

inchikey cached property

inchikey: str | None

InChIKey for compound lookup.

formula cached property

formula: str

Molecular formula in Hill notation.

molecular_weight cached property

molecular_weight: float

Molecular weight in g/mol.

composition cached property

composition: dict[str, int]

Elemental composition as atom counts.

Returns

dict[str, int] Mapping of element symbol to atom count

num_atoms cached property

num_atoms: int

Total number of atoms in the molecule.

warnings property

warnings: list[str]

List of warnings generated during compound creation.

phase property

phase: str

Phase of the compound ('g', 'l', or 's').

enthalpy_of_formation property

enthalpy_of_formation: ThermoPropertyAccessor

Enthalpy of formation accessor.

Can be used as property (298.15 K) or method with temperature:

Examples

compound = Compound.from_smiles('CCO')

As property (298.15 K) - backward compatible

hf = compound.enthalpy_of_formation print(hf.value) # kJ/mol

As method with temperature

hf_500 = compound.enthalpy_of_formation(T=500) print(hf_500.value) # kJ/mol at 500 K

Vectorized with NumPy

import numpy as np temps = np.linspace(300, 1000, 100) values = compound.enthalpy_of_formation(T=temps) # ndarray

delta_hf_kJ_mol property

delta_hf_kJ_mol: float

ΔHf° in kJ/mol at 298.15 K (backwards compatible float access).

entropy property

entropy: ThermoPropertyAccessor

Standard entropy accessor.

Can be used as property (298.15 K) or method with temperature:

Examples

compound = Compound.from_smiles('CCO')

As property (298.15 K)

s = compound.entropy print(s.value) # J/(mol·K)

As method with temperature

s_500 = compound.entropy(T=500) print(s_500.value) # J/(mol·K) at 500 K

entropy_J_mol_K property

entropy_J_mol_K: float

S° in J/(mol·K) at 298.15 K (backwards compatible float access).

oxygen_balance cached property

oxygen_balance: float

Oxygen balance (OB%) for the compound.

Positive values indicate oxygen excess, negative values oxygen deficiency.

oxygen_balance_percent property

oxygen_balance_percent: float

Alias for oxygen_balance (backwards compatibility).

__init__

__init__(rdmol: Mol, original_smiles: str | None = None, phase: str = 'g')

Initialize Compound from RDKit Mol object.

Use Compound.from_smiles() for the standard entry point.

from_smiles classmethod

from_smiles(smiles: str, phase: str = 'g') -> Compound

Create a Compound from a SMILES string.

Parameters

smiles : str SMILES string representing the molecule phase : str Phase of the compound ('g' for gas, 'l' for liquid, 's' for solid). Default is 'g'.

Returns

Compound Validated compound object

heat_capacity

heat_capacity(temperature_K: float = 298.15) -> ThermoProperty

Heat capacity Cp at specified temperature.

Parameters

temperature_K : float Temperature in Kelvin (default: 298.15 K)

Returns

ThermoProperty Heat capacity with value in J/(mol·K), uncertainty, and source

thermo_at

thermo_at(*, T: float) -> ThermoState

Get thermodynamic state at specified temperature.

Returns an immutable ThermoState object with all properties (H, S, Cp, G) calculated at the same temperature.

Parameters

T : float Temperature in Kelvin (keyword-only)

Returns

ThermoState Immutable state with H, S, Cp, G properties

Examples

compound = Compound.from_smiles('CCO') state = compound.thermo_at(T=500) print(f"H = {state.H.value:.2f} kJ/mol") print(f"S = {state.S.value:.2f} J/(mol·K)") print(f"Cp = {state.Cp.value:.2f} J/(mol·K)") print(f"G = {state.G.value:.2f} kJ/mol")

max_decomposition

max_decomposition(*, method: str = 'hierarchy', gas_temperature_K: float = 298.15) -> DecompositionResult

Calculate maximum heat of decomposition.

Parameters

method : {'hierarchy', 'lp', 'both'}, default 'hierarchy' Calculation method: - 'hierarchy': CHETAH analytical priority rules (default) - 'lp': Linear Programming optimization - 'both': Run both and compare results gas_temperature_K : float, default 298.15 Temperature for gas volume calculation using PV=nRT

Returns

DecompositionResult or DecompositionComparison Decomposition result with ΔHd, products, and gas generation data. Returns DecompositionComparison when method='both'.

Examples

compound = Compound.from_smiles('CC') result = compound.max_decomposition() # hierarchy result_lp = compound.max_decomposition(method='lp') result_both = compound.max_decomposition(method='both') print(result.gas_volume_L_g) # L/g at 298.15 K print(result.gas_moles) # moles gas per mol compound print(result.gas_composition) # {"H2": 1.0}

evaluate_hazard

evaluate_hazard() -> HazardResult

Perform full hazard evaluation.

Returns

HazardResult Complete hazard assessment with classification and criteria


Class Overview

The Compound class is the primary entry point for PHOENIX. It represents a single chemical compound and provides access to molecular properties, thermodynamic data, and hazard evaluation.

from phoenix import Compound

compound = Compound.from_smiles("CCO")
print(compound)  # C2H6O (MW=46.07)

Factory Methods

from_smiles

@classmethod
def from_smiles(cls, smiles: str, phase: str = "g") -> Compound

Create a Compound from a SMILES string.

Parameters:

Name Type Default Description
smiles str required SMILES string
phase str "g" Phase: 'g' (gas), 'l' (liquid), or 's' (solid)

Returns: Compound

Raises:

  • InvalidSmilesError - If SMILES cannot be parsed
  • UnsupportedElementError - If contains unsupported elements
  • UnsupportedStructureError - If charged or radical

Example:

from phoenix import Compound

ethanol = Compound.from_smiles("CCO")
print(f"Formula: {ethanol.formula}")     # C2H6O
print(f"MW: {ethanol.molecular_weight}") # 46.07

# Specify phase for condensed-phase calculations
water_liquid = Compound.from_smiles("O", phase="l")
print(f"Phase: {water_liquid.phase}")    # l

Properties

Molecular Information

Property Type Description
formula str Molecular formula (Hill notation)
molecular_weight float MW in g/mol
composition dict[str, int] Element counts
num_atoms int Total atom count
canonical_smiles str Canonicalized SMILES
original_smiles str | None Input SMILES
inchikey str | None InChIKey identifier
phase str Phase ('g', 'l', 's')
warnings list[str] Validation warnings
rdmol Chem.Mol RDKit molecule object

Example with Output:

from phoenix import Compound

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

print(f"Formula: {tnt.formula}")            # C7H5N3O6
print(f"MW: {tnt.molecular_weight:.2f}")    # 227.13
print(f"Composition: {tnt.composition}")    # {'C': 7, 'N': 3, 'O': 6, 'H': 5}
print(f"Atom count: {tnt.num_atoms}")       # 21
print(f"InChIKey: {tnt.inchikey}")          # SPSSULHKWOKEEL-UHFFFAOYSA-N

Thermodynamic Properties

Property Type Description
enthalpy_of_formation ThermoPropertyAccessor ΔHf° accessor
delta_hf_kJ_mol float ΔHf° at 298.15 K (shortcut)
entropy ThermoPropertyAccessor S° accessor
entropy_J_mol_K float S° at 298.15 K (shortcut)
oxygen_balance float Oxygen balance (OB%)
oxygen_balance_percent float Alias for oxygen_balance

Example with Output:

from phoenix import Compound

ethanol = Compound.from_smiles("CCO")

# Access as property (returns value at 298.15 K)
hf = ethanol.enthalpy_of_formation
print(f"ΔHf° = {hf.value:.1f} {hf.unit}")  # ΔHf° = -235.1 kJ/mol

# Quick access shortcuts
print(f"ΔHf° = {ethanol.delta_hf_kJ_mol:.1f} kJ/mol")  # -235.1 kJ/mol
print(f"S° = {ethanol.entropy_J_mol_K:.1f} J/(mol·K)") # 289.9 J/(mol·K)
print(f"OB% = {ethanol.oxygen_balance:.1f}%")          # -208.6%

ThermoPropertyAccessor

The enthalpy_of_formation and entropy properties return a ThermoPropertyAccessor that supports both property and method access:

from phoenix import Compound
import numpy as np

compound = Compound.from_smiles("CCO")

# As property: value at 298.15 K
hf = compound.enthalpy_of_formation
print(hf.value)  # -235.1

# As method: value at specified temperature
hf_500 = compound.enthalpy_of_formation(T=500)
print(f"ΔHf(500K) = {hf_500.value:.1f} kJ/mol")  # -227.8 kJ/mol

# Vectorized with NumPy arrays
temps = np.array([300, 400, 500, 600])
values = compound.enthalpy_of_formation(T=temps)
print(values)  # Array of ΔHf values at each temperature

Methods

Thermodynamic Methods

heat_capacity

def heat_capacity(self, temperature_K: float = 298.15) -> ThermoProperty

Calculate heat capacity at specified temperature.

Parameters:

Name Type Default Description
temperature_K float 298.15 Temperature in Kelvin

Returns: ThermoProperty with value in J/(mol·K)

Example:

compound = Compound.from_smiles("CCO")
cp = compound.heat_capacity(temperature_K=400)
print(f"Cp(400K) = {cp.value:.1f} {cp.unit}")  # Cp(400K) = 92.3 J/(mol·K)

thermo_at

def thermo_at(self, *, T: float) -> ThermoState

Get all thermodynamic properties at specified temperature. Returns a ThermoState object with cached properties.

Parameters:

Name Type Description
T float Temperature in Kelvin (keyword-only)

Returns: ThermoState with properties: H, S, Cp, G

Example:

compound = Compound.from_smiles("CCO")
state = compound.thermo_at(T=500)

print(f"H(500K)  = {state.H.value:.1f} kJ/mol")      # -227.8 kJ/mol
print(f"S(500K)  = {state.S.value:.1f} J/(mol·K)")   # 336.2 J/(mol·K)
print(f"Cp(500K) = {state.Cp.value:.1f} J/(mol·K)")  # 109.5 J/(mol·K)
print(f"G(500K)  = {state.G.value:.1f} kJ/mol")      # -395.9 kJ/mol

# Alternative property names
print(state.enthalpy.value)      # Same as state.H
print(state.entropy.value)       # Same as state.S
print(state.heat_capacity.value) # Same as state.Cp
print(state.gibbs_energy.value)  # Same as state.G

Hazard Methods

max_decomposition

def max_decomposition(
    self,
    *,
    method: str = "hierarchy",
    gas_temperature_K: float = 298.15,
) -> DecompositionResult | DecompositionComparison

Calculate maximum heat of decomposition (ΔHd).

Parameters:

Name Type Default Description
method str "hierarchy" Method: 'hierarchy', 'lp', or 'both'
gas_temperature_K float 298.15 Temperature for gas volume calculations

Returns:

  • DecompositionResult when method is 'hierarchy' or 'lp'
  • DecompositionComparison when method is 'both'

Example:

from phoenix import Compound

nb = Compound.from_smiles("c1ccccc1[N+](=O)[O-]")  # Nitrobenzene

# Default (hierarchy method)
decomp = nb.max_decomposition()
print(f"ΔHd = {decomp.delta_hd_kJ_mol:.1f} kJ/mol")  # -552.2 kJ/mol
print(f"ΔHd = {decomp.delta_hd_cal_g:.1f} cal/g")    # -1072.0 cal/g

# Decomposition products
for product, moles in decomp.products.items():
    if moles > 0.01:
        print(f"  {product}: {moles:.2f} mol")
# Output:
#   N2: 0.50 mol
#   H2O: 2.00 mol
#   C: 6.00 mol
#   H2: 0.50 mol

# Compare both methods
comparison = nb.max_decomposition(method="both")
print(f"Hierarchy: {comparison.hierarchy_delta_hd:.1f} kJ/mol")
print(f"LP: {comparison.lp_delta_hd:.1f} kJ/mol")
print(f"Deviation: {comparison.deviation_percent:.2f}%")

evaluate_hazard

def evaluate_hazard(self) -> HazardResult

Perform full hazard evaluation using CHETAH criteria.

Returns: HazardResult with classification and decomposition data

Example:

from phoenix import Compound

tnt = Compound.from_smiles("Cc1c([N+](=O)[O-])cc([N+](=O)[O-])cc1[N+](=O)[O-]")
result = tnt.evaluate_hazard()

print(f"Hazard Class: {result.hazard_class}")        # HIGH
print(f"Max ΔHd: {result.max_decomposition_kJ_mol:.1f} kJ/mol")  # -1294.8 kJ/mol
print(f"Max ΔHd: {result.max_decomposition_cal_g:.1f} cal/g")    # -1362.4 cal/g

# Triggered CHETAH criteria
print(f"Triggered criteria: {result.triggered_criteria}")
# Example: ['Criterion 1: ΔHd < -300 cal/g']

# Functional group alerts
for alert in result.functional_group_alerts:
    print(f"Alert: {alert.group_name} - {alert.severity}")

detect_functional_groups

def detect_functional_groups(self) -> list[FunctionalGroupAlert]

Detect hazardous functional groups in the molecule.

Returns: List of FunctionalGroupAlert objects

Example:

from phoenix import Compound

rdx = Compound.from_smiles("O=[N+]([O-])N1CN([N+](=O)[O-])CN([N+](=O)[O-])C1")
alerts = rdx.detect_functional_groups()

for alert in alerts:
    print(f"{alert.group_name}: {alert.count} occurrence(s)")
    print(f"  Severity: {alert.severity}")
# Output:
#   Nitro (N-NO2): 3 occurrence(s)
#     Severity: HIGH

Magic Methods

Method Description Example Output
__repr__ Debug representation Compound('CCO')
__str__ String representation C2H6O (MW=46.07)
__eq__ Equality by canonical SMILES compound1 == compound2
__hash__ Hash by canonical SMILES Enables use in sets/dicts
from phoenix import Compound

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

print(repr(c1))    # Compound('CCO')
print(str(c1))     # C2H6O (MW=46.07)
print(c1 == c2)    # True (same canonical SMILES)

# Use in collections
compounds = {c1, c2}  # Set contains only 1 element
print(len(compounds)) # 1

Constants

Supported Elements

SUPPORTED_ELEMENTS = frozenset({"C", "H", "N", "O", "S", "P", "F", "Cl", "Br"})

Compounds containing other elements raise UnsupportedElementError.

Large Molecule Warning

LARGE_MOLECULE_THRESHOLD = 100  # atoms

Molecules with more than 100 atoms generate a UserWarning:

import warnings
from phoenix import Compound

with warnings.catch_warnings(record=True) as w:
    warnings.simplefilter("always")
    large_mol = Compound.from_smiles("C" * 101)  # Very large alkane
    if w:
        print(w[0].message)  # Warning about large molecule