"""
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