Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Wyckoff inference test #327

Merged
merged 2 commits into from
Nov 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading