chemistrykit.crystal#

chemistrykit.crystal: crystallography and solid-state chemistry.

The 7 crystal systems and the general unit-cell-volume formula; hard- sphere packing (packing fraction, coordination number, atoms per cell) for the simple cubic, body-centered cubic, face-centered cubic, and (ideal) hexagonal close-packed lattices; ionic-crystal lattice energy via the Born-Lande and Kapustinskii equations, and the Madelung constant of the NaCl structure from a genuinely converging (Evjen-method) lattice summation; Bragg’s law and powder-XRD peak positions with structure factors (including systematic absences) for cubic lattices; and Schottky/Frenkel point-defect equilibrium; Miller indices from face intercepts, interfacial angles, and the 14 Bravais lattices; Scherrer crystallite sizes; and Pauling’s radius-ratio rule and Goldschmidt’s perovskite tolerance factor.

class chemistrykit.crystal.BodyCenteredCubicPacking[source]#

Bases: LatticePacking

Body-centered cubic (BCC) packing: 2 lattice points per cell, spheres touching along the body diagonal.

The body diagonal has length \(\sqrt3 a\) and spans 4 sphere radii (corner sphere - center sphere - opposite corner sphere), so \(r=\sqrt3a/4\); 2 atoms per conventional cell (8 corners x 1/8 + 1 body center), 8 nearest neighbors (Ashcroft & Mermin, Ch. 4).

Examples

The exact atomic packing factor is \(\sqrt3\pi/8\approx0.6802\):

>>> bool(round(float(BodyCenteredCubicPacking().packing_fraction()), 6) == round(float(np.sqrt(3.0) * np.pi / 8.0), 6))
True
atomic_radius(a)[source]#

Return the touching-sphere radius implied by lattice constant a.

Parameters:

a (float)

Returns:

float or ndarray

atoms_per_cell: float = 2.0#

Number of lattice points (spheres) per conventional unit cell.

Type:

float

coordination_number: int = 8#

Number of nearest neighbors each sphere touches.

Type:

int

unit_cell_volume(a)[source]#

Return the (conventional) unit-cell volume for lattice constant a.

Parameters:

a (float) – Cubic (or, for HCP, basal) lattice constant.

Returns:

float or ndarray

class chemistrykit.crystal.BornLandeLatticeEnergy(cation_charge, anion_charge, r0, born_exponent, madelung_constant=None)[source]#

Bases: LatticeEnergyModel

The Born-Lande equation for ionic-crystal lattice energy.

\[U = -\frac{N_A M |z_+z_-|e^2}{4\pi\varepsilon_0 r_0}\left(1-\frac1n\right)\]

The Madelung constant M accounts for the electrostatic sum over the whole lattice; the Born-repulsion correction \((1-1/n)\) accounts for short-range electron-cloud repulsion at the equilibrium spacing r0 (n the Born exponent, from chemistrykit.crystal.utils.reference_data.average_born_exponent()) (Atkins & de Paula, Physical Chemistry, 11th ed., eq. 20A.5).

Parameters:
  • cation_charge (int) – Cation charge number (e.g. 1 for Na+, 2 for Mg2+); sign is ignored (absolute value used).

  • anion_charge (int) – Anion charge number (e.g. 1 for Cl-, 2 for O2-); sign is ignored.

  • r0 (float) – Equilibrium nearest-neighbor (cation-anion) distance, in meters.

  • born_exponent (float) – Born exponent n (typically 5-12; see chemistrykit.crystal.utils.reference_data.BORN_EXPONENTS).

  • madelung_constant (float | None) – Madelung constant of the crystal structure (the default is only appropriate for a rock-salt-structure salt; other structures (CsCl, fluorite, …) have different Madelung constants not computed by this package, and must be supplied explicitly).

Examples

NaCl (\(r_0=282\,\text{pm}\), Born exponent 8 – the Ne/Ar average): the Born-Lande estimate comes out close to, but (as expected for this level of approximation) somewhat less negative than, the experimental Born-Haber-cycle value of about \(-787\,\text{kJ/mol}\):

>>> model = BornLandeLatticeEnergy(cation_charge=1, anion_charge=1, r0=282e-12, born_exponent=8.0)
>>> round(model.lattice_energy() / 1000.0, 1)  # kJ/mol
-753.4
lattice_energy()[source]#

Return the (negative, exothermic-formation-convention) lattice energy.

Return type:

float

Returns:

float – Lattice energy, in J/mol (negative for a stable ionic solid).

class chemistrykit.crystal.FaceCenteredCubicPacking[source]#

Bases: LatticePacking

