Extensible periodic table of the elements with support for mass, density and X-ray/neutron scattering information
—
Fundamental physical constants, crystallographic calculations, uncertainty parsing, periodic table data visualization tools, and biomolecule support for scientific applications.
Fundamental physical constants used throughout the periodictable package for calculations and unit conversions.
# From periodictable.constants module
avogadro_number: float = 6.02214076e23 # Avogadro's number (mol⁻¹)
planck_constant: float = 6.62607015e-34 # Planck constant (J⋅Hz⁻¹)
electron_volt: float = 1.602176634e-19 # Electron volt (J/eV)
speed_of_light: float = 299792458 # Speed of light (m/s)
electron_radius: float = 2.8179403205e-15 # Classical electron radius (m)
neutron_mass: float = 1.00866491606 # Neutron mass (u)
atomic_mass_constant: float = 1.66053906892e-27 # Atomic mass unit (kg/u)
electron_mass: float = 5.485799090441e-4 # Electron mass (u)Usage examples:
from periodictable.constants import *
import periodictable as pt
# Unit conversions
print(f"Avogadro's number: {avogadro_number:.6e} mol⁻¹")
print(f"eV to Joules: {electron_volt:.6e} J/eV")
print(f"Speed of light: {speed_of_light} m/s")
# Molecular mass calculations
water = pt.formula("H2O")
mass_grams = water.mass * atomic_mass_constant * 1000 # Convert to grams
molecules_per_mole = avogadro_number
molar_mass = mass_grams * molecules_per_mole
print(f"Water molar mass: {molar_mass:.2f} g/mol")
# Energy calculations
lambda_cu = 1.5418 # Å, Cu K-alpha
energy_joules = planck_constant * speed_of_light / (lambda_cu * 1e-10)
energy_ev = energy_joules / electron_volt
energy_kev = energy_ev / 1000
print(f"Cu K-alpha: {lambda_cu} Å = {energy_kev:.3f} keV")
# Classical electron radius in X-ray calculations
print(f"Electron radius: {electron_radius:.6e} m")Calculate unit cell volumes and properties for crystallographic applications and density estimations.
def cell_volume(a: float = None, b: float = None, c: float = None,
alpha: float = None, beta: float = None,
gamma: float = None) -> float:
"""
Calculate crystallographic unit cell volume.
Args:
a, b, c: Unit cell lengths in Ångström
alpha, beta, gamma: Unit cell angles in degrees
Returns:
Unit cell volume in ų (cubic Ångström)
Note:
For cubic: provide a only
For tetragonal: provide a, c
For orthorhombic: provide a, b, c
For hexagonal: provide a, c (gamma=120° assumed)
For general case: provide all parameters
"""Usage examples:
from periodictable.util import cell_volume
import math
# Cubic crystal (e.g., simple cubic silicon)
a_cubic = 5.43 # Å, silicon lattice parameter
vol_cubic = cell_volume(a=a_cubic)
print(f"Cubic Si unit cell: {vol_cubic:.2f} ų")
# Face-centered cubic (real silicon structure)
a_fcc = 5.43 # Å
vol_fcc = cell_volume(a=a_fcc) # Same as cubic for FCC primitive cell
atoms_per_unit_cell = 8 # FCC has 8 atoms per conventional cell
vol_per_atom = vol_fcc / atoms_per_unit_cell
print(f"Si volume per atom: {vol_per_atom:.2f} ų/atom")
# Hexagonal (e.g., graphite)
a_hex = 2.46 # Å
c_hex = 6.71 # Å
vol_hex = cell_volume(a=a_hex, c=c_hex, gamma=120)
print(f"Hexagonal graphite: {vol_hex:.2f} ų")
# Orthorhombic
a_ortho, b_ortho, c_ortho = 4.0, 5.0, 6.0 # Å
vol_ortho = cell_volume(a=a_ortho, b=b_ortho, c=c_ortho)
print(f"Orthorhombic cell: {vol_ortho:.2f} ų")
# General triclinic case
vol_triclinic = cell_volume(a=5.0, b=6.0, c=7.0,
alpha=85, beta=90, gamma=95)
print(f"Triclinic cell: {vol_triclinic:.2f} ų")
# Density calculation from unit cell
silicon = pt.formula("Si")
atoms_per_cell = 8 # Diamond cubic
cell_vol_cm3 = vol_cubic * 1e-24 # Convert ų to cm³
mass_per_cell = atoms_per_cell * silicon.mass * 1.66054e-24 # u to g
calculated_density = mass_per_cell / cell_vol_cm3
print(f"Calculated Si density: {calculated_density:.2f} g/cm³")Parse scientific notation with uncertainties in parenthetical format commonly used in scientific literature.
def parse_uncertainty(s: str) -> tuple:
"""
Parse uncertainty notation like "23.0035(12)" into value and uncertainty.
Args:
s: String with uncertainty notation
Returns:
tuple: (value, uncertainty) as floats
Examples:
"23.0035(12)" -> (23.0035, 0.0012)
"1.234(5)" -> (1.234, 0.005)
"456.78(123)" -> (456.78, 1.23)
"""Usage examples:
from periodictable.util import parse_uncertainty
# Scientific measurements with uncertainties
measurements = [
"23.0035(12)", # NIST atomic mass format
"1.234(5)", # Standard uncertainty notation
"456.78(123)", # Large uncertainty
"0.0012345(67)", # Small values
"1234567(89)" # Integer-like with uncertainty
]
print("Parsed uncertainties:")
for measurement in measurements:
value, uncertainty = parse_uncertainty(measurement)
print(f"{measurement:12s} -> {value} ± {uncertainty}")
# Use in atomic mass calculations
import periodictable as pt
# Simulate parsing atomic mass with uncertainty
mass_string = "55.845(2)" # Iron atomic mass with uncertainty
mass_value, mass_uncertainty = parse_uncertainty(mass_string)
print(f"\nIron atomic mass: {mass_value} ± {mass_uncertainty} u")
print(f"Relative uncertainty: {mass_uncertainty/mass_value:.2e}")
# Error propagation example
iron = pt.Fe
print(f"Tabulated Fe mass: {iron.mass} u")
print(f"Difference from parsed: {abs(iron.mass - mass_value):.6f} u")Create visual representations and formatted tables of periodic table data for analysis and presentation.
def table_plot(data, form: str = "line", label: str = None,
title: str = None) -> None:
"""
Plot periodic table data in various formats.
Args:
data: Dictionary mapping elements to values, or property name string
form: Plot type ("line", "bar", "scatter", "table")
label: Y-axis label for the data
title: Plot title
Note:
Requires matplotlib for plotting functionality
"""Usage examples:
from periodictable.plot import table_plot
import periodictable as pt
# Plot atomic masses
try:
mass_data = {el: el.mass for el in pt.elements if el.mass}
table_plot(mass_data, form="line", label="Atomic Mass (u)",
title="Atomic Masses vs Atomic Number")
# Plot densities
density_data = {el: el.density for el in pt.elements
if el.density and el.number <= 86}
table_plot(density_data, form="scatter", label="Density (g/cm³)",
title="Element Densities")
# Plot covalent radii (triggers lazy loading)
radii_data = {}
for el in pt.elements:
if el.number <= 54: # First few periods
try:
radii_data[el] = el.covalent_radius
except (AttributeError, TypeError):
pass
table_plot(radii_data, form="bar", label="Covalent Radius (Å)",
title="Covalent Radii")
except ImportError:
print("Matplotlib required for plotting")
# Create data tables without plotting
elements_subset = [pt.H, pt.He, pt.Li, pt.Be, pt.B, pt.C, pt.N, pt.O]
print("Light element properties:")
print("El Mass Density")
for el in elements_subset:
density = el.density if el.density else "N/A"
print(f"{el.symbol:2s} {el.mass:8.4f} {density}")Support for biological molecules including amino acids, nucleic acids, and common biochemical compounds with labile hydrogen tracking.
# From periodictable.fasta module
class Molecule:
"""Biomolecule with labile hydrogen support for deuteration studies."""
def __init__(self, formula: str = None, name: str = None,
labile: int = 0, charge: int = 0):
"""
Args:
formula: Chemical formula
name: Molecule name
labile: Number of labile hydrogens
charge: Net charge
"""
class Sequence(Molecule):
"""Read amino acid and DNA/RNA sequences from FASTA-like strings."""
def __init__(self, sequence: str, molecule_type: str = "protein"):
"""
Args:
sequence: FASTA sequence string
molecule_type: "protein", "DNA", or "RNA"
"""
# Standard biochemical data
AMINO_ACID_CODES: dict # 20 standard amino acids by single letter
RNA_CODES: dict # RNA nucleotides (A, U, G, C)
DNA_CODES: dict # DNA nucleotides (A, T, G, C)
RNA_BASES: dict # Individual RNA bases
DNA_BASES: dict # Individual DNA bases
NUCLEIC_ACID_COMPONENTS: dict # Nucleic acid molecular components
LIPIDS: dict # Common lipid molecules
CARBOHYDRATE_RESIDUES: dict # Sugar and carbohydrate units
# Reference SLD values
H2O_SLD: float = -0.5603 # H2O neutron SLD at 20°C (10⁻⁶ Ų⁻²)
D2O_SLD: float = 6.3350 # D2O neutron SLD at 20°C (10⁻⁶ Ų⁻²)Usage examples:
from periodictable.fasta import *
import periodictable as pt
# Amino acids
print("Standard amino acids:")
for code, amino_acid in list(AMINO_ACID_CODES.items())[:5]:
print(f"{code}: {amino_acid.name} - {amino_acid.formula}")
# Create peptide sequence
peptide_seq = "MVLSPADKTNVKAAW" # Sample sequence
protein = Sequence(peptide_seq, molecule_type="protein")
print(f"\nPeptide formula: {protein.formula}")
print(f"Molecular mass: {protein.mass:.2f} u")
# DNA sequence
dna_seq = "ATCGATCGATCG"
dna = Sequence(dna_seq, molecule_type="DNA")
print(f"DNA formula: {dna.formula}")
# Labile hydrogen tracking for deuteration
# Water molecules
h2o = Molecule("H2O", labile=2) # Both H are labile
print(f"H2O labile H: {h2o.labile}")
# Reference SLD values for contrast calculations
print(f"\nSolvent SLD values:")
print(f"H2O: {H2O_SLD:.4f} × 10⁻⁶ Ų⁻²")
print(f"D2O: {D2O_SLD:.4f} × 10⁻⁶ Ų⁻²")
print(f"Contrast: {D2O_SLD - H2O_SLD:.4f} × 10⁻⁶ Ų⁻²")
# Lipids and carbohydrates
print("\nBiochemical molecules:")
for name, molecule in list(LIPIDS.items())[:3]:
print(f"{name}: {molecule.formula}")
for name, molecule in list(CARBOHYDRATE_RESIDUES.items())[:3]:
print(f"{name}: {molecule.formula}")Support for neutron activation analysis calculations including sample preparation and flux environment modeling.
# From periodictable.activation module
class Sample:
"""Neutron activation sample with composition and irradiation history."""
def __init__(self, formula, mass: float = None, name: str = None):
"""
Args:
formula: Sample chemical formula
mass: Sample mass in grams
name: Sample identifier
"""
class ActivationEnvironment:
"""Neutron flux environment for activation calculations."""
def __init__(self, flux: float, energy: str = "thermal"):
"""
Args:
flux: Neutron flux in n/cm²/s
energy: Neutron energy spectrum ("thermal", "epithermal", "fast")
"""
class ActivationResult:
"""Results from neutron activation calculations."""
@property
def activity(self) -> dict:
"""Activities by isotope in Bq."""
@property
def dose_rate(self) -> float:
"""Dose rate in μSv/hr at 1m."""Usage examples:
from periodictable.activation import *
import periodictable as pt
# Create sample for activation analysis
steel_sample = Sample("Fe + 0.8%C + 2%Mn", mass=10.0, name="Steel sample")
aluminum_sample = Sample("Al", mass=5.0, name="Al foil")
# Define neutron environment
reactor_flux = ActivationEnvironment(flux=1e13, energy="thermal") # n/cm²/s
print(f"Reactor thermal flux: {reactor_flux.flux:.0e} n/cm²/s")
# Calculate activation (conceptual - requires specific implementation)
print(f"Steel sample: {steel_sample.formula}")
print(f"Sample mass: {steel_sample.mass} g")
print(f"Environment: {reactor_flux.energy} neutrons")Essential utility functions for working with periodic table objects, managing custom tables, and type checking atomic objects.
# Table management functions
def default_table(table=None) -> PeriodicTable:
"""Return the default periodic table unless a specific table is requested."""
def change_table(atom, table) -> Union[Element, Isotope, Ion]:
"""Get the same element, isotope, or ion from a different periodic table."""
# Type checking functions
def isatom(val) -> bool:
"""Test if value is any atomic object (element, isotope, or ion)."""
def iselement(val) -> bool:
"""Test if value is an element (not isotope or ion)."""
def isisotope(val) -> bool:
"""Test if value is an isotope."""
def ision(val) -> bool:
"""Test if value is an ion."""Usage examples:
import periodictable as pt
from periodictable.core import (default_table, change_table,
isatom, iselement, isisotope, ision)
# Table management
default = default_table() # Get the standard table
print(f"Default table contains {len(list(default))} elements")
# Custom table workflow (conceptual)
# custom_table = create_custom_table() # User-defined table
iron_default = pt.Fe
# iron_custom = change_table(iron_default, custom_table)
# Type checking for safe operations
iron = pt.Fe
iron56 = pt.Fe[56]
iron2_ion = pt.Fe.ion[2]
print(f"Iron is atom: {isatom(iron)}") # True
print(f"Iron is element: {iselement(iron)}") # True
print(f"Iron-56 is isotope: {isisotope(iron56)}") # True
print(f"Fe²⁺ is ion: {ision(iron2_ion)}") # True
# Safe property access patterns
def get_mass_info(atom_obj):
"""Safely extract mass information from any atomic object."""
if isatom(atom_obj):
mass = atom_obj.mass
if isisotope(atom_obj):
return f"Isotope {atom_obj.symbol}-{atom_obj.isotope}: {mass:.4f} u"
elif ision(atom_obj):
charge_sign = "+" if atom_obj.charge > 0 else ""
return f"Ion {atom_obj.symbol}{charge_sign}{atom_obj.charge}: {mass:.4f} u"
else:
return f"Element {atom_obj.symbol}: {mass:.4f} u"
else:
return "Not an atomic object"
# Test type checking
for obj in [iron, iron56, iron2_ion, "not_atom"]:
print(get_mass_info(obj))# Constants (all float values)
avogadro_number: float
planck_constant: float
electron_volt: float
speed_of_light: float
electron_radius: float
neutron_mass: float
atomic_mass_constant: float
electron_mass: float
# Biomolecule data (all dict mappings)
AMINO_ACID_CODES: dict
RNA_CODES: dict
DNA_CODES: dict
RNA_BASES: dict
DNA_BASES: dict
NUCLEIC_ACID_COMPONENTS: dict
LIPIDS: dict
CARBOHYDRATE_RESIDUES: dict
# Reference values
H2O_SLD: float
D2O_SLD: float
class Molecule:
"""Biomolecule with labile hydrogen tracking."""
formula: str
name: str
labile: int
charge: int
mass: float
class Sequence(Molecule):
"""Biological sequence (protein, DNA, RNA)."""
def __init__(self, sequence: str, molecule_type: str = "protein"): ...
class Sample:
"""Activation analysis sample."""
formula: str
mass: float
name: str
class ActivationEnvironment:
"""Neutron flux environment."""
flux: float
energy: str
class ActivationResult:
"""Activation calculation results."""
@property
def activity(self) -> dict: ...
@property
def dose_rate(self) -> float: ...Install with Tessl CLI
npx tessl i tessl/pypi-periodictable