Skip to content

Functional Group Detection

PHOENIX detects reactive functional groups that may indicate chemical hazards.

Overview

Functional group detection uses SMARTS patterns to identify structural features associated with:

  • Explosive potential
  • Thermal instability
  • Shock sensitivity
  • Reactivity concerns

Screening Only

Functional group alerts are for screening purposes. The presence of a functional group does not guarantee hazardous behavior.

Quick Usage

from phoenix import Compound

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

# Check alerts in hazard result
if result.functional_group_alerts:
    print("Alerts detected:")
    for alert in result.functional_group_alerts:
        print(f"  - {alert}")

Detailed Detection

For more information about detected groups:

from phoenix.hazard.functional_groups import detect_functional_groups
from phoenix import Compound

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

for alert in alerts:
    print(f"Name: {alert.name}")
    print(f"  Count: {alert.count}")
    print(f"  SMARTS: {alert.smarts}")
    print(f"  Risk: {alert.description}")

FunctionalGroupAlert

Each detection returns a FunctionalGroupAlert:

Attribute Type Description
name str Name of the functional group
smarts str SMARTS pattern used
count int Number of occurrences
description str Hazard description

Detected Groups

Nitro Compounds

Name SMARTS Risk
Nitro group [N+](=O)[O-] Shock-sensitive, explosive
Aromatic nitro c[N+](=O)[O-] Potential explosive
# Examples
nitrobenzene = Compound.from_smiles("c1ccccc1[N+](=O)[O-]")
nitromethane = Compound.from_smiles("C[N+](=O)[O-]")

Peroxides

Name SMARTS Risk
Peroxide [OX2][OX2] Heat/shock/friction sensitive
Organic peroxide [CX4][OX2][OX2][CX4] Explosive decomposition
Peroxy acid [CX3](=O)[OX2][OX2] Oxidizer, explosive
Hydroperoxide [OX2][OX2H] Unstable
# Examples
h2o2 = Compound.from_smiles("OO")
mekp = Compound.from_smiles("CC(C)(OO)c1ccccc1")  # MEKP-like

Azo and Diazo Compounds

Name SMARTS Risk
Azo group [NX2]=[NX2] Gas-releasing decomposition
Diazo group [CX3]=[N+]=[N-] Highly reactive
Diazonium [N+]#N Shock-sensitive explosive
# Examples
azobenzene = Compound.from_smiles("c1ccc(N=Nc2ccccc2)cc1")

Azides

Name SMARTS Risk
Azide group [N-]=[N+]=[N-] Primary explosive
Organic azide [CX4][N-]=[N+]=[N-] Detonation risk
# Example
methyl_azide = Compound.from_smiles("CN=[N+]=[N-]")

N-Oxides and Nitroso

Name SMARTS Risk
N-oxide [NX3+]([O-]) Oxidizer potential
Nitroso group [NX2]=O Unstable

Nitrates and Nitrites

Name SMARTS Risk
Nitrate ester [OX2][N+](=O)[O-] Explosive (nitroglycerin)
Nitrite ester [OX2][NX2]=O Unstable
# Example (nitroglycerin-like)
nitrate = Compound.from_smiles("CON(=O)=O")  # Methyl nitrate

Nitrogen-Nitrogen Bonds

Name SMARTS Risk
Hydrazine [NX3H2][NX3H2] Toxic, explosive
Hydrazide [CX3](=O)[NX3][NX3] Violent decomposition
# Examples
hydrazine = Compound.from_smiles("NN")

Strained Rings

Name SMARTS Risk
Epoxide C1OC1 Reactive, violent polymerization
Aziridine C1NC1 Strained, reactive
# Examples
ethylene_oxide = Compound.from_smiles("C1CO1")

Acyl Halides and Anhydrides

Name SMARTS Risk
Acyl halide [CX3](=O)[F,Cl,Br,I] Highly reactive
Acid anhydride [CX3](=O)[OX2][CX3](=O) Water-reactive
# Examples
acetyl_chloride = Compound.from_smiles("CC(=O)Cl")
acetic_anhydride = Compound.from_smiles("CC(=O)OC(=O)C")

Other

Name SMARTS Risk
Acetylide [CX2]#[CX2-] Shock-sensitive

Multiple Groups

Compounds with multiple reactive groups are flagged individually:

# TNT has multiple nitro groups
tnt = Compound.from_smiles("Cc1c([N+](=O)[O-])cc([N+](=O)[O-])cc1[N+](=O)[O-]")
alerts = detect_functional_groups(tnt)

for alert in alerts:
    print(f"{alert.name}: {alert.count} occurrence(s)")

Using in Hazard Evaluation

Functional groups trigger CHETAH Criterion 4:

from phoenix import Compound

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

# Check if criterion 4 triggered
if 4 in result.triggered_criteria:
    print("Criterion 4 triggered by functional groups:")
    for alert in result.functional_group_alerts:
        print(f"  - {alert}")

Custom SMARTS Queries

For advanced analysis, use RDKit directly:

from rdkit import Chem
from phoenix import Compound

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

# Custom SMARTS pattern
custom_pattern = "[c][N+](=O)[O-]"  # Aromatic nitro
pattern = Chem.MolFromSmarts(custom_pattern)

matches = mol.GetSubstructMatches(pattern)
print(f"Found {len(matches)} matches")

Combining with Other Criteria

Cross-reference functional groups with other hazard indicators:

from phoenix import Compound

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

print(f"Hazard Class: {result.hazard_class}")
print(f"Criteria: {result.triggered_criteria}")
print(f"ΔHd: {result.max_decomposition_cal_g:.0f} cal/g")
print(f"OB%: {result.oxygen_balance_percent:.1f}%")
print(f"Alerts: {result.functional_group_alerts}")

# Correlation check
if 1 in result.triggered_criteria and 4 in result.triggered_criteria:
    print("WARNING: High ΔHd confirmed by functional group presence")

Batch Analysis of Alerts

from phoenix import screen

smiles_list = [
    "c1ccccc1[N+](=O)[O-]",  # Nitrobenzene
    "NN",                     # Hydrazine
    "C1CO1",                  # Ethylene oxide
    "CCO",                    # Ethanol (no alerts)
]

results = screen(smiles_list)
df = results.dataframe

# Count compounds with alerts
has_alerts = df['alerts'].apply(lambda x: len(x) > 0 if x else False)
print(f"Compounds with alerts: {has_alerts.sum()}/{len(df)}")

# Most common alerts
from collections import Counter
all_alerts = []
for alerts in df['alerts'].dropna():
    all_alerts.extend(alerts)

print("\nAlert frequency:")
for alert, count in Counter(all_alerts).most_common():
    print(f"  {alert}: {count}")

Next Steps