Face-centered cubic (FCC) packing: 4 lattice points per cell, spheres touching along a face diagonal.

The face diagonal has length \(\sqrt2a\) and spans 4 sphere radii, so \(r=\sqrt2a/4\); 4 atoms per conventional cell (8 corners x 1/8 + 6 faces x 1/2), 12 nearest neighbors – the close-packed maximum for identical spheres (Ashcroft & Mermin, Ch. 4).

Examples

The exact atomic packing factor, \(\pi/(3\sqrt2)\approx0.7405\), is the well-known maximum density for identical spheres on a regular lattice:

>>> bool(round(float(FaceCenteredCubicPacking().packing_fraction()), 6) == round(float(np.pi / (3.0 * np.sqrt(2.0))), 6))
True
atomic_radius(a)[source]#

Return the touching-sphere radius implied by lattice constant a.

Parameters:

a (float)

Returns:

float or ndarray

atoms_per_cell: float = 4.0#

Number of lattice points (spheres) per conventional unit cell.

Type:

float

coordination_number: int = 12#

Number of nearest neighbors each sphere touches.

Type:

int

unit_cell_volume(a)[source]#

Return the (conventional) unit-cell volume for lattice constant a.

Parameters:

a (float) – Cubic (or, for HCP, basal) lattice constant.

Returns:

float or ndarray

class chemistrykit.crystal.HexagonalClosePacking(c_over_a=None)[source]#

Bases: LatticePacking

Hexagonal close-packed (HCP) packing on the primitive hexagonal cell.

The primitive hexagonal cell (basal rhombus of side a, height c) has volume \(V=\frac{\sqrt3}{2}a^2c\) and contains 2 lattice points (the ABAB stacking’s 2-atom basis); each sphere touches 12 nearest neighbors (6 in-plane, 3 in the layer above, 3 below), the same coordination as FCC – both are close-packed arrangements of identical spheres, differing only in stacking sequence (ABAB… vs. ABCABC…), which is why they share the same maximum packing fraction.

Parameters:

c_over_a (float | None) – The stacking-axis-to-basal-plane-edge ratio \(c/a\). Defaults to the ideal close-packing value \(\sqrt{8/3}\approx1.633\) (Ashcroft & Mermin, Ch. 4), derived purely from geometry (equal touching spheres stacked ABAB); real HCP metals deviate from this (e.g. Mg: 1.624, Zn: 1.856 – Zn’s anomalously large ratio means its actual packing fraction is below the ideal value below, an approximation this model does not otherwise account for), so passing the real c_over_a gives a more accurate, but no longer exactly \(\pi/(3\sqrt2)\), packing fraction.

Examples

At the ideal \(c/a\), the packing fraction exactly matches FCC’s \(\pi/(3\sqrt2)\approx0.7405\) – verified numerically here rather than assumed, since it is not obvious by inspection that two differently-stacked lattices share an atomic packing factor:

>>> from chemistrykit.crystal.systems.packing import FaceCenteredCubicPacking
>>> hcp_pf = HexagonalClosePacking().packing_fraction()
>>> fcc_pf = FaceCenteredCubicPacking().packing_fraction()
>>> round(float(hcp_pf), 9) == round(float(fcc_pf), 9)
True

A non-ideal c_over_a (e.g. zinc’s 1.856) gives a lower packing fraction, since the spheres no longer touch snugly in the stacking direction:

>>> HexagonalClosePacking(c_over_a=1.856).packing_fraction() < HexagonalClosePacking().packing_fraction()
True
atomic_radius(a)[source]#

Return the touching-sphere radius implied by lattice constant a.

Parameters:

a (float)

Returns:

float or ndarray

atoms_per_cell: float = 2.0#

Number of lattice points (spheres) per conventional unit cell.

Type:

float

coordination_number: int = 12#

Number of nearest neighbors each sphere touches.

Type:

int

unit_cell_volume(a)[source]#

Return the (conventional) unit-cell volume for lattice constant a.

Parameters:

a (float) – Cubic (or, for HCP, basal) lattice constant.

Returns:

float or ndarray

class chemistrykit.crystal.KapustinskiiLatticeEnergy(n_ions, cation_charge, anion_charge, r_cation_pm, r_anion_pm)[source]#

Bases: LatticeEnergyModel

The Kapustinskii equation: an ionic-radii-only estimate of lattice energy, no Madelung constant required.

\[U = -\frac{\kappa\,\nu\,|z_+z_-|}{r_++r_-}\left(1-\frac{d}{r_++r_-}\right)\]

