Skip to content

Commit

Permalink
Add Wyckoff inference test (#327)
Browse files Browse the repository at this point in the history
  • Loading branch information
kbarros authored Nov 3, 2024
1 parent 3e0815e commit 3ea477d
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 26 deletions.
6 changes: 2 additions & 4 deletions src/Symmetry/Crystal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -397,11 +397,9 @@ function crystal_from_spacegroup(latvecs::Mat3, positions::Vector{Vec3}, types::
wyckoffs = find_wyckoff_for_position.(Ref(sg), positions; symprec)
orbits = crystallographic_orbits_distinct(sg.symops, positions; symprec, wyckoffs)

# Check that inferred orbits match known multiplicities of the Wyckoffs
# Symmetry-propagated orbits must match known multiplicities of the Wyckoffs
foreach(orbits, wyckoffs) do orbit, wyckoff
if wyckoff.multiplicity length(orbit) / abs(det(sg.setting.R))
error("Internal error filling Wyckoff positions! Please report this.")
end
@assert wyckoff.multiplicity length(orbit) / abs(det(sg.setting.R))
end

all_positions = reduce(vcat, orbits)
Expand Down
23 changes: 10 additions & 13 deletions src/Symmetry/LatticeUtils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,27 +61,25 @@ function is_standard_form(latvecs::Mat3)
return latvecs conventional_latvecs
end

"""
CellType
An enumeration over the different types of 3D Bravais unit cells.
"""
# Labels for the 7 lattice systems. Note that these are subtly distinct from the
# 7 crystal systems. In particular, the rhombohedral lattice system (a=b=c,
# α=β=γ) should not be confused with the trigonal crystal system. Trigonal
# spacegroups are characterized by a 3-fold rotational symmetry. All trigonal
# spacegroups (143-167) admit a hexagonal setting. Some of these (146, 148, 155,
# 160, 161, 166, 167) additionally admit a rhombohedral setting.
@enum CellType begin
triclinic
monoclinic
orthorhombic
tetragonal
# Rhombohedral is a special case. It is a lattice type (a=b=c, α=β=γ) but
# not a spacegroup type. Trigonal space groups are conventionally described
# using either hexagonal or rhombohedral lattices.
rhombohedral
hexagonal
cubic
end

# Infer the `CellType` of a unit cell from its lattice vectors, i.e. the columns
# of `latvecs`. Report an error if the unit cell is not in conventional form,
# which would invalidate the table of symops for a given Hall number.
# Infer the CellType (lattice system) from lattice vectors. Report an error if
# the unit cell is not in conventional form, which would invalidate the table of
# symops for a given Hall number.
function cell_type(latvecs::Mat3)
a, b, c, α, β, γ = lattice_params(latvecs)

Expand Down Expand Up @@ -121,8 +119,7 @@ function cell_type(latvecs::Mat3)
return triclinic
end

# Return the standard cell convention for a given Hall number using the
# convention of spglib, listed at
# The lattice system that is expected for a given Hall number. Taken from:
# http://pmsl.planet.sci.kobe-u.ac.jp/~seto/?page_id=37
function cell_type(hall_number::Int)
if 1 <= hall_number <= 2
Expand Down
17 changes: 9 additions & 8 deletions src/Symmetry/WyckoffData.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ struct WyckoffExpr
end

# Extracts (F, c) from expressions of the form (F θ + c), where θ = [x, y, z].
function WyckoffExpr(str)
function WyckoffExpr(str::String)
s = SymOp(strip(str, ['(', ')']))
@assert all(in((-2, -1, 0, 1, 2)), s.R)
return WyckoffExpr(s.R, s.T)
end

Expand Down Expand Up @@ -48,17 +49,17 @@ end
# return nothing if this equation cannot be satisfied. Write F θ = b + Δb,
# involving b = r - c, and arbitrary integers Δb. Given candidate Δb, one can
# use the pseudo-inverse of F to find least squares solution θ, and then check
# whether it satisfies the original linear system of equations. A search over
# integer shifts Δb seems unavoidable.
# whether it satisfies the original linear system of equations.
function position_to_wyckoff_params(r::Vec3, w::WyckoffExpr; symprec=1e-8)
(; F, c) = w
# Wrapping components to [0, 1] simplifies the search in the next step.
b = wrap_to_unit_cell(r - c; symprec)
# Loop over 8 vector shifts Δb ∈ [{-1,0}, {-1,0}, {-1,0}]. In the general
# case, one might need to search over all Δb ∈ ℕ³. This reduced search space
# has been empirically determined through consideration of all Wyckoffs of
# the 230 spacegroups. Similar logic appears in Crystalline.jl:
# https://github.com/thchr/Crystalline.jl/blob/31fa2f61a5db62f4dfd6b0e8fb994d8aacef3d5c/src/wyckoff.jl#L321-L335
# If F were arbitrary, one would need to search over all offsets Δb ∈ ℕ³.
# Fortunately, the actual matrices are constrained: matrix elements Fᵢⱼ are
# in {0, ±1, ±2} for hexagonal crystal systems, and in {0, ±1} for all other
# systems. Empirical tests indicate that just eight shifts are sufficient:
# Δb ∈ [{-1,0}, {-1,0}, {-1,0}]. Removing these shifts will cause failures
# in the "Wyckoff table" unit test.
for Δb in Iterators.product((-1, 0), (-1, 0), (-1, 0))
Δb = Vec3(Δb)
θ = pinv(F) * (b + Δb)
Expand Down
14 changes: 13 additions & 1 deletion test/test_symmetry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,23 @@
end
=#

# Test that the orbit of a Wyckoff matches the multiplicity data.
for sgnum in 1:230
sg = Sunny.Spacegroup(Sunny.standard_setting[sgnum])
for (mult, letter, sitesym, pos) in Sunny.wyckoff_table[sgnum]
orbit = Sunny.crystallographic_orbit(sg.symops, Sunny.WyckoffExpr(pos))
@assert length(orbit) == mult
@test length(orbit) == mult
end
end

# Test that Wyckoffs can be correctly inferred from a position
niters = 20
for sgnum in rand(1:230, niters)
for (_, letter, _, pos) in Sunny.wyckoff_table[sgnum]
w = Sunny.WyckoffExpr(pos)
θ = 10 * randn(3)
r = w.F * θ + w.c
@test letter == Sunny.find_wyckoff_for_position(sgnum, r; symprec=1e-8).letter
end
end
end
Expand Down

0 comments on commit 3ea477d

Please sign in to comment.