Source code for smact.lattice_parameters

"""
This module can be used to calculate roughly the lattice parameters of a lattice type.

Based on the radii of the species on each site.
"""

from __future__ import annotations

import numpy as np

# Wurtzite geometry: tetrahedral bond angle ~ 109.47 deg = 2*arccos(-1/3)
_TETRAHEDRAL_HALF_ANGLE_SIN = np.sqrt(2.0 / 3.0)  # sin(109.47 deg / 2) = sqrt(2/3) ≈ 0.8165
_TETRAHEDRAL_COMPLEMENT_SIN = 1.0 / 3.0  # sin(109.47 deg - 90 deg) = 1/3 ≈ 0.3333

# Body-centred tetragonal: empirical a/r ratio (β-Sn structure)
_BCT_A_FACTOR = 3.86

# Litharge (B10, PbO-type) empirical factors
_LITHARGE_SUM_FACTOR = 1.31
_LITHARGE_C_OVER_A = 1.26  # c/a from PbO data (http://www.mindat.org/min-2466.html)


def _check_positive(values: list[float] | float, name: str = "radius", expected_length: int | None = None) -> None:
    """Raise ValueError if any radius value is not positive or wrong length."""
    if isinstance(values, (int, float)):
        values = [values]
    if expected_length is not None and len(values) != expected_length:
        msg = f"Expected {expected_length} {name} values, got {len(values)}"
        raise ValueError(msg)
    if any(v <= 0 for v in values):
        msg = f"All {name} values must be positive, got {values}"
        raise ValueError(msg)