with \(\kappa=1.2025\times10^5\,\text{kJ pm mol}^{-1}\) and \(d=34.5\,\text{pm}\) empirical constants fitted so this reproduces experimental/Born-Lande lattice energies without needing a structure-specific Madelung constant – useful for salts (or hypothetical salts) whose crystal structure is unknown (A. F. Kapustinskii, Q. Rev. Chem. Soc. 10, 283 (1956); Atkins & de Paula, Physical Chemistry, 11th ed., Ch. 20A.2). This is itself an approximation, typically agreeing with more detailed calculations to within about 5%.

Parameters:
  • n_ions (int) – Number of ions per formula unit (e.g. 2 for NaCl, 3 for CaF2/Na2O).

  • cation_charge (int) – Cation charge number; sign ignored.

  • anion_charge (int) – Anion charge number; sign ignored.

  • r_cation_pm (float) – Cation radius, in picometers (e.g. a Shannon effective ionic radius, chemistrykit.crystal.utils.reference_data.SHANNON_IONIC_RADII_PM).

  • r_anion_pm (float) – Anion radius, in picometers.

Examples

NaCl again, this time from ionic radii alone (no Madelung constant, no crystal structure assumed) – close to, but not identical to, the Born-Lande estimate above, as expected of two different approximations to the same quantity:

>>> model = KapustinskiiLatticeEnergy(n_ions=2, cation_charge=1, anion_charge=1, r_cation_pm=102.0, r_anion_pm=181.0)
>>> round(model.lattice_energy() / 1000.0, 1)  # kJ/mol
-746.2
lattice_energy()[source]#

Return the (negative, exothermic-formation-convention) lattice energy.

Return type:

float

Returns:

float – Lattice energy, in J/mol (negative for a stable ionic solid).

class chemistrykit.crystal.LatticeEnergyModel[source]#

Bases: ABC

Common interface for a model estimating an ionic crystal’s lattice energy.

Concrete subclasses (chemistrykit.crystal.systems.lattice_energy.BornLandeLatticeEnergy, KapustinskiiLatticeEnergy) implement lattice_energy().

abstractmethod lattice_energy()[source]#

Return the (negative, exothermic-formation-convention) lattice energy.

Return type:

float

Returns:

float – Lattice energy, in J/mol (negative for a stable ionic solid).

class chemistrykit.crystal.LatticePacking[source]#

Bases: ABC

Common interface for a hard-sphere packing model of a simple Bravais lattice.

Concrete subclasses (chemistrykit.crystal.systems.packing.SimpleCubicPacking, BodyCenteredCubicPacking, FaceCenteredCubicPacking, HexagonalClosePacking) set the class attributes coordination_number and atoms_per_cell, and implement unit_cell_volume() and atomic_radius(); packing_fraction() is then available for every subclass for free, exactly as chemistrykit.surface.core.base_system.AdsorptionIsotherm.fractional_coverage() is built once atop each concrete isotherm’s loading.

abstractmethod atomic_radius(a)[source]#

Return the touching-sphere radius implied by lattice constant a.

Parameters:

a (float)

Returns:

float or ndarray

atoms_per_cell: float#

Number of lattice points (spheres) per conventional unit cell.

Type:

float

coordination_number: int#

Number of nearest neighbors each sphere touches.

Type:

int

packing_fraction(a=1.0)[source]#

Return the fraction of the unit cell’s volume occupied by touching spheres.

\[\text{APF} = \frac{Z \cdot \frac{4}{3}\pi r^3}{V_{cell}}\]

with Z = atoms_per_cell and r from atomic_radius() – independent of a for any of these lattices (both the sphere volume and the cell volume scale as \(a^3\)), so the default a=1.0 is only a placeholder unit.

Parameters:

a (float) – Lattice constant (cancels out of the result).

Returns:

float or ndarray

abstractmethod unit_cell_volume(a)[source]#

Return the (conventional) unit-cell volume for lattice constant a.

Parameters:

a (float) – Cubic (or, for HCP, basal) lattice constant.

Return type:

float

Returns:

float or ndarray

class chemistrykit.crystal.RadiusRatioPrediction(radius_ratio, coordination_number, geometry)[source]#

Bases: object

The coordination predicted by the radius-ratio rule for one cation-anion pair.

Parameters:
  • radius_ratio (float)

  • coordination_number (int)

  • geometry (str)

coordination_number: int#

Predicted number of anions around the cation.

Type:

int

geometry: str#

Predicted coordination polyhedron.

Type:

str

radius_ratio: float#

\(r_+/r_-\).

Type:

float

class chemistrykit.crystal.SimpleCubicPacking[source]#

Bases: LatticePacking

Simple cubic (SC) packing: one lattice point per cell, spheres touching along the cell edge.

