Extensible periodic table of the elements with support for mass, density and X-ray/neutron scattering information
—
Parse chemical formulas, calculate molecular properties, create mixtures by weight or volume, and perform isotope substitutions with full support for composite material calculations and property estimation.
Parse chemical formula strings into Formula objects with automatic composition analysis and property calculation capabilities.
def formula(compound: str = None, density: float = None,
natural_density: float = None, name: str = None,
table: PeriodicTable = None) -> Formula:
"""
Parse chemical formula string into Formula object.
Args:
compound: Chemical formula string (e.g., 'H2O', 'CaCO3', 'C6H12O6')
density: Known density in g/cm³
natural_density: Density with natural isotope abundances
name: Descriptive name for the compound
table: Custom periodic table to use for parsing
Returns:
Formula object with composition and properties
"""Usage examples:
import periodictable as pt
# Simple molecules
water = pt.formula("H2O")
methane = pt.formula("CH4")
glucose = pt.formula("C6H12O6")
# Complex formulas with parentheses
calcium_phosphate = pt.formula("Ca3(PO4)2")
hydrated_salt = pt.formula("CuSO4·5H2O", name="Copper sulfate pentahydrate")
# With known density
quartz = pt.formula("SiO2", density=2.65, name="Quartz")
# Isotope-specific formulas
heavy_water = pt.formula("D2O", name="Heavy water")
carbon13_methane = pt.formula("13CH4")
# Formula with charges
sodium_chloride = pt.formula("NaCl") # Neutral overallCreate homogeneous mixtures by specifying weight percentages of different materials with automatic density and property calculations.
def mix_by_weight(*args, density: float = None,
natural_density: float = None, name: str = None,
table: PeriodicTable = None) -> Formula:
"""
Create mixture apportioned by weight percentages.
Args:
*args: Alternating formula/weight pairs (formula1, weight1, formula2, weight2, ...)
density: Known density of mixture
natural_density: Density with natural abundances
name: Descriptive name for mixture
table: Custom periodic table
Returns:
Formula representing the mixture
"""Usage examples:
import periodictable as pt
# Simple mixture: 80% H2O, 20% D2O by weight
water_mixture = pt.mix_by_weight("H2O", 80, "D2O", 20, name="Mixed water")
# Complex mixture
steel_alloy = pt.mix_by_weight(
"Fe", 95.0, # 95% iron
"C", 0.8, # 0.8% carbon
"Mn", 2.0, # 2% manganese
"Si", 1.2, # 1.2% silicon
"Cr", 1.0, # 1% chromium
density=7.85,
name="Carbon steel"
)
# Using Formula objects
concrete_mix = pt.mix_by_weight(
pt.formula("Ca(OH)2"), 15, # Lime
pt.formula("SiO2"), 60, # Silica
pt.formula("Al2O3"), 25, # Alumina
name="Concrete mixture"
)
print(f"Steel composition: {steel_alloy.atoms}")
print(f"Steel density: {steel_alloy.density} g/cm³")Create homogeneous mixtures by specifying volume percentages with density calculations based on component volumes.
def mix_by_volume(*args, density: float = None,
natural_density: float = None, name: str = None,
table: PeriodicTable = None) -> Formula:
"""
Create mixture apportioned by volume percentages.
Args:
*args: Alternating formula/volume pairs (formula1, vol1, formula2, vol2, ...)
density: Known density of mixture
natural_density: Density with natural abundances
name: Descriptive name for mixture
table: Custom periodic table
Returns:
Formula representing the mixture
"""Usage examples:
import periodictable as pt
# Air composition by volume
air = pt.mix_by_volume(
"N2", 78.08, # Nitrogen
"O2", 20.95, # Oxygen
"Ar", 0.93, # Argon
"CO2", 0.04, # Carbon dioxide
name="Air at STP"
)
# Liquid mixture
ethanol_water = pt.mix_by_volume(
pt.formula("C2H5OH", density=0.789), 40, # 40% ethanol by volume
pt.formula("H2O", density=1.0), 60, # 60% water by volume
name="40% ethanol solution"
)
print(f"Air molecular composition: {air.hill}")
print(f"Ethanol solution density: {ethanol_water.density:.3f} g/cm³")Access comprehensive molecular properties including mass, charge, composition analysis, and derived quantities.
class Formula:
"""Chemical formula with composition and property calculations."""
# Core composition
atoms: dict # Dictionary mapping atoms to counts
mass: float # Molecular mass in atomic mass units (u)
molecular_mass: float # Molecular mass in grams
# Physical properties
density: float # Mass density in g/cm³
natural_density: float # Density with natural isotopes
# Chemical properties
charge: int # Net electrical charge
hill: str # Formula in Hill notation (C, H, others alphabetical)
# Composition analysis
mass_fraction: dict # Fractional mass of each component
def natural_mass_ratio(self) -> float:
"""Ratio of natural to isotope-substituted mass."""Usage examples:
import periodictable as pt
# Create and analyze glucose
glucose = pt.formula("C6H12O6", density=1.54)
# Basic properties
print(f"Molecular formula: {glucose.hill}") # C6H12O6
print(f"Molecular mass: {glucose.mass:.2f} u") # 180.16 u
print(f"Mass in grams: {glucose.molecular_mass:.2e} g") # 2.99e-22 g
print(f"Density: {glucose.density} g/cm³") # 1.54
# Composition analysis
print("Mass fractions:")
for atom, fraction in glucose.mass_fraction.items():
print(f" {atom.symbol}: {fraction:.3f}")
# Atomic composition
print("Atomic composition:")
for atom, count in glucose.atoms.items():
print(f" {atom.symbol}: {count}")
# For charged species
salt = pt.formula("Na+Cl-") # Conceptual - parser handles neutral compounds
print(f"Net charge: {salt.charge}") # 0 (neutral overall)Replace specific elements with isotopes to study isotope effects on properties and scattering characteristics.
def replace(self, source, target, portion: float = 1) -> 'Formula':
"""
Create new formula with isotope substitution.
Args:
source: Element to replace (Element, Isotope, or Ion)
target: Replacement isotope/element
portion: Fraction to replace (0-1, default 1 for complete)
Returns:
New Formula with substitution applied
"""Usage examples:
import periodictable as pt
# Deuteration of water
water = pt.formula("H2O")
heavy_water = water.replace(pt.H, pt.D) # Replace all H with D
print(f"H2O mass: {water.mass:.3f} u") # ~18.015 u
print(f"D2O mass: {heavy_water.mass:.3f} u") # ~20.028 u
# Partial deuteration (50%)
partial_d2o = water.replace(pt.H, pt.D, portion=0.5)
print(f"50% deuterated mass: {partial_d2o.mass:.3f} u")
# Carbon-13 labeling in glucose
glucose = pt.formula("C6H12O6")
c13_glucose = glucose.replace(pt.C, pt.C[13]) # All carbon to C-13
print(f"Natural glucose: {glucose.mass:.3f} u")
print(f"C-13 glucose: {c13_glucose.mass:.3f} u")
# Partial labeling
glucose_1c13 = glucose.replace(pt.C, pt.C[13], portion=1/6) # One C-13
print(f"One C-13 glucose: {glucose_1c13.mass:.3f} u")Estimate molecular volumes and densities using crystallographic data and packing factors for materials without known densities.
def volume(self, packing_factor: str = None, **kwargs) -> float:
"""
Estimate unit cell volume using atomic radii and packing factors.
Args:
packing_factor: Crystal structure ('cubic', 'fcc', 'bcc', 'hcp', 'diamond')
**kwargs: Additional crystallographic parameters
Returns:
Estimated volume in Ų
"""Available packing factors and their values:
PACKING_FACTORS = {
'cubic': π/6, # Simple cubic (52.4%)
'bcc': π*√3/8, # Body-centered cubic (68.0%)
'fcc': π/√18, # Face-centered cubic (74.0%)
'hcp': π/√18, # Hexagonal close-packed (74.0%)
'diamond': π*√3/16 # Diamond structure (34.0%)
}Usage examples:
import periodictable as pt
# Estimate volume for different crystal structures
silicon = pt.formula("Si")
# Different packing assumptions
vol_diamond = silicon.volume(packing_factor='diamond') # Diamond structure
vol_fcc = silicon.volume(packing_factor='fcc') # FCC assumption
print(f"Si diamond structure volume: {vol_diamond:.2f} Ų")
print(f"Si FCC structure volume: {vol_fcc:.2f} Ų")
# For compounds - estimates based on component radii
nacl = pt.formula("NaCl")
nacl_volume = nacl.volume(packing_factor='cubic')
print(f"NaCl estimated volume: {nacl_volume:.2f} Ų")
# Compare with known density
quartz = pt.formula("SiO2", density=2.65)
estimated_vol = quartz.volume()
actual_vol = quartz.mass / (quartz.density * 6.022e23) * 1e24 # Convert to Ų
print(f"SiO2 estimated volume: {estimated_vol:.2f} Ų")
print(f"SiO2 actual volume: {actual_vol:.2f} Ų")Switch formulas between different periodic tables and access custom element data for specialized calculations.
def change_table(self, table: PeriodicTable) -> 'Formula':
"""
Create equivalent formula using different periodic table.
Args:
table: Target periodic table with custom data
Returns:
New Formula using the specified table
"""Usage examples:
import periodictable as pt
from periodictable.core import PeriodicTable
# Create custom table with modified properties
custom_table = PeriodicTable()
# ... customize table properties ...
# Convert formula to use custom table
water = pt.formula("H2O")
custom_water = water.change_table(custom_table)
print(f"Original table: {water.atoms[pt.H].mass}")
print(f"Custom table: {custom_water.atoms[custom_table.H].mass}")Additional functions for formula parsing, composition analysis, and formatting for different output formats.
def count_elements(compound, by_isotope: bool = False) -> dict:
"""
Count elements in a compound without creating Formula object.
Args:
compound: Formula string or Formula object
by_isotope: Count isotopes separately if True
Returns:
Dictionary of element/isotope counts
"""
def pretty(compound, mode: str = 'unicode') -> str:
"""
Format chemical formula with subscripts and superscripts.
Args:
compound: Formula string or Formula object
mode: Output format ('unicode', 'html', 'latex')
Returns:
Formatted formula string
"""
def parse_formula(formula_str: str, table: PeriodicTable = None) -> list:
"""
Parse formula string into list of (element, count) tuples.
Args:
formula_str: Chemical formula string
table: Periodic table to use for parsing
Returns:
List of (atom, count) tuples
"""Usage examples:
import periodictable as pt
from periodictable.formulas import count_elements, pretty, parse_formula
# Count elements
glucose_counts = count_elements("C6H12O6")
print(glucose_counts) # {'C': 6, 'H': 12, 'O': 6}
# Format with subscripts
water_pretty = pretty("H2O")
print(water_pretty) # H₂O (with Unicode subscripts)
caffeine_pretty = pretty("C8H10N4O2", mode='html')
print(caffeine_pretty) # C<sub>8</sub>H<sub>10</sub>N<sub>4</sub>O<sub>2</sub>
# Parse formula structure
parsed = parse_formula("Ca(OH)2")
print(parsed) # [(Ca, 1), (O, 2), (H, 2)]class Formula:
"""Chemical formula with composition and property calculations."""
atoms: dict
mass: float
molecular_mass: float
density: float
natural_density: float
charge: int
hill: str
mass_fraction: dict
def replace(self, source, target, portion: float = 1) -> 'Formula': ...
def volume(self, packing_factor: str = None, **kwargs) -> float: ...
def change_table(self, table: PeriodicTable) -> 'Formula': ...
def natural_mass_ratio(self) -> float: ...
def neutron_sld(self, wavelength: float = None, energy: float = None) -> tuple: ...
def xray_sld(self, energy: float = None, wavelength: float = None) -> tuple: ...
PACKING_FACTORS: dict = {
'cubic': float,
'bcc': float,
'fcc': float,
'hcp': float,
'diamond': float
}Install with Tessl CLI
npx tessl i tessl/pypi-periodictable