[docs] def cubic_perovskite( shannon_radius: list[float], ) -> tuple[float, float, float, float, float, float]: # Cubic Perovskite """ The lattice parameters of the cubic perovskite structure. Args: ---- shannon_radius (list) : The radii of the A, B, X ions Returns: ------- (tuple): float values of lattice constants and angles (a, b, c, alpha, beta, gamma) """ _check_positive(shannon_radius, expected_length=3) limiting_factors = [ 2 * (shannon_radius[1] + shannon_radius[2]), # B-X contact along cube edge np.sqrt(2) * (shannon_radius[0] + shannon_radius[2]), # A-X contact along face diagonal ] a = max(limiting_factors) b = a c = a alpha = 90 beta = 90 gamma = 90 return a, b, c, alpha, beta, gamma
[docs] def wurtzite( shannon_radius: list[float], ) -> tuple[float, float, float, float, float, float]: """ The lattice parameters of the wurtzite structure. Args: ---- shannon_radius (list) : The radii of the a,b ions Returns: ------- (tuple): float values of lattice constants and angles (a, b, c, alpha, beta, gamma) """ _check_positive(shannon_radius, expected_length=2) shannon_radius = sorted(shannon_radius, reverse=True) # Geometry assumes atom A is larger # "Ideal" wurtzite structure # c/a = 1.633, u = 0.375 # alpha = 90 beta = 90 gamma = 120 # # Scenario A: A atoms are touching # i.e. height is that of two tetrahegons with side length a # = 2 * sqrt(2/3) * a if shannon_radius[0] > _TETRAHEDRAL_HALF_ANGLE_SIN * (shannon_radius[0] + shannon_radius[1]): a = 2 * shannon_radius[0] b = a c = 2 * np.sqrt(2.0 / 3.0) * a else: # Scenario B: regular wurtzite, similar sizes a = 2 * _TETRAHEDRAL_HALF_ANGLE_SIN * (shannon_radius[0] + shannon_radius[1]) b = a c = (shannon_radius[0] + shannon_radius[1]) * (2 + 2 * _TETRAHEDRAL_COMPLEMENT_SIN) return a, b, c, alpha, beta, gamma
# A1#
[docs] def fcc( covalent_radius: float, ) -> tuple[float, float, float, float, float, float]: """ The lattice parameters of the A1. Args: ---- covalent_radius (float) : The covalent radius Returns: ------- (tuple): float values of lattice constants and angles (a, b, c, alpha, beta, gamma) """ _check_positive(covalent_radius) a = 2 * 2**0.5 * covalent_radius b = 2 * 2**0.5 * covalent_radius c = 2 * 2**0.5 * covalent_radius alpha = 90 beta = 90 gamma = 90 return a, b, c, alpha, beta, gamma
# A2#
[docs] def bcc( covalent_radius: float, ) -> tuple[float, float, float, float, float, float]: """ The lattice parameters of the A2. Args: ---- covalent_radius (float) : The covalent radius Returns: ------- (tuple): float values of lattice constants and angles (a, b, c, alpha, beta, gamma) """ _check_positive(covalent_radius) a = 4 * covalent_radius / np.sqrt(3) b = a c = a alpha = 90 beta = 90 gamma = 90 return a, b, c, alpha, beta, gamma
# A3#
[docs] def hcp( covalent_radius: float, ) -> tuple[float, float, float, float, float, float]: """ The lattice parameters of the hcp. Args: ---- covalent_radius (float) : The covalent radius Returns: ------- (tuple): float values of lattice constants and angles (a, b, c, alpha, beta, gamma) """ _check_positive(covalent_radius) a = 2 * covalent_radius b = a c = (4.0 / 3.0) * 6**0.5 * covalent_radius alpha = 90 beta = 90 gamma = 120 return a, b, c, alpha, beta, gamma
# A4#
[docs] def diamond( covalent_radius: float, ) -> tuple[float, float, float, float, float, float]: """ The lattice parameters of the diamond. Args: ---- covalent_radius (float) : The covalent radius Returns: ------- (tuple): float values of lattice constants and angles (a, b, c, alpha, beta, gamma) """ _check_positive(covalent_radius) a = 8 * covalent_radius / np.sqrt(3) b = a c = a alpha = 90 beta = 90 gamma = 90 return a, b, c, alpha, beta, gamma
# A5#
[docs] def bct( covalent_radius: float, ) -> tuple[float, float, float, float, float, float]: """ The lattice parameters of the bct. Args: ---- covalent_radius (float) : The covalent radius Returns: ------- (tuple): float values of lattice constants and angles (a, b, c, alpha, beta, gamma) """ _check_positive(covalent_radius) a = _BCT_A_FACTOR * covalent_radius b = a c = 2 * covalent_radius alpha = 90 beta = 90 gamma = 90 return a, b, c, alpha, beta, gamma
# B1
[docs] def rocksalt( shannon_radius: list[float], ) -> tuple[float, float, float, float, float, float]: """ The lattice parameters of rocksalt. Args: ---- shannon_radius (list) : The radii of the a,b ions Returns: ------- (tuple): float values of lattice constants and angles (a, b, c, alpha, beta, gamma) """ _check_positive(shannon_radius, expected_length=2) # In rocksalt, same-species ions sit at face-diagonal distance = a*sqrt(2)/2, # so a >= 2*sqrt(2)*r for each species. The third factor is the nearest-neighbour # cation-anion distance along the cube edge: a >= 2*(r_A + r_B). limiting_factors = [ 2 * 2**0.5 * shannon_radius[0], 2 * 2**0.5 * shannon_radius[1], 2 * shannon_radius[0] + 2 * shannon_radius[1], ] a = max(limiting_factors) b = a c = a alpha = 90 beta = 90 gamma = 90 return a, b, c, alpha, beta, gamma
# B2
[docs] def b2( shannon_radius: list[float], ) -> tuple[float, float, float, float, float, float]: """ The lattice parameters of b2. Args: ---- shannon_radius (list) : The radii of the a,b ions Returns: ------- (tuple): float values of lattice constants and angles (a, b, c, alpha, beta, gamma) """ _check_positive(shannon_radius, expected_length=2) # In B2 (CsCl), the body diagonal relates both species: a*sqrt(3)/2 = r_A + r_B, # so a >= 2*(r_A + r_B)/sqrt(3). The other two factors are same-species contacts. limiting_factors = [ 2 * (shannon_radius[0] + shannon_radius[1]) / np.sqrt(3), 2 * shannon_radius[1], 2 * shannon_radius[0], ] a = max(limiting_factors) b = a c = a alpha = 90 beta = 90 gamma = 90 return a, b, c, alpha, beta, gamma
# B3
[docs] def zincblende( shannon_radius: list[float], ) -> tuple[float, float, float, float, float, float]: """ The lattice parameters of Zinc Blende. Args: ---- shannon_radius (list) : The radii of the a,b ions Returns: ------- (tuple): float values of lattice constants and angles (a, b, c, alpha, beta, gamma) """ _check_positive(shannon_radius, expected_length=2) # In zinc-blende, same-species ions sit at face-diagonal distance = a*sqrt(2)/2, # and the nearest-neighbour A-B distance along the body diagonal = a*sqrt(3)/4. limiting_factors = [ 2 * (max(shannon_radius) * np.sqrt(2)), 4 * (shannon_radius[0] + shannon_radius[1]) / np.sqrt(3), ] a = max(limiting_factors) b = a c = a alpha = 90 beta = 90 gamma = 90 return a, b, c, alpha, beta, gamma
# Zn-S-Zn angle is ~109.5 degrees (from a tetrahedron). It is exactly 2*invCos(-1/3). # The distance of that Zn-Zn (diagonally to half the face) is (using the cosine rule) is # root[2(r1+r2)^2 - 2(r1+r2)^(2)cos(ZnSZn angle)]. # B10
[docs] def b10( shannon_radius: list[float], ) -> tuple[float, float, float, float, float, float]: # Litharge """ The lattice parameters of Litharge. Args: ---- shannon_radius (list) : The radii of the a,b ions Returns: ------- (tuple): float values of lattice constants and angles (a, b, c, alpha, beta, gamma) """ _check_positive(shannon_radius, expected_length=2) limiting_factors = [ 4 * (max(shannon_radius)) / np.sqrt(2), sum(shannon_radius) * _LITHARGE_SUM_FACTOR, ] a = max(limiting_factors) b = a c = a * _LITHARGE_C_OVER_A alpha = 90 beta = 90 gamma = 90 return a, b, c, alpha, beta, gamma
[docs] def stuffed_wurtzite( shannon_radii: list[float], ) -> tuple[float, float, float, float, float, float]: """ The stuffed wurtzite structure (e.g. LiGaGe) space group P63/mc. Args: ---- shannon_radii (list) : The radii of the a,b,c ions Returns: ------- (tuple): float values of lattice constants and angles (a, b, c, alpha, beta, gamma) """ _check_positive(shannon_radii, expected_length=3) rac = shannon_radii[2] + shannon_radii[1] x = rac * np.sin(np.radians(19.5)) c = 2 * rac + x y = rac * np.sin(np.radians(70.5)) a = y * np.sin(np.radians(120)) / np.sin(np.radians(30)) b = a alpha = 90 beta = 90 gamma = 120 return a, b, c, alpha, beta, gamma