Touching along the edge gives \(r = a/2\), one sphere per cell, and 6 nearest neighbors (Ashcroft & Mermin, Ch. 4).

Examples

The exact, textbook atomic packing factor is \(\pi/6\approx0.5236\) (the least efficient of the four lattices here):

>>> bool(round(float(SimpleCubicPacking().packing_fraction()), 6) == round(np.pi / 6.0, 6))
True
atomic_radius(a)[source]#

Return the touching-sphere radius implied by lattice constant a.

Parameters:

a (float)

Returns:

float or ndarray

atoms_per_cell: float = 1.0#

Number of lattice points (spheres) per conventional unit cell.

Type:

float

coordination_number: int = 6#

Number of nearest neighbors each sphere touches.

Type:

int

unit_cell_volume(a)[source]#

Return the (conventional) unit-cell volume for lattice constant a.

Parameters:

a (float) – Cubic (or, for HCP, basal) lattice constant.

Returns:

float or ndarray

class chemistrykit.crystal.XRDPeak(hkl, d_spacing, two_theta, relative_intensity)[source]#

Bases: object

A single allowed powder-XRD reflection.

Parameters:
d_spacing: float#

Interplanar spacing, same length unit as the input lattice constant.

Type:

float

hkl: tuple#

Miller indices \((h,k,l)\).

Type:

tuple of int

relative_intensity: float#

\(|F_{hkl}|^2\), not multiplicity- or Lorentz-polarization-corrected.

Type:

float

two_theta: float#

Diffraction angle \(2\theta\), in degrees.

Type:

float

chemistrykit.crystal.bragg_angle(d, wavelength, order=1)[source]#

Solve Bragg’s law \(n\lambda=2d\sin\theta\) for the diffraction angle \(\theta\).

Parameters:
  • d (float) – Interplanar (d-)spacing, in the same length unit as wavelength.

  • wavelength (float) – X-ray wavelength.

  • order (int) – Diffraction order n.

Returns:

float or ndarray – Bragg angle \(\theta\), in radians. nan where \(n\lambda/(2d) > 1\) (no diffraction possible at that spacing/wavelength/order).

Examples

Cu-Kalpha (\(\lambda=154.18\,\text{pm}\)) reflecting off a 3.0 A (300 pm) d-spacing:

>>> import numpy as np
>>> theta = bragg_angle(d=300.0, wavelength=154.18)
>>> round(float(np.degrees(theta)), 3)
14.89
>>> bool(round(float(2.0 * 300.0 * np.sin(theta)), 4) == round(154.18, 4))
True
chemistrykit.crystal.classify_crystal_system(a, b, c, alpha, beta, gamma, tol=1e-06)[source]#

Classify a unit cell into one of the 7 crystal systems from its lattice parameters.

Parameters:
  • a (float) – Unit-cell edge lengths (any consistent length unit).

  • b (float) – Unit-cell edge lengths (any consistent length unit).

  • c (float) – Unit-cell edge lengths (any consistent length unit).

  • alpha (float) – Interaxial angles, in degrees (\(\alpha\) between b and c, \(\beta\) between a and c, \(\gamma\) between a and b, the standard crystallographic convention).

  • beta (float) – Interaxial angles, in degrees (\(\alpha\) between b and c, \(\beta\) between a and c, \(\gamma\) between a and b, the standard crystallographic convention).

  • gamma (float) – Interaxial angles, in degrees (\(\alpha\) between b and c, \(\beta\) between a and c, \(\gamma\) between a and b, the standard crystallographic convention).

  • tol (float) – Absolute tolerance for treating two lengths, or an angle and a reference angle, as equal.

Return type:

str

Returns:

str – One of "cubic", "tetragonal", "orthorhombic", "hexagonal", "trigonal", "monoclinic", "triclinic".

Examples

A cell with all edges and angles equal to a right angle is cubic:

>>> classify_crystal_system(5.0, 5.0, 5.0, 90.0, 90.0, 90.0)
'cubic'

Hexagonal graphite-like cell (\(a=b\neq c\), \(\gamma=120°\)):

>>> classify_crystal_system(2.46, 2.46, 6.71, 90.0, 90.0, 120.0)
'hexagonal'

A cell with no special equalities at all is triclinic:

>>> classify_crystal_system(5.0, 6.0, 7.0, 80.0, 85.0, 95.0)
'triclinic'
chemistrykit.crystal.cubic_lattice_points(centering, a=1.0, n_cells=1)[source]#

Cartesian lattice points of a cubic Bravais lattice inside an n_cells-cube block of conventional cells.

Generates every point \(a(\mathbf{n}+\mathbf{f})\) with integer cell index \(\mathbf{n}\) and centering offset \(\mathbf{f}\) (see BRAVAIS_LATTICES) that lies in the closed cube \([0, n_{cells}a]^3\) – so corner and face points shared with neighboring cells are included, as in a textbook unit-cell drawing.

Parameters:
  • centering (str) – Primitive (simple), body-centered, or face-centered cubic.

  • a (float) – Conventional cubic lattice constant.

  • n_cells (int) – Number of conventional cells along each axis.

Return type:

ndarray

Returns:

ndarray, shape (n_points, 3)

Examples

One conventional cell drawn with all shared corner/face points: 8 corners (P), plus a body center (I), or plus 6 face centers (F):

>>> [len(cubic_lattice_points(c)) for c in ("P", "I", "F")]
[8, 9, 14]
chemistrykit.crystal.d_spacing_cubic(a, h, k, l)[source]#

Interplanar spacing for the \((hkl)\) plane of a cubic lattice, \(d=a/\sqrt{h^2+k^2+l^2}\).

Parameters:
  • a (float) – Cubic lattice constant.

  • h (int) – Miller indices (not all zero).

  • k (int) – Miller indices (not all zero).

  • l (int) – Miller indices (not all zero).

Returns:

float

Examples

>>> round(float(d_spacing_cubic(a=4.0, h=1, k=1, l=1)), 6)
2.309401
chemistrykit.crystal.frenkel_defect_concentration(N, N_interstitial, delta_h, T)[source]#

Equilibrium number of Frenkel defects (a cation displaced to an interstitial site).

\[n_F = \sqrt{NN_i}\exp\!\left(-\frac{\Delta H_F}{2k_BT}\right)\]

valid for \(n_F\ll N,N_i\) (West, Solid State Chemistry and its Applications, 2nd ed., Ch. 1.4, eq. 1.14); reduces to the Schottky form with \(N_i\to N\) up to the geometric- vs. arithmetic-mean site count.

Parameters:
  • N (float) – Number of normal cation lattice sites.

  • N_interstitial (float) – Number of available interstitial sites.

  • delta_h (float) – Enthalpy of formation of one Frenkel defect (one vacancy + one interstitial), in J.

  • T (float or array-like of float) – Absolute temperature(s), in K.

Returns:

float or ndarray

Examples

>>> import numpy as np
>>> T = np.array([300.0, 600.0, 900.0])
>>> n = frenkel_defect_concentration(N=1e20, N_interstitial=1e20, delta_h=2.5e-19, T=T)
>>> bool(np.all(np.diff(n) > 0))
True

With equal site counts N=N_i, this reduces to the Schottky formula’s functional form (same exponential factor, prefactor N instead of sqrt(N*N)=N – they coincide):

>>> from chemistrykit.crystal.systems.defects import schottky_defect_concentration
>>> n_frenkel = frenkel_defect_concentration(N=1e20, N_interstitial=1e20, delta_h=2.5e-19, T=500.0)
>>> n_schottky = schottky_defect_concentration(N=1e20, delta_h=2.5e-19, T=500.0)
>>> bool(abs(n_frenkel - n_schottky) / n_schottky < 1e-12)
True
chemistrykit.crystal.goldschmidt_tolerance_factor(r_a, r_b, r_x)[source]#

Goldschmidt’s tolerance factor for an \(ABX_3\) perovskite.

\[t = \frac{r_A + r_X}{\sqrt{2}\,(r_B + r_X)}\]

In the ideal cubic perovskite the A-X distance is the half face diagonal, \(a/\sqrt2\), and the B-X distance is the half edge, \(a/2\), so touching hard spheres give exactly \(t=1\) (V. M. Goldschmidt, Naturwissenschaften 14 (1926), 477-485). Roughly, \(0.9\lesssim t\lesssim1\) gives a cubic perovskite, \(t\) somewhat below 0.9 a tilted, lower-symmetry perovskite, and \(t>1\) a hexagonal or tetragonal (ferroelectric) distortion.

Parameters:
  • r_a (float or array-like of float) – Ionic radii of the A cation (12-coordinate), B cation (6-coordinate), and X anion, in the same unit.

  • r_b (float or array-like of float) – Ionic radii of the A cation (12-coordinate), B cation (6-coordinate), and X anion, in the same unit.

  • r_x (float or array-like of float) – Ionic radii of the A cation (12-coordinate), B cation (6-coordinate), and X anion, in the same unit.

Returns:

float or ndarray

Examples

SrTiO3, the archetypal cubic perovskite (Shannon radii Sr2+ XII 144 pm, Ti4+ VI 60.5 pm, O2- 140 pm), is almost exactly ideal:

>>> round(goldschmidt_tolerance_factor(144.0, 60.5, 140.0), 3)
1.002

Radii that satisfy \(r_A+r_X=\sqrt2(r_B+r_X)\) give \(t=1\) exactly:

>>> import numpy as np
>>> r_b, r_x = 60.0, 140.0
>>> round(goldschmidt_tolerance_factor(np.sqrt(2.0) * (r_b + r_x) - r_x, r_b, r_x), 12)
1.0
chemistrykit.crystal.interplanar_angle_cubic(hkl_1, hkl_2)[source]#

Angle between two crystal faces (planes) \((h_1k_1l_1)\) and \((h_2k_2l_2)\) of a cubic crystal, in degrees.

\[\cos\phi = \frac{h_1h_2+k_1k_2+l_1l_2} {\sqrt{h_1^2+k_1^2+l_1^2}\sqrt{h_2^2+k_2^2+l_2^2}}\]

the angle between the face normals, which in a cubic crystal lie along \([hkl]\) (West, Solid State Chemistry and its Applications, 2nd ed., Ch. 1). It depends only on the indices, not on the lattice constant or the size of the faces – Steno’s constancy of interfacial angles.

Parameters:
  • hkl_1 (tuple of (int, int, int)) – Miller indices of the two planes (neither all zero).

  • hkl_2 (tuple of (int, int, int)) – Miller indices of the two planes (neither all zero).

Return type:

float

Returns:

float – Angle between the plane normals, in degrees, in \([0, 180]\).

Examples

Adjacent cube faces are perpendicular; a cube face and an octahedral face meet at \(\arccos(1/\sqrt3)\approx54.74°\):

>>> interplanar_angle_cubic((1, 0, 0), (0, 1, 0))
90.0
>>> round(interplanar_angle_cubic((1, 0, 0), (1, 1, 1)), 2)
54.74
chemistrykit.crystal.madelung_constant_nacl(n_shells=12)[source]#

Compute the NaCl Madelung constant via Evjen’s converging lattice summation.

Parameters:

n_shells (int) – Half-width (in simple-cubic lattice units) of the summation region passed to chemistrykit.crystal.utils.lattice_sums.evjen_lattice_sum_cubic_alternating(). The default already agrees with the literature value (MADELUNG_CONSTANT_NACL_LITERATURE) to 5 significant figures.

Return type:

float

Returns:

float

Examples

>>> round(madelung_constant_nacl(), 4)
1.7476
>>> round(madelung_constant_nacl(20), 6)
1.747565
chemistrykit.crystal.miller_indices_from_intercepts(a_intercept, b_intercept, c_intercept)[source]#

Miller indices \((hkl)\) of a crystal face from its axial intercepts (Hauy’s law of rational indices).

A face cutting the three crystallographic axes at \(p a\), \(q b\), \(r c\) (intercepts in units of the cell edges) has indices proportional to \((1/p, 1/q, 1/r)\), cleared of fractions and common factors to the smallest integer triple. Hauy’s law states that for every natural face the intercept ratios are rational – small whole-number ratios – which is exactly what makes this integer triple exist (Hauy, Traité de Minéralogie, 1801; notation of W. H. Miller, A Treatise on Crystallography, 1839).

Parameters:
  • a_intercept (int, float, Fraction, or math.inf) – Intercepts in units of a, b, c; math.inf for a face parallel to that axis. Floats are converted with fractions.Fraction.limit_denominator() (denominator <= 1000). Negative intercepts give negative indices.

  • b_intercept (int, float, Fraction, or math.inf) – Intercepts in units of a, b, c; math.inf for a face parallel to that axis. Floats are converted with fractions.Fraction.limit_denominator() (denominator <= 1000). Negative intercepts give negative indices.

  • c_intercept (int, float, Fraction, or math.inf) – Intercepts in units of a, b, c; math.inf for a face parallel to that axis. Floats are converted with fractions.Fraction.limit_denominator() (denominator <= 1000). Negative intercepts give negative indices.

Return type:

tuple

Returns:

tuple of (int, int, int)

Examples

A face cutting the axes at 1, 2, and 3 cell edges is the (632) face:

>>> miller_indices_from_intercepts(1, 2, 3)
(6, 3, 2)

A cube face parallel to b and c:

>>> import math
>>> miller_indices_from_intercepts(1, math.inf, math.inf)
(1, 0, 0)
chemistrykit.crystal.powder_xrd_peaks(lattice_type, a, wavelength, hkl_max=3)[source]#

Enumerate the allowed powder-XRD peaks of a cubic Bravais lattice up to a Miller-index cutoff.

Iterates every \((h,k,l)\) with \(0\le h,k,l\le\) hkl_max (not all zero), computes each reflection’s structure factor via structure_factor() (dropping systematically-absent reflections, \(|F_{hkl}|^2<10^{-9}\)) and Bragg angle via bragg_angle() (dropping reflections with no real solution), and returns the surviving peaks sorted by \(2\theta\).

Parameters:
  • lattice_type (str) – Cubic Bravais lattice type (see _CUBIC_BASES).

  • a (float) – Cubic lattice constant, in the same length unit as wavelength.

  • wavelength (float) – X-ray wavelength.

  • hkl_max (int) – Largest Miller index to search (non-negative indices only, since \(d_{hkl}\) and \(|F_{hkl}|\) depend only on \(h^2+k^2+l^2\) and the mixed-parity pattern, both invariant under sign flips of any index).

Return type:

list

Returns:

list of XRDPeak

Examples

BCC iron’s first powder line is (110), not (100) – (100) is systematically absent since \(1+0+0=1\) is odd:

>>> peaks = powder_xrd_peaks("BCC", a=286.65, wavelength=154.18, hkl_max=2)
>>> peaks[0].hkl
(1, 1, 0)

FCC’s first line is (111) – (100) and (110) are both absent (mixed parity):

>>> peaks = powder_xrd_peaks("FCC", a=408.6, wavelength=154.18, hkl_max=2)
>>> peaks[0].hkl
(1, 1, 1)
chemistrykit.crystal.radius_ratio_coordination(r_cation, r_anion)[source]#

Predict a cation’s coordination number from the radius ratio \(r_+/r_-\) (Pauling’s first rule).

Pauling’s first rule (L. Pauling, J. Am. Chem. Soc. 51 (1929), 1010-1026): a coordination polyhedron of anions forms around each cation, with the coordination number fixed by the radius ratio. The largest polyhedron is chosen whose anions can all touch the cation without overlapping one another, i.e. the highest coordination whose lower limit in RADIUS_RATIO_LIMITS does not exceed \(r_+/r_-\). For example, octahedral coordination needs \(r_+/r_-\ge\sqrt2-1\approx0.414\), since at that ratio four anions around the cation’s equator touch each other.

Parameters:
  • r_cation (float) – Ionic radii, in the same unit.

  • r_anion (float) – Ionic radii, in the same unit.

Return type:

RadiusRatioPrediction

Returns:

RadiusRatioPrediction

Examples

NaCl (Shannon radii 102 and 181 pm) is predicted octahedral, as in rock salt:

>>> p = radius_ratio_coordination(102.0, 181.0)
>>> p.coordination_number, p.geometry
(6, 'octahedral')

CsCl’s large cation gives eightfold (cubic) coordination:

>>> radius_ratio_coordination(174.0, 181.0).coordination_number
8
chemistrykit.crystal.scherrer_crystallite_size(fwhm_deg, two_theta_deg, wavelength, shape_factor=0.9)[source]#

Mean crystallite size from powder-XRD line broadening, via the Scherrer equation.

\[\tau = \frac{K\lambda}{\beta\cos\theta}\]

with \(\beta\) the peak’s full width at half maximum in radians of \(2\theta\) (instrumental broadening already subtracted), \(\theta\) the Bragg angle, and K a dimensionless shape factor (about 0.9 for roughly spherical crystallites) (P. Scherrer, Nachr. Ges. Wiss. Göttingen, 1918, 98-100; A. L. Patterson, Phys. Rev. 56 (1939), 978).

Parameters:
  • fwhm_deg (float or array-like of float) – Full width at half maximum of the peak, in degrees of \(2\theta\).

  • two_theta_deg (float or array-like of float) – Peak position \(2\theta\), in degrees.

  • wavelength (float) – X-ray wavelength; the result is in the same length unit.

  • shape_factor (float) – Scherrer constant K.

Returns:

float or ndarray – Mean crystallite dimension perpendicular to the diffracting planes.

Examples

A Cu-Kalpha peak at \(2\theta=38.2°\) that is 0.5° wide comes from crystallites about 17 nm across:

>>> round(scherrer_crystallite_size(0.5, 38.2, wavelength=0.15418), 1)
16.8

Size is inversely proportional to width – halve the width, double the size:

>>> tau_1 = scherrer_crystallite_size(0.5, 38.2, 0.15418)
>>> tau_2 = scherrer_crystallite_size(0.25, 38.2, 0.15418)
>>> round(tau_2 / tau_1, 12)
2.0
chemistrykit.crystal.schottky_defect_concentration(N, delta_h, T)[source]#

Equilibrium number of Schottky defects (paired cation+anion vacancies).

\[n_S = N\exp\!\left(-\frac{\Delta H_S}{2k_BT}\right)\]

valid for \(n_S\ll N\) (West, Solid State Chemistry and its Applications, 2nd ed., Ch. 1.4, eq. 1.10).

Parameters:
  • N (float) – Number of cation (equivalently, formula-unit) sites in the crystal.

  • delta_h (float) – Enthalpy of formation of one Schottky defect (one cation vacancy + one anion vacancy), in J.

  • T (float or array-like of float) – Absolute temperature(s), in K.

Returns:

float or ndarray – Number of Schottky defects, \(n_S \le N\).

Examples

Defect concentration increases with temperature (more thermal energy to pay the formation enthalpy):

>>> import numpy as np
>>> T = np.array([300.0, 600.0, 900.0])
>>> n = schottky_defect_concentration(N=1e20, delta_h=2.0e-19, T=T)
>>> bool(np.all(np.diff(n) > 0))
True

At very low temperature, essentially no defects form:

>>> round(float(schottky_defect_concentration(N=1e20, delta_h=2.0e-19, T=1.0)), 6)
0.0
chemistrykit.crystal.structure_factor(hkl, basis, scattering_factors=None)[source]#

The kinematic structure factor \(F_{hkl}=\sum_j f_j\exp[2\pi i(hx_j+ky_j+lz_j)]\).

Summed over the atoms in one unit cell at fractional coordinates basis; \(|F_{hkl}|^2\) is proportional to a reflection’s diffracted intensity, and \(F_{hkl}=0\) identically for certain \((hkl)\) combinations depending on the lattice centering – “systematic absences” (West, Solid State Chemistry and its Applications, 2nd ed., Ch. 5.4).

Parameters:
  • hkl (tuple of (int, int, int)) – Miller indices.

  • basis (sequence of (float, float, float)) – Fractional atomic coordinates of every atom in the unit cell.

  • scattering_factors (sequence of float, optional) – Per-atom scattering factor/amplitude f_j, same length as basis. Defaults to 1.0 for every atom (structure of the lattice alone, ignoring atomic form-factor variation).

Return type:

complex

Returns:

complex

Examples

A single atom per cell (simple cubic) never has a systematically absent reflection – \(F_{hkl}=f\) for every (hkl):

>>> abs(structure_factor((1, 1, 0), [(0.0, 0.0, 0.0)]))
1.0

Body-centered cubic: \(F_{hkl}=0\) whenever \(h+k+l\) is odd:

>>> bcc_basis = [(0.0, 0.0, 0.0), (0.5, 0.5, 0.5)]
>>> round(abs(structure_factor((1, 0, 0), bcc_basis)), 9)
0.0
>>> round(abs(structure_factor((1, 1, 0), bcc_basis)), 9)
2.0
chemistrykit.crystal.unit_cell_volume(a, b, c, alpha, beta, gamma)[source]#

General unit-cell volume from the six lattice parameters, valid for any crystal system.

\[V = abc\sqrt{1-\cos^2\alpha-\cos^2\beta-\cos^2\gamma+2\cos\alpha\cos\beta\cos\gamma}\]

(Ashcroft & Mermin, Solid State Physics, 1976, derived from the scalar triple product of the three edge vectors). Reduces to \(V=abc\) for any right-angle cell (cubic/tetragonal/ orthorhombic) and to \(V=\frac{\sqrt3}{2}a^2c\) for hexagonal.

Parameters:
  • a (float) – Unit-cell edge lengths.

  • b (float) – Unit-cell edge lengths.

  • c (float) – Unit-cell edge lengths.

  • alpha (float) – Interaxial angles, in degrees.

  • beta (float) – Interaxial angles, in degrees.

  • gamma (float) – Interaxial angles, in degrees.

Returns:

float or ndarray

Examples

A right-angle (orthorhombic/cubic/tetragonal) cell reduces to \(abc\):

>>> round(float(unit_cell_volume(2.0, 3.0, 4.0, 90.0, 90.0, 90.0)), 6)
24.0

A hexagonal cell (\(a=b\), \(\gamma=120°\)) matches the textbook shortcut \(\frac{\sqrt3}{2}a^2c\):

>>> import numpy as np
>>> a, c = 2.46, 6.71
>>> V_general = unit_cell_volume(a, a, c, 90.0, 90.0, 120.0)
>>> V_hex_shortcut = (np.sqrt(3.0) / 2.0) * a**2 * c
>>> round(float(V_general), 6) == round(float(V_hex_shortcut), 6